OILS / builtin / pure_ysh.py View on Github | oilshell.org

131 lines, 81 significant
1"""
2builtin/pure_ysh.py - YSH builtins that don't do I/O.
3"""
4from __future__ import print_function
5
6from _devbuild.gen.runtime_asdl import cmd_value
7from _devbuild.gen.syntax_asdl import command_t, loc, loc_t
8from _devbuild.gen.value_asdl import value, value_e, value_t
9from core import error
10from core import state
11from core import vm
12from frontend import flag_util
13from frontend import typed_args
14from mycpp import mylib
15from mycpp.mylib import tagswitch, NewDict
16
17from typing import TYPE_CHECKING, cast, Any, Dict, List
18
19if TYPE_CHECKING:
20 from display import ui
21 from osh.cmd_eval import CommandEvaluator
22
23
24class Shvar(vm._Builtin):
25
26 def __init__(self, mem, search_path, cmd_ev):
27 # type: (state.Mem, state.SearchPath, CommandEvaluator) -> None
28 self.mem = mem
29 self.search_path = search_path # to clear PATH
30 self.cmd_ev = cmd_ev # To run blocks
31
32 def Run(self, cmd_val):
33 # type: (cmd_value.Argv) -> int
34 _, arg_r = flag_util.ParseCmdVal('shvar',
35 cmd_val,
36 accept_typed_args=True)
37
38 cmd = typed_args.OptionalBlock(cmd_val)
39 if not cmd:
40 # TODO: I think shvar LANG=C should just mutate
41 # But should there be a whitelist?
42 raise error.Usage('expected a block', loc.Missing)
43
44 vars = NewDict() # type: Dict[str, value_t]
45 args, arg_locs = arg_r.Rest2()
46 if len(args) == 0:
47 raise error.Usage('Expected name=value', loc.Missing)
48
49 for i, arg in enumerate(args):
50 name, s = mylib.split_once(arg, '=')
51 if s is None:
52 raise error.Usage('Expected name=value', arg_locs[i])
53 v = value.Str(s) # type: value_t
54 vars[name] = v
55
56 # Important fix: shvar PATH='' { } must make all binaries invisible
57 if name == 'PATH':
58 self.search_path.ClearCache()
59
60 with state.ctx_Eval(self.mem, None, None, vars):
61 unused = self.cmd_ev.EvalCommand(cmd)
62
63 return 0
64
65
66class PushRegisters(vm._Builtin):
67
68 def __init__(self, mem, cmd_ev):
69 # type: (state.Mem, CommandEvaluator) -> None
70 self.mem = mem
71 self.cmd_ev = cmd_ev # To run blocks
72
73 def Run(self, cmd_val):
74 # type: (cmd_value.Argv) -> int
75 _, arg_r = flag_util.ParseCmdVal('push-registers',
76 cmd_val,
77 accept_typed_args=True)
78
79 cmd = typed_args.OptionalBlock(cmd_val)
80 if not cmd:
81 raise error.Usage('expected a block', loc.Missing)
82
83 with state.ctx_Registers(self.mem):
84 unused = self.cmd_ev.EvalCommand(cmd)
85
86 # make it "SILENT" in terms of not mutating $?
87 # TODO: Revisit this. It might be better to provide the headless shell
88 # with a way to SET $? instead. Needs to be tested/prototyped.
89 return self.mem.last_status[-1]
90
91
92class Append(vm._Builtin):
93 """Push word args onto an List.
94
95 Not doing typed args since you can do
96
97 :: mylist->append(42)
98 """
99
100 def __init__(self, mem, errfmt):
101 # type: (state.Mem, ui.ErrorFormatter) -> None
102 self.mem = mem
103 self.errfmt = errfmt
104
105 def Run(self, cmd_val):
106 # type: (cmd_value.Argv) -> int
107
108 # This means we ignore -- , which is consistent
109 _, arg_r = flag_util.ParseCmdVal('append',
110 cmd_val,
111 accept_typed_args=True)
112
113 rd = typed_args.ReaderForProc(cmd_val)
114 val = rd.PosValue()
115 rd.Done()
116
117 UP_val = val
118 with tagswitch(val) as case:
119 if case(value_e.BashArray):
120 val = cast(value.BashArray, UP_val)
121 val.strs.extend(arg_r.Rest())
122 elif case(value_e.List):
123 val = cast(value.List, UP_val)
124 typed = [value.Str(s)
125 for s in arg_r.Rest()] # type: List[value_t]
126 val.items.extend(typed)
127 else:
128 raise error.TypeErr(val, 'expected List or BashArray',
129 loc.Missing)
130
131 return 0