Ejemplo n.º 1
0
def _EvalLhsArith(node, mem, arith_ev):
    # type: (sh_lhs_expr_t, Mem, ArithEvaluator) -> lvalue_t
    """sh_lhs_expr -> lvalue.
  
  Very similar to EvalLhs above in core/cmd_exec.
  """
    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    with tagswitch(node) as case:
        if case(sh_lhs_expr_e.Name):  # (( i = 42 ))
            node = cast(sh_lhs_expr__Name, UP_node)

            lval = lvalue.Named(node.name)  # type: lvalue_t
            # TODO: location info.  Use the = token?
            #lval.spids.append(spid)

        elif case(sh_lhs_expr_e.IndexedName):  # (( a[42] = 42 ))
            node = cast(sh_lhs_expr__IndexedName, UP_node)

            # The index of MaybeStrArray needs to be coerced to int, but not the
            # index of an AssocArray.
            if mem.IsAssocArray(node.name, scope_e.Dynamic):
                key = arith_ev.EvalWordToString(node.index)
                lval = lvalue.Keyed(node.name, key)
            else:
                index = arith_ev.EvalToInt(node.index)
                lval = lvalue.Indexed(node.name, index)
                # TODO: location info.  Use the = token?
                #lval.spids.append(node.spids[0])

        else:
            raise AssertionError(node.tag_())

    return lval
Ejemplo n.º 2
0
  def EvalArithLhs(self, anode, span_id):
    # type: (arith_expr_t, int) -> lvalue_t
    """
    For (( a[x] = 1 )) etc.
    """
    UP_anode = anode
    if anode.tag_() == arith_expr_e.Binary:
      anode = cast(arith_expr__Binary, UP_anode)
      if anode.op_id == Id.Arith_LBracket:
        var_name, span_id = self._VarRefOrWord(anode.left)
        if var_name is not None:
          if self.mem.IsAssocArray(var_name, scope_e.Dynamic):
            key = self.EvalWordToString(anode.right)
            lval2 = lvalue.Keyed(var_name, key)
            lval2.spids.append(span_id)
            lval = lval2  # type: lvalue_t
            return lval
          else:
            index = self.EvalToInt(anode.right)
            lval3 = lvalue.Indexed(var_name, index)
            lval3.spids.append(span_id)
            lval = lval3
            return lval

    var_name, span_id = self._VarRefOrWord(anode)
    if var_name is not None:
      lval1 = lvalue.Named(var_name)
      lval1.spids.append(span_id)
      lval = lval1
      return lval

    # e.g. unset 'x-y'.  status 2 for runtime parse error
    e_die('Invalid place to modify', span_id=span_id, status=2)
Ejemplo n.º 3
0
def _EvalLhsArith(node, mem, arith_ev):
  """sh_lhs_expr -> lvalue.
  
  Very similar to EvalLhs above in core/cmd_exec.
  """
  assert isinstance(node, sh_lhs_expr_t), node

  if node.tag == sh_lhs_expr_e.Name:  # (( i = 42 ))
    lval = lvalue.Named(node.name)
    # TODO: location info.  Use the = token?
    #lval.spids.append(spid)
    return lval

  elif node.tag == sh_lhs_expr_e.IndexedName:  # (( a[42] = 42 ))
    # The index of MaybeStrArray needs to be coerced to int, but not the index of
    # an AssocArray.
    if mem.IsAssocArray(node.name, scope_e.Dynamic):
      key = arith_ev.EvalWordToString(node.index)
      lval = lvalue.Keyed(node.name, key)
    else:
      index = arith_ev.EvalToIndex(node.index)
      lval = lvalue.Indexed(node.name, index)
      # TODO: location info.  Use the = token?
      #lval.spids.append(node.spids[0])

  else:
    raise AssertionError(node.tag)

  return lval
