コード例 #1
0
  def __init__(self, mem, fd_state, status_lines, funcs, readline, completion,
               comp_lookup, exec_opts, arena):
    """
    Args:
      mem: Mem instance for storing variables
      fd_state: FdState() for managing descriptors
      status_lines: shared with completion.  TODO: Move this to the end.
      funcs: registry of functions (these names are completed)
      completion: completion module, if available
      comp_lookup: completion pattern/action
      exec_opts: ExecOpts
      arena: for printing error locations
    """
    self.mem = mem
    self.fd_state = fd_state
    self.status_lines = status_lines
    # function space is different than var space.  Not hierarchical.
    self.funcs = funcs
    self.completion = completion
    # Completion hooks, set by 'complete' builtin.
    self.comp_lookup = comp_lookup
    # This is for shopt and set -o.  They are initialized by flags.
    self.exec_opts = exec_opts
    self.exec_opts.readline = readline
    self.arena = arena

    self.splitter = legacy.SplitContext(self.mem)
    self.word_ev = word_eval.NormalWordEvaluator(
        mem, exec_opts, self.splitter, self)
    self.arith_ev = expr_eval.ArithEvaluator(mem, exec_opts, self.word_ev)
    self.bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, self.word_ev)

    self.traps = {}  # signal/hook name -> callable
    self.nodes_to_run = []  # list of nodes, appended to by signal handlers
    self.dir_stack = state.DirStack()

    # TODO: Pass these in from main()
    self.aliases = {}  # alias name -> string
    self.targets = []  # make syntax enters stuff here -- Target()
                       # metaprogramming or regular target syntax
                       # Whether argv[0] is make determines if it is executed

    self.waiter = process.Waiter()
    # sleep 5 & puts a (PID, job#) entry here.  And then "jobs" displays it.
    self.job_state = process.JobState()

    self.loop_level = 0  # for detecting bad top-level break/continue

    self.tracer = Tracer(exec_opts, mem, self.word_ev)
    self.check_command_sub_status = False  # a hack
コード例 #2
0
  def __init__(self, mem, fd_state, funcs, comp_lookup, exec_opts, parse_ctx,
               devtools):
    """
    Args:
      mem: Mem instance for storing variables
      fd_state: FdState() for managing descriptors
      funcs: dict of functions
      comp_lookup: registry of completion hooks
      exec_opts: ExecOpts
      parse_ctx: for instantiating parsers
    """
    self.mem = mem
    self.fd_state = fd_state
    self.funcs = funcs
    # Completion hooks, set by 'complete' builtin.
    self.comp_lookup = comp_lookup
    # This is for shopt and set -o.  They are initialized by flags.
    self.exec_opts = exec_opts
    self.parse_ctx = parse_ctx
    self.arena = parse_ctx.arena
    self.aliases = parse_ctx.aliases  # alias name -> string
    self.dumper = devtools.dumper
    self.debug_f = devtools.debug_f  # Used by ShellFuncAction too

    self.splitter = legacy.SplitContext(self.mem)
    self.word_ev = word_eval.NormalWordEvaluator(mem, exec_opts, self.splitter,
                                                 self.arena, self)
    self.arith_ev = expr_eval.ArithEvaluator(mem, exec_opts, self.word_ev,
                                             self.arena)

    self.bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, self.word_ev,
                                           self.arena)

    self.traps = {}  # signal/hook name -> callable
    self.nodes_to_run = []  # list of nodes, appended to by signal handlers
    self.dir_stack = state.DirStack()

    self.targets = []  # make syntax enters stuff here -- Target()
                       # metaprogramming or regular target syntax
                       # Whether argv[0] is make determines if it is executed

    self.waiter = process.Waiter()
    # sleep 5 & puts a (PID, job#) entry here.  And then "jobs" displays it.
    self.job_state = process.JobState()
    self.tracer = Tracer(parse_ctx, exec_opts, mem, self.word_ev,
                         devtools.trace_f)

    self.loop_level = 0  # for detecting bad top-level break/continue
    self.check_command_sub_status = False  # a hack
