Exemple #1
0
  def testPushTemp(self):
    mem = _InitMem()

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

    mem.PushTemp()

    self.assertEqual(2, len(mem.var_stack))

    # x=temp E=3 read x <<< 'line'
    mem.SetVar(
        lvalue.Named('x'), value.Str('temp'), scope_e.LocalOnly)
    mem.SetVar(
        lvalue.Named('E'), value.Str('3'), scope_e.LocalOnly)
    mem.SetVar(
        lvalue.Named('x'), value.Str('line'), scope_e.LocalOnly)

    self.assertEqual('3', mem.var_stack[-1]['E'].val.s)
    self.assertEqual('line', mem.var_stack[-1]['x'].val.s)
    self.assertEqual('1', mem.var_stack[-2]['x'].val.s)

    mem.PopTemp()
    self.assertEqual(1, len(mem.var_stack))
    self.assertEqual('1', mem.var_stack[-1]['x'].val.s)
Exemple #2
0
    def testExportThenAssign(self):
        """Regression Test"""
        mem = _InitMem()

        # export U
        mem.SetVar(lvalue.Named('U'), None, (var_flags_e.Exported, ),
                   scope_e.Dynamic)
        print(mem)

        # U=u
        mem.SetVar(lvalue.Named('U'), value.Str('u'), (), scope_e.Dynamic)
        print(mem)
        e = mem.GetExported()
        self.assertEqual('u', e['U'])
Exemple #3
0
  def Run(self, cmd_val):
    # type: (cmd_value__Assign) -> int
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()
    attrs = flag_spec.Parse('readonly', arg_r)
    arg = arg_types.readonly(attrs.attrs)

    if arg.p or len(cmd_val.pairs) == 0:
      return _PrintVariables(self.mem, cmd_val, attrs, True, builtin=_READONLY)

    for pair in cmd_val.pairs:
      if pair.rval is None:
        if arg.a:
          rval = value.MaybeStrArray([])  # type: value_t
        elif arg.A:
          rval = value.AssocArray({})
        else:
          rval = None
      else:
        rval = pair.rval

      rval = _ReconcileTypes(rval, arg.a, arg.A, pair.spid)

      # NOTE:
      # - when rval is None, only flags are changed
      # - dynamic scope because flags on locals can be changed, etc.
      self.mem.SetVar(lvalue.Named(pair.var_name), rval, scope_e.Dynamic,
                      flags=state.SetReadOnly)

    return 0
Exemple #4
0
  def Run(self, cmd_val):
    # type: (cmd_value__Assign) -> int
    arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
    arg_r.Next()
    attrs = flag_spec.Parse('export_', arg_r)
    arg = arg_types.export_(attrs.attrs)
    #arg = attrs

    if arg.f:
      e_usage(
          "doesn't accept -f because it's dangerous.  "
          "(The code can usually be restructured with 'source')")

    if arg.p or len(cmd_val.pairs) == 0:
      return _PrintVariables(self.mem, cmd_val, attrs, True, builtin=_EXPORT)


    if arg.n:
      for pair in cmd_val.pairs:
        if pair.rval is not None:
          e_usage("doesn't accept RHS with -n", span_id=pair.spid)

        # NOTE: we don't care if it wasn't found, like bash.
        self.mem.ClearFlag(pair.var_name, state.ClearExport, scope_e.Dynamic)
    else:
      for pair in cmd_val.pairs:
        # NOTE: when rval is None, only flags are changed
        self.mem.SetVar(lvalue.Named(pair.var_name), pair.rval, scope_e.Dynamic,
                        flags=state.SetExport)

    return 0
Exemple #5
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
Exemple #6
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
Exemple #7
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)
Exemple #8
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
Exemple #9
0
 def _EvalTempEnv(self, more_env, flags):
   """For FOO=1 cmd."""
   for env_pair in more_env:
     val = self.word_ev.EvalWordToString(env_pair.val)
     # Set each var so the next one can reference it.  Example:
     # FOO=1 BAR=$FOO ls /
     self.mem.SetVar(lvalue.Named(env_pair.name), val, flags,
                     scope_e.LocalOnly)
