Exemplo n.º 1
0
    def _UnsetVar(self, arg, spid, proc_fallback):
        # type: (str, int, bool) -> bool
        """
    Returns:
      bool: whether the 'unset' builtin should succeed with code 0.
    """
        arena = self.parse_ctx.arena

        a_parser = self.parse_ctx.MakeArithParser(arg)
        arena.PushSource(source.ArgvWord(spid))
        try:
            anode = a_parser.Parse()
        except error.Parse as e:
            # show parse error
            ui.PrettyPrintError(e, arena)
            # point to word
            e_usage('Invalid unset expression', span_id=spid)
        finally:
            arena.PopSource()

        lval = self.arith_ev.EvalArithLhs(anode, spid)

        # Prevent attacks like these by default:
        #
        # unset -v 'A["$(echo K; rm *)"]'
        if not self.exec_opts.eval_unsafe_arith(
        ) and lval.tag_() != lvalue_e.Named:
            e_die(
                'Expected a variable name.  Expressions are allowed with shopt -s eval_unsafe_arith',
                span_id=spid)

        #log('lval %s', lval)
        found = False
        try:
            # not strict
            found = self.mem.Unset(lval, scope_e.Dynamic, False)
        except error.Runtime as e:
            # note: in bash, myreadonly=X fails, but declare myreadonly=X doens't
            # fail because it's a builtin.  So I guess the same is true of 'unset'.
            e.span_id = spid
            ui.PrettyPrintError(e, arena)
            return False

        if proc_fallback and not found:
            if arg in self.funcs:
                del self.funcs[arg]

        return True
Exemplo n.º 2
0
def main(argv):
  # type: (List[str]) -> int
  arena = alloc.Arena()
  arena.PushSource(source.Stdin(''))

  parse_opts = parse_lib.OilParseOptions()
  # Dummy value; not respecting aliases!
  aliases = {}  # type: Dict[str, Any]
  # parse `` and a[x+1]=bar differently

  loader = pyutil.GetResourceLoader()
  oil_grammar = meta.LoadOilGrammar(loader)

  parse_ctx = parse_lib.ParseContext(arena, parse_opts, aliases, oil_grammar,
                                     one_pass_parse=True)

  line_reader = reader.FileLineReader(mylib.Stdin(), arena)
  c_parser = parse_ctx.MakeOshParser(line_reader)

  try:
    node = main_loop.ParseWholeFile(c_parser)
  except util.ParseError as e:
    ui.PrettyPrintError(e, arena)
    return 2
  assert node is not None

  tree = node.AbbreviatedTree()
  #tree = node.PrettyTree()

  ast_f = fmt.DetectConsoleOutput(mylib.Stdout())
  fmt.PrintTree(tree, ast_f)
  ast_f.write('\n')

  return 0
Exemplo n.º 3
0
def main(argv):
  # type: (List[str]) -> int
  arena = alloc.Arena()
  arena.PushSource(source.Stdin(''))

  loader = pyutil.GetResourceLoader()
  oil_grammar = meta.LoadOilGrammar(loader)

  parse_ctx = None
  e_parser = expr_parse.ExprParser(parse_ctx, oil_grammar)

  line_lexer = lexer.LineLexer('', arena)
  line_reader = reader.FileLineReader(sys.stdin, arena)
  lex = lexer.Lexer(line_lexer, line_reader)

  try:
    pnode, _ = e_parser.Parse(lex, grammar_nt.command_expr)
  except error.Parse as e:
    ui.PrettyPrintError(e, arena)
    return 2

  #print(pnode)
  tr = expr_to_ast.Transformer(oil_grammar)
  node = tr.Expr(pnode)

  assert node is not None

  tree = node.AbbreviatedTree()
  #tree = node.PrettyTree()

  ast_f = fmt.DetectConsoleOutput(sys.stdout)
  fmt.PrintTree(tree, ast_f)
  ast_f.write('\n')

  return 0
