1 | // See GazelleV_LeftArrowScriptParser |
2 | |
3 | // TODO: decide whether we allow calling methods/getting fields on |
4 | // a null reference (just returning null), or whether we throw a |
5 | // NullPointerException. Currently we just return null. Probably |
6 | // that's pretty cool. Null propagation as a default, just like in |
7 | // JavaX. Just more automatic! |
8 | |
9 | sclass GazelleV_LeftArrowScript {
|
10 | // Base = any script element with a reference to its source code |
11 | |
12 | asclass Base is IHasTokenRangeWithSrc {
|
13 | TokenRangeWithSrc src; |
14 | |
15 | public void setTokenRangeWithSrc(TokenRangeWithSrc src) { this.src = src; }
|
16 | public TokenRangeWithSrc tokenRangeWithSrc() { ret src; }
|
17 | |
18 | RuntimeException rethrowWithSrc(Throwable e) {
|
19 | if (src != null) |
20 | throw rethrowAndAppendToMessage(e, squareBracketed(str(src))); |
21 | else |
22 | throw rethrow(e); |
23 | } |
24 | } |
25 | |
26 | // Evaluable = a script element that can be evaluated |
27 | |
28 | interface Evaluable extends IF0 {
|
29 | public O get(VarContext ctx default new); |
30 | public default LASValueDescriptor returnType() { null; }
|
31 | public default Evaluable optimize() { this; }
|
32 | } |
33 | |
34 | // Base + Evaluable + explicitly stored return type |
35 | |
36 | asclass BaseEvaluable > Base is Evaluable {
|
37 | settable LASValueDescriptor returnType; |
38 | } |
39 | |
40 | /*interface Cmd {
|
41 | // returns true if a return was issued |
42 | public bool run(VarContext ctx default new); |
43 | }*/ |
44 | |
45 | static new AtomicLong scriptIDCounter; |
46 | static long scriptID() { ret incAtomicLong(scriptIDCounter); }
|
47 | |
48 | sclass Script > Base is Evaluable {
|
49 | long id = scriptID(); // just for printing |
50 | Map<S, FunctionDef> functionDefs; |
51 | Evaluable[] steps; |
52 | |
53 | public O get(VarContext ctx) {
|
54 | O result = null; |
55 | for (step : steps) {
|
56 | ping(); |
57 | result = step.get(ctx); |
58 | |
59 | // exiting from anything? |
60 | |
61 | var exiting = ctx.exitFromScript; |
62 | if (exiting != null) {
|
63 | printVars ifdef ReturnFromScript_debug("Checking exitFromScript",
|
64 | +ctx, +exiting, script := this); |
65 | |
66 | // we're the exit point |
67 | if (exiting == this) |
68 | ctx.exitFromScript = null; |
69 | |
70 | // exit further |
71 | break; |
72 | } |
73 | } |
74 | ret result; |
75 | } |
76 | |
77 | S toStringLong() { ret pnlToLines(steps); }
|
78 | toString { ret "Script " + n2(id); }
|
79 | |
80 | FunctionDef getFunction(S name) { ret mapGet(functionDefs, name); }
|
81 | } // end of Script |
82 | |
83 | srecord noeq FunctionDef(S name, LS args, Evaluable body) > Base {
|
84 | public O call(VarContext ctx, O... args) {
|
85 | var ctx2 = new VarContext(ctx); |
86 | int n = min(l(args), l(this.args)); |
87 | for i to n: |
88 | ctx2.put(this.args.get(i), args[i]); |
89 | print ifdef GazelleV_LeftArrowScript_debug(ctx2 := ctx2.vars); |
90 | ret body.get(ctx2); |
91 | } |
92 | } |
93 | |
94 | srecord noeq Assignment(S var, Evaluable expression) > Base is Evaluable {
|
95 | public O get(VarContext ctx) {
|
96 | O o = expression.get(ctx); |
97 | ctx.set(var, o); |
98 | ret o; |
99 | } |
100 | |
101 | toString { ret var + " <- " + expression; }
|
102 | } |
103 | |
104 | persistable sclass NewObject > Base is Evaluable {
|
105 | Class c; |
106 | L<Evaluable> args; |
107 | |
108 | *(Class *c) {}
|
109 | *(Class *c, L<Evaluable> *args) {}
|
110 | |
111 | public O get(VarContext ctx) {
|
112 | ret callConstructor(c, mapToArray(args, arg -> arg.get(ctx))); |
113 | } |
114 | |
115 | toString { ret "new " + formatFunctionCall(className(c), args); }
|
116 | } |
117 | |
118 | srecord noeq CallFunction(FunctionDef f, L<Evaluable> args) > Base is Evaluable {
|
119 | public O get(VarContext ctx) {
|
120 | //ping(); |
121 | ret f.call(ctx, mapToArray(args, a -> a.get(ctx)); |
122 | } |
123 | |
124 | toString { ret formatFunctionCall(f.name, args); }
|
125 | } |
126 | |
127 | srecord noeq GetVar(S var) > BaseEvaluable {
|
128 | public O get(VarContext ctx) {
|
129 | ret ctx.get(var); |
130 | } |
131 | |
132 | toString { ret var; }
|
133 | } |
134 | |
135 | srecord noeq Const(O value) > Base is Evaluable {
|
136 | public O get(VarContext ctx) {
|
137 | ret value; |
138 | } |
139 | |
140 | toString { ret strOrClassName(value); }
|
141 | |
142 | public LASValueDescriptor returnType() {
|
143 | ret new LASValueDescriptor.KnownValue(value); |
144 | } |
145 | } |
146 | |
147 | srecord noeq GetStaticField(Field field) > Base is Evaluable {
|
148 | public O get(VarContext ctx) ctex {
|
149 | ret field.get(null); |
150 | } |
151 | } |
152 | |
153 | srecord noeq CallMethodOrGetField(Evaluable target, S name) > Base is Evaluable {
|
154 | public O get(VarContext ctx) {
|
155 | try {
|
156 | O object = target.get(ctx); |
157 | if (object == null) |
158 | null; // throw new NullPointerException(); |
159 | |
160 | // could optimize more for sure |
161 | if (canCallWithVarargs(object, name)) |
162 | ret call(object, name); |
163 | |
164 | // TODO: better error message when neither field nor method found |
165 | ret _get(object, name); |
166 | } catch e {
|
167 | throw rethrowWithSrc(e); |
168 | } |
169 | } |
170 | } |
171 | |
172 | sclass GetVarContext > Base is Evaluable {
|
173 | public O get(VarContext ctx) { ret ctx; }
|
174 | } |
175 | |
176 | srecord noeq ThrowMethodNotFoundException(CallMethod instruction) > Base is Evaluable {
|
177 | public O get(VarContext ctx) {
|
178 | fail("Method not found: " + instruction);
|
179 | } |
180 | } |
181 | |
182 | srecord noeq ThrowNullPointerException(CallMethod instruction) > Base is Evaluable {
|
183 | public O get(VarContext ctx) {
|
184 | fail("Null pointer exception: " + instruction);
|
185 | } |
186 | } |
187 | |
188 | srecord noeq CallMethod(Evaluable target, S methodName, L<Evaluable> args) > Base is Evaluable {
|
189 | public O get(VarContext ctx) {
|
190 | ret call(target.get(ctx), methodName, mapToArray(args, arg -> arg.get(ctx))); |
191 | } |
192 | |
193 | toString { ret target + "." + formatFunctionCall(methodName, args); }
|
194 | |
195 | public Evaluable optimize() {
|
196 | var targetType = target.returnType(); |
197 | if (targetType.knownValue()) {
|
198 | O o = targetType.value(); |
199 | if (o == null) ret new ThrowNullPointerException(this); |
200 | |
201 | Class[] argTypes = new[l(args)]; |
202 | for i over args: {
|
203 | var type = args.get(i).returnType(); |
204 | if (!type.javaClassIsExact()) |
205 | this; |
206 | argTypes[i] = type.javaClass(); |
207 | } |
208 | |
209 | // TODO: varargs |
210 | var method = findMethod_precise_onTypes(o, methodName, argTypes); |
211 | if (method == null) ret new ThrowMethodNotFoundException(this); |
212 | |
213 | ret new DirectMethodCallOnKnownTarget(o instanceof Class ? null : o, method, args); |
214 | } |
215 | |
216 | this; |
217 | } |
218 | } |
219 | |
220 | srecord noeq DirectMethodCallOnKnownTarget(O target, Method method, L<Evaluable> args) > Base is Evaluable {
|
221 | public O get(VarContext ctx) {
|
222 | ret invokeMethod(method, target, mapToArray(args, arg -> arg.get(ctx))); |
223 | } |
224 | |
225 | toString { ret (target == null ? "" : target + ".") + formatFunctionCall(str(method), args); }
|
226 | |
227 | public LASValueDescriptor returnType() {
|
228 | ret LASValueDescriptor.fromClass(method.getReturnType()); |
229 | } |
230 | } |
231 | |
232 | srecord noeq While(Evaluable condition, Evaluable body) > Base is Evaluable {
|
233 | public O get(VarContext ctx) {
|
234 | while (ping() && (Bool) condition.get(ctx)) {
|
235 | body.get(ctx); |
236 | } |
237 | |
238 | // while loops don't return anything |
239 | null; |
240 | } |
241 | } |
242 | |
243 | srecord noeq ForEach(Evaluable collection, S var, Evaluable body) > Base is Evaluable {
|
244 | public O get(VarContext ctx) {
|
245 | var coll = collection.get(ctx); |
246 | Iterator iterator; |
247 | new L out; |
248 | if (coll cast O[]) |
249 | for (element : coll) {
|
250 | ping(); |
251 | ctx.set(var, element); |
252 | out.add(body.get(ctx)); |
253 | } |
254 | else if (coll cast Iterable) {
|
255 | for (element : coll) {
|
256 | ping(); |
257 | ctx.set(var, element); |
258 | out.add(body.get(ctx)); |
259 | } |
260 | } else if (coll == null) {} // ok
|
261 | else |
262 | fail("Not iterable: " + className(coll));
|
263 | |
264 | ctx.unset(var); |
265 | ret out; |
266 | } |
267 | } |
268 | |
269 | srecord noeq IfThen(Evaluable condition, Evaluable body, |
270 | Evaluable elseBranch) > Base is Evaluable {
|
271 | |
272 | IfThen(Evaluable condition, Evaluable body) {
|
273 | this.condition = condition; |
274 | this.body = body; |
275 | } |
276 | |
277 | public O get(VarContext ctx) {
|
278 | if ((Bool) condition.get(ctx)) |
279 | ret body.get(ctx); |
280 | else if (elseBranch != null) |
281 | ret elseBranch.get(ctx); |
282 | else |
283 | null; |
284 | } |
285 | } |
286 | |
287 | srecord noeq ReturnFromScript(Script script, Evaluable value) > Base is Evaluable {
|
288 | public O get(VarContext ctx) {
|
289 | O result = value.get(ctx); |
290 | printVars ifdef ReturnFromScript_debug("ReturnFromScript",
|
291 | +result, +ctx, +script); |
292 | ctx.exitFromScript(script); |
293 | ret result; |
294 | } |
295 | |
296 | toString {
|
297 | ret formatFunctionCall ReturnFromScript(script, value); |
298 | } |
299 | } |
300 | } |
Began life as a copy of #1033981
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
| Snippet ID: | #1034737 |
| Snippet name: | GazelleV_LeftArrowScript [backup] |
| Eternal ID of this version: | #1034737/1 |
| Text MD5: | 3b48664fc921e99eafe8c78903369d21 |
| Author: | stefan |
| Category: | javax |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2022-03-08 00:42:43 |
| Source code size: | 8898 bytes / 300 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 389 / 411 |
| Referenced in: | [show references] |