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

253 lines, 136 significant
1"""Methods on Obj that is the io type"""
2from __future__ import print_function
3
4from _devbuild.gen.value_asdl import value, value_e, value_t
5from _devbuild.gen.syntax_asdl import loc_t
6
7from core import error
8from core import num
9from core import state
10from core import vm
11from frontend import typed_args
12from mycpp.mylib import log, NewDict
13from osh import prompt
14
15from typing import Dict, List, Optional, cast, TYPE_CHECKING
16if TYPE_CHECKING:
17 from _devbuild.gen.runtime_asdl import Cell
18 from osh import cmd_eval
19 from ysh import expr_eval
20
21_ = log
22
23EVAL_NULL = 1
24EVAL_DICT = 2
25
26
27def _CheckPosArgs(pos_args_raw, blame_loc):
28 # type: (Optional[List[value_t]], loc_t) -> Optional[List[str]]
29
30 if pos_args_raw is None:
31 return None
32
33 pos_args = [] # type: List[str]
34 for arg in pos_args_raw:
35 if arg.tag() != value_e.Str:
36 raise error.TypeErr(arg, "Expected pos_args to be a List of Strs",
37 blame_loc)
38
39 pos_args.append(cast(value.Str, arg).s)
40 return pos_args
41
42
43class EvalExpr(vm._Callable):
44
45 def __init__(self, expr_ev):
46 # type: (expr_eval.ExprEvaluator) -> None
47 self.expr_ev = expr_ev
48 self.mem = expr_ev.mem
49
50 def Call(self, rd):
51 # type: (typed_args.Reader) -> value_t
52 unused_self = rd.PosObj()
53 lazy = rd.PosExpr()
54
55 dollar0 = rd.NamedStr("dollar0", None)
56 pos_args_raw = rd.NamedList("pos_args", None)
57 vars_ = rd.NamedDict("vars", None)
58 rd.Done()
59
60 pos_args = _CheckPosArgs(pos_args_raw, rd.LeftParenToken())
61
62 # Note: ctx_Eval is on the outside, while ctx_EnclosedFrame is used in
63 # EvalExprClosure
64 with state.ctx_EnclosedFrame(self.mem, lazy.captured_frame,
65 lazy.module_frame, None):
66 with state.ctx_Eval(self.mem, dollar0, pos_args, vars_):
67 result = self.expr_ev.EvalExpr(lazy.e, rd.LeftParenToken())
68
69 return result
70
71
72def _PrintFrame(prefix, frame):
73 # type: (str, Dict[str, Cell]) -> None
74 print('%s %s' % (prefix, ' '.join(frame.keys())))
75
76 rear = frame.get('__E__')
77 if rear:
78 rear_val = rear.val
79 if rear_val.tag() == value_e.Frame:
80 r = cast(value.Frame, rear_val)
81 _PrintFrame('--> ' + prefix, r.frame)
82
83
84class EvalInFrame(vm._Callable):
85 """
86 For making "inline procs"
87 """
88
89 def __init__(self, mem, cmd_ev):
90 # type: (state.Mem, cmd_eval.CommandEvaluator) -> None
91 self.mem = mem
92 self.cmd_ev = cmd_ev
93
94 def Call(self, rd):
95 # type: (typed_args.Reader) -> value_t
96 frag = rd.PosCommandFrag()
97 bound = rd.PosFrame()
98
99 # TODO: EvalCommandFrag()
100
101 return value.Null
102
103
104class Eval(vm._Callable):
105 """
106 These are similar:
107
108 var cmd = ^(echo hi)
109 call io->eval(cmd)
110
111 Also give the top namespace
112
113 call io->evalToDict(cmd)
114
115 The CALLER must handle errors.
116 """
117
118 def __init__(self, mem, cmd_ev, which):
119 # type: (state.Mem, cmd_eval.CommandEvaluator, int) -> None
120 self.mem = mem
121 self.cmd_ev = cmd_ev
122 self.which = which
123
124 def Call(self, rd):
125 # type: (typed_args.Reader) -> value_t
126 unused = rd.PosValue()
127 bound = rd.PosCommand()
128
129 cmd = typed_args.GetCommandFrag(bound)
130
131 dollar0 = rd.NamedStr("dollar0", None)
132 pos_args_raw = rd.NamedList("pos_args", None)
133 vars_ = rd.NamedDict("vars", None)
134 rd.Done()
135
136 pos_args = _CheckPosArgs(pos_args_raw, rd.LeftParenToken())
137
138 if self.which == EVAL_NULL:
139 # _PrintFrame('[captured]', captured_frame)
140 with state.ctx_EnclosedFrame(self.mem, bound.captured_frame,
141 bound.module_frame, None):
142 # _PrintFrame('[new]', self.cmd_ev.mem.var_stack[-1])
143 with state.ctx_Eval(self.mem, dollar0, pos_args, vars_):
144 unused_status = self.cmd_ev.EvalCommandFrag(cmd)
145 return value.Null
146
147 elif self.which == EVAL_DICT:
148 # TODO: dollar0, pos_args, vars_ not supported
149 # Does ctx_EnclosedFrame has different scoping rules? For "vars"?
150
151 bindings = NewDict() # type: Dict[str, value_t]
152 with state.ctx_EnclosedFrame(self.mem, bound.captured_frame,
153 bound.module_frame, bindings):
154 unused_status = self.cmd_ev.EvalCommandFrag(cmd)
155 return value.Dict(bindings)
156
157 else:
158 raise AssertionError()
159
160
161class CaptureStdout(vm._Callable):
162
163 def __init__(self, mem, shell_ex):
164 # type: (state.Mem, vm._Executor) -> None
165 self.mem = mem
166 self.shell_ex = shell_ex
167
168 def Call(self, rd):
169 # type: (typed_args.Reader) -> value_t
170
171 unused = rd.PosValue()
172 cmd = rd.PosCommand()
173 rd.Done() # no more args
174
175 frag = typed_args.GetCommandFrag(cmd)
176 with state.ctx_EnclosedFrame(self.mem, cmd.captured_frame,
177 cmd.module_frame, None):
178 status, stdout_str = self.shell_ex.CaptureStdout(frag)
179 if status != 0:
180 # Note that $() raises error.ErrExit with the status.
181 # But I think that results in a more confusing error message, so we
182 # "wrap" the errors.
183 properties = {
184 'status': num.ToBig(status)
185 } # type: Dict[str, value_t]
186 raise error.Structured(
187 4, 'captureStdout(): command failed with status %d' % status,
188 rd.LeftParenToken(), properties)
189
190 return value.Str(stdout_str)
191
192
193class PromptVal(vm._Callable):
194 """
195 _io->promptVal('$') is like \$
196 It expands to $ or # when root
197 """
198
199 def __init__(self, prompt_ev):
200 # type: (prompt.Evaluator) -> None
201 self.prompt_ev = prompt_ev
202
203 def Call(self, rd):
204 # type: (typed_args.Reader) -> value_t
205
206 # "self" param is guaranteed to succeed
207 unused = rd.PosValue()
208 what = rd.PosStr()
209 rd.Done() # no more args
210
211 # Bug fix: protect against crash later in PromptVal()
212 if len(what) != 1:
213 raise error.Expr(
214 'promptVal() expected a single char, got %r' % what,
215 rd.LeftParenToken())
216
217 return value.Str(self.prompt_ev.PromptVal(what))
218
219
220# TODO: Implement these
221
222
223class Time(vm._Callable):
224
225 def __init__(self):
226 # type: () -> None
227 pass
228
229 def Call(self, rd):
230 # type: (typed_args.Reader) -> value_t
231 return value.Null
232
233
234class Strftime(vm._Callable):
235
236 def __init__(self):
237 # type: () -> None
238 pass
239
240 def Call(self, rd):
241 # type: (typed_args.Reader) -> value_t
242 return value.Null
243
244
245class Glob(vm._Callable):
246
247 def __init__(self):
248 # type: () -> None
249 pass
250
251 def Call(self, rd):
252 # type: (typed_args.Reader) -> value_t
253 return value.Null