Exemplo n.º 1
0
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
Exemplo n.º 2
0
  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_())