Exemple #10
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)
Exemple #11
0
    def _All(self, var_name):
        # type: (str) -> int
        contents = _ReadAll()

        # No error conditions?

        lhs = lvalue.Named(var_name)
        self.mem.SetValue(lhs, value.Str(contents), scope_e.LocalOnly)
        return 0
Exemple #12
0
  def _UnsetVar(self, name, spid):
    if not match.IsValidVarName(name):
      raise args.UsageError(
          'got invalid variable name %r' % name, span_id=spid)

    ok, found = self.mem.Unset(lvalue.Named(name), scope_e.Dynamic)
    if not ok:
      self.errfmt.Print("Can't unset readonly variable %r", name,
                        span_id=spid)
    return ok, found
Exemple #13
0
    def testGetVar(self):
        mem = _InitMem()

        # readonly a=x
        mem.SetVar(lvalue.Named('a'), value.Str('x'), (var_flags_e.ReadOnly, ),
                   scope_e.Dynamic)

        val = mem.GetVar('a', scope_e.Dynamic)
        test_lib.AssertAsdlEqual(self, value.Str('x'), val)

        val = mem.GetVar('undef', scope_e.Dynamic)
        test_lib.AssertAsdlEqual(self, value.Undef(), val)
Exemple #14
0
    def EvalLHS(self, node):
        if 0:
            print('EvalLHS()')
            node.PrettyPrint()
            print('')

        if node.tag == expr_e.Var:
            return lvalue.Named(node.name.val)
        else:
            # TODO:
            # subscripts, tuple unpacking, starred expressions, etc.

            raise NotImplementedError(node.__class__.__name__)
Exemple #15
0
    def __init__(
            self,
            parse_ctx,  # type: ParseContext
            exec_opts,  # type: optview.Exec
            mutable_opts,  # type: MutableOpts
            mem,  # type: Mem
            f,  # type: _DebugFile
    ):
        # type: (...) -> None
        """
    Args:
      parse_ctx: For parsing PS4.
      exec_opts: For xtrace setting
      mem: for retrieving PS4
      word_ev: for evaluating PS4
    """
        self.parse_ctx = parse_ctx
        self.exec_opts = exec_opts
        self.mutable_opts = mutable_opts
        self.mem = mem
        self.f = f  # can be stderr, the --debug-file, etc.

        self.word_ev = None  # type: NormalWordEvaluator

        self.ind = 0  # changed by process, proc, source, eval
        self.indents = ['']  # "pooled" to avoid allocations

        # PS4 value -> compound_word.  PS4 is scoped.
        self.parse_cache = {}  # type: Dict[str, compound_word]

        # Mutate objects to save allocations
        self.val_indent = value.Str('')
        self.val_punct = value.Str('')
        self.val_pid_str = value.Str('')  # mutated by SetProcess

        # Can these be global constants?  I don't think we have that in ASDL yet.
        self.lval_indent = lvalue.Named('SHX_indent')
        self.lval_punct = lvalue.Named('SHX_punct')
        self.lval_pid_str = lvalue.Named('SHX_pid_str')
Exemple #16
0
    def testGetValue(self):
        mem = _InitMem()

        # readonly a=x
        mem.SetValue(lvalue.Named('a'),
                     value.Str('x'),
                     scope_e.Dynamic,
                     flags=state.SetReadOnly)

        val = mem.GetValue('a', scope_e.Dynamic)
        test_lib.AssertAsdlEqual(self, value.Str('x'), val)

        val = mem.GetValue('undef', scope_e.Dynamic)
        test_lib.AssertAsdlEqual(self, value.Undef(), val)
