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

234 lines, 123 significant
1#!/usr/bin/env python2
2"""
3func_reflect.py - Functions for reflecting on Oils code - OSH or YSH.
4"""
5from __future__ import print_function
6
7from _devbuild.gen.runtime_asdl import (scope_e)
8from _devbuild.gen.syntax_asdl import source
9from _devbuild.gen.value_asdl import (value, value_e, value_t, cmd_frag)
10
11from core import alloc
12from core import error
13from core import main_loop
14from core import state
15from core import vm
16from data_lang import j8
17from frontend import location
18from frontend import reader
19from frontend import typed_args
20from mycpp import mops
21from mycpp.mylib import log, tagswitch
22from ysh import expr_eval
23
24from typing import TYPE_CHECKING
25if TYPE_CHECKING:
26 from frontend import parse_lib
27 from display import ui
28
29_ = log
30
31
32class Id(vm._Callable):
33 """Return an integer object ID, like Python's id().
34
35 Long shot: pointer tagging, boxless value_t, and small string optimization
36 could mean that value.Str is no longer heap-allocated, and thus doesn't
37 have a GC ID?
38
39 What about value.{Bool,Int,Float}?
40
41 I guess only mutable objects can have IDs then
42 """
43
44 def __init__(self):
45 # type: () -> None
46 vm._Callable.__init__(self)
47
48 def Call(self, rd):
49 # type: (typed_args.Reader) -> value_t
50 unused_vm = rd.PosValue() # vm.id()
51 val = rd.PosValue()
52 rd.Done()
53
54 # Select mutable values for now
55 with tagswitch(val) as case:
56 if case(value_e.List, value_e.Dict, value_e.Obj):
57 id_ = j8.HeapValueId(val)
58 return value.Int(mops.IntWiden(id_))
59 else:
60 raise error.TypeErr(val, 'id() expected List, Dict, or Obj',
61 rd.BlamePos())
62 raise AssertionError()
63
64
65class GetFrame(vm._Callable):
66
67 def __init__(self, mem):
68 # type: (state.Mem) -> None
69 vm._Callable.__init__(self)
70 self.mem = mem
71
72 def Call(self, rd):
73 # type: (typed_args.Reader) -> value_t
74 unused_self = rd.PosObj()
75 index = mops.BigTruncate(rd.PosInt())
76 rd.Done()
77
78 length = len(self.mem.var_stack)
79 if index < 0:
80 index += length
81 if 0 <= index and index < length:
82 return value.Frame(self.mem.var_stack[index])
83 else:
84 raise error.Structured(3, "Invalid frame %d" % index,
85 rd.LeftParenToken())
86
87
88class BindFrame(vm._Callable):
89
90 def __init__(self):
91 # type: () -> None
92 vm._Callable.__init__(self)
93
94 def Call(self, rd):
95 # type: (typed_args.Reader) -> value_t
96
97 # TODO: also take an ExprFrag -> Expr
98
99 frag = rd.PosCommandFrag()
100 frame = rd.PosFrame()
101 rd.Done()
102 return value.Null
103 # TODO: I guess you have to bind 2 frames?
104 #return Command(cmd_frag.Expr(frag), frame, None)
105
106
107class Shvar_get(vm._Callable):
108 """Look up with dynamic scope."""
109
110 def __init__(self, mem):
111 # type: (state.Mem) -> None
112 vm._Callable.__init__(self)
113 self.mem = mem
114
115 def Call(self, rd):
116 # type: (typed_args.Reader) -> value_t
117 name = rd.PosStr()
118 rd.Done()
119 return state.DynamicGetVar(self.mem, name, scope_e.Dynamic)
120
121
122class GetVar(vm._Callable):
123 """Look up a variable, with normal scoping rules."""
124
125 def __init__(self, mem):
126 # type: (state.Mem) -> None
127 vm._Callable.__init__(self)
128 self.mem = mem
129
130 def Call(self, rd):
131 # type: (typed_args.Reader) -> value_t
132 name = rd.PosStr()
133 rd.Done()
134 return state.DynamicGetVar(self.mem, name, scope_e.LocalOrGlobal)
135
136
137class SetVar(vm._Callable):
138 """Set a variable in the local scope.
139
140 We could have a separae setGlobal() too.
141 """
142
143 def __init__(self, mem):
144 # type: (state.Mem) -> None
145 vm._Callable.__init__(self)
146 self.mem = mem
147
148 def Call(self, rd):
149 # type: (typed_args.Reader) -> value_t
150 var_name = rd.PosStr()
151 val = rd.PosValue()
152 rd.Done()
153 self.mem.SetNamed(location.LName(var_name), val, scope_e.LocalOnly)
154 return value.Null
155
156
157class ParseCommand(vm._Callable):
158
159 def __init__(self, parse_ctx, mem, errfmt):
160 # type: (parse_lib.ParseContext, state.Mem, ui.ErrorFormatter) -> None
161 self.parse_ctx = parse_ctx
162 self.mem = mem
163 self.errfmt = errfmt
164
165 def Call(self, rd):
166 # type: (typed_args.Reader) -> value_t
167 code_str = rd.PosStr()
168 rd.Done()
169
170 line_reader = reader.StringLineReader(code_str, self.parse_ctx.arena)
171 c_parser = self.parse_ctx.MakeOshParser(line_reader)
172
173 # TODO: it would be nice to point to the location of the expression
174 # argument
175 src = source.Dynamic('parseCommand()', rd.LeftParenToken())
176 with alloc.ctx_SourceCode(self.parse_ctx.arena, src):
177 try:
178 cmd = main_loop.ParseWholeFile(c_parser)
179 except error.Parse as e:
180 # This prints the location
181 self.errfmt.PrettyPrintError(e)
182
183 # TODO: add inner location info to this structured error
184 raise error.Structured(3, "Syntax error in parseCommand()",
185 rd.LeftParenToken())
186
187 # TODO: It's a little weird that this captures?
188 # We should have scoping like 'eval $mystr'
189 # Or we should have
190 #
191 # var c = parseCommand('echo hi') # raw AST
192 # var block = Block(c) # attachs the current frame
193 #
194 # Yeah we might need this for value.Expr too, to control evaluation of
195 # names
196 #
197 # value.Expr vs. value.BoundExpr - it's bound to the frame it's defined
198 # in
199 # value.Command vs. value.Block - BoundCommand?
200
201 return value.Command(cmd_frag.Expr(cmd), self.mem.CurrentFrame(),
202 self.mem.GlobalFrame())
203
204
205class ParseExpr(vm._Callable):
206
207 def __init__(self, parse_ctx, errfmt):
208 # type: (parse_lib.ParseContext, ui.ErrorFormatter) -> None
209 self.parse_ctx = parse_ctx
210 self.errfmt = errfmt
211
212 def Call(self, rd):
213 # type: (typed_args.Reader) -> value_t
214 code_str = rd.PosStr()
215 rd.Done()
216
217 return value.Null
218
219
220class EvalExpr(vm._Callable):
221
222 def __init__(self, expr_ev):
223 # type: (expr_eval.ExprEvaluator) -> None
224 self.expr_ev = expr_ev
225
226 def Call(self, rd):
227 # type: (typed_args.Reader) -> value_t
228 unused_self = rd.PosObj()
229 lazy = rd.PosExpr()
230 rd.Done()
231
232 result = self.expr_ev.EvalExprClosure(lazy, rd.LeftParenToken())
233
234 return result