Skip to content

Commit

Permalink
[word_eval] Fix bugs in ${var@a} implementation.
Browse files Browse the repository at this point in the history
Addresses issue #690.

Also fix translation of bin/osh_eval.
  • Loading branch information
Andy Chu committed Apr 7, 2020
1 parent 16bbec6 commit 14548dc
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 7 deletions.
3 changes: 2 additions & 1 deletion mycpp/cppgen_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ def visit_func_def(self, o: 'mypy.nodes.FuncDef') -> T:
class_name == 'BoolEvaluator' and func_name in ('_EvalCompoundWord', '_StringToIntegerOrError') or
class_name == 'CommandEvaluator' and func_name == '_Execute' or
class_name is None and func_name == '_PackFlags' or
class_name == 'Mem' and func_name in ('GetVar', 'SetVar') or
class_name == 'Mem' and func_name in ('GetVar', 'SetVar', 'GetCell') or
class_name == 'SearchPath' and func_name == 'Lookup' or
# osh/sh_expr_eval.py
class_name is None and func_name == 'EvalLhsAndLookup' or
Expand Down Expand Up @@ -2022,6 +2022,7 @@ def visit_import_from(self, o: 'mypy.nodes.ImportFrom') -> T:
'part_value_e', 'part_value',
'cmd_value_e', 'cmd_value',
'redirect_arg_e', 'redirect_arg',
'a_index_e', 'a_index',
):
is_namespace = True

Expand Down
30 changes: 24 additions & 6 deletions osh/word_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,6 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
if (part.prefix_op is not None and
part.suffix_op is not None and
part.suffix_op.tag_() == suffix_op_e.Nullary):
var_name = None

names = self.mem.VarNamesStartingWith(part.token.val)
names.sort()
Expand Down Expand Up @@ -972,7 +971,13 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
else: # no bracket op
# When the array is "$@", var_name is None
if var_name and val.tag_() in (value_e.MaybeStrArray, value_e.AssocArray):
if var_name in _STRING_AND_ARRAY:
suffix_op2 = part.suffix_op
if (suffix_op2 and suffix_op2.tag_() == suffix_op_e.Nullary and
cast(Token, suffix_op2).id == Id.VOp0_a):
# ${array@a} is a string
# TODO: An IR for ${} might simplify these lengthy conditions
pass
elif var_name in _STRING_AND_ARRAY:
bash_array_compat = True
else:
e_die("Array %r can't be referred to as a scalar (without @ or *)",
Expand Down Expand Up @@ -1001,20 +1006,33 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):
# readline gets rid of these, so we should too.
p = prompt.replace('\x01', '').replace('\x02', '')
val = value.Str(p)

elif op_id == Id.VOp0_Q:
assert val.tag_() == value_e.Str, val
val = cast(value__Str, val)
val = value.Str(string_ops.ShellQuote(val.s))

elif op_id == Id.VOp0_a:
# We're ONLY simluating -a and -A, not -r -x -n for now. See
# spec/ble-idioms.test.sh.
s = ''
chars = [] # type: List[str]
with tagswitch(val) as case:
if case(value_e.MaybeStrArray):
s += 'a'
chars.append('a')
elif case(value_e.AssocArray):
s += 'A'
val = value.Str(s)
chars.append('A')

if var_name is not None: # e.g. ${?@a} is allowed
cell = self.mem.GetCell(var_name)
if cell.readonly:
chars.append('r')
if cell.exported:
chars.append('x')
if cell.nameref:
chars.append('n')

val = value.Str(''.join(chars))

else:
e_die('Var op %r not implemented', op.val, token=op)

Expand Down
23 changes: 23 additions & 0 deletions spec/var-op-bash.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,26 @@ echo ${!hello@}
hello1 hello2 hello3
hello hello1 hello2 hello3
## END

#### ${var@a} for attributes
array=(one two)
echo ${array@a}
declare -r array=(one two)
echo ${array@a}
declare -rx PYTHONPATH=hi
echo ${PYTHONPATH@a}

# bash and osh differ here
#declare -rxn x=z
#echo ${x@a}
## STDOUT:
a
ar
rx
## END

#### ${var@a} error conditions
echo [${?@a}]
## STDOUT:
[]
## END

0 comments on commit 14548dc

Please sign in to comment.