Exemple #17
0
  def _Line(self, arg, var_name):
    # type: (arg_types.read, str) -> int
    line = _ReadLine()
    if len(line) == 0:  # EOF
      return 1

    if not arg.with_eol:
      if line.endswith('\r\n'):
        line = line[:-2]
      elif line.endswith('\n'):
        line = line[:-1]

    lhs = lvalue.Named(var_name)
    self.mem.SetVar(lhs, value.Str(line), scope_e.LocalOnly)
    return 0
Exemple #18
0
            def _gen():
                while True:
                    try:
                        loop_val = it.next()  # e.g. x
                    except StopIteration:
                        break
                    self.mem.SetVar(lvalue.Named(iter_name),
                                    value.Obj(loop_val), scope_e.LocalOnly)

                    if comp.cond:
                        b = self.EvalExpr(comp.cond)
                    else:
                        b = True

                    if b:
                        item = self.EvalExpr(node.elt)  # e.g. x*2
                        yield item
Exemple #19
0
    def EvalPlaceExpr(self, place):
        # type: (place_expr_t) -> Union[lvalue__Named, lvalue__ObjIndex, lvalue__ObjAttr]
        if place.tag == place_expr_e.Var:
            return lvalue.Named(place.name.val)

        if place.tag == place_expr_e.Subscript:
            obj = self.EvalExpr(place.obj)
            index = self._EvalIndices(place.indices)
            return lvalue.ObjIndex(obj, index)

        if place.tag == place_expr_e.Attribute:
            obj = self.EvalExpr(place.obj)
            if place.op.id == Id.Expr_RArrow:
                index = place.attr.val
                return lvalue.ObjIndex(obj, index)
            else:
                return lvalue.ObjAttr(obj, place.attr.val)

        raise NotImplementedError(place)
Exemple #20
0
  def testSearchPath(self):
    mem = _InitMem()
    #print(mem)
    search_path = state.SearchPath(mem)

    # Relative path works without $PATH
    self.assertEqual(None, search_path.Lookup('__nonexistent__'))
    self.assertEqual('bin/osh', search_path.Lookup('bin/osh'))
    self.assertEqual(None, search_path.Lookup('grep'))

    # Set $PATH
    mem.SetVar(lvalue.Named('PATH'), value.Str('/bin:/usr/bin'),
               (), scope_e.GlobalOnly)

    self.assertEqual(None, search_path.Lookup('__nonexistent__'))
    self.assertEqual('bin/osh', search_path.Lookup('bin/osh'))

    # Not hermetic, but should be true on POSIX systems.
    self.assertEqual('/usr/bin/env', search_path.Lookup('env'))
Exemple #21
0
    def _Line(self, arg, var_name):
        # type: (arg_types.read, str) -> int
        line = _ReadLine()
        if len(line) == 0:  # EOF
            return 1

        if not arg.with_eol:
            if line.endswith('\r\n'):
                line = line[:-2]
            elif line.endswith('\n'):
                line = line[:-1]

        # Lines that don't start with a single quote aren't QSN.  They may contain
        # a single quote internally, like:
        #
        # Fool's Gold
        if arg.q and line.startswith("'"):
            arena = self.parse_ctx.arena
            line_reader = reader.StringLineReader(line, arena)
            lexer = self.parse_ctx._MakeLexer(line_reader)

            # The parser only yields valid tokens:
            #     Char_Literals, Char_OneChar, Char_Hex, Char_UBraced
            # So we can use word_compile.EvalCStringToken, which is also used for
            # $''.
            # Important: we don't generate Id.Unknown_Backslash because that is valid
            # in echo -e.  We just make it Id.Unknown_Tok?
            try:
                # TODO: read should know about stdin, and redirects, and pipelines?
                with alloc.ctx_Location(arena, source.Stdin('')):
                    tokens = qsn_native.Parse(lexer)
            except error.Parse as e:
                ui.PrettyPrintError(e, arena)
                return 1
            tmp = [word_compile.EvalCStringToken(t) for t in tokens]
            line = ''.join(tmp)

        lhs = lvalue.Named(var_name)
        self.mem.SetValue(lhs, value.Str(line), scope_e.LocalOnly)
        return 0
