Skip to content

Commit

Permalink
[osh] Implement $(( x[0] )) for value.Undef and value.Str
Browse files Browse the repository at this point in the history
shopt -s strict_arith gives errors for these cases, but we handle them
by default.

Reported by Koiche Murase on Zulip:

    https://oilshell.zulipchat.com/#narrow/stream/121540-oil-discuss/topic/osh.20-c.20'echo.20.24.28.28arr.5B0.5D.29.29'.20fails
  • Loading branch information
Andy C committed Jun 29, 2024
1 parent b15c7f3 commit 435acd1
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
27 changes: 25 additions & 2 deletions osh/sh_expr_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -709,11 +709,34 @@ def Eval(self, node):
key = self.EvalWordToString(node.right)
s = left.d.get(key)

elif case(value_e.Str):
left = cast(value.Str, UP_left)
if self.exec_opts.strict_arith():
e_die(
"Value of type Str can't be indexed (strict_arith)",
node.op)
index = self.EvalToBigInt(node.right)
# s[0] evaluates to s
# s[1] evaluates to Undef
s = left.s if mops.Equal(index,
mops.ZERO) else None

elif case(value_e.Undef):
if self.exec_opts.strict_arith():
e_die(
"Value of type Undef can't be indexed (strict_arith)",
node.op)
s = None # value.Undef

# There isn't a way to distinguish Undef vs. empty
# string, even with set -o nounset?
# s = ''

else:
# TODO: Add error context
e_die(
'Expected array or assoc in index expression, got %s'
% ui.ValType(left))
"Value of type %s can't be indexed" %
ui.ValType(left), node.op)

if s is None:
val = value.Undef
Expand Down
114 changes: 114 additions & 0 deletions spec/arith.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -759,3 +759,117 @@ echo $((-10 % -3))
1
-1
## END

#### undef[0]
case $SH in dash) exit ;; esac

echo ARITH $(( undef[0] ))
echo status=$?
echo

(( undef[0] ))
echo status=$?
echo

echo UNDEF ${undef[0]}
echo status=$?

## STDOUT:
ARITH 0
status=0

status=1

UNDEF
status=0
## END
## N-I dash STDOUT:
## END

#### undef[0] with nounset
case $SH in dash) exit ;; esac

set -o nounset
echo UNSET $(( undef[0] ))
echo status=$?

## status: 1
## STDOUT:
## END

## N-I dash status: 0

## BUG mksh/zsh status: 0
## BUG mksh/zsh STDOUT:
UNSET 0
status=0
## END

## N-I dash STDOUT:
## END

#### s[0] with string abc
case $SH in dash) exit ;; esac

s='abc'
echo abc $(( s[0] )) $(( s[1] ))
echo status=$?
echo

(( s[0] ))
echo status=$?
echo

## STDOUT:
abc 0 0
status=0

status=1

## END
## N-I dash STDOUT:
## END

#### s[0] with string 42
case $SH in dash) exit ;; esac

s='42'
echo 42 $(( s[0] )) $(( s[1] ))
echo status=$?

## STDOUT:
42 42 0
status=0
## END
## N-I dash STDOUT:
## END

## BUG zsh STDOUT:
42 0 4
status=0
## END

#### s[0] with string '12 34'

s='12 34'
echo '12 34' $(( s[0] )) $(( s[1] ))
echo status=$?

## status: 1
## STDOUT:
## END

## OK dash status: 2

## BUG zsh status: 0
## BUG zsh STDOUT:
12 34 0 1
status=0
## END

# bash prints an error, but doesn't fail

## BUG bash status: 0
## BUG bash STDOUT:
status=1
## END
6 changes: 6 additions & 0 deletions test/runtime-errors.sh
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,12 @@ test-unset_expr() {
_osh-error-2 'unset -v 1+2'
}

test-strict-arith() {
_osh-error-1 'shopt -s strict_arith; echo $(( undef[0] ))'
_osh-error-1 'shopt -s strict_arith; s=abc; echo $(( s[0] ))'
_osh-error-1 'shopt -s strict_arith; var i = 42; echo $(( i[0] ))'
}

# Only dash flags this as an error.
unquoted-string_to_int_arith() {
local x='ZZZ'
Expand Down

0 comments on commit 435acd1

Please sign in to comment.