Skip to content

Commit

Permalink
[refactor, osh-language] Consolidate evaluation of arrays.
Browse files Browse the repository at this point in the history
- LHS and RHS.
- Arith and Word/Command

See comment with the 6 cases.

Addresses issue #207.
  • Loading branch information
Andy Chu committed Jul 13, 2019
1 parent 9dea54a commit a863699
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 62 deletions.
32 changes: 3 additions & 29 deletions osh/cmd_exec.py
Expand Up @@ -21,7 +21,7 @@

from _devbuild.gen.id_kind_asdl import Id
from _devbuild.gen.syntax_asdl import (
command_e, redir_e, lhs_expr_e, lhs_expr_t, assign_op_e, source
command_e, redir_e, assign_op_e, source
)
from _devbuild.gen.syntax_asdl import word as osh_word # TODO: Rename
from _devbuild.gen.runtime_asdl import (
Expand Down Expand Up @@ -405,33 +405,6 @@ def _CheckStatus(self, status, node):
'Exiting with status %d (%sPID %d)', status, reason, posix.getpid(),
span_id=span_id, status=status)

def _EvalLhs(self, node, spid, lookup_mode):
"""lhs_expr -> lvalue.
Used for a=b and a[x]=b
TODO: Rationalize with expr_eval EvalLhsAndLookup, which is used for a+=b
and a[x]+=b.
"""
assert isinstance(node, lhs_expr_t), node

if node.tag == lhs_expr_e.LhsName: # a=x
lval = lvalue.LhsName(node.name)
lval.spids.append(spid)
return lval

if node.tag == lhs_expr_e.LhsIndexedName: # a[1+2]=x
# 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)
index = self.arith_ev.Eval(node.index, int_coerce=int_coerce)

lval = lvalue.LhsIndexedName(node.name, index)
lval.spids.append(node.spids[0]) # copy left-most token over
return lval

raise AssertionError(node.tag)

def _EvalRedirect(self, n):
fd = REDIR_DEFAULT_FD[n.op.id] if n.fd == const.NO_INTEGER else n.fd
if n.tag == redir_e.Redir:
Expand Down Expand Up @@ -840,7 +813,8 @@ def _Dispatch(self, node, fork_external):

else: # plain assignment
spid = pair.spids[0] # Source location for tracing
lval = self._EvalLhs(pair.lhs, spid, lookup_mode)
lval = expr_eval.EvalLhs(pair.lhs, self.arith_ev, self.mem, spid,
lookup_mode)

# RHS can be a string or array.
if pair.rhs:
Expand Down
107 changes: 77 additions & 30 deletions osh/expr_eval.py
Expand Up @@ -98,36 +98,21 @@ def _StringToInteger(s, span_id=const.NO_INTEGER):
return integer


class _ExprEvaluator(object):
"""Shared between arith and bool evaluators.
They both:
1. Convert strings to integers, respecting shopt -s strict_arith.
2. Look up variables and evaluate words.
"""

def __init__(self, mem, exec_opts, word_ev, errfmt):
self.mem = mem
self.exec_opts = exec_opts
self.word_ev = word_ev # type = word_eval.WordEvaluator
self.errfmt = errfmt

def _StringToIntegerOrError(self, s, blame_word=None,
span_id=const.NO_INTEGER):
"""Used by both [[ $x -gt 3 ]] and (( $x ))."""
if span_id == const.NO_INTEGER and blame_word:
span_id = word.LeftMostSpanForWord(blame_word)

try:
i = _StringToInteger(s, span_id=span_id)
except util.FatalRuntimeError as e:
if self.exec_opts.strict_arith:
raise
else:
self.errfmt.PrettyPrintError(e, prefix='warning: ')
i = 0
return i
#
# Common logic for Arith and Command/Word variants of the same expression
#
# Calls EvalLhs()
# a[$key]=$val # osh/cmd_exec.py:814 (command_e.Assignment)
# (( a[key] = val )) # osh/expr_eval.py:326 (_EvalLhsArith)
#
# Calls EvalLhsAndLookup():
# a[$key]+=$val # osh/cmd_exec.py:795 (assign_op_e.PlusEqual)
# (( a[key] += val )) # osh/expr_eval.py:308 (_EvalLhsAndLookupArith)
#
# Uses Python's [] operator
# val=${a[$key]} # osh/word_eval.py:633 (bracket_op_e.ArrayIndex)
# (( val = a[key] )) # osh/expr_eval.py:490 (Id.Arith_LBracket)
#


def _LookupVar(name, mem, exec_opts):
Expand All @@ -139,6 +124,34 @@ def _LookupVar(name, mem, exec_opts):
return val


def EvalLhs(node, arith_ev, mem, spid, lookup_mode):
"""lhs_expr -> lvalue.
Used for a=b and a[x]=b
TODO: Rationalize with expr_eval EvalLhsAndLookup, which is used for a+=b
and a[x]+=b.
"""
assert isinstance(node, lhs_expr_t), node

if node.tag == lhs_expr_e.LhsName: # a=x
lval = lvalue.LhsName(node.name)
lval.spids.append(spid)
return lval

if node.tag == lhs_expr_e.LhsIndexedName: # a[1+2]=x
# The index of StrArray needs to be coerced to int, but not the index of
# an AssocArray.
int_coerce = not mem.IsAssocArray(node.name, lookup_mode)
index = arith_ev.Eval(node.index, int_coerce=int_coerce)

lval = lvalue.LhsIndexedName(node.name, index)
lval.spids.append(node.spids[0]) # copy left-most token over
return lval

raise AssertionError(node.tag)


def EvalLhsAndLookup(node, arith_ev, mem, exec_opts):
"""Evaluate the operand for i++, a[0]++, i+=2, a[0]+=2 as an R-value.
Expand Down Expand Up @@ -208,6 +221,40 @@ def EvalLhsAndLookup(node, arith_ev, mem, exec_opts):
return val, lval




class _ExprEvaluator(object):
"""Shared between arith and bool evaluators.
They both:
1. Convert strings to integers, respecting shopt -s strict_arith.
2. Look up variables and evaluate words.
"""

def __init__(self, mem, exec_opts, word_ev, errfmt):
self.mem = mem
self.exec_opts = exec_opts
self.word_ev = word_ev # type = word_eval.WordEvaluator
self.errfmt = errfmt

def _StringToIntegerOrError(self, s, blame_word=None,
span_id=const.NO_INTEGER):
"""Used by both [[ $x -gt 3 ]] and (( $x ))."""
if span_id == const.NO_INTEGER and blame_word:
span_id = word.LeftMostSpanForWord(blame_word)

try:
i = _StringToInteger(s, span_id=span_id)
except util.FatalRuntimeError as e:
if self.exec_opts.strict_arith:
raise
else:
self.errfmt.PrettyPrintError(e, prefix='warning: ')
i = 0
return i


class ArithEvaluator(_ExprEvaluator):

def _ValToArith(self, val, span_id, int_coerce=True):
Expand Down
8 changes: 5 additions & 3 deletions osh/word_eval.py
Expand Up @@ -657,10 +657,12 @@ def _EvalBracedVarSub(self, part, part_vals, quoted):

elif val.tag == value_e.AssocArray:
key = self.arith_ev.Eval(anode, int_coerce=False)
try:
val = value.Str(val.d[key])
except KeyError:
s = val.d.get(key)

if s is None:
val = value.Undef()
else:
val = value.Str(s)

else:
raise AssertionError(val.__class__.__name__)
Expand Down

0 comments on commit a863699

Please sign in to comment.