def OldValue(lval, mem, exec_opts): # type: (lvalue_t, Mem, optview.Exec) -> value_t """ Used by s+='x' and (( i += 1 )) TODO: We need a stricter and less ambiguous version for Oil. Problem: - why does lvalue have Indexed and Keyed, while sh_lhs_expr only has IndexedName? - should I have lvalue.Named and lvalue.Indexed only? - and Indexed uses the index_t type? - well that might be Str or Int """ assert isinstance(lval, lvalue_t), lval # TODO: refactor lvalue_t to make this simpler UP_lval = lval with tagswitch(lval) as case: if case(lvalue_e.Named): # (( i++ )) lval = cast(lvalue__Named, UP_lval) var_name = lval.name elif case(lvalue_e.Indexed): # (( a[i]++ )) lval = cast(lvalue__Indexed, UP_lval) var_name = lval.name elif case(lvalue_e.Keyed): # (( A['K']++ )) ? I think this works lval = cast(lvalue__Keyed, UP_lval) var_name = lval.name else: raise AssertionError() val = _LookupVar(var_name, mem, exec_opts) UP_val = val with tagswitch(lval) as case: if case(lvalue_e.Named): return val elif case(lvalue_e.Indexed): lval = cast(lvalue__Indexed, UP_lval) array_val = None # type: value__MaybeStrArray with tagswitch(val) as case2: if case2(value_e.Undef): array_val = value.MaybeStrArray([]) elif case2(value_e.MaybeStrArray): tmp = cast(value__MaybeStrArray, UP_val) # mycpp rewrite: add tmp. cast() creates a new var in inner scope array_val = tmp else: e_die("Can't use [] on value of type %s", ui.ValType(val)) s = word_eval.GetArrayItem(array_val.strs, lval.index) if s is None: val = value.Str('') # NOTE: Other logic is value.Undef()? 0? else: assert isinstance(s, str), s val = value.Str(s) elif case(lvalue_e.Keyed): lval = cast(lvalue__Keyed, UP_lval) assoc_val = None # type: value__AssocArray with tagswitch(val) as case2: if case2(value_e.Undef): # This never happens, because undef[x]+= is assumed to raise AssertionError() elif case2(value_e.AssocArray): tmp2 = cast(value__AssocArray, UP_val) # mycpp rewrite: add tmp. cast() creates a new var in inner scope assoc_val = tmp2 else: e_die("Can't use [] on value of type %s", ui.ValType(val)) s = assoc_val.d.get(lval.key) if s is None: val = value.Str('') else: val = value.Str(s) else: raise AssertionError() return val
def Eval(self, node): # type: (arith_expr_t) -> value_t """ Args: node: arith_expr_t Returns: None for Undef (e.g. empty cell) TODO: Don't return 0! int for Str List[int] for MaybeStrArray Dict[str, str] for AssocArray (TODO: Should we support this?) NOTE: (( A['x'] = 'x' )) and (( x = A['x'] )) are syntactically valid in bash, but don't do what you'd think. 'x' sometimes a variable name and sometimes a key. """ # OSH semantics: Variable NAMES cannot be formed dynamically; but INTEGERS # can. ${foo:-3}4 is OK. $? will be a compound word too, so we don't have # to handle that as a special case. UP_node = node with tagswitch(node) as case: if case(arith_expr_e.VarRef): # $(( x )) (can be array) tok = cast(Token, UP_node) return _LookupVar(tok.val, self.mem, self.exec_opts) elif case(arith_expr_e.Word): # $(( $x )) $(( ${x}${y} )), etc. w = cast(compound_word, UP_node) return self.word_ev.EvalWordToString(w) elif case(arith_expr_e.UnaryAssign): # a++ node = cast(arith_expr__UnaryAssign, UP_node) op_id = node.op_id old_int, lval = self._EvalLhsAndLookupArith(node.child) if op_id == Id.Node_PostDPlus: # post-increment new_int = old_int + 1 ret = old_int elif op_id == Id.Node_PostDMinus: # post-decrement new_int = old_int - 1 ret = old_int elif op_id == Id.Arith_DPlus: # pre-increment new_int = old_int + 1 ret = new_int elif op_id == Id.Arith_DMinus: # pre-decrement new_int = old_int - 1 ret = new_int else: raise AssertionError(op_id) #log('old %d new %d ret %d', old_int, new_int, ret) self._Store(lval, new_int) return value.Int(ret) elif case(arith_expr_e.BinaryAssign): # a=1, a+=5, a[1]+=5 node = cast(arith_expr__BinaryAssign, UP_node) op_id = node.op_id if op_id == Id.Arith_Equal: # Don't really need a span ID here, because tdop.CheckLhsExpr should # have done all the validation. lval = self.EvalArithLhs(node.left, runtime.NO_SPID) rhs_int = self.EvalToInt(node.right) self._Store(lval, rhs_int) return value.Int(rhs_int) old_int, lval = self._EvalLhsAndLookupArith(node.left) rhs = self.EvalToInt(node.right) if op_id == Id.Arith_PlusEqual: new_int = old_int + rhs elif op_id == Id.Arith_MinusEqual: new_int = old_int - rhs elif op_id == Id.Arith_StarEqual: new_int = old_int * rhs elif op_id == Id.Arith_SlashEqual: if rhs == 0: e_die('Divide by zero') # TODO: location new_int = old_int / rhs elif op_id == Id.Arith_PercentEqual: if rhs == 0: e_die('Divide by zero') # TODO: location new_int = old_int % rhs elif op_id == Id.Arith_DGreatEqual: new_int = old_int >> rhs elif op_id == Id.Arith_DLessEqual: new_int = old_int << rhs elif op_id == Id.Arith_AmpEqual: new_int = old_int & rhs elif op_id == Id.Arith_PipeEqual: new_int = old_int | rhs elif op_id == Id.Arith_CaretEqual: new_int = old_int ^ rhs else: raise AssertionError(op_id) # shouldn't get here self._Store(lval, new_int) return value.Int(new_int) elif case(arith_expr_e.Unary): node = cast(arith_expr__Unary, UP_node) op_id = node.op_id i = self.EvalToInt(node.child) if op_id == Id.Node_UnaryPlus: ret = i elif op_id == Id.Node_UnaryMinus: ret = -i elif op_id == Id.Arith_Bang: # logical negation ret = 1 if i == 0 else 0 elif op_id == Id.Arith_Tilde: # bitwise complement ret = ~i else: raise AssertionError(op_id) # shouldn't get here return value.Int(ret) elif case(arith_expr_e.Binary): node = cast(arith_expr__Binary, UP_node) op_id = node.op_id # Short-circuit evaluation for || and &&. if op_id == Id.Arith_DPipe: lhs = self.EvalToInt(node.left) if lhs == 0: rhs = self.EvalToInt(node.right) ret = int(rhs != 0) else: ret = 1 # true return value.Int(ret) if op_id == Id.Arith_DAmp: lhs = self.EvalToInt(node.left) if lhs == 0: ret = 0 # false else: rhs = self.EvalToInt(node.right) ret = int(rhs != 0) return value.Int(ret) if op_id == Id.Arith_LBracket: # NOTE: Similar to bracket_op_e.ArrayIndex in osh/word_eval.py left = self.Eval(node.left) UP_left = left with tagswitch(left) as case: if case(value_e.MaybeStrArray): array_val = cast(value__MaybeStrArray, UP_left) index = self.EvalToInt(node.right) s = word_eval.GetArrayItem(array_val.strs, index) elif case(value_e.AssocArray): left = cast(value__AssocArray, UP_left) key = self.EvalWordToString(node.right) s = left.d.get(key) else: # TODO: Add error context e_die('Expected array or assoc in index expression, got %s', ui.ValType(left)) if s is None: val = value.Undef() # type: value_t else: val = value.Str(s) return val if op_id == Id.Arith_Comma: self.EvalToInt(node.left) # throw away result ret = self.EvalToInt(node.right) return value.Int(ret) # Rest are integers lhs = self.EvalToInt(node.left) rhs = self.EvalToInt(node.right) if op_id == Id.Arith_Plus: ret = lhs + rhs elif op_id == Id.Arith_Minus: ret = lhs - rhs elif op_id == Id.Arith_Star: ret = lhs * rhs elif op_id == Id.Arith_Slash: if rhs == 0: # TODO: Could also blame / e_die('Divide by zero', span_id=location.SpanForArithExpr(node.right)) ret = lhs / rhs elif op_id == Id.Arith_Percent: if rhs == 0: # TODO: Could also blame / e_die('Divide by zero', span_id=location.SpanForArithExpr(node.right)) ret = lhs % rhs elif op_id == Id.Arith_DStar: # OVM is stripped of certain functions that are somehow necessary for # exponentiation. # Python/ovm_stub_pystrtod.c:21: PyOS_double_to_string: Assertion `0' # failed. if rhs < 0: e_die("Exponent can't be less than zero") # TODO: error location ret = 1 for i in xrange(rhs): ret *= lhs elif op_id == Id.Arith_DEqual: ret = int(lhs == rhs) elif op_id == Id.Arith_NEqual: ret = int(lhs != rhs) elif op_id == Id.Arith_Great: ret = int(lhs > rhs) elif op_id == Id.Arith_GreatEqual: ret = int(lhs >= rhs) elif op_id == Id.Arith_Less: ret = int(lhs < rhs) elif op_id == Id.Arith_LessEqual: ret = int(lhs <= rhs) elif op_id == Id.Arith_Pipe: ret = lhs | rhs elif op_id == Id.Arith_Amp: ret = lhs & rhs elif op_id == Id.Arith_Caret: ret = lhs ^ rhs # Note: how to define shift of negative numbers? elif op_id == Id.Arith_DLess: ret = lhs << rhs elif op_id == Id.Arith_DGreat: ret = lhs >> rhs else: raise AssertionError(op_id) return value.Int(ret) elif case(arith_expr_e.TernaryOp): node = cast(arith_expr__TernaryOp, UP_node) cond = self.EvalToInt(node.cond) if cond: # nonzero return self.Eval(node.true_expr) else: return self.Eval(node.false_expr) else: raise AssertionError(node.tag_())