Ejemplo n.º 4
0
def EvalLhs(node, arith_ev, mem, spid, lookup_mode):
  """sh_lhs_expr -> lvalue.

  Used for a=b and a[x]=b
  """
  assert isinstance(node, sh_lhs_expr_t), node

  if node.tag == sh_lhs_expr_e.Name:  # a=x
    lval = lvalue.Named(node.name)
    lval.spids.append(spid)

  elif node.tag == sh_lhs_expr_e.IndexedName:  # a[1+2]=x

    if mem.IsAssocArray(node.name, lookup_mode):
      key = arith_ev.EvalWordToString(node.index)
      lval = lvalue.Keyed(node.name, key)
      lval.spids.append(node.spids[0])  # copy left-most token over
    else:
      index = arith_ev.EvalToIndex(node.index)
      lval = lvalue.Indexed(node.name, index)
      lval.spids.append(node.spids[0])  # copy left-most token over

  else:
    raise AssertionError(node.tag)

  return lval
    def _EvalLhsAndLookupArith(self, node):
        # type: (arith_expr_t) -> Tuple[int, lvalue_t]
        """ For x = y  and   x += y  and  ++x """

        lval = self.EvalArithLhs(node, runtime.NO_SPID)
        val = OldValue(lval, self.mem, self.exec_opts)

        # BASH_LINENO, arr (array name with shopt -s compat_array), etc.
        if val.tag_() in (value_e.MaybeStrArray, value_e.AssocArray
                          ) and lval.tag_() == lvalue_e.Named:
            named_lval = cast(lvalue__Named, lval)
            if word_eval.CheckCompatArray(named_lval.name, self.exec_opts):
                if val.tag_() == value_e.MaybeStrArray:
                    lval = lvalue.Indexed(named_lval.name, 0)
                elif val.tag_() == value_e.AssocArray:
                    lval = lvalue.Keyed(named_lval.name, '0')
                val = word_eval.ResolveCompatArray(val)

        # This error message could be better, but we already have one
        #if val.tag_() == value_e.MaybeStrArray:
        #  e_die("Can't use assignment like ++ or += on arrays")

        span_id = location.SpanForArithExpr(node)
        i = self._ValToIntOrError(val, span_id=span_id)
        return i, lval
Ejemplo n.º 6
0
    def testUnset(self):
        mem = _InitMem()
        # unset a
        mem.Unset(lvalue.Named('a'), scope_e.Dynamic)

        return  # not implemented yet

        # unset a[1]
        mem.Unset(lvalue.Indexed('a', 1), scope_e.Dynamic)
Ejemplo n.º 7
0
def EvalLhs(node, arith_ev, mem, spid, lookup_mode):
    # type: (sh_lhs_expr_t, ArithEvaluator, Mem, int, scope_t) -> lvalue_t
    """sh_lhs_expr -> lvalue.

  Used for a=b and a[x]=b
  """
    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    lval = None  # type: lvalue_t
    with tagswitch(node) as case:
        if case(sh_lhs_expr_e.Name):  # a=x
            node = cast(sh_lhs_expr__Name, UP_node)

            # Note: C++ constructor doesn't take spids directly.  Should we add that?
            lval1 = lvalue.Named(node.name)
            lval1.spids.append(spid)
            lval = lval1

        elif case(sh_lhs_expr_e.IndexedName):  # a[1+2]=x
            node = cast(sh_lhs_expr__IndexedName, UP_node)

            if mem.IsAssocArray(node.name, lookup_mode):
                key = arith_ev.EvalWordToString(node.index)
                # copy left-mode spid
                lval2 = lvalue.Keyed(node.name, key)
                lval2.spids.append(node.spids[0])
                lval = lval2
            else:
                index = arith_ev.EvalToInt(node.index)
                # copy left-mode spid
                lval3 = lvalue.Indexed(node.name, index)
                lval3.spids.append(node.spids[0])
                lval = lval3

        else:
            raise AssertionError(node.tag_())

    return lval