Exemplo n.º 4
0
    def Run(self):
        # type: () -> None
        val = self.mem.GetValue('PROMPT_COMMAND')
        if val.tag_() != value_e.Str:
            return

        # PROMPT_COMMAND almost never changes, so we try to cache its parsing.
        # This avoids memory allocations.
        prompt_cmd = cast(value__Str, val).s
        node = self.parse_cache.get(prompt_cmd)
        if node is None:
            line_reader = reader.StringLineReader(prompt_cmd, self.arena)
            c_parser = self.parse_ctx.MakeOshParser(line_reader)

            # NOTE: This is similar to CommandEvaluator.ParseTrapCode().
            # TODO: Add spid
            with alloc.ctx_Location(self.arena,
                                    source.PromptCommand(runtime.NO_SPID)):
                try:
                    node = main_loop.ParseWholeFile(c_parser)
                except error.Parse as e:
                    ui.PrettyPrintError(e, self.arena)
                    return  # don't execute

            self.parse_cache[prompt_cmd] = node

        # Save this so PROMPT_COMMAND can't set $?
        with state.ctx_Status(self.mem):
            # Catches fatal execution error
            self.cmd_ev.ExecuteAndCatch(node)
Exemplo n.º 5
0
def main(argv):
    # type: (List[str]) -> int
    pool = alloc.Pool()
    arena = pool.NewArena()
    arena.PushSource('<stdin>')

    line_reader = reader.FileLineReader(sys.stdin, arena)
    # Dummy value; not respecting aliases!
    aliases = {}  # type: Dict[str, Any]
    # parse `` and a[x+1]=bar differently
    parse_ctx = parse_lib.ParseContext(arena, aliases, one_pass_parse=True)
    c_parser = parse_ctx.MakeOshParser(line_reader)

    try:
        node = main_loop.ParseWholeFile(c_parser)
    except util.ParseError as e:
        ui.PrettyPrintError(e, arena)
        return 2
    assert node is not None

    tree = node.AbbreviatedTree()
    #tree = node.PrettyTree()

    ast_f = fmt.DetectConsoleOutput(sys.stdout)
    fmt.PrintTree(tree, ast_f)
    ast_f.write('\n')

    return 0
Exemplo n.º 6
0
def punshow():
    args = parser.parse_args()
    logger.debug("argparsed: %r", args)

    if args.allowed_executable_varsubs:
        allowed_executable_varsubs.update(args.allowed_executable_varsubs)

    # this is a lie; we'll look up against PATH without it--but it might be a common mis-use?
    assert ("SHELL_RUNTIME_DEPENDENCY_PATH"
            in os.environ), "SHELL_RUNTIME_DEPENDENCY_PATH must be set"

    # adopt the runtime dependency path for resolving external executables
    os.environ["PATH"] = os.environ["SHELL_RUNTIME_DEPENDENCY_PATH"]

    try:
        if len(args.scripts) == 0:
            resolved = ResolvedScript()
            # TODO: before this is okay, you've gotta move everything else that prints to a log, stderr, or a dynamic logging function that can choose depending on mode.
            resolved_scripts["<stdin>"] = resolved
            resolved.write_to()

        for script in args.scripts:
            resolved = resolve_script(os.path.abspath(script))

    except IOError as e:
        sys.stderr.write("whoooo buddy " + str(e))
        return 2
    except ResolutionError as e:
        e.print_if_needed()
        # return e.exit_status
    except error._ErrorWithLocation as e:
        ui.PrettyPrintError(e)
Exemplo n.º 7
0
    def _UnsetVar(self, arg, spid, proc_fallback):
        # type: (str, int, bool) -> bool
        """
    Returns:
      bool: whether the 'unset' builtin should succeed with code 0.
    """
        arena = self.parse_ctx.arena
        a_parser = self.parse_ctx.MakeArithParser(arg)

        with alloc.ctx_Location(arena, source.ArgvWord(spid)):
            try:
                anode = a_parser.Parse()
            except error.Parse as e:
                ui.PrettyPrintError(e, arena)  # show parse error
                e_usage('Invalid unset expression', span_id=spid)

        lval = self.arith_ev.EvalArithLhs(anode, spid)

        # Prevent attacks like these by default:
        #
        # unset -v 'A["$(echo K; rm *)"]'
        if not self.exec_opts.eval_unsafe_arith(
        ) and lval.tag_() != lvalue_e.Named:
            e_usage(
                'expected a variable name.  shopt -s eval_unsafe_arith allows expressions',
                span_id=spid)

        #log('lval %s', lval)
        found = False
        try:
            # Note: This has 'setvar' semantics.  It could be 'setref' too?
            # So it composes?
            found = self.mem.Unset(lval, False)
        except error.Runtime as e:
            # note: in bash, myreadonly=X fails, but declare myreadonly=X doens't
            # fail because it's a builtin.  So I guess the same is true of 'unset'.
            e.span_id = spid
            ui.PrettyPrintError(e, arena)
            return False

        if proc_fallback and not found:
            if arg in self.funcs:
                del self.funcs[arg]

        return True
