def _InitMem(): # empty environment, no arena. arena = test_lib.MakeArena('<state_test.py>') line_id = arena.AddLine(1, 'foo') span = ast.line_span(line_id, 0, 1) # dummy arena.AddLineSpan(span) return state.Mem('', [], {}, arena)
def MakeTestEvaluator(): arena = alloc.SideArena('<MakeTestEvaluator>') mem = state.Mem('', [], {}, arena) exec_opts = state.ExecOpts(mem, None) splitter = legacy.SplitContext(mem) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter, arena) return ev
def setUp(self): arena = test_lib.MakeArena('<ui_test.py>') mem = state.Mem('', [], arena, []) parse_ctx = test_lib.InitParseContext() self.p = prompt.Evaluator('osh', parse_ctx, mem) # note: this has a separate 'mem' object self.p.word_ev = test_lib.InitWordEvaluator()
def EvalCode(code_str, parse_ctx, comp_lookup=None, mem=None, aliases=None): """ Unit tests can evaluate code strings and then use the resulting CommandEvaluator. """ arena = parse_ctx.arena comp_lookup = comp_lookup or completion.Lookup() mem = mem or state.Mem('', [], arena, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts state.InitMem(mem, {}, '0.1') mutable_opts.Init() line_reader, _ = InitLexer(code_str, arena) c_parser = parse_ctx.MakeOshParser(line_reader) cmd_ev = InitCommandEvaluator(parse_ctx=parse_ctx, comp_lookup=comp_lookup, arena=arena, mem=mem, aliases=aliases) main_loop.Batch(cmd_ev, c_parser, arena) # Parse and execute! return cmd_ev
def _MakeRootCompleter(parse_ctx=None, comp_lookup=None): compopt_state = completion.OptionState() comp_ui_state = comp_ui.State() comp_lookup = comp_lookup or completion.Lookup() mem = state.Mem('', [], None, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts state.InitMem(mem, {}, '0.1') mutable_opts.Init() if not parse_ctx: parse_ctx = test_lib.InitParseContext(parse_opts=parse_opts) parse_ctx.Init_Trail(parse_lib.Trail()) parse_ctx.Init_OnePassParse(True) if 1: # enable for details debug_f = util.DebugFile(sys.stdout) else: debug_f = util.NullDebugFile() ev = test_lib.InitWordEvaluator(exec_opts=exec_opts) return completion.RootCompleter(ev, mem, comp_lookup, compopt_state, comp_ui_state, parse_ctx, debug_f)
def ParseAndEval(code_str): arena = test_lib.MakeArena('<arith_parse_test.py>') parse_ctx = test_lib.InitParseContext(arena=arena) w_parser = test_lib.InitWordParser(code_str, arena=arena) # This is weird but works w_parser._Next(lex_mode_e.Arith) # Calling private method anode = w_parser.a_parser.Parse() print('node:', anode) mem = state.Mem('', [], arena, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts state.InitMem(mem, {}, '0.1') splitter = split.SplitContext(mem) errfmt = ui.ErrorFormatter(arena) word_ev = word_eval.CompletionWordEvaluator(mem, exec_opts, mutable_opts, splitter, errfmt) arith_ev = sh_expr_eval.ArithEvaluator(mem, exec_opts, parse_ctx, arena) arith_ev.word_ev = word_ev return arith_ev.EvalToInt(anode)
def testArgv2(self): mem = state.Mem('', ['x', 'y'], {}, None) mem.Shift(1) self.assertEqual(['y'], mem.GetArgv()) mem.SetArgv(['i', 'j', 'k']) self.assertEqual(['i', 'j', 'k'], mem.GetArgv())
def testExternalCommandAction(self): mem = state.Mem('dummy', [], None, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts a = completion.ExternalCommandAction(mem) comp = self._CompApi([], 0, 'f') print(list(a.Matches(comp)))
def testUnset(self): mem = state.Mem('', [], {}) # unset a mem.Unset(runtime.LhsName('a'), scope.Dynamic) return # not implemented yet # unset a[1] mem.Unset(runtime.LhsIndexedName('a', 1), scope.Dynamic)
def _InitMem(): # empty environment, no arena. arena = test_lib.MakeArena('<state_test.py>') line_id = arena.AddLine(1, 'foo') unused = arena.AddLineSpan(line_id, 0, 1) # dummy mem = state.Mem('', [], arena, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts return mem
def testGetVar(self): mem = state.Mem('', [], {}) # readonly a=x mem.SetVar(runtime.LhsName('a'), runtime.Str('x'), (var_flags.ReadOnly, ), scope.Dynamic) val = mem.GetVar('a', scope.Dynamic) self.assertEqual(runtime.Str('x'), val) val = mem.GetVar('undef', scope.Dynamic) self.assertEqual(runtime.Undef(), val)
def InitParseContext(arena=None, oil_grammar=None, aliases=None, parse_opts=None): arena = arena or MakeArena('<test_lib>') if aliases is None: aliases = {} mem = state.Mem('', [], arena, []) if parse_opts is None: parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) parse_ctx = parse_lib.ParseContext(arena, parse_opts, aliases, oil_grammar) return parse_ctx
def testExportThenAssign(self): """Regression Test""" mem = state.Mem('', [], {}) # export U mem.SetVar(runtime.LhsName('U'), None, (var_flags.Exported, ), scope.Dynamic) print(mem) # U=u mem.SetVar(runtime.LhsName('U'), runtime.Str('u'), (), scope.Dynamic) print(mem) e = mem.GetExported() self.assertEqual({'U': 'u'}, e)
def InitExecutor(arena=None): arena = arena or MakeArena('<InitExecutor>') mem = state.Mem('', [], {}, arena) fd_state = process.FdState() funcs = {} comp_funcs = {} # For the tests, we do not use 'readline'. exec_opts = state.ExecOpts(mem, None) parse_ctx = parse_lib.ParseContext(arena, {}) debug_f = util.DebugFile(sys.stderr) devtools = dev.DevTools(dev.CrashDumper(''), debug_f, debug_f) return cmd_exec.Executor(mem, fd_state, funcs, comp_funcs, exec_opts, parse_ctx, devtools)
def ParseAndEval(code_str): arena = test_lib.MakeArena('<arith_parse_test.py>') parse_ctx = parse_lib.ParseContext(arena, {}) w_parser, _ = parse_ctx.MakeParserForCompletion(code_str, arena) w_parser._Next(lex_mode_e.ARITH) # Calling private method anode = w_parser._ReadArithExpr() # need the right lex state? print('node:', anode) mem = state.Mem('', [], {}, arena) exec_opts = state.ExecOpts(mem, None) splitter = legacy.SplitContext(mem) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter, arena) arith_ev = expr_eval.ArithEvaluator(mem, exec_opts, ev, arena) value = arith_ev.Eval(anode) return value
def InitWordParser(word_str, oil_at=False, arena=None): arena = arena or MakeArena('<test_lib>') mem = state.Mem('', [], arena, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) # CUSTOM SETTING mutable_opts.opt0_array[option_i.parse_at] = oil_at loader = pyutil.GetResourceLoader() oil_grammar = pyutil.LoadOilGrammar(loader) parse_ctx = parse_lib.ParseContext(arena, parse_opts, {}, oil_grammar) line_reader, _ = InitLexer(word_str, arena) c_parser = parse_ctx.MakeOshParser(line_reader) # Hack return c_parser.w_parser
def InitWordEvaluator(exec_opts=None): arena = MakeArena('<InitWordEvaluator>') mem = state.Mem('', [], arena, []) state.InitMem(mem, {}, '0.1') if exec_opts is None: parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts # circular dep cmd_deps = cmd_eval.Deps() cmd_deps.trap_nodes = [] splitter = split.SplitContext(mem) errfmt = ui.ErrorFormatter(arena) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter, errfmt) return ev
def ParseAndEval(code_str): arena = test_lib.MakeArena('<arith_parse_test.py>') w_parser, _ = parse_lib.MakeParserForCompletion(code_str, arena) anode = w_parser._ReadArithExpr() # need the right lex state? if not anode: raise ExprSyntaxError("failed %s" % w_parser.Error()) print('node:', anode) mem = state.Mem('', [], {}, None) exec_opts = state.ExecOpts(mem) splitter = legacy.SplitContext(mem) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter) arith_ev = expr_eval.ArithEvaluator(mem, exec_opts, ev) value = arith_ev.Eval(anode) return value
def InitWordEvaluator(): arena = MakeArena('<InitWordEvaluator>') mem = state.Mem('', [], arena, []) state.InitMem(mem, {}) opt_array = [False] * option_i.ARRAY_SIZE errexit = state._ErrExit() parse_opts = optview.Parse(opt_array) exec_opts = optview.Exec(opt_array, errexit) mem.exec_opts = exec_opts # circular dep exec_deps = cmd_exec.Deps() exec_deps.trap_nodes = [] splitter = split.SplitContext(mem) errfmt = ui.ErrorFormatter(arena) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter, errfmt) return ev
def setUp(self): self.arena = test_lib.MakeArena('process_test.py') mem = state.Mem('', [], self.arena, []) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) mem.exec_opts = exec_opts state.InitMem(mem, {}, '0.1') self.job_state = process.JobState() sig_state = pyos.SignalState() self.tracer = dev.Tracer(None, exec_opts, mutable_opts, mem, mylib.Stderr()) self.waiter = process.Waiter(self.job_state, exec_opts, sig_state, self.tracer) errfmt = ui.ErrorFormatter(self.arena) self.fd_state = process.FdState(errfmt, self.job_state, None, self.tracer) self.ext_prog = process.ExternalProgram('', self.fd_state, errfmt, util.NullDebugFile())
def ParseAndEval(code_str): arena = test_lib.MakeArena('<arith_parse_test.py>') w_parser = test_lib.InitWordParser(code_str, arena=arena) w_parser._Next(lex_mode_e.Arith) # Calling private method anode = w_parser._ReadArithExpr() # need the right lex state? print('node:', anode) mem = state.Mem('', [], arena, []) state.InitMem(mem, {}) parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None) splitter = split.SplitContext(mem) errfmt = ui.ErrorFormatter(arena) word_ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter, errfmt) arith_ev = sh_expr_eval.ArithEvaluator(mem, exec_opts, arena) arith_ev.word_ev = word_ev return arith_ev.EvalToInt(anode)
def EvalCode(code_str, parse_ctx, comp_lookup=None, mem=None, aliases=None): """ Unit tests can evaluate code strings and then use the resulting Executor. """ arena = parse_ctx.arena comp_lookup = comp_lookup or completion.Lookup() mem = mem or state.Mem('', [], arena, []) state.InitMem(mem, {}) line_reader, _ = InitLexer(code_str, arena) c_parser = parse_ctx.MakeOshParser(line_reader) ex = InitExecutor(parse_ctx=parse_ctx, comp_lookup=comp_lookup, arena=arena, mem=mem, aliases=aliases) main_loop.Batch(ex, c_parser, arena) # Parse and execute! return ex
def testArgv(self): mem = state.Mem('', [], {}) mem.PushCall('my-func', ['a', 'b']) self.assertEqual(['a', 'b'], mem.GetArgv()) mem.PushCall('my-func', ['x', 'y']) self.assertEqual(['x', 'y'], mem.GetArgv()) status = mem.Shift(1) self.assertEqual(['y'], mem.GetArgv()) self.assertEqual(0, status) status = mem.Shift(1) self.assertEqual([], mem.GetArgv()) self.assertEqual(0, status) status = mem.Shift(1) self.assertEqual([], mem.GetArgv()) self.assertEqual(1, status) # error mem.PopCall() self.assertEqual(['a', 'b'], mem.GetArgv())
def OshMain(argv, login_shell): spec = args.FlagsAndOptions() spec.ShortFlag('-c', args.Str, quit_parsing_flags=True) # command string spec.ShortFlag('-i') # interactive # TODO: -h too spec.LongFlag('--help') spec.LongFlag('--version') spec.LongFlag('--ast-format', ['text', 'abbrev-text', 'html', 'abbrev-html', 'oheap'], default='abbrev-text') spec.LongFlag('--show-ast') # execute and show spec.LongFlag('--fix') spec.LongFlag('--debug-spans') spec.LongFlag('--print-status') spec.LongFlag( '--trace', ['cmd-parse', 'word-parse', 'lexer']) # NOTE: can only trace one now spec.LongFlag('--hijack-shebang') builtin.AddOptionsToArgSpec(spec) try: opts, opt_index = spec.Parse(argv) except args.UsageError as e: util.usage(str(e)) return 2 if opts.help: loader = util.GetResourceLoader() # TOOD: Use Global builtin.Help(['osh-usage'], loader) return 0 if opts.version: # OSH version is the only binary in Oil right now, so it's all one version. _ShowVersion() return 0 trace_state = util.TraceState() if 'cmd-parse' == opts.trace: util.WrapMethods(cmd_parse.CommandParser, trace_state) if 'word-parse' == opts.trace: util.WrapMethods(word_parse.WordParser, trace_state) if 'lexer' == opts.trace: util.WrapMethods(lexer.Lexer, trace_state) if opt_index == len(argv): dollar0 = sys.argv[0] # e.g. bin/osh else: dollar0 = argv[opt_index] # the script name, or the arg after -c # TODO: Create a --parse action or 'osh parse' or 'oil osh-parse' # osh-fix # It uses a different memory-management model. It's a batch program and not # an interactive program. pool = alloc.Pool() arena = pool.NewArena() # TODO: Maybe wrap this initialization sequence up in an oil_State, like # lua_State. status_lines = ui.MakeStatusLines() mem = state.Mem(dollar0, argv[opt_index + 1:], os.environ) funcs = {} # Passed to Executor for 'complete', and passed to completion.Init if completion: comp_lookup = completion.CompletionLookup() else: # TODO: NullLookup? comp_lookup = None exec_opts = state.ExecOpts() builtin.SetExecOpts(exec_opts, opts.opt_changes) # TODO: How to get a handle to initialized builtins here? # tokens.py has it. I think you just make a separate table, with # metaprogramming. ex = cmd_exec.Executor(mem, status_lines, funcs, completion, comp_lookup, exec_opts, arena) # NOTE: The rc file can contain both commands and functions... ideally we # would only want to save nodes/lines for the functions. try: rc_path = 'oilrc' arena.PushSource(rc_path) with open(rc_path) as f: rc_line_reader = reader.FileLineReader(f, arena=arena) _, rc_c_parser = parse_lib.MakeParser(rc_line_reader, arena) try: rc_node = rc_c_parser.ParseWholeFile() if not rc_node: # TODO: Error should return a token, and then the token should have a # arena index, and then look that up in the arena. err = rc_c_parser.Error() ui.PrintErrorStack(err, arena, sys.stderr) return 2 # parse error is code 2 finally: arena.PopSource() status = ex.Execute(rc_node) #print('oilrc:', status, cflow, file=sys.stderr) # Ignore bad status? except IOError as e: if e.errno != errno.ENOENT: raise if opts.c is not None: arena.PushSource('<command string>') line_reader = reader.StringLineReader(opts.c, arena=arena) interactive = False elif opts.i: # force interactive arena.PushSource('<stdin -i>') line_reader = reader.InteractiveLineReader(OSH_PS1, arena=arena) interactive = True else: try: script_name = argv[opt_index] except IndexError: if sys.stdin.isatty(): arena.PushSource('<interactive>') line_reader = reader.InteractiveLineReader(OSH_PS1, arena=arena) interactive = True else: arena.PushSource('<stdin>') line_reader = reader.FileLineReader(sys.stdin, arena=arena) interactive = False else: arena.PushSource(script_name) # TODO: Does this open file descriptor need to be moved beyond 3..9 ? # Yes! See dash input.c setinputfile. It calls savefd(). # TODO: It also needs to be closed later. try: f = open(script_name) except IOError as e: util.error("Couldn't open %r: %s", script_name, os.strerror(e.errno)) return 1 line_reader = reader.FileLineReader(f, arena=arena) interactive = False # TODO: assert arena.NumSourcePaths() == 1 # TODO: .rc file needs its own arena. w_parser, c_parser = parse_lib.MakeParser(line_reader, arena) if interactive: # NOTE: We're using a different evaluator here. The completion system can # also run functions... it gets the Executor through Executor._Complete. if HAVE_READLINE: ev = word_eval.CompletionWordEvaluator(mem, exec_opts) status_out = completion.StatusOutput(status_lines, exec_opts) completion.Init(builtin.BUILTIN_DEF, mem, funcs, comp_lookup, status_out, ev) # TODO: Could instantiate "printer" instead of showing ops InteractiveLoop(opts, ex, c_parser, w_parser, line_reader) status = 0 # TODO: set code else: # Parse the whole thing up front #print('Parsing file') tlog('ParseWholeFile') # TODO: Do I need ParseAndEvalLoop? How is it different than # InteractiveLoop? try: node = c_parser.ParseWholeFile() except util.ParseError as e: ui.PrettyPrintError(e, arena, sys.stderr) print('parse error: %s' % e.UserErrorString(), file=sys.stderr) return 2 else: # TODO: Remove this older form of error handling. if not node: err = c_parser.Error() ui.PrintErrorStack(err, arena, sys.stderr) return 2 # parse error is code 2 do_exec = True if opts.fix: osh2oil.PrintAsOil(arena, node, opts.debug_spans) do_exec = False if exec_opts.noexec: do_exec = False if exec_opts.noexec or opts.show_ast: # -n shows the AST if opts.ast_format == 'oheap': # TODO: Make this a separate flag? if sys.stdout.isatty(): raise RuntimeError( 'ERROR: Not dumping binary data to a TTY.') f = sys.stdout enc = encode.Params() out = encode.BinOutput(f) encode.EncodeRoot(node, enc, out) else: # text output f = sys.stdout if opts.ast_format in ('text', 'abbrev-text'): ast_f = fmt.DetectConsoleOutput(f) elif opts.ast_format in ('html', 'abbrev-html'): ast_f = fmt.HtmlOutput(f) else: raise AssertionError abbrev_hook = (ast.AbbreviateNodes if 'abbrev-' in opts.ast_format else None) tree = fmt.MakeTree(node, abbrev_hook=abbrev_hook) ast_f.FileHeader() fmt.PrintTree(tree, ast_f) ast_f.FileFooter() ast_f.write('\n') #util.log("Execution skipped because 'noexec' is on ") status = 0 if do_exec: tlog('Execute(node)') status = ex.Execute(node) else: status = 0 return status
def InitCommandEvaluator(parse_ctx=None, comp_lookup=None, arena=None, mem=None, aliases=None, ext_prog=None): opt0_array = state.InitOpts() opt_stacks = [None] * option_i.ARRAY_SIZE if parse_ctx: arena = parse_ctx.arena parse_opts = parse_ctx.parse_opts else: parse_ctx = InitParseContext() mem = mem or state.Mem('', [], arena, []) exec_opts = optview.Exec(opt0_array, opt_stacks) mutable_opts = state.MutableOpts(mem, opt0_array, opt_stacks, None) mem.exec_opts = exec_opts state.InitMem(mem, {}, '0.1') mutable_opts.Init() # No 'readline' in the tests. errfmt = ui.ErrorFormatter(arena) job_state = process.JobState() fd_state = process.FdState(errfmt, job_state) aliases = {} if aliases is None else aliases procs = {} compopt_state = completion.OptionState() comp_lookup = comp_lookup or completion.Lookup() readline = None # simulate not having it new_var = builtin_assign.NewVar(mem, procs, errfmt) assign_builtins = { builtin_i.declare: new_var, builtin_i.typeset: new_var, builtin_i.local: new_var, builtin_i.export_: builtin_assign.Export(mem, errfmt), builtin_i.readonly: builtin_assign.Readonly(mem, errfmt), } builtins = { # Lookup builtin_i.echo: builtin_pure.Echo(exec_opts), builtin_i.shift: builtin_assign.Shift(mem), builtin_i.history: builtin_lib.History(readline, mylib.Stdout()), builtin_i.compopt: builtin_comp.CompOpt(compopt_state, errfmt), builtin_i.compadjust: builtin_comp.CompAdjust(mem), builtin_i.alias: builtin_pure.Alias(aliases, errfmt), builtin_i.unalias: builtin_pure.UnAlias(aliases, errfmt), } debug_f = util.DebugFile(sys.stderr) cmd_deps = cmd_eval.Deps() cmd_deps.mutable_opts = mutable_opts cmd_deps.trap_nodes = [] search_path = state.SearchPath(mem) waiter = process.Waiter(job_state, exec_opts) ext_prog = \ ext_prog or process.ExternalProgram('', fd_state, errfmt, debug_f) cmd_deps.dumper = dev.CrashDumper('') cmd_deps.debug_f = debug_f splitter = split.SplitContext(mem) arith_ev = sh_expr_eval.ArithEvaluator(mem, exec_opts, parse_ctx, errfmt) bool_ev = sh_expr_eval.BoolEvaluator(mem, exec_opts, parse_ctx, errfmt) expr_ev = expr_eval.OilEvaluator(mem, procs, splitter, errfmt) word_ev = word_eval.NormalWordEvaluator(mem, exec_opts, mutable_opts, splitter, errfmt) cmd_ev = cmd_eval.CommandEvaluator(mem, exec_opts, errfmt, procs, assign_builtins, arena, cmd_deps) shell_ex = executor.ShellExecutor( mem, exec_opts, mutable_opts, procs, builtins, search_path, ext_prog, waiter, job_state, fd_state, errfmt) assert cmd_ev.mutable_opts is not None, cmd_ev prompt_ev = prompt.Evaluator('osh', parse_ctx, mem) tracer = dev.Tracer(parse_ctx, exec_opts, mutable_opts, mem, word_ev, debug_f) vm.InitCircularDeps(arith_ev, bool_ev, expr_ev, word_ev, cmd_ev, shell_ex, prompt_ev, tracer) spec_builder = builtin_comp.SpecBuilder(cmd_ev, parse_ctx, word_ev, splitter, comp_lookup) # Add some builtins that depend on the executor! complete_builtin = builtin_comp.Complete(spec_builder, comp_lookup) builtins[builtin_i.complete] = complete_builtin builtins[builtin_i.compgen] = builtin_comp.CompGen(spec_builder) return cmd_ev
def testExternalCommandAction(self): mem = state.Mem('dummy', [], {}, None) a = completion.ExternalCommandAction(mem) print(list(a.Matches([], 0, 'f')))
def _MakeTestEvaluator(): mem = state.Mem('', [], {}, None) exec_opts = state.ExecOpts(mem) splitter = legacy.SplitContext(mem) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter) return ev
from osh import builtin_misc from asdl import runtime Process = process.Process ExternalThunk = process.ExternalThunk def Banner(msg): print('-' * 60) print(msg) # TODO: Put these all in a function. _ARENA = test_lib.MakeArena('process_test.py') _MEM = state.Mem('', [], _ARENA, []) state.InitMem(_MEM, {}, '0.1') _OPT_ARRAY = [False] * option_i.ARRAY_SIZE _PARSE_OPTS = optview.Parse(_OPT_ARRAY) _ERREXIT = state._ErrExit() _EXEC_OPTS = state.MutableOpts(_MEM, _OPT_ARRAY, _ERREXIT, None) _JOB_STATE = process.JobState() _WAITER = process.Waiter(_JOB_STATE, _EXEC_OPTS) _ERRFMT = ui.ErrorFormatter(_ARENA) _FD_STATE = process.FdState(_ERRFMT, _JOB_STATE) _EXT_PROG = process.ExternalProgram('', _FD_STATE, _ERRFMT, util.NullDebugFile()) def _CommandNode(code_str, arena):
def OshMain(argv0, argv, login_shell): spec = args.FlagsAndOptions() spec.ShortFlag('-c', args.Str, quit_parsing_flags=True) # command string spec.ShortFlag('-i') # interactive # TODO: -h too spec.LongFlag('--help') spec.LongFlag('--version') spec.LongFlag( '--ast-format', ['text', 'abbrev-text', 'html', 'abbrev-html', 'oheap', 'none'], default='abbrev-text') spec.LongFlag('--show-ast') # execute and show spec.LongFlag('--fix') spec.LongFlag('--debug-spans') # For oshc translate spec.LongFlag('--print-status') spec.LongFlag( '--trace', ['cmd-parse', 'word-parse', 'lexer']) # NOTE: can only trace one now spec.LongFlag('--hijack-shebang') # For benchmarks/*.sh spec.LongFlag('--parser-mem-dump', args.Str) spec.LongFlag('--runtime-mem-dump', args.Str) builtin.AddOptionsToArgSpec(spec) try: opts, opt_index = spec.Parse(argv) except args.UsageError as e: util.usage(str(e)) return 2 if opts.help: loader = util.GetResourceLoader() builtin.Help(['osh-usage'], loader) return 0 if opts.version: # OSH version is the only binary in Oil right now, so it's all one version. _ShowVersion() return 0 trace_state = util.TraceState() if 'cmd-parse' == opts.trace: util.WrapMethods(cmd_parse.CommandParser, trace_state) if 'word-parse' == opts.trace: util.WrapMethods(word_parse.WordParser, trace_state) if 'lexer' == opts.trace: util.WrapMethods(lexer.Lexer, trace_state) if opt_index == len(argv): dollar0 = argv0 else: dollar0 = argv[opt_index] # the script name, or the arg after -c # TODO: Create a --parse action or 'osh parse' or 'oil osh-parse' # osh-fix # It uses a different memory-management model. It's a batch program and not # an interactive program. pool = alloc.Pool() arena = pool.NewArena() # TODO: Maybe wrap this initialization sequence up in an oil_State, like # lua_State. status_lines = ui.MakeStatusLines() mem = state.Mem(dollar0, argv[opt_index + 1:], os.environ, arena) funcs = {} # Passed to Executor for 'complete', and passed to completion.Init if completion: comp_lookup = completion.CompletionLookup() else: # TODO: NullLookup? comp_lookup = None exec_opts = state.ExecOpts(mem) builtin.SetExecOpts(exec_opts, opts.opt_changes) fd_state = process.FdState() ex = cmd_exec.Executor(mem, fd_state, status_lines, funcs, readline, completion, comp_lookup, exec_opts, arena) # NOTE: The rc file can contain both commands and functions... ideally we # would only want to save nodes/lines for the functions. try: rc_path = 'oilrc' arena.PushSource(rc_path) with open(rc_path) as f: rc_line_reader = reader.FileLineReader(f, arena) _, rc_c_parser = parse_lib.MakeParser(rc_line_reader, arena) try: rc_node = rc_c_parser.ParseWholeFile() if not rc_node: err = rc_c_parser.Error() ui.PrintErrorStack(err, arena, sys.stderr) return 2 # parse error is code 2 finally: arena.PopSource() status = ex.Execute(rc_node) #print('oilrc:', status, cflow, file=sys.stderr) # Ignore bad status? except IOError as e: if e.errno != errno.ENOENT: raise if opts.c is not None: arena.PushSource('<command string>') line_reader = reader.StringLineReader(opts.c, arena) if opts.i: # -c and -i can be combined exec_opts.interactive = True elif opts.i: # force interactive arena.PushSource('<stdin -i>') line_reader = reader.InteractiveLineReader(OSH_PS1, arena) exec_opts.interactive = True else: try: script_name = argv[opt_index] except IndexError: if sys.stdin.isatty(): arena.PushSource('<interactive>') line_reader = reader.InteractiveLineReader(OSH_PS1, arena) exec_opts.interactive = True else: arena.PushSource('<stdin>') line_reader = reader.FileLineReader(sys.stdin, arena) else: arena.PushSource(script_name) try: f = fd_state.Open(script_name) except OSError as e: util.error("Couldn't open %r: %s", script_name, os.strerror(e.errno)) return 1 line_reader = reader.FileLineReader(f, arena) # TODO: assert arena.NumSourcePaths() == 1 # TODO: .rc file needs its own arena. w_parser, c_parser = parse_lib.MakeParser(line_reader, arena) if exec_opts.interactive: # NOTE: We're using a different evaluator here. The completion system can # also run functions... it gets the Executor through Executor._Complete. if HAVE_READLINE: splitter = legacy.SplitContext(mem) ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter) status_out = completion.StatusOutput(status_lines, exec_opts) completion.Init(pool, builtin.BUILTIN_DEF, mem, funcs, comp_lookup, status_out, ev) return InteractiveLoop(opts, ex, c_parser, w_parser, line_reader) else: # Parse the whole thing up front #print('Parsing file') _tlog('ParseWholeFile') # TODO: Do I need ParseAndEvalLoop? How is it different than # InteractiveLoop? try: node = c_parser.ParseWholeFile() except util.ParseError as e: ui.PrettyPrintError(e, arena, sys.stderr) print('parse error: %s' % e.UserErrorString(), file=sys.stderr) return 2 else: # TODO: Remove this older form of error handling. if not node: err = c_parser.Error() assert err, err # can't be empty ui.PrintErrorStack(err, arena, sys.stderr) return 2 # parse error is code 2 do_exec = True if opts.fix: #log('SPANS: %s', arena.spans) osh2oil.PrintAsOil(arena, node, opts.debug_spans) do_exec = False if exec_opts.noexec: do_exec = False # Do this after parsing the entire file. There could be another option to # do it before exiting runtime? if opts.parser_mem_dump: # This might be superstition, but we want to let the value stabilize # after parsing. bash -c 'cat /proc/$$/status' gives different results # with a sleep. time.sleep(0.001) input_path = '/proc/%d/status' % os.getpid() with open(input_path) as f, open(opts.parser_mem_dump, 'w') as f2: contents = f.read() f2.write(contents) log('Wrote %s to %s (--parser-mem-dump)', input_path, opts.parser_mem_dump) # -n prints AST, --show-ast prints and executes if exec_opts.noexec or opts.show_ast: if opts.ast_format == 'none': print('AST not printed.', file=sys.stderr) elif opts.ast_format == 'oheap': # TODO: Make this a separate flag? if sys.stdout.isatty(): raise RuntimeError( 'ERROR: Not dumping binary data to a TTY.') f = sys.stdout enc = encode.Params() out = encode.BinOutput(f) encode.EncodeRoot(node, enc, out) else: # text output f = sys.stdout if opts.ast_format in ('text', 'abbrev-text'): ast_f = fmt.DetectConsoleOutput(f) elif opts.ast_format in ('html', 'abbrev-html'): ast_f = fmt.HtmlOutput(f) else: raise AssertionError abbrev_hook = (ast_lib.AbbreviateNodes if 'abbrev-' in opts.ast_format else None) tree = fmt.MakeTree(node, abbrev_hook=abbrev_hook) ast_f.FileHeader() fmt.PrintTree(tree, ast_f) ast_f.FileFooter() ast_f.write('\n') #util.log("Execution skipped because 'noexec' is on ") status = 0 if do_exec: _tlog('Execute(node)') status = ex.ExecuteAndRunExitTrap(node) # NOTE: 'exit 1' is ControlFlow and gets here, but subshell/commandsub # don't because they call sys.exit(). if opts.runtime_mem_dump: # This might be superstition, but we want to let the value stabilize # after parsing. bash -c 'cat /proc/$$/status' gives different results # with a sleep. time.sleep(0.001) input_path = '/proc/%d/status' % os.getpid() with open(input_path) as f, open(opts.runtime_mem_dump, 'w') as f2: contents = f.read() f2.write(contents) log('Wrote %s to %s (--runtime-mem-dump)', input_path, opts.runtime_mem_dump) else: status = 0 return status
from core import test_lib from core import word_eval from core import ui from core.id_kind import Id from osh import ast_ as ast from osh import parse_lib assign_op_e = ast.assign_op_e A1 = completion.WordsAction(['foo.py', 'foo', 'bar.py']) C1 = completion.ChainedCompleter([A1]) status_lines = [ui.TestStatusLine()] * 10 # A bunch of dummies mem = state.Mem('', [], {}, None) exec_opts = state.ExecOpts(mem) STATUS = completion.StatusOutput(status_lines, exec_opts) V1 = completion.WordsAction(['$var1', '$var2', '$another_var']) EMPTY = completion.WordsAction(['grep', 'sed', 'test']) FIRST = completion.WordsAction(['grep', 'sed', 'test']) class CompletionTest(unittest.TestCase): def testLookup(self): c = completion.CompletionLookup() c.RegisterName('grep', C1) print(c.GetCompleterForName('grep')) print(c.GetCompleterForName('/usr/bin/grep'))