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

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