コード例 #3
0
ファイル: cmd_exec.py プロジェクト: neuroradiology/oil
  def __init__(self, mem, status_lines, funcs, completion, comp_lookup,
               exec_opts, arena):
    """
    Args:
      mem: Mem instance for storing variables
      status_lines: shared with completion.  TODO: Move this to the end.
      funcs: registry of functions (these names are completed)
      completion: completion module, if available
      comp_lookup: completion pattern/action
      exec_opts: ExecOpts
      arena: for printing error locations
    """
    self.mem = mem
    self.status_lines = status_lines  
    # function space is different than var space.  Not hierarchical.
    self.funcs = funcs
    self.completion = completion
    # Completion hooks, set by 'complete' builtin.
    self.comp_lookup = comp_lookup
    # This is for shopt and set -o.  They are initialized by flags.
    self.exec_opts = exec_opts
    self.arena = arena

    self.ev = word_eval.NormalWordEvaluator(mem, exec_opts, self)
    self.arith_ev = expr_eval.ArithEvaluator(mem, exec_opts, self.ev)
    self.bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, self.ev)

    self.traps = {}
    self.fd_state = process.FdState()
    self.dir_stack = []

    # TODO: Pass these in from main()
    self.aliases = {}  # alias name -> string
    self.targets = []  # make syntax enters stuff here -- Target()
                       # metaprogramming or regular target syntax
                       # Whether argv[0] is make determines if it is executed

    self.waiter = process.Waiter()
    # sleep 5 & puts a (PID, job#) entry here.  And then "jobs" displays it.
    self.job_state = process.JobState()
コード例 #4
0
ファイル: test_builtin.py プロジェクト: jedahan/oil
def Test(argv, need_right_bracket):
    """The test/[ builtin.

  The only difference between test and [ is that [ needs a matching ].
  """
    if need_right_bracket:
        if not argv or argv[-1] != ']':
            util.error('[: missing closing ]')
            return 2
        del argv[-1]

    w_parser = _StringWordEmitter(argv)
    b_parser = bool_parse.BoolParser(w_parser)

    # There is a fundamental ambiguity due to poor language design, in cases like:
    # [ -z ]
    # [ -z -a ]
    # [ -z -a ] ]
    #
    # See posixtest() in bash's test.c:
    # "This is an implementation of a Posix.2 proposal by David Korn."
    # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args.  We do
    # the same here.
    #
    # Another ambiguity:
    # -a is both a unary prefix operator and an infix operator.  How to fix this
    # ambiguity?

    bool_node = None
    n = len(argv)
    try:
        if n == 0:
            return 1  # [ ] is False
        elif n == 1:
            bool_node = _StringWordTest(argv[0])
        elif n == 2:
            bool_node = _TwoArgs(argv)
        elif n == 3:
            bool_node = _ThreeArgs(argv)
        if n == 4:
            a0 = argv[0]
            if a0 == '!':
                child = _ThreeArgs(argv[1:])
                bool_node = ast.LogicalNot(child)
            elif a0 == '(' and argv[3] == ')':
                bool_node = _TwoArgs(argv[1:3])
            else:
                pass  # fallthrough

        if bool_node is None:
            bool_node = b_parser.ParseForBuiltin()

    except util.ParseError as e:
        # TODO: There should be a nice method to print argv.  And some way to point
        # to the error.
        log("Error parsing %s", argv)
        util.error("test: %s", e.UserErrorString())
        return 2  # parse error is 2

    # mem: Don't need it for BASH_REMATCH?  Or I guess you could support it
    # exec_opts: don't need it, but might need it later

    mem = None  # Not necessary
    word_ev = _WordEvaluator()
    arena = None

    # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]].  This is a
    # weird case of [[ being less strict.
    class _DummyExecOpts():
        def __init__(self):
            self.strict_arith = True

    exec_opts = _DummyExecOpts()

    bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev, arena)
    try:
        b = bool_ev.Eval(bool_node)
    except util.FatalRuntimeError as e:
        # e.g. [ -t xxx ]
        # TODO: Printing the location would be nice.
        util.error('test: %s', e.UserErrorString())
        return 2  # because this is more like a parser error.

    status = 0 if b else 1
    return status
