Skip to content

Commit

Permalink
[diagnostics] Add location information to lhs_expr.
Browse files Browse the repository at this point in the history
This shows some errors in bash_completion and git-completion.bash.

Misc comments and cleanup.
  • Loading branch information
Andy Chu committed Oct 1, 2018
1 parent 4b1fa54 commit 4e33320
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 14 deletions.
11 changes: 4 additions & 7 deletions core/cmd_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,13 +410,10 @@ def _EvalLhs(self, node, spid, lookup_mode):
return lval

if node.tag == lhs_expr_e.LhsIndexedName: # a[1+2]=x
# TODO: Look up node.name and check if the cell is AssocArray, or if the
# type is AssocArray.
# The index of StrArray needs to be coerced to int, but not the index of
# an AssocArray.
int_coerce = not self.mem.IsAssocArray(node.name, lookup_mode)
#log('int_coerce %s', int_coerce)
#log('**************** node.index %s', node.index)
index = self.arith_ev.Eval(node.index, int_coerce=int_coerce)
#log('**************** done eval')

lval = runtime.LhsIndexedName(node.name, index)
lval.spids.append(node.spids[0]) # copy left-most token over
Expand Down Expand Up @@ -773,8 +770,8 @@ def _Dispatch(self, node, fork_external):
if pair.op == assign_op_e.PlusEqual:
assert pair.rhs, pair.rhs # I don't think a+= is valid?
val = self.word_ev.EvalRhsWord(pair.rhs)
old_val, lval = expr_eval.EvalLhs(pair.lhs, self.arith_ev, self.mem,
self.exec_opts)
old_val, lval = expr_eval.EvalLhsAndLookup(pair.lhs, self.arith_ev,
self.mem, self.exec_opts)
sig = (old_val.tag, val.tag)
if sig == (value_e.Undef, value_e.Str):
pass # val is RHS
Expand Down
1 change: 1 addition & 0 deletions core/comp_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ def Complete(argv, ex, comp_lookup):

COMPGEN_SPEC = args.FlagsAndOptions() # for -o and -A

# TODO: Add -l for COMP_LINE. -p for COMP_POINT ?
_DefineFlags(COMPGEN_SPEC)
_DefineOptions(COMPGEN_SPEC)
_DefineActions(COMPGEN_SPEC)
Expand Down
17 changes: 12 additions & 5 deletions core/expr_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def _LookupVar(name, mem, exec_opts):
return val