Exemplo n.º 8
0
  def ExecuteAndCatch(self, node, fork_external=True):
    """Execute a subprogram, handling _ControlFlow and fatal exceptions.

    Args:
      node: LST subtree
      fork_external: whether external commands require forking

    Returns:
      TODO: use enum 'why' instead of the 2 booleans

    Used by main_loop.py.

    Also:
    - SubProgramThunk for pipelines, subshell, command sub, process sub
    - TODO: Signals besides EXIT trap

    Most other clients call _Execute():
    - _Source() for source builtin
    - _Eval() for eval builtin
    - _RunFunc() for function call
    """
    is_return = False
    is_fatal = False
    try:
      status = self._Execute(node, fork_external=fork_external)
    except _ControlFlow as e:
      # Return at top level is OK, unlike in bash.
      if e.IsReturn():
        is_return = True
        status = e.StatusCode()
      else:
        # Invalid control flow
        self.errfmt.Print(
            "Loop and control flow can't be in different processes",
            span_id=e.token.span_id)
        is_fatal = True
        # All shells exit 0 here.  It could be hidden behind
        # strict-control-flow if the incompatibility causes problems.
        status = 1
    except util.ParseError as e:
      self.dumper.MaybeCollect(self, e)  # Do this before unwinding stack
      raise
    except util.FatalRuntimeError as e:
      self.dumper.MaybeCollect(self, e)  # Do this before unwinding stack

      if not e.HasLocation():  # Last resort!
        e.span_id = self.mem.CurrentSpanId()

      ui.PrettyPrintError(e, self.arena, prefix='fatal: ')
      is_fatal = True
      status = e.exit_status if e.exit_status is not None else 1

    self.dumper.MaybeDump(status)

    self.mem.SetLastStatus(status)
    return is_return, is_fatal
Exemplo n.º 9
0
def _assert_ParseCommandListError(test, code_str):
    arena, c_parser = InitCommandParser(code_str)

    try:
        node = c_parser._ParseCommandLine()
    except util.ParseError as e:
        ui.PrettyPrintError(e, arena, sys.stdout)
    else:
        print('UNEXPECTED:')
        ast_lib.PrettyPrint(node)
        test.fail("Expected %r to fail" % code_str)
Exemplo n.º 10
0
 def RunFuncForCompletion(self, func_node, argv):
   try:
     status = self._RunFunc(func_node, argv)
   except util.FatalRuntimeError as e:
     ui.PrettyPrintError(e, self.arena, sys.stderr)
     status = e.exit_status if e.exit_status is not None else 1
   except _ControlFlow as e:
      # shouldn't be able to exit the shell from a completion hook!
     util.error('Attempted to exit from completion hook.')
     status = 1
   return status
Exemplo n.º 11
0
    def __init__(self, script_path=None):

        # generally, defer work until we know the script loaded
        with (open(script_path) if script_path else sys.stdin) as script:
            arena = alloc.Arena()
            parse_ctx = parse_lib.ParseContext(
                arena=arena,
                parse_opts=optview.Parse(NO_OPTIONS),
                aliases={},  # dummy
                oil_grammar=None,
            )
            parse_ctx.Init_OnePassParse(True)

            if script_path:
                # TODO: is there a real difference between using mainfile and
                # sourcedfile? (this gets re-used for sourced scripts)
                arena.PushSource(source.MainFile(script_path))
            else:
                arena.PushSource(source.Stdin())

            try:
                node = main_loop.ParseWholeFile(
                    self._make_parser(parse_ctx, script, arena))
            except error.Parse as e:
                ui.PrettyPrintError(e, arena)
                raise

        assert node is not None

        # actually initialize
        self.arena = arena
        # TODO: not certain we don't want more, but minimize for now
        self.aliases = set()
        self.builtins = defaultdict(list)
        self.commands = defaultdict(list)
        self.sources = defaultdict(list)
        self.funcs_defined = set()
        self.resolved_commands = dict()
        self.resolved_functions = dict()
        self.resolved_aliases = dict()
        self.resolved_source = dict()
        self.parsed_source = dict()
        self.unresolved_commands = set()
        # unresolved functions doesn't make sense because we can't disambiguate an unresolved function from an unresolved external command...
        self.unresolved_source = set()
        self.word_obs = dict()

        # "resolve"
        try:
            self.Visit(node)
            self.resolve_records()
        except ResolutionError as e:
            e.print_if_needed()
            raise