コード例 #5
0
ファイル: cmd_exec.py プロジェクト: harlowja/oil
  def _Execute(self, node):
    """
    Args:
      node: of type AstNode
    """
    redirects = self._EvalRedirects(node)

    # TODO: Only eval argv[0] once.  It can have side effects!
    if node.tag == command_e.SimpleCommand:
      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)

      more_env = self.mem.GetExported()
      self._EvalEnv(node.more_env, more_env)
      thunk = self._GetThunkForSimpleCommand(argv, more_env)

      # Don't waste a process if we'd launch one anyway.
      if thunk.IsExternal():
        p = process.Process(thunk, fd_state=self.fd_state, redirects=redirects)
        status = p.Run()

      else:  # Internal
        #log('ARGV %s', argv)

        # NOTE: _EvalRedirects turns LST nodes into core/process.py nodes.  And
        # then we use polymorphism here.  Does it make sense to use functional
        # style based on the RedirType?  Might be easier to read.

        self.fd_state.PushFrame()
        for r in redirects:
          r.ApplyInParent(self.fd_state)

        status = thunk.RunInParent()
        restore_fd_state = thunk.ShouldRestoreFdState()

        # Special case for exec 1>&2 (with no args): we permanently change the
        # fd state.  BUT we don't want to restore later.
        # TODO: Instead of this, maybe r.ApplyPermaent(self.fd_state)?
        if restore_fd_state:
          self.fd_state.PopAndRestore()
        else:
          self.fd_state.PopAndForget()

    elif node.tag == command_e.Sentence:
      # TODO: Compile this away.
      status = self._Execute(node.command)

    elif node.tag == command_e.Pipeline:
      status = self._RunPipeline(node)

    elif node.tag == command_e.Subshell:
      # This makes sure we don't waste a process if we'd launch one anyway.
      p = self._GetProcessForNode(node.children[0])
      status = p.Run()

    elif node.tag == command_e.DBracket:
      bool_ev = expr_eval.BoolEvaluator(self.mem, self.ev)
      ok = bool_ev.Eval(node.expr)
      if ok:
        status = 0 if bool_ev.Result() else 1
      else:
        e_die('Error evaluating boolean: %s' % bool_ev.Error())

    elif node.tag == command_e.DParen:
      arith_ev = expr_eval.ArithEvaluator(self.mem, self.ev)
      ok = arith_ev.Eval(node.child)
      if ok:
        i = arith_ev.Result()
        # Negate the value: non-zero in arithmetic is true, which is zero in
        # shell land
        status = 0 if i != 0 else 1
      else:
        e_die('Error evaluating (( )): %s' % arith_ev.Error())

    elif node.tag == command_e.Assignment:
      pairs = []
      for pair in node.pairs:
        if pair.rhs:
          # RHS can be a string or array.
          val = self.ev.EvalWordToAny(pair.rhs)
          assert isinstance(val, runtime.value), val
        else:
          # 'local x' is equivalent to local x=""
          val = runtime.Str('')
        pairs.append((pair.lhs, val))

      if node.keyword == Id.Assign_Local:
        self.mem.SetLocals(pairs)
      else:
        # NOTE: could be readonly/export/etc.
        self.mem.SetLocalsOrGlobals(pairs)

      # TODO: This should be eval of RHS, unlike bash!
      status = 0

    elif node.tag == command_e.ControlFlow:
      if node.arg_word:  # Evaluate the argument
        _, val = self.ev.EvalWordToString(node.arg_word)
        assert val.tag == value_e.Str
        arg = int(val.s)  # They all take integers
      else:
        arg = 0  # return 0, break 0 levels, etc.

      raise _ControlFlow(node.token, arg)

    # The only difference between these two is that CommandList has no
    # redirects.  We already took care of that above.
    elif node.tag in (command_e.CommandList, command_e.BraceGroup):
      self.fd_state.PushFrame()
      for r in redirects:
        r.ApplyInParent(self.fd_state)

      status = 0  # for empty list
      for child in node.children:
        status = self._Execute(child)  # last status wins

      self.fd_state.PopAndRestore()

    elif node.tag == command_e.AndOr:
      #print(node.children)
      left, right = node.children
      status = self._Execute(left)

      if node.op_id == Id.Op_DPipe:
        if status != 0:
          status = self._Execute(right)
      elif node.op_id == Id.Op_DAmp:
        if status == 0:
          status = self._Execute(right)
      else:
        raise AssertionError

    elif node.tag in (command_e.While, command_e.Until):
      # TODO: Compile this out?
      if node.tag == command_e.While:
        _DonePredicate = lambda status: status != 0
      else:
        _DonePredicate = lambda status: status == 0

      while True:
        status = self._Execute(node.cond)
        done = status != 0
        if _DonePredicate(status):
          break
        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForEach:
      iter_name = node.iter_name
      if node.do_arg_iter:
        iter_list = self.mem.GetArgv()
      else:
        words = braces.BraceExpandWords(node.iter_words)
        iter_list = self.ev.EvalWordSequence(words)
        # We need word splitting and so forth
        # NOTE: This expands globs too.  TODO: We should pass in a Globber()
        # object.
      status = 0  # in case we don't loop
      for x in iter_list:
        #log('> ForEach setting %r', x)
        self.mem.SetLocal(iter_name, runtime.Str(x))
        #log('<')

        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForExpr:
      raise NotImplementedError(node.tag)

    elif node.tag == command_e.DoGroup:
      # Delegate to command list
      # TODO: This should be compiled out!
      status = self._Execute(node.child)

    elif node.tag == command_e.FuncDef:
      self.funcs[node.name] = node
      status = 0

    elif node.tag == command_e.If:
      done = False
      for arm in node.arms:
        status = self._Execute(arm.cond)
        if status == 0:
          status = self._Execute(arm.action)
          done = True
          break
      # TODO: The compiler should flatten this
      if not done and node.else_action is not None:
        status = self._Execute(node.else_action)

    elif node.tag == command_e.NoOp:
      status = 0  # make it true

    elif node.tag == command_e.Case:
      ok, val = self.ev.EvalWordToString(node.to_match)
      assert ok
      to_match = val.s

      status = 0  # If there are no arms, it should be zero?
      done = False

      for arm in node.arms:
        for pat_word in arm.pat_list:
          # NOTE: Is it OK that we're evaluating these as we go?
          ok, pat_val = self.ev.EvalWordToString(pat_word, do_fnmatch=True)
          assert ok
          #log('Matching word %r against pattern %r', to_match, pat_val.s)
          if libc.fnmatch(pat_val.s, to_match):
            status = self._Execute(arm.action)
            done = True  # TODO: Parse ;;& and for fallthrough and such?
        if done:
          break

    elif node.tag == command_e.TimeBlock:
      # TODO:
      # - When do we need RUSAGE_CHILDREN?
      # - Respect TIMEFORMAT environment variable.
      # "If this variable is not set, Bash acts as if it had the value"
      # $'\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'
      # "A trailing newline is added when the format string is displayed."

      start_t = time.time()  # calls gettimeofday() under the hood
      start_u = resource.getrusage(resource.RUSAGE_SELF)
      status = self._Execute(node.pipeline)
      end_t = time.time()
      end_u = resource.getrusage(resource.RUSAGE_SELF)

      real = end_t - start_t
      user = end_u.ru_utime - start_u.ru_utime
      sys_ = end_u.ru_stime - start_u.ru_stime
      print('real\t%.3f' % real, file=sys.stderr)
      print('user\t%.3f' % user, file=sys.stderr)
      print('sys\t%.3f' % sys_, file=sys.stderr)

    else:
      raise AssertionError(node.tag)

    if self.exec_opts.errexit and status != 0:
      if node.tag == command_e.SimpleCommand:
        # TODO: Add context
        e_die('%r command exited with status %d (%s)', argv[0], status, node.words[0])
      else:
        e_die('%r command exited with status %d', node.__class__.__name__, status)

    # TODO: Is this the right place to put it?  Does it need a stack for
    # function calls?
    self.mem.last_status = status
    return status