Exemple #22
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
Exemple #23
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
Exemple #24
0
    def testSearchPath(self):
        mem = _InitMem()
        #print(mem)
        search_path = state.SearchPath(mem)

        # Relative path works without $PATH
        self.assertEqual(None, search_path.Lookup('__nonexistent__'))
        self.assertEqual('bin/osh', search_path.Lookup('bin/osh'))
        self.assertEqual(None, search_path.Lookup('grep'))

        # Set $PATH
        mem.SetVar(lvalue.Named('PATH'), value.Str('/bin:/usr/bin'), (),
                   scope_e.GlobalOnly)

        self.assertEqual(None, search_path.Lookup('__nonexistent__'))
        self.assertEqual('bin/osh', search_path.Lookup('bin/osh'))

        # Not hermetic, but should be true on POSIX systems.
        # Also see https://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/
        #  - on some systems, /bin is a symlink to /usr/bin
        if os.path.isfile('/bin/env'):
            self.assertEqual(search_path.Lookup('env'), '/bin/env')
        else:
            self.assertEqual(search_path.Lookup('env'), '/usr/bin/env')
Exemple #25
0
 def _WriteFdToMem(self, fd_name, fd):
     # type: (str, int) -> None
     if self.mem:
         # setvar, not setref
         state.SetLocalShopt(self.mem, lvalue.Named(fd_name),
                             value.Str(str(fd)))
