OILS / builtin / method_io.py View on Github | oils.pub

256 lines, 137 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 # TODO: Add debug_frame here, with ctx_Eval or ctx_EvalDebugFrame
139 with state.ctx_TokenDebugFrame(self.mem, rd.LeftParenToken()):
140 if self.which == EVAL_NULL:
141 # _PrintFrame('[captured]', captured_frame)
142 with state.ctx_EnclosedFrame(self.mem, bound.captured_frame,
143 bound.module_frame, None):
144 # _PrintFrame('[new]', self.cmd_ev.mem.var_stack[-1])
145 with state.ctx_Eval(self.mem, dollar0, pos_args, vars_):
146 unused_status = self.cmd_ev.EvalCommandFrag(cmd)
147 return value.Null
148
149 elif self.which == EVAL_DICT:
150 # TODO: dollar0, pos_args, vars_ not supported
151 #
152 # Does ctx_EnclosedFrame has different scoping rules? For "vars"?
153
154 bindings = NewDict() # type: Dict[str, value_t]
155 with state.ctx_EnclosedFrame(self.mem, bound.captured_frame,
156 bound.module_frame, bindings):
157 unused_status = self.cmd_ev.EvalCommandFrag(cmd)
158 return value.Dict(bindings)
159
160 else:
161 raise AssertionError()
162
163
164class CaptureStdout(vm._Callable):
165
166 def __init__(self, mem, shell_ex):
167 # type: (state.Mem, vm._Executor) -> None
168 self.mem = mem
169 self.shell_ex = shell_ex
170
171 def Call(self, rd):
172 # type: (typed_args.Reader) -> value_t
173
174 unused = rd.PosValue()
175 cmd = rd.PosCommand()
176 rd.Done() # no more args
177
178 frag = typed_args.GetCommandFrag(cmd)
179 with state.ctx_EnclosedFrame(self.mem, cmd.captured_frame,
180 cmd.module_frame, None):
181 status, stdout_str = self.shell_ex.CaptureStdout(frag)
182 if status != 0:
183 # Note that $() raises error.ErrExit with the status.
184 # But I think that results in a more confusing error message, so we
185 # "wrap" the errors.
186 properties = {
187 'status': num.ToBig(status)
188 } # type: Dict[str, value_t]
189 raise error.Structured(
190 4, 'captureStdout(): command failed with status %d' % status,
191 rd.LeftParenToken(), properties)
192
193 return value.Str(stdout_str)
194
195
196class PromptVal(vm._Callable):
197 """
198 _io->promptVal('$') is like \$
199 It expands to $ or # when root
200 """
201
202 def __init__(self, prompt_ev):
203 # type: (prompt.Evaluator) -> None
204 self.prompt_ev = prompt_ev
205
206 def Call(self, rd):
207 # type: (typed_args.Reader) -> value_t
208
209 # "self" param is guaranteed to succeed
210 unused = rd.PosValue()
211 what = rd.PosStr()
212 rd.Done() # no more args
213
214 # Bug fix: protect against crash later in PromptVal()
215 if len(what) != 1:
216 raise error.Expr(
217 'promptVal() expected a single char, got %r' % what,
218 rd.LeftParenToken())
219
220 return value.Str(self.prompt_ev.PromptVal(what))
221
222
223# TODO: Implement these
224
225
226class Time(vm._Callable):
227
228 def __init__(self):
229 # type: () -> None
230 pass
231
232 def Call(self, rd):
233 # type: (typed_args.Reader) -> value_t
234 return value.Null
235
236
237class Strftime(vm._Callable):
238
239 def __init__(self):
240 # type: () -> None
241 pass
242
243 def Call(self, rd):
244 # type: (typed_args.Reader) -> value_t
245 return value.Null
246
247
248class Glob(vm._Callable):
249
250 def __init__(self):
251 # type: () -> None
252 pass
253
254 def Call(self, rd):
255 # type: (typed_args.Reader) -> value_t
256 return value.Null