コード例 #6
0
def Test(argv, need_right_bracket):
    """The test/[ builtin.

  The only difference between test and [ is that [ needs a matching ].
  """
    if need_right_bracket:
        if argv[-1] != ']':
            util.error('[: missing closing ]')
            return 2
        del argv[-1]

    w_parser = _StringWordEmitter(argv)
    b_parser = bool_parse.BoolParser(w_parser)

    # There is a fundamental ambiguity due to poor language design, in cases like:
    # [ -z ]
    # [ -z -a ]
    # [ -z -a ] ]
    #
    # See posixtest() in bash's test.c:
    # "This is an implementation of a Posix.2 proposal by David Korn."
    # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args.  We do
    # the same here.
    #
    # Another ambiguity:
    # -a is both a unary prefix operator and an infix operator.  How to fix this
    # ambiguity?

    bool_node = None
    n = len(argv)
    try:
        if n == 0:
            return 1  # [ ] is False
        elif n == 1:
            bool_node = _StringWordTest(argv[0])
        elif n == 2:
            bool_node = _TwoArgs(argv)
        elif n == 3:
            bool_node = _ThreeArgs(argv)
        if n == 4:
            a0 = argv[0]
            if a0 == '!':
                child = _ThreeArgs(argv[1:])
                bool_node = ast.LogicalNot(child)
            elif a0 == '(' and argv[3] == ')':
                bool_node = _TwoArgs(argv[1:3])
            else:
                pass  # fallthrough

        if bool_node is None:
            bool_node = b_parser.ParseForBuiltin()
            #log('Bool expr %s', bool_node)

            if bool_node is None:
                for e in b_parser.Error():
                    log("test: %s", e.UserErrorString())
                # TODO: There should be a nice method to print argv.  And some way to
                # point to the error.
                log("Error parsing test/[ expression: %s", argv)
                return 2  # parse error is 2

    except util.ParseError as e:
        util.error(e.UserErrorString())
        return 2

    # mem: Don't need it for BASH_REMATCH?  Or I guess you could support it
    # exec_opts: don't need it, but might need it later

    mem = None
    exec_opts = None
    word_ev = _WordEvaluator()

    bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev)
    try:
        b = bool_ev.Eval(bool_node)
    except util.FatalRuntimeError as e:
        # e.g. [ -t xxx ]
        # TODO: Printing the location would be nice.
        print('test: %s' % e.UserErrorString(), file=sys.stderr)
        return 2

    status = 0 if b else 1
    return status