Exemple #26
0
    def EvalExpr(self, node):
        # type: (expr_t) -> Any
        """
    This is a naive PyObject evaluator!  It uses the type dispatch of the host
    Python interpreter.

    Returns:
      A Python object of ANY type.  Should be wrapped in value.Obj() for
      storing in Mem.
    """
        if 0:
            print('EvalExpr()')
            node.PrettyPrint()
            print('')

        if node.tag == expr_e.Const:
            id_ = node.c.id

            if id_ == Id.Expr_DecInt:
                return int(node.c.val)
            elif id_ == Id.Expr_BinInt:
                return int(node.c.val, 2)
            elif id_ == Id.Expr_OctInt:
                return int(node.c.val, 8)
            elif id_ == Id.Expr_HexInt:
                return int(node.c.val, 16)

            elif id_ == Id.Expr_Float:
                return float(node.c.val)

            elif id_ == Id.Expr_Null:
                return None
            elif id_ == Id.Expr_True:
                return True
            elif id_ == Id.Expr_False:
                return False

            elif id_ == Id.Expr_Name:
                # for {name: 'bob'}
                # Maybe also :Symbol?
                return node.c.val

            # NOTE: We could allow Ellipsis for a[:, ...] here, but we're not using
            # it yet.
            raise AssertionError(id_)

        if node.tag == expr_e.Var:
            return self.LookupVar(node.name.val)

        if node.tag == expr_e.CommandSub:
            return self.ex.RunCommandSub(node.command_list)

        if node.tag == expr_e.ShArrayLiteral:
            words = braces.BraceExpandWords(node.words)
            strs = self.word_ev.EvalWordSequence(words)
            #log('ARRAY LITERAL EVALUATED TO -> %s', strs)
            return objects.StrArray(strs)

        if node.tag == expr_e.DoubleQuoted:
            # In an ideal world, I would *statically* disallow:
            # - "$@" and "${array[@]}"
            # - backticks like `echo hi`
            # - $(( 1+2 )) and $[] -- although useful for refactoring
            #   - not sure: ${x%%} -- could disallow this
            #     - these enters the ArgDQ state: "${a:-foo bar}" ?
            # But that would complicate the parser/evaluator.  So just rely on
            # strict_array to disallow the bad parts.
            return self.word_ev.EvalDoubleQuotedToString(node)

        if node.tag == expr_e.SingleQuoted:
            return word_eval.EvalSingleQuoted(node)

        if node.tag == expr_e.BracedVarSub:
            return self.word_ev.EvalBracedVarSubToString(node)

        if node.tag == expr_e.SimpleVarSub:
            return self.word_ev.EvalSimpleVarSubToString(node.token)

        if node.tag == expr_e.Unary:
            child = self.EvalExpr(node.child)
            if node.op.id == Id.Arith_Minus:
                return -child
            if node.op.id == Id.Arith_Tilde:
                return ~child
            if node.op.id == Id.Expr_Not:
                return not child

            raise NotImplementedError(node.op.id)

        if node.tag == expr_e.Binary:
            left = self.EvalExpr(node.left)
            right = self.EvalExpr(node.right)

            if node.op.id == Id.Arith_Plus:
                return left + right
            if node.op.id == Id.Arith_Minus:
                return left - right
            if node.op.id == Id.Arith_Star:
                return left * right
            if node.op.id == Id.Arith_Slash:
                # NOTE: from __future__ import division changes 5/2!
                # But just make it explicit.
                return float(left) / right  # floating point division

            if node.op.id == Id.Expr_Div:
                return left // right  # integer divison
            if node.op.id == Id.Expr_Mod:
                return left % right

            if node.op.id == Id.Arith_Caret:  # Exponentiation
                return left**right

            # Bitwise
            if node.op.id == Id.Arith_Amp:
                return left & right
            if node.op.id == Id.Arith_Pipe:
                return left | right
            if node.op.id == Id.Expr_Xor:
                return left ^ right
            if node.op.id == Id.Arith_DGreat:
                return left >> right
            if node.op.id == Id.Arith_DLess:
                return left << right

            # Logical
            if node.op.id == Id.Expr_And:
                return left and right
            if node.op.id == Id.Expr_Or:
                return left or right

            raise NotImplementedError(node.op.id)

        if node.tag == expr_e.Range:  # 1:10  or  1:10:2
            lower = self.EvalExpr(node.lower)
            upper = self.EvalExpr(node.upper)
            return xrange(lower, upper)

        if node.tag == expr_e.Slice:  # a[:0]
            lower = self.EvalExpr(node.lower) if node.lower else None
            upper = self.EvalExpr(node.upper) if node.upper else None
            return slice(lower, upper)

        if node.tag == expr_e.Compare:
            left = self.EvalExpr(node.left)
            result = True  # Implicit and
            for op, right_expr in zip(node.ops, node.comparators):

                right = self.EvalExpr(right_expr)

                if op.id == Id.Arith_Less:
                    result = left < right
                elif op.id == Id.Arith_Great:
                    result = left > right
                elif op.id == Id.Arith_GreatEqual:
                    result = left >= right
                elif op.id == Id.Arith_LessEqual:
                    result = left <= right
                elif op.id == Id.Arith_DEqual:
                    result = left == right

                elif op.id == Id.Expr_In:
                    result = left in right
                elif op.id == Id.Node_NotIn:
                    result = left not in right

                elif op.id == Id.Expr_Is:
                    result = left is right
                elif op.id == Id.Node_IsNot:
                    result = left is not right

                else:
                    try:
                        if op.id == Id.Arith_Tilde:
                            result = self._EvalMatch(left, right, True)

                        elif op.id == Id.Expr_NotTilde:
                            result = not self._EvalMatch(left, right, False)

                        else:
                            raise AssertionError(op.id)
                    except RuntimeError as e:
                        # Status 2 indicates a regex parse error.  This is fatal in OSH but
                        # not in bash, which treats [[ like a command with an exit code.
                        e_die("Invalid regex %r",
                              right,
                              span_id=op.span_id,
                              status=2)

                if not result:
                    return result

                left = right
            return result

        if node.tag == expr_e.IfExp:
            b = self.EvalExpr(node.test)
            if b:
                return self.EvalExpr(node.body)
            else:
                return self.EvalExpr(node.orelse)

        if node.tag == expr_e.List:
            return [self.EvalExpr(e) for e in node.elts]

        if node.tag == expr_e.Tuple:
            return tuple(self.EvalExpr(e) for e in node.elts)

        if node.tag == expr_e.Dict:
            # NOTE: some keys are expr.Const
            keys = [self.EvalExpr(e) for e in node.keys]

            values = []
            for i, e in enumerate(node.values):
                if e.tag == expr_e.Implicit:
                    v = self.LookupVar(keys[i])  # {name}
                else:
                    v = self.EvalExpr(e)
                values.append(v)

            return dict(zip(keys, values))

        if node.tag == expr_e.ListComp:

            # TODO:
            # - Consolidate with command_e.OilForIn in osh/cmd_exec.py?
            # - Do I have to push a temp frame here?
            #   Hm... lexical or dynamic scope is an issue.
            result = []
            comp = node.generators[0]
            obj = self.EvalExpr(comp.iter)

            # TODO: Handle x,y etc.
            iter_name = comp.lhs[0].name.val

            if isinstance(obj, str):
                e_die("Strings aren't iterable")
            else:
                it = obj.__iter__()

            while True:
                try:
                    loop_val = it.next()  # e.g. x
                except StopIteration:
                    break
                self.mem.SetVar(lvalue.Named(iter_name), value.Obj(loop_val),
                                scope_e.LocalOnly)

                if comp.cond:
                    b = self.EvalExpr(comp.cond)
                else:
                    b = True

                if b:
                    item = self.EvalExpr(node.elt)  # e.g. x*2
                    result.append(item)

            return result

        if node.tag == expr_e.GeneratorExp:
            comp = node.generators[0]
            obj = self.EvalExpr(comp.iter)

            # TODO: Support (x for x, y in ...)
            iter_name = comp.lhs[0].name.val

            it = obj.__iter__()

            # TODO: There is probably a much better way to do this!
            #       The scope of the loop variable is wrong, etc.

            def _gen():
                while True:
                    try:
                        loop_val = it.next()  # e.g. x
                    except StopIteration:
                        break
                    self.mem.SetVar(lvalue.Named(iter_name),
                                    value.Obj(loop_val), scope_e.LocalOnly)

                    if comp.cond:
                        b = self.EvalExpr(comp.cond)
                    else:
                        b = True

                    if b:
                        item = self.EvalExpr(node.elt)  # e.g. x*2
                        yield item

            return _gen()

        if node.tag == expr_e.Lambda:
            return objects.Lambda(node, self.ex)

        if node.tag == expr_e.FuncCall:
            func = self.EvalExpr(node.func)
            pos_args, named_args = self.EvalArgList(node.args)
            ret = func(*pos_args, **named_args)
            return ret

        if node.tag == expr_e.Subscript:
            obj = self.EvalExpr(node.obj)
            index = self._EvalIndices(node.indices)
            return obj[index]

        # TODO: obj.method() should be separate
        if node.tag == expr_e.Attribute:  # obj.attr
            o = self.EvalExpr(node.obj)
            id_ = node.op.id
            if id_ == Id.Expr_Dot:
                name = node.attr.val
                # TODO: Does this do the bound method thing we do NOT want?
                return getattr(o, name)

            if id_ == Id.Expr_RArrow:  # d->key is like d['key']
                name = node.attr.val
                return o[name]

            if id_ == Id.Expr_DColon:  # StaticName::member
                raise NotImplementedError(id_)

                # TODO: We should prevent virtual lookup here?  This is a pure static
                # namespace lookup?
                # But Python doesn't any hook for this.
                # Maybe we can just check that it's a module?  And modules don't lookup
                # in a supertype or __class__, etc.

            raise AssertionError(id_)

        if node.tag == expr_e.RegexLiteral:  # obj.attr
            # TODO: Should this just be an object that ~ calls?
            return objects.Regex(self.EvalRegex(node.regex))

        if node.tag == expr_e.ArrayLiteral:  # obj.attr
            items = [self.EvalExpr(item) for item in node.items]
            if items:
                # Determine type at runtime?  If we have something like @[(i) (j)]
                # then we don't know its type until runtime.

                first = items[0]
                if isinstance(first, bool):
                    return objects.BoolArray(bool(x) for x in items)
                elif isinstance(first, int):
                    return objects.IntArray(int(x) for x in items)
                elif isinstance(first, float):
                    return objects.FloatArray(float(x) for x in items)
                elif isinstance(first, str):
                    return objects.StrArray(str(x) for x in items)
                else:
                    raise AssertionError(first)
            else:
                # TODO: Should this have an unknown type?
                # What happens when you mutate or extend it?  You have to make sure
                # that the type tags match?
                return objects.BoolArray(items)

        raise NotImplementedError(node.__class__.__name__)