Exemplo n.º 12
0
  def Matches(self, comp):
    try:
      val = self.word_ev.EvalWordToString(self.arg_word)
    except util.FatalRuntimeError as e:
      ui.PrettyPrintError(e, self.arena)
      raise

    # SplitForWordEval() Allows \ escapes
    candidates = self.splitter.SplitForWordEval(val.s)
    for c in candidates:
      if c.startswith(comp.to_complete):
        yield c
Exemplo n.º 13
0
def Interactive(opts, ex, c_parser, arena):
    status = 0
    while True:
        # Reset internal newline state.  NOTE: It would actually be correct to
        # reinitialize all objects (except Env) on every iteration.
        c_parser.Reset()
        c_parser.ResetInputObjects()

        try:
            w = c_parser.Peek()  # may raise HistoryError or ParseError

            c_id = word.CommandId(w)
            if c_id == Id.Op_Newline:  # print PS1 again, not PS2
                continue  # next command
            elif c_id == Id.Eof_Real:  # InteractiveLineReader prints ^D
                break  # end

            node = c_parser.ParseLogicalLine(
            )  # ditto, HistoryError or ParseError
        except util.HistoryError as e:  # e.g. expansion failed
            # Where this happens:
            # for i in 1 2 3; do
            #   !invalid
            # done
            print(e.UserErrorString())
            continue
        except util.ParseError as e:
            ui.PrettyPrintError(e, arena)
            # NOTE: This should set the status interactively!  Bash does this.
            status = 2
            continue

        if node is None:  # EOF
            # NOTE: We don't care if there are pending here docs in the interative case.
            break

        is_control_flow, is_fatal = ex.ExecuteAndCatch(node)
        status = ex.LastStatus()
        if is_control_flow:  # e.g. 'exit' in the middle of a script
            break
        if is_fatal:  # e.g. divide by zero
            continue

        # TODO: Replace this with a shell hook?  with 'trap', or it could be just
        # like command_not_found.  The hook can be 'echo $?' or something more
        # complicated, i.e. with timetamps.
        if opts.print_status:
            print('STATUS', repr(status))

    if ex.MaybeRunExitTrap():
        return ex.LastStatus()
    else:
        return status  # could be a parse error
Exemplo n.º 14
0
def _assert_ParseCommandListError(test, code_str):
    arena = test_lib.MakeArena('<cmd_parse_test>')
    c_parser = test_lib.InitCommandParser(code_str, arena=arena)

    try:
        node = c_parser._ParseCommandLine()
    except error.Parse as e:
        ui.PrettyPrintError(e, arena)
    else:
        print('UNEXPECTED:')
        node.PrettyPrint()
        test.fail("Expected %r to fail" % code_str)
Exemplo n.º 15
0
def Batch(ex, c_parser, arena, nodes_out=None):
    # type: (Any, CommandParser, Arena, Optional[List[command_t]]) -> Any
    """Loop for batch execution.

  Args:
    nodes_out: if set to a list, the input lines are parsed, and LST nodes are
      appended to it instead of executed.  For 'sh -n'.

  Can this be combined with interative loop?  Differences:
  
  - Handling of parse errors.
  - Have to detect here docs at the end?

  Not a problem:
  - Get rid of --print-status and --show-ast for now
  - Get rid of EOF difference

  TODO:
  - Do source / eval need this?
    - 'source' needs to parse incrementally so that aliases are respected
    - I doubt 'eval' does!  You can test it.
  - In contrast, 'trap' should parse up front?
  - What about $() ?
  """
    status = 0
    while True:
        try:
            node = c_parser.ParseLogicalLine()  # can raise ParseError
            if node is None:  # EOF
                c_parser.CheckForPendingHereDocs()  # can raise ParseError
                break
        except util.ParseError as e:
            ui.PrettyPrintError(e, arena)
            status = 2
            break

        if nodes_out is not None:
            nodes_out.append(node)
            continue

        #log('parsed %s', node)

        is_control_flow, is_fatal = ex.ExecuteAndCatch(node)
        status = ex.LastStatus()
        # e.g. divide by zero or 'exit' in the middle of a script
        if is_control_flow or is_fatal:
            break

    if ex.MaybeRunExitTrap():
        return ex.LastStatus()
    else:
        return status  # could be a parse error