コード例 #7
0
    def Execute(self, node):
        """
    Args:
      node: of type AstNode
    """
        redirects = self._EvalRedirects(node)

        # TODO: Change this to its own enum?
        # or add EBuiltin.THROW     _throw?  For testing.
        # Is this different han exit?  exit should really be throw.  Because we
        # want to be able to unwind the stack, show stats, etc.  Exiting in the
        # middle is bad.
        # exit and _throw could be the same, except _throw takes an error message,
        # and exits 1, and shows traceback.
        cflow = EBuiltin.NONE

        # TODO: Only eval argv[0] once.  It can have side effects!
        if node.tag == command_e.SimpleCommand:
            argv = self.ev.EvalWords(node.words)
            if argv is None:
                err = self.ev.Error()
                # TODO: Throw shell exception
                raise AssertionError('Error evaluating words: %s' % err)
            more_env = self.ev.EvalEnv(node.more_env)
            if more_env is None:
                print(self.error_stack)
                # TODO: throw exception
                raise AssertionError()
            thunk = self._GetThunkForSimpleCommand(argv, more_env)

            # Don't waste a process if we'd launch one anyway.
            if thunk.IsExternal():
                p = Process(thunk, fd_state=self.fd_state, redirects=redirects)
                status = p.Run()

                if os.WIFEXITED(status):
                    status = os.WEXITSTATUS(status)
                    #print('exited with code', code)
                else:
                    sig = os.WTERMSIG(status)
                    #print('exited with signal', sig)
                    # TODO: Is this right?
                    status = 0

            else:  # Internal
                for r in redirects:
                    r.ApplyInParent(self.fd_state)

                status, cflow = thunk.RunInParent()
                restore_fd_state = thunk.ShouldRestoreFdState()

                # Special case for exec 1>&2 (with no args): we permanently change the
                # fd state.  BUT we don't want to restore later.
                #
                # TODO: Instead of this, maybe r.ApplyPermaent(self.fd_state)?
                if restore_fd_state:
                    self.fd_state.RestoreAll()
                else:
                    self.fd_state.ForgetAll()

        elif node.tag == command_e.Sentence:
            # TODO: Compile this away
            status, cflow = self.Execute(node.command)

        elif node.tag == command_e.Pipeline:
            status, cflow = self._RunPipeline(node)

        elif node.tag == command_e.Subshell:
            # This makes sure we don't waste a process if we'd launch one anyway.
            p = self._GetProcessForNode(node.children[0])
            status = p.Run()

        elif node.tag == command_e.DBracket:
            bool_ev = expr_eval.BoolEvaluator(self.mem, self.ev)
            ok = bool_ev.Eval(node.expr)
            if ok:
                status = 0 if bool_ev.Result() else 1
            else:
                raise AssertionError('Error evaluating boolean: %s' %
                                     bool_ev.Error())

        elif node.tag == command_e.DParen:
            arith_ev = expr_eval.ArithEvaluator(self.mem, self.ev)
            ok = arith_ev.Eval(node.child)
            if ok:
                i = arith_ev.Result()
                # Negate the value: non-zero in arithmetic is true, which is zero in
                # shell land
                status = 0 if i != 0 else 1
            else:
                raise AssertionError('Error evaluating (( )): %s' %
                                     arith_ev.Error())

        elif node.tag == command_e.Assignment:
            pairs = []
            for pair in node.pairs:
                # NOTE: do_glob=False, because foo=*.a makes foo equal to '*.a',
                # literally.

                # TODO: Also have to evaluate the right hand side.
                ok, val = self.ev.EvalCompoundWord(pair.rhs)
                if not ok:
                    return None
                pairs.append((pair.lhs, val))

            flags = 0  # TODO: Calculate from keyword/flags
            if node.keyword == Id.Assign_Local:
                self.mem.SetLocal(pairs, flags)
            else:  # could be readonly/export/etc.
                self.mem.SetGlobal(pairs, flags)

            # TODO: This should be eval of RHS, unlike bash!
            status = 0

        # The only difference between these two is that CommandList has no
        # redirects.  We already took care of that above.
        elif node.tag in (command_e.CommandList, command_e.BraceGroup):
            status = 0  # for empty list
            for child in node.children:
                status, cflow = self.Execute(child)  # last status wins
                if cflow in (EBuiltin.BREAK, EBuiltin.CONTINUE):
                    break

        elif node.tag == command_e.AndOr:
            #print(node.children)
            left, right = node.children
            status, cflow = self.Execute(left)

            if node.op_id == Id.Op_DPipe:
                if status != 0:
                    status, cflow = self.Execute(right)
            elif node.op_id == Id.Op_DAmp:
                if status == 0:
                    status, cflow = self.Execute(right)
            else:
                raise AssertionError

        elif node.tag == command_e.While:
            while True:
                status, _ = self.Execute(node.cond)
                if status != 0:
                    break
                status, cflow = self.Execute(node.body)  # last one wins
                if cflow == EBuiltin.BREAK:
                    cflow = EBuiltin.NONE  # reset since we respected it
                    break
                if cflow == EBuiltin.CONTINUE:
                    cflow = EBuiltin.NONE  # reset since we respected it

        elif node.tag == command_e.ForEach:
            iter_name = node.iter_name
            if node.do_arg_iter:
                iter_list = self.mem.GetArgv()
            else:
                iter_list = self.ev.EvalWords(node.iter_words)
                # We need word splitting and so forth
                # NOTE: This expands globs too.  TODO: We should pass in a Globber()
                # object.
            status = 0  # in case we don't loop
            cflow = EBuiltin.NONE
            for x in iter_list:
                self.mem.SetSimpleVar(iter_name, Value.FromString(x))

                status, cflow = self.Execute(node.body)

                if cflow == EBuiltin.BREAK:
                    cflow = EBuiltin.NONE  # reset since we respected it
                    break
                if cflow == EBuiltin.CONTINUE:
                    cflow = EBuiltin.NONE  # reset since we respected it

        elif node.tag == command_e.DoGroup:
            # Delegate to command list
            # TODO: This should be compiled out!
            status, cflow = self.Execute(node.child)

        elif node.tag == command_e.FuncDef:
            self.funcs[node.name] = node
            status = 0

        elif node.tag == command_e.If:
            done = False
            for arm in node.arms:
                status, _ = self.Execute(arm.cond)
                if status == 0:
                    status, _ = self.Execute(arm.action)
                    done = True
                    break
            # TODO: The compiler should flatten this
            if not done and node.else_action is not None:
                status, _ = self.Execute(node.else_action)

        elif node.tag == command_e.NoOp:
            status = 0  # make it true

        elif node.tag == command_e.Case:
            raise NotImplementedError

        else:
            raise AssertionError(node.tag)

        if self.exec_opts.errexit:
            if status != 0:
                # TODO: token should be set to what?  Is it node.begin_word and
                # node.end_word?
                token = None
                tb = self.mem.GetTraceback(token)
                self._SetException(
                    tb, "Command %s exited with code %d" % ('TODO', status))
                # cflow should be EXCEPT

        # TODO: Is this the right place to put it?  Does it need a stack for
        # function calls?
        self.mem.last_status = status
        return status, cflow