Ejemplo n.º 8
0
  def EvalShellLhs(self, node, spid, lookup_mode):
    # type: (sh_lhs_expr_t, int, scope_t) -> lvalue_t
    """Evaluate a shell LHS expression, i.e. place expression.

    For  a=b  and  a[x]=b  etc.
    """
    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    lval = None  # type: lvalue_t
    with tagswitch(node) as case:
      if case(sh_lhs_expr_e.Name):  # a=x
        node = cast(sh_lhs_expr__Name, UP_node)

        # Note: C++ constructor doesn't take spids directly.  Should we add that?
        lval1 = lvalue.Named(node.name)
        lval1.spids.append(spid)
        lval = lval1

      elif case(sh_lhs_expr_e.IndexedName):  # a[1+2]=x
        node = cast(sh_lhs_expr__IndexedName, UP_node)

        if self.mem.IsAssocArray(node.name, lookup_mode):
          key = self.EvalWordToString(node.index)
          lval2 = lvalue.Keyed(node.name, key)
          lval2.spids.append(node.spids[0])
          lval = lval2
        else:
          index = self.EvalToInt(node.index)
          lval3 = lvalue.Indexed(node.name, index)
          lval3.spids.append(node.spids[0])
          lval = lval3

      else:
        raise AssertionError(node.tag_())

    return lval
Ejemplo n.º 9
0
def EvalLhsAndLookup(node,
                     arith_ev,
                     mem,
                     exec_opts,
                     lookup_mode=scope_e.Dynamic):
    # type: (sh_lhs_expr_t, ArithEvaluator, Mem, optview.Exec, scope_t) -> Tuple[value_t, lvalue_t]
    """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'.

  Args:
    node: syntax_asdl.sh_lhs_expr

  Returns:
    value_t, lvalue_t
  """
    #log('sh_lhs_expr NODE %s', node)

    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    with tagswitch(node) as case:
        if case(sh_lhs_expr_e.Name):  # a = b
            node = cast(sh_lhs_expr__Name, UP_node)
            # Problem: It can't be an array?
            # a=(1 2)
            # (( a++ ))
            lval = lvalue.Named(node.name)  # type: lvalue_t
            val = _LookupVar(node.name, mem, exec_opts)

        elif case(sh_lhs_expr_e.IndexedName):  # a[1] = b
            node = cast(sh_lhs_expr__IndexedName, UP_node)
            # See tdop.IsIndexable for valid values:
            # - VarRef (not Name): a[1]
            # - FuncCall: f(x), 1
            # - Binary LBracket: f[1][1] -- no semantics for this?

            val = mem.GetVar(node.name)

            UP_val = val
            with tagswitch(val) as case2:
                if case2(value_e.Str):
                    e_die("Can't assign to characters of string %r", node.name)

                elif case2(value_e.Undef):
                    # compatible behavior: Treat it like an array.
                    # TODO: Does this code ever get triggered?  It seems like the error is
                    # caught earlier.

                    index = arith_ev.EvalToInt(node.index)
                    lval = lvalue.Indexed(node.name, index)
                    if exec_opts.nounset():
                        e_die("Undefined variable can't be indexed")
                    else:
                        val = value.Str('')

                elif case2(value_e.MaybeStrArray):
                    array_val = cast(value__MaybeStrArray, UP_val)

                    #log('ARRAY %s -> %s, index %d', node.name, array, index)
                    index = arith_ev.EvalToInt(node.index)
                    lval = lvalue.Indexed(node.name, index)
                    # NOTE: Similar logic in RHS Arith_LBracket
                    try:
                        s = array_val.strs[index]
                    except IndexError:
                        s = None

                    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 case2(value_e.AssocArray):  # declare -A a; a['x']+=1
                    assoc_val = cast(value__AssocArray, UP_val)

                    key = arith_ev.EvalWordToString(node.index)
                    lval = lvalue.Keyed(node.name, key)

                    s = assoc_val.d.get(key)
                    if s is None:
                        val = value.Str('')
                    else:
                        val = value.Str(s)

                else:
                    raise AssertionError(val.tag_())

        else:
            raise AssertionError(node.tag_())

    return val, lval