Exemplo n.º 16
0
    def Matches(self, comp):
        # type: (Api) -> Iterator[Union[Iterator, Iterator[str]]]
        try:
            val = self.word_ev.EvalWordToString(self.arg_word)
        except error.FatalRuntime as e:
            ui.PrettyPrintError(e, self.arena)
            raise

        # SplitForWordEval() Allows \ escapes
        candidates = self.splitter.SplitForWordEval(val.s)
        for c in candidates:
            if c.startswith(comp.to_complete):
                yield c
Exemplo n.º 17
0
  def RunFuncForCompletion(self, func_node, argv):
    try:
      status = self._RunFunc(func_node, argv)
    except util.FatalRuntimeError as e:
      ui.PrettyPrintError(e, self.arena)
      status = e.exit_status if e.exit_status is not None else 1
    except _ControlFlow as e:
      # shouldn't be able to exit the shell from a completion hook!
      # TODO: Avoid overwriting the prompt!
      self.errfmt.Print('Attempted to exit from completion hook.',
                        span_id=e.token.span_id)

      status = 1
    # NOTE: (IOError, OSError) are caught in completion.py:ReadlineCallback
    return status
Exemplo n.º 18
0
def Batch(cmd_ev, c_parser, arena, cmd_flags=0):
    # type: (CommandEvaluator, CommandParser, Arena, int) -> int
    """Loop for batch execution.

  Returns:
    int status, e.g. 2 on parse error

  Can this be combined with interative loop?  Differences:
  
  - Handling of parse errors.
  - Have to detect here docs at the end?

  Not a problem:
  - Get rid of --print-status and --show-ast for now
  - Get rid of EOF difference

  TODO:
  - Do source / eval need this?
    - 'source' needs to parse incrementally so that aliases are respected
    - I doubt 'eval' does!  You can test it.
  - In contrast, 'trap' should parse up front?
  - What about $() ?
  """
    status = 0
    while True:
        try:
            node = c_parser.ParseLogicalLine()  # can raise ParseError
            if node is None:  # EOF
                c_parser.CheckForPendingHereDocs()  # can raise ParseError
                break
        except error.Parse as e:
            ui.PrettyPrintError(e, arena)
            status = 2
            break

        # Only optimize if we're on the last line like -c "echo hi" etc.
        if (cmd_flags & cmd_eval.IsMainProgram
                and c_parser.line_reader.LastLineHint()):
            cmd_flags |= cmd_eval.Optimize

        # can't optimize this because we haven't seen the end yet
        is_return, is_fatal = cmd_ev.ExecuteAndCatch(node, cmd_flags=cmd_flags)
        status = cmd_ev.LastStatus()
        # e.g. 'return' in middle of script, or divide by zero
        if is_return or is_fatal:
            break

    return status
Exemplo n.º 19
0
  def ExecuteAndCatch(self, node, fork_external=True):
    """Execute a subprogram, handling _ControlFlow and fatal exceptions.

    Args:
      node: LST subtree
      fork_external: whether external commands require forking

    Returns:
      TODO: use enum 'why' instead of the 2 booleans

    Used by main_loop.py.

    Also:
    - SubProgramThunk for pipelines, subshell, command sub, process sub
    - TODO: Signals besides EXIT trap

    Most other clients call _Execute():
    - _Source() for source builtin
    - _Eval() for eval builtin
    - _RunFunc() for function call
    """
    is_control_flow = False
    is_fatal = False
    try:
      status = self._Execute(node, fork_external=fork_external)
    except _ControlFlow as e:
      # Return at top level is OK, unlike in bash.
      if e.IsReturn() or e.IsExit():
        is_control_flow = True
        status = e.StatusCode()
      else:
        raise  # Invalid
    except util.ParseError as e:
      self.dumper.MaybeCollect(self, e)  # Do this before unwinding stack
      raise
    except util.FatalRuntimeError as e:
      self.dumper.MaybeCollect(self, e)  # Do this before unwinding stack
      ui.PrettyPrintError(e, self.arena)
      is_fatal = True
      status = e.exit_status if e.exit_status is not None else 1

    self.dumper.MaybeDump(status)

    self.mem.last_status = status
    return is_control_flow, is_fatal