コード例 #8
0
  def _Execute(self, node):
    """
    Args:
      node: of type AstNode
    """
    redirects = self._EvalRedirects(node)

    # TODO: Only eval argv[0] once.  It can have side effects!
    if node.tag == command_e.SimpleCommand:
      words = braces.BraceExpandWords(node.words)
      argv = self.ev.EvalWordSequence(words)

      if argv is None:
        self.error_stack.extend(self.ev.Error())
        raise _FatalError()
      more_env = self.mem.GetExported()
      self._EvalEnv(node.more_env, more_env)
      thunk = self._GetThunkForSimpleCommand(argv, more_env)

      # Don't waste a process if we'd launch one anyway.
      if thunk.IsExternal():
        p = Process(thunk, fd_state=self.fd_state, redirects=redirects)
        status = p.Run()

      else:  # Internal
        #log('ARGV %s', argv)

        # NOTE: _EvalRedirects turns LST nodes into core/process.py nodes.  And
        # then we use polymorphism here.  Does it make sense to use functional
        # style based on the RedirType?  Might be easier to read.

        self.fd_state.PushFrame()
        for r in redirects:
          r.ApplyInParent(self.fd_state)

        status = thunk.RunInParent()
        restore_fd_state = thunk.ShouldRestoreFdState()

        # Special case for exec 1>&2 (with no args): we permanently change the
        # fd state.  BUT we don't want to restore later.
        # TODO: Instead of this, maybe r.ApplyPermaent(self.fd_state)?
        if restore_fd_state:
          self.fd_state.PopAndRestore()
        else:
          self.fd_state.PopAndForget()

    elif node.tag == command_e.Sentence:
      # TODO: Compile this away.
      status = self._Execute(node.command)

    elif node.tag == command_e.Pipeline:
      status = self._RunPipeline(node)

    elif node.tag == command_e.Subshell:
      # This makes sure we don't waste a process if we'd launch one anyway.
      p = self._GetProcessForNode(node.children[0])
      status = p.Run()

    elif node.tag == command_e.DBracket:
      bool_ev = expr_eval.BoolEvaluator(self.mem, self.ev)
      ok = bool_ev.Eval(node.expr)
      if ok:
        status = 0 if bool_ev.Result() else 1
      else:
        raise AssertionError('Error evaluating boolean: %s' % bool_ev.Error())

    elif node.tag == command_e.DParen:
      arith_ev = expr_eval.ArithEvaluator(self.mem, self.ev)
      ok = arith_ev.Eval(node.child)
      if ok:
        i = arith_ev.Result()
        # Negate the value: non-zero in arithmetic is true, which is zero in
        # shell land
        status = 0 if i != 0 else 1
      else:
        raise AssertionError('Error evaluating (( )): %s' % arith_ev.Error())

    elif node.tag == command_e.Assignment:
      pairs = []
      for pair in node.pairs:
        # RHS can be a string or array.
        ok, val = self.ev.EvalWordToAny(pair.rhs)
        assert isinstance(val, runtime.value), val
        #log('RHS %s -> %s', pair.rhs, val)
        if not ok:
          self.error_stack.extend(self.ev.Error())
          raise _FatalError()
        pairs.append((pair.lhs, val))

      if node.keyword == Id.Assign_Local:
        self.mem.SetLocals(pairs)
      else:  # could be readonly/export/etc.
        self.mem.SetGlobals(pairs)

      # TODO: This should be eval of RHS, unlike bash!
      status = 0

    elif node.tag == command_e.ControlFlow:
      if node.arg_word:  # Evaluate the argument
        ok, val = self.ev.EvalWordToString(node.arg_word)
        if not ok:
          self.error_stack.extend(self.ev.Error())
          raise _FatalError()
        assert val.tag == value_e.Str
        arg = int(val.s)  # They all take integers
      else:
        arg = 0  # return 0, break 0 levels, etc.

      raise _ControlFlow(node.token, arg)

    # The only difference between these two is that CommandList has no
    # redirects.  We already took care of that above.
    elif node.tag in (command_e.CommandList, command_e.BraceGroup):
      status = 0  # for empty list
      for child in node.children:
        status = self._Execute(child)  # last status wins

    elif node.tag == command_e.AndOr:
      #print(node.children)
      left, right = node.children
      status = self._Execute(left)

      if node.op_id == Id.Op_DPipe:
        if status != 0:
          status = self._Execute(right)
      elif node.op_id == Id.Op_DAmp:
        if status == 0:
          status = self._Execute(right)
      else:
        raise AssertionError

    elif node.tag in (command_e.While, command_e.Until):
      # TODO: Compile this out?
      if node.tag == command_e.While:
        _DonePredicate = lambda status: status != 0
      else:
        _DonePredicate = lambda status: status == 0

      while True:
        status = self._Execute(node.cond)
        done = status != 0
        if _DonePredicate(status):
          break
        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForEach:
      iter_name = node.iter_name
      if node.do_arg_iter:
        iter_list = self.mem.GetArgv()
      else:
        words = braces.BraceExpandWords(node.iter_words)
        iter_list = self.ev.EvalWordSequence(words)
        # We need word splitting and so forth
        # NOTE: This expands globs too.  TODO: We should pass in a Globber()
        # object.
      status = 0  # in case we don't loop
      for x in iter_list:
        #log('> ForEach setting %r', x)
        self.mem.SetLocal(iter_name, runtime.Str(x))
        #log('<')

        try:
          status = self._Execute(node.body)  # last one wins
        except _ControlFlow as e:
          if e.IsBreak():
            status = 0
            break
          elif e.IsContinue():
            status = 0
            continue
          else:  # return needs to pop up more
            raise

    elif node.tag == command_e.ForExpr:
      raise NotImplementedError(node.tag)

    elif node.tag == command_e.DoGroup:
      # Delegate to command list
      # TODO: This should be compiled out!
      status = self._Execute(node.child)

    elif node.tag == command_e.FuncDef:
      self.funcs[node.name] = node
      status = 0

    elif node.tag == command_e.If:
      done = False
      for arm in node.arms:
        status = self._Execute(arm.cond)
        if status == 0:
          status = self._Execute(arm.action)
          done = True
          break
      # TODO: The compiler should flatten this
      if not done and node.else_action is not None:
        status = self._Execute(node.else_action)

    elif node.tag == command_e.NoOp:
      status = 0  # make it true

    elif node.tag == command_e.Case:
      ok, val = self.ev.EvalWordToString(node.to_match)
      assert ok
      to_match = val.s

      status = 0  # If there are no arms, it should be zero?
      done = False
      for arm in node.arms:
        for pat_word in arm.pat_list:
          # NOTE: Is it OK that we're evaluating these as we go?
          ok, pat_val = self.ev.EvalWordToString(pat_word, do_fnmatch=True)
          assert ok
          #log('Matching word %r against pattern %r', to_match, pat_val.s)
          if libc.fnmatch(pat_val.s, to_match):
            status = self._Execute(arm.action)
            done = True  # TODO: Parse ;;& and for fallthrough and such?
        if done:
          break

    else:
      raise AssertionError(node.tag)

    if self.exec_opts.errexit:
      if status != 0:
        # TODO: token should be set to what?  Is it node.begin_word and
        # node.end_word?
        token = None
        tb = self.mem.GetTraceback(token)
        self._SetException(tb,
            "Command %s exited with code %d" % ('TODO', status))
        # TODO: raise _ControlFlow?  Except?
        # Dummy?

    # TODO: Is this the right place to put it?  Does it need a stack for
    # function calls?
    self.mem.last_status = status
    return status