Ejemplo n.º 10
0
    def testSetVarClearFlag(self):
        mem = _InitMem()
        print(mem)

        mem.PushCall('my-func', 0, ['ONE'])
        self.assertEqual(2, len(mem.var_stack))  # internal details

        # local x=y
        mem.SetVar(lvalue.Named('x'), value.Str('y'), (), scope_e.LocalOnly)
        self.assertEqual('y', mem.var_stack[-1]['x'].val.s)

        # New frame
        mem.PushCall('my-func', 0, ['TWO'])
        self.assertEqual(3, len(mem.var_stack))  # internal details

        # x=y -- test out dynamic scope
        mem.SetVar(lvalue.Named('x'), value.Str('YYY'), (), scope_e.Dynamic)
        self.assertEqual('YYY', mem.var_stack[-2]['x'].val.s)
        self.assertEqual(None, mem.var_stack[-1].get('x'))

        # myglobal=g
        mem.SetVar(lvalue.Named('myglobal'), value.Str('g'), (),
                   scope_e.Dynamic)
        self.assertEqual('g', mem.var_stack[0]['myglobal'].val.s)
        self.assertEqual(False, mem.var_stack[0]['myglobal'].exported)

        # 'export PYTHONPATH=/'
        mem.SetVar(lvalue.Named('PYTHONPATH'), value.Str('/'),
                   (var_flags_e.Exported, ), scope_e.Dynamic)
        self.assertEqual('/', mem.var_stack[0]['PYTHONPATH'].val.s)
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

        ex = mem.GetExported()
        self.assertEqual('/', ex['PYTHONPATH'])

        mem.SetVar(lvalue.Named('PYTHONPATH'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

        # 'export myglobal'.  None means don't touch it.  Undef would be confusing
        # because it might mean "unset", but we have a separated API for that.
        mem.SetVar(lvalue.Named('myglobal'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['myglobal'].exported)

        # export g2  -- define and export empty
        mem.SetVar(lvalue.Named('g2'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        self.assertEqual(value_e.Undef, mem.var_stack[0]['g2'].val.tag)
        self.assertEqual(True, mem.var_stack[0]['g2'].exported)

        # readonly myglobal
        self.assertEqual(False, mem.var_stack[0]['myglobal'].readonly)
        mem.SetVar(lvalue.Named('myglobal'), None, (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['myglobal'].readonly)

        mem.SetVar(lvalue.Named('PYTHONPATH'), value.Str('/lib'), (),
                   scope_e.Dynamic)
        self.assertEqual('/lib', mem.var_stack[0]['PYTHONPATH'].val.s)
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

        # COMPREPLY=(1 2 3)
        # invariant to enforce: arrays can't be exported
        mem.SetVar(lvalue.Named('COMPREPLY'),
                   value.MaybeStrArray(['1', '2', '3']), (),
                   scope_e.GlobalOnly)
        self.assertEqual(['1', '2', '3'],
                         mem.var_stack[0]['COMPREPLY'].val.strs)

        # export COMPREPLY
        try:
            mem.SetVar(lvalue.Named('COMPREPLY'), None,
                       (var_flags_e.Exported, ), scope_e.Dynamic)
        except error.FatalRuntime as e:
            pass
        else:
            self.fail("Expected failure")

        # readonly r=1
        mem.SetVar(lvalue.Named('r'), value.Str('1'), (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual('1', mem.var_stack[0]['r'].val.s)
        self.assertEqual(False, mem.var_stack[0]['r'].exported)
        self.assertEqual(True, mem.var_stack[0]['r'].readonly)
        print(mem)

        # r=newvalue
        try:
            mem.SetVar(lvalue.Named('r'), value.Str('newvalue'), (),
                       scope_e.Dynamic)
        except error.FatalRuntime as e:
            pass
        else:
            self.fail("Expected failure")

        # readonly r2  -- define empty readonly
        mem.SetVar(lvalue.Named('r2'), None, (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual(value_e.Undef, mem.var_stack[0]['r2'].val.tag)
        self.assertEqual(True, mem.var_stack[0]['r2'].readonly)

        # export -n PYTHONPATH
        # Remove the exported property.  NOTE: scope is LocalOnly for Oil?
        self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)
        mem.ClearFlag('PYTHONPATH', var_flags_e.Exported, scope_e.Dynamic)
        self.assertEqual(False, mem.var_stack[0]['PYTHONPATH'].exported)

        lhs = lvalue.Indexed('a', 1)
        lhs.spids.append(0)
        # a[1]=2
        mem.SetVar(lhs, value.Str('2'), (), scope_e.Dynamic)
        self.assertEqual([None, '2'], mem.var_stack[0]['a'].val.strs)

        # a[1]=3
        mem.SetVar(lhs, value.Str('3'), (), scope_e.Dynamic)
        self.assertEqual([None, '3'], mem.var_stack[0]['a'].val.strs)

        # a[1]=(x y z)  # illegal but doesn't parse anyway
        if 0:
            try:
                mem.SetVar(lhs, value.MaybeStrArray(['x', 'y', 'z']), (),
                           scope_e.Dynamic)
            except error.FatalRuntime as e:
                pass
            else:
                self.fail("Expected failure")

        # readonly a
        mem.SetVar(lvalue.Named('a'), None, (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)
        self.assertEqual(True, mem.var_stack[0]['a'].readonly)

        try:
            # a[1]=3
            mem.SetVar(lhs, value.Str('3'), (), scope_e.Dynamic)
        except error.FatalRuntime as e:
            pass
        else:
            self.fail("Expected failure")
Ejemplo n.º 11
0
def EvalLhsAndLookup(node, arith_ev, mem, exec_opts,
                     lookup_mode=scope_e.Dynamic):
  """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'.

  Args:
    node: syntax_asdl.lhs_expr

  Returns:
    value_t, lvalue_t
  """
  #log('lhs_expr NODE %s', node)

  assert isinstance(node, lhs_expr_t), node

  if node.tag == lhs_expr_e.LhsName:  # a = b
    # Problem: It can't be an array?
    # a=(1 2)
    # (( a++ ))
    lval = lvalue.Named(node.name)
    val = _LookupVar(node.name, mem, exec_opts)

  elif node.tag == lhs_expr_e.LhsIndexedName:  # a[1] = b
    # See tdop.IsIndexable for valid values:
    # - ArithVarRef (not LhsName): a[1]
    # - FuncCall: f(x), 1
    # - ArithBinary LBracket: f[1][1] -- no semantics for this?

    val = mem.GetVar(node.name)

    if val.tag == value_e.Str:
      e_die("Can't assign to characters of string %r", node.name)

    elif val.tag == value_e.Undef:
      # compatible behavior: Treat it like an array.
      # TODO: Does this code ever get triggered?  It seems like the error is
      # caught earlier.

      index = arith_ev.Eval(node.index)
      lval = lvalue.Indexed(node.name, index)
      if exec_opts.nounset:
        e_die("Undefined variable can't be indexed")
      else:
        val = value.Str('')

    elif val.tag == value_e.StrArray:

      #log('ARRAY %s -> %s, index %d', node.name, array, index)
      array = val.strs
      index = arith_ev.Eval(node.index)
      lval = lvalue.Indexed(node.name, index)
      # NOTE: Similar logic in RHS Arith_LBracket
      try:
        s = array[index]
      except IndexError:
        s = None

      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 val.tag == value_e.AssocArray:  # declare -A a; a['x']+=1
      key = arith_ev.EvalWordToString(node.index)
      lval = lvalue.Keyed(node.name, key)

      s = val.d.get(key)
      if s is None:
        val = value.Str('')
      else:
        val = value.Str(s)

    else:
      raise AssertionError(val.tag)

  else:
    raise AssertionError(node.tag)

  return val, lval