Exemplo n.º 20
0
  def _ParseTrapCode(self, code_str):
    # type: (str) -> command_t
    """
    Returns:
      A node, or None if the code is invalid.
    """
    line_reader = reader.StringLineReader(code_str, self.arena)
    c_parser = self.parse_ctx.MakeOshParser(line_reader)

    # TODO: the SPID should be passed through argv.  Use ArgvWord?
    with alloc.ctx_Location(self.arena, source.Trap(runtime.NO_SPID)):
      try:
        node = main_loop.ParseWholeFile(c_parser)
      except error.Parse as e:
        ui.PrettyPrintError(e, self.arena)
        return None

    return node
Exemplo n.º 21
0
def _assertParseMethod(test, code_str, method, expect_success=True):
    arena = test_lib.MakeArena('<cmd_parse_test>')
    c_parser = test_lib.InitCommandParser(code_str, arena=arena)
    m = getattr(c_parser, method)
    try:
        node = m()

    except error.Parse as e:
        ui.PrettyPrintError(e, arena)
        if expect_success:
            test.fail('%r failed' % code_str)
        node = None
    else:
        node.PrettyPrint()
        if not expect_success:
            test.fail('Expected %r to fail ' % code_str)

    return node
Exemplo n.º 22
0
  def Execute(self, node, fork_external=True):
    """Execute a top level LST node."""
    # Use exceptions internally, but exit codes externally.
    try:
      status = self._Execute(node, fork_external=fork_external)
    except _ControlFlow as e:
      # TODO: pretty print error with e.token
      log('osh failed: Unexpected %r at top level' % e.token.val)
      status = 1
    except util.FatalRuntimeError as e:
      # TODO:
      ui.PrettyPrintError(e, self.arena, sys.stderr)
      print('osh failed: %s' % e.UserErrorString(), file=sys.stderr)
      status = e.exit_status if e.exit_status is not None else 1

    # TODO: Hook this up
    #print('break / continue can only be used inside loop')
    #status = 129  # TODO: Fix this.  Use correct macros
    return status
Exemplo n.º 23
0
def _assertParseMethod(test, code_str, method, expect_success=True):
    arena, c_parser = InitCommandParser(code_str)
    m = getattr(c_parser, method)
    try:
        if method == 'ParseSimpleCommand':
            node = m([])  # required cur_aliases arg
        else:
            node = m()

    except util.ParseError as e:
        ui.PrettyPrintError(e, arena, sys.stdout)
        if expect_success:
            test.fail('%r failed' % code_str)
        node = None
    else:
        ast_lib.PrettyPrint(node)
        if not expect_success:
            test.fail('Expected %r to fail ' % code_str)

    return node
Exemplo n.º 24
0
  def ExecuteAndCatch(self, node, fork_external=True):
    """Used directly by the interactive loop."""
    is_control_flow = False
    try:
      status = self._Execute(node, fork_external=fork_external)
    except _ControlFlow as e:
      # Return at top level is OK, unlike in bash.
      if e.IsReturn() or e.IsExit():
        is_control_flow = True
        status = e.StatusCode()
      else:
        raise  # Invalid
    except util.FatalRuntimeError as e:
      ui.PrettyPrintError(e, self.arena)
      print('osh failed: %s' % e.UserErrorString(), file=sys.stderr)
      status = e.exit_status if e.exit_status is not None else 1
      # TODO: dump self.mem if requested.  Maybe speify with OIL_DUMP_PREFIX.

    # Other exceptions: SystemExit for sys.exit()
    return status, is_control_flow
Exemplo n.º 25
0
  def ParseTrapCode(self, code_str):
    """
    Returns:
      A node, or None if the code is invalid.
    """
    line_reader = reader.StringLineReader(code_str, self.arena)
    c_parser = self.parse_ctx.MakeOshParser(line_reader)

    # TODO: the SPID should be passed through argv
    self.arena.PushSource(source.Trap(const.NO_INTEGER))
    try:
      try:
        node = main_loop.ParseWholeFile(c_parser)
      except util.ParseError as e:
        ui.PrettyPrintError(e, self.arena)
        return None

    finally:
      self.arena.PopSource()

    return node