Exemple #27
0
 def _WriteFdToMem(self, fd_name, fd):
     # type: (str, int) -> None
     if self.mem:
         self.mem.SetVar(lvalue.Named(fd_name), value.Str(str(fd)),
                         scope_e.Dynamic)
Exemple #28
0
    def Run(self, cmd_val):
        # type: (cmd_value__Assign) -> int
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()
        attrs = flag_spec.Parse('new_var', arg_r)
        arg = arg_types.new_var(attrs.attrs)

        status = 0

        if arg.f:
            names = arg_r.Rest()
            if len(names):
                # NOTE: in bash, -f shows the function body, while -F shows the name.
                # Right now we just show the name.
                status = self._PrintFuncs(names)
            else:
                e_usage('passed -f without args')
            return status

        if arg.F:
            names = arg_r.Rest()
            if len(names):
                status = self._PrintFuncs(names)
            else:  # weird bash quirk: they're printed in a different format!
                for func_name in sorted(self.funcs):
                    print('declare -f %s' % (func_name))
            return status

        if arg.p:  # Lookup and print variables.
            return _PrintVariables(self.mem, cmd_val, attrs, True)
        elif len(cmd_val.pairs) == 0:
            return _PrintVariables(self.mem, cmd_val, attrs, False)

        #
        # Set variables
        #

        #raise error.Usage("doesn't understand %s" % cmd_val.argv[1:])
        if cmd_val.builtin_id == builtin_i.local:
            lookup_mode = scope_e.LocalOnly
        else:  # declare/typeset
            if arg.g:
                lookup_mode = scope_e.GlobalOnly
            else:
                lookup_mode = scope_e.LocalOnly

        flags = 0
        if arg.x == '-':
            flags |= state.SetExport
        if arg.r == '-':
            flags |= state.SetReadOnly
        if arg.n == '-':
            flags |= state.SetNameref

        flags_to_clear = 0
        if arg.x == '+':
            flags |= state.ClearExport
        if arg.r == '+':
            flags |= state.ClearReadOnly
        if arg.n == '+':
            flags |= state.ClearNameref

        for pair in cmd_val.pairs:
            rval = pair.rval
            # declare -a foo=(a b); declare -a foo;  should not reset to empty array
            if rval is None and (arg.a or arg.A):
                old_val = self.mem.GetVar(pair.var_name)
                if arg.a:
                    if old_val.tag_() != value_e.MaybeStrArray:
                        rval = value.MaybeStrArray([])
                elif arg.A:
                    if old_val.tag_() != value_e.AssocArray:
                        rval = value.AssocArray({})

            rval = _ReconcileTypes(rval, arg.a, arg.A, pair.spid)
            self.mem.SetVar(lvalue.Named(pair.var_name),
                            rval,
                            lookup_mode,
                            flags=flags)

        return status
Exemple #29
0
def SetLocalString(mem, name, s):
  # type: (state.Mem, str, str) -> None
  """Bind a local string."""
  assert isinstance(s, str)
  mem.SetValue(lvalue.Named(name), value.Str(s), scope_e.LocalOnly)
Exemple #30
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