def EvalLhs(node, arith_ev, mem, exec_opts):
def EvalLhsAndLookup(node, arith_ev, mem, exec_opts):
"""Evaluate the operand for i++, a[0]++, i+=2, a[0]+=2 as an R-value.
Also used by the Executor for s+='x' and a[42]+='x'.
Expand Down Expand Up @@ -234,11 +234,13 @@ def _ValToArith(self, val, span_id, int_coerce=True):
if val.tag == value_e.Undef: # 'nounset' already handled before got here
# Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
#log('blame_word %s arena %s', blame_word, self.arena)
e_die('Coercing undefined value to 0 in arithmetic context', span_id=span_id)
e_die('Coercing undefined value to 0 in arithmetic context',
span_id=span_id)
return 0

if val.tag == value_e.Str:
return _StringToInteger(val.s, span_id=span_id) # may raise FatalRuntimeError
# may raise FatalRuntimeError
return _StringToInteger(val.s, span_id=span_id)

if val.tag == value_e.StrArray: # array is valid on RHS, but not on left
return val.strs
Expand All @@ -264,6 +266,7 @@ def _ValToArithOrError(self, val, int_coerce=True, blame_word=None,
span_id=const.NO_INTEGER):
if span_id == const.NO_INTEGER and blame_word:
span_id = word.LeftMostSpanForWord(blame_word)
#log('_ValToArithOrError span=%s blame=%s', span_id, blame_word)

try:
i = self._ValToArith(val, span_id, int_coerce=int_coerce)
Expand All @@ -287,17 +290,20 @@ def _LookupVar(self, name):

def _EvalLhsToArith(self, node):
"""
Args:
node: lhs_expr
Returns:
int or list of strings, runtime.lvalue
"""
val, lval = EvalLhs(node, self, self.mem, self.exec_opts)
val, lval = EvalLhsAndLookup(node, self, self.mem, self.exec_opts)

if val.tag == value_e.StrArray:
e_die("Can't use assignment like ++ or += on arrays")

# TODO: attribute a span ID here. There are a few cases, like UnaryAssign
# and BinaryAssign.
i = self._ValToArithOrError(val, span_id=const.NO_INTEGER)
span_id = word.SpanForLhsExpr(node)
i = self._ValToArithOrError(val, span_id=span_id)
return i, lval

def _Store(self, lval, new_int):
Expand Down Expand Up @@ -362,6 +368,7 @@ def Eval(self, node, int_coerce=True):
if op_id == Id.Arith_Equal:
# NOTE: We don't need old_int for this case. Evaluating it has no side
# effects, so it's harmless.
# TODO: Use a function like core/cmd_exec.EvalLhs
new_int = rhs
elif op_id == Id.Arith_PlusEqual:
new_int = old_int + rhs
Expand Down
6 changes: 5 additions & 1 deletion core/tdop.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ def ToLValue(node):
"""
# foo = bar, foo[1] = bar
if node.tag == arith_expr_e.ArithVarRef:
return ast.LhsName(node.token.val)
# For consistency with osh/cmd_parse.py, append a span_id.
# TODO: (( a[ x ] = 1 )) and a[x]=1 should use different LST nodes.
lhs_expr = ast.LhsName(node.token.val)
lhs_expr.spids.append(node.token.span_id)
return lhs_expr
if node.tag == arith_expr_e.ArithBinary:
# For example, a[0][0] = 1 is NOT valid.
if (node.op_id == Id.Arith_LBracket and
Expand Down
12 changes: 12 additions & 0 deletions core/word.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
word_e = ast.word_e
word_part_e = ast.word_part_e
assign_op_e = ast.assign_op_e
lhs_expr_e = ast.lhs_expr_e


def _LiteralPartId(p):
Expand Down Expand Up @@ -523,6 +524,17 @@ def CommandKind(w):


# Stubs for converting RHS of assignment to expression mode.
# For osh2oil.py
def IsVarSub(w):
# Return whether it's any var sub, or a double quoted one
return False


def SpanForLhsExpr(node):
if node.spids:
return node.spids[0]
else:
return const.NO_INTEGER
# TODO: LhsIndexedName needs span_id.
#if node.tag == lhs_expr_e.LhsName:
#elif node.tag == lhs_expr_e.LhsIndexedName:
5 changes: 4 additions & 1 deletion osh/cmd_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def _MakeAssignPair(parse_ctx, preparsed):
op = assign_op_e.Equal

lhs_expr = ast.LhsName(var_name)
lhs_expr.spids.append(left_token.span_id)

elif left_token.id == Id.Lit_ArrayLhsOpen: # a[x++]=1
var_name = left_token.val[:-1]
Expand Down Expand Up @@ -249,7 +250,9 @@ def _MakeAssignment(parse_ctx, assign_kw, suffix_words):
if not match.IsValidVarName(static_val):
p_die('Invalid variable name %r', static_val, word=w)

pair = ast.assign_pair(ast.LhsName(static_val), assign_op_e.Equal, None)
lhs_expr = ast.LhsName(static_val)
lhs_expr.spids.append(word.LeftMostSpanForWord(w))
pair = ast.assign_pair(lhs_expr, assign_op_e.Equal, None)

left_spid = word.LeftMostSpanForWord(w)
pair.spids.append(left_spid)
Expand Down

0 comments on commit 4e33320

Please sign in to comment.