Exemplo n.º 26
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
Exemplo n.º 27
0
  def _RunFunc(self, func_node, argv):
    """Used to run SimpleCommand and to run registered completion hooks."""
    # These are redirects at DEFINITION SITE.  You can also have redirects at
    # the CALL SITE.  For example:
    #
    # f() { echo hi; } 1>&2
    # f 2>&1

    try:
      def_redirects = self._EvalRedirects(func_node)
    except util.RedirectEvalError as e:
      ui.PrettyPrintError(e, self.arena)
      return 1

    if def_redirects:
      if not self.fd_state.Push(def_redirects, self.waiter):
        return 1  # error

    self.mem.PushCall(func_node.name, func_node.spids[0], argv)

    # Redirects still valid for functions.
    # Here doc causes a pipe and Process(SubProgramThunk).
    try:
      status = self._Execute(func_node.body)
    except _ControlFlow as e:
      if e.IsReturn():
        status = e.StatusCode()
      else:
        # break/continue used in the wrong place.
        e_die('Unexpected %r (in function call)', e.token.val, token=e.token)
    except (util.FatalRuntimeError, util.ParseError) as e:
      self.dumper.MaybeCollect(self, e)  # Do this before unwinding stack
      raise
    finally:
      self.mem.PopCall()
      if def_redirects:
        self.fd_state.Pop()

    return status
Exemplo n.º 28
0
def TeaMain(argv):
    # type: (str, List[str]) -> int
    arena = alloc.Arena()
    try:
        script_name = argv[1]
        arena.PushSource(source.MainFile(script_name))
    except IndexError:
        arena.PushSource(source.Stdin())
        f = sys.stdin
    else:
        try:
            f = open(script_name)
        except IOError as e:
            stderr_line("tea: Couldn't open %r: %s", script_name,
                        posix.strerror(e.errno))
            return 2

    aliases = {}  # Dummy value; not respecting aliases!

    loader = pyutil.GetResourceLoader()
    oil_grammar = pyutil.LoadOilGrammar(loader)

    # Not used in tea, but OK...
    opt0_array = state.InitOpts()
    parse_opts = optview.Parse(opt0_array)

    # parse `` and a[x+1]=bar differently
    parse_ctx = parse_lib.ParseContext(arena, parse_opts, aliases, oil_grammar)

    line_reader = reader.FileLineReader(f, arena)

    try:
        parse_ctx.ParseTeaModule(line_reader)
        status = 0
    except error.Parse as e:
        ui.PrettyPrintError(e, arena)
        status = 2

    return status
Exemplo n.º 29
0
  def ParseTrapCode(self, code_str):
    """
    Returns:
      A node, or None if the code is invalid.
    """
    line_reader = reader.StringLineReader(code_str, self.arena)
    _, c_parser = self.parse_ctx.MakeParser(line_reader)

    source_name = '<trap string>'
    self.arena.PushSource(source_name)

    try:
      try:
        node = main_loop.ParseWholeFile(c_parser)
      except util.ParseError as e:
        util.error('Parse error in %r:', source_name)
        ui.PrettyPrintError(e, self.arena, sys.stderr)
        return None

    finally:
      self.arena.PopSource()

    return node
Exemplo n.º 30
0
def Interactive(opts, ex, c_parser, arena):
    status = 0
    while True:
        # Reset internal newline state.  NOTE: It would actually be correct to
        # reinitialize all objects (except Env) on every iteration.
        c_parser.Reset()
        c_parser.ResetInputObjects()

        try:
            node = c_parser.ParseLogicalLine()
        except util.ParseError as e:
            ui.PrettyPrintError(e, arena)
            # NOTE: This should set the status interactively!  Bash does this.
            status = 2
            continue

        if node is None:  # EOF
            # NOTE: We don't care if there are pending here docs in the interative case.
            break

        is_control_flow, is_fatal = ex.ExecuteAndCatch(node)
        status = ex.LastStatus()
        if is_control_flow:  # e.g. 'exit' in the middle of a script
            break
        if is_fatal:  # e.g. divide by zero
            continue

        # TODO: Replace this with a shell hook?  with 'trap', or it could be just
        # like command_not_found.  The hook can be 'echo $?' or something more
        # complicated, i.e. with timetamps.
        if opts.print_status:
            print('STATUS', repr(status))

    if ex.MaybeRunExitTrap():
        return ex.LastStatus()
    else:
        return status  # could be a parse error