1 | /* e.g. |
2 | |
3 | overlay <- ScreenOverlay |
4 | bounds <- rightScreenBounds |
5 | overlay bounds bounds |
6 | overlay show |
7 | |
8 | Can separate commands with ";" also. |
9 | For how to define functions in a script see #1033988. |
10 | |
11 | Note: LeftArrowScriptAutoCompleter uses a lot of this class's internals |
12 | |
13 | */ |
14 | |
15 | sclass GazelleV_LeftArrowScriptParser > SimpleLeftToRightParser { |
16 | replace Toolbox with GazelleV_LeftArrowScript. |
17 | delegate Script to Toolbox. |
18 | delegate Evaluable to Toolbox. |
19 | replace const with _const. |
20 | |
21 | settable new G22Utils g22utils; |
22 | L functionContainers; |
23 | |
24 | new LinkedHashSet<S> knownVars; |
25 | BuildingScript currentReturnableScript; |
26 | int parenLevels; |
27 | |
28 | // object can be a class |
29 | srecord MethodOnObject(O object, S method) {} |
30 | |
31 | class BuildingScript { |
32 | bool returnable; |
33 | new Script script; |
34 | new L<Evaluable> steps; |
35 | Map<S, Toolbox.FunctionDef> functionDefs = new Map; |
36 | |
37 | *(bool *returnable) {} |
38 | |
39 | void add(Evaluable step) { if (step != null) steps.add(step); } |
40 | |
41 | Evaluable get() { |
42 | // if the last command is a return from THIS script, |
43 | // convert into a simple expression |
44 | |
45 | var lastStep = last(steps); |
46 | if (lastStep cast Toolbox.ReturnFromScript) |
47 | if (lastStep.script == script) |
48 | replaceLast(steps, lastStep.value); |
49 | |
50 | // if the script is only step, is not returnable |
51 | // and defines no functions, replace it with its only step |
52 | |
53 | if (!returnable && l(steps) == 1 && empty(functionDefs)) |
54 | ret first(steps); |
55 | |
56 | // make and return actual script |
57 | |
58 | script.functionDefs = functionDefs; |
59 | script.steps = toTypedArray(Evaluable.class, steps); |
60 | ret script; |
61 | } |
62 | |
63 | S toStringLong() { ret pnlToLines(steps); } |
64 | toString { ret formatRecordVars BuildingScript(+script, +returnable); } |
65 | } |
66 | |
67 | Script parse(S text) { |
68 | setText(text); |
69 | ret (Script) parseScript(true); |
70 | } |
71 | |
72 | // if returnable is true, it's always a Script |
73 | Evaluable parseScript(bool returnable) { |
74 | BuildingScript script = new(returnable); |
75 | var lastReturnableScript = currentReturnableScript; |
76 | if (returnable) currentReturnableScript = script; |
77 | try { |
78 | parseScript_2(script); |
79 | var builtScript = script!; |
80 | currentReturnableScript = lastReturnableScript; |
81 | ret builtScript; |
82 | } catch e { |
83 | print("Parsed so far:\n" + script); |
84 | |
85 | throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn()))); |
86 | } |
87 | } |
88 | |
89 | void parseScript_2(BuildingScript script) { |
90 | while (mainLoop()) { |
91 | if (is(";")) continue with next(); |
92 | if (is("}")) break; |
93 | |
94 | S t = token(); |
95 | print("First token of command: " + t); |
96 | |
97 | if (is("def")) |
98 | continue with parseFunctionDefinition(); |
99 | |
100 | if (is("while")) |
101 | continue with script.add(parseWhileLoop()); |
102 | |
103 | if (is("for")) |
104 | continue with script.add(parseForEach()); |
105 | |
106 | if (is("if")) |
107 | continue with script.add(parseIfStatement()); |
108 | |
109 | // return is just ignored for now |
110 | if (is("return")) { |
111 | consume(); |
112 | var expr = parseExpr(); |
113 | continue with script.add(new Toolbox.ReturnFromScript(currentReturnableScript.script, expr)); |
114 | } |
115 | |
116 | print("next tokens: " + quote(token(1)) + " " + quote(token(2))); |
117 | if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) { |
118 | print("Found assignment"); |
119 | next(3); |
120 | knownVars.add(t); |
121 | script.add(new Toolbox.Assignment(t, parseExpr())); |
122 | } else |
123 | script.add(parseExpr()); |
124 | } |
125 | } |
126 | |
127 | Evaluable parseOptionalInnerExpression() { |
128 | printVars parseOptionalInnerExpression(+token()); |
129 | if (atCmdEnd() || is("{")) null; |
130 | ret parseInnerExpr(); |
131 | } |
132 | |
133 | Evaluable const(O o) { ret new Toolbox.Const(o); } |
134 | |
135 | Evaluable parseInnerExpr() { ret parseExpr(true); } |
136 | |
137 | Evaluable parseExpr(bool inner default false) { |
138 | if (atEnd()) null; |
139 | |
140 | S t = token(); |
141 | printVars parseExpr(token := t); |
142 | if (is(";")) null; // empty command |
143 | |
144 | // int or double literal |
145 | if (is("-") && empty(nextSpace()) && startsWithDigit(token(1)) |
146 | || startsWithDigit(t)) { |
147 | t = consumeMultiTokenLiteral(); |
148 | ret isInteger(t) ? const(parseInt(t)) : const(parseDouble(t)); |
149 | } |
150 | |
151 | if (isQuoted(t)) { |
152 | consume(); |
153 | ret const(unquote(t)); |
154 | } |
155 | |
156 | if (isIdentifier(t)) { |
157 | consume(); |
158 | print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner); |
159 | ret parseExprStartingWithIdentifier(t, inner); |
160 | } |
161 | |
162 | // nested expression |
163 | |
164 | if (eq(t, "(")) { |
165 | parenLevels++; |
166 | consume(); |
167 | print("Consumed opening parens (level " + parenLevels + ")"); |
168 | var e = parseExpr(); |
169 | print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")"); |
170 | consume(")"); |
171 | parenLevels--; |
172 | |
173 | ret inner ? e : parseCall(e); |
174 | } |
175 | |
176 | fail("Identifier, literal or opening parentheses expected (got: " + quote(t)); |
177 | } |
178 | |
179 | // t is last consumed token (the identifier the expression starts with) |
180 | Evaluable parseExprStartingWithIdentifier(S t, bool inner) { |
181 | if (eq(t, "true")) ret const(true); |
182 | if (eq(t, "false")) ret const(false); |
183 | if (eq(t, "null")) ret const(null); |
184 | |
185 | if (eq(t, "new")) { |
186 | S className = assertIdentifier(tpp()); |
187 | O o = findExternalObject(className); |
188 | if (o cast Class) |
189 | ret new Toolbox.NewObject(o, parseArguments()); |
190 | fail("Class not found: " + className); |
191 | } |
192 | |
193 | if (knownVars.contains(t)) { |
194 | var e = new Toolbox.GetVar(t); |
195 | print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression")); |
196 | ret inner ? e : parseCall(e); |
197 | } |
198 | |
199 | if (!inner) { |
200 | var fdef = currentReturnableScript.functionDefs.get(t); |
201 | if (fdef != null) |
202 | ret new Toolbox.CallFunction(fdef, parseArguments()); |
203 | } |
204 | |
205 | O o = findExternalObject(t); |
206 | if (o == null) |
207 | fail("Unknown object: " + t); |
208 | else if (inner) |
209 | ret const(o); |
210 | else if (o cast Class) { |
211 | /*if (atCmdEnd()) |
212 | ret new Toolbox.NewObject(o);*/ |
213 | |
214 | /* old object creation syntax (e.g. Pair new a b) |
215 | if (is("new")) { |
216 | next(); |
217 | ret new Toolbox.NewObject(o, parseArguments()); |
218 | } else*/ if (isIdentifier()) { |
219 | S name = tpp(); |
220 | |
221 | // look for method first |
222 | |
223 | if (hasMethodNamed(o, name)) |
224 | ret new Toolbox.CallMethod(const(o), name, parseArguments()); |
225 | |
226 | // look for field second |
227 | |
228 | var field = getField(o, name); |
229 | if (field != null) { |
230 | assertCmdEnd(); |
231 | ret new Toolbox.GetStaticField(field); |
232 | } |
233 | |
234 | fail(name + " not found in " + o + " (looked for method or field)"); |
235 | } else |
236 | fail("Method name expected: " + token()); |
237 | } else if (o cast MethodOnObject) { |
238 | if (inner) fail("Can't call methods in arguments"); |
239 | ret new Toolbox.CallMethod(const(o.object), o.method, parseArguments()); |
240 | } else |
241 | ret parseCall(const(o)); |
242 | } |
243 | |
244 | L<Evaluable> parseArguments() { |
245 | //ret collectWhileNotNull(-> parseOptionalInnerExpression()); |
246 | |
247 | new L<Evaluable> l; |
248 | try { |
249 | while (true) { |
250 | Evaluable a = parseOptionalInnerExpression(); |
251 | if (a == null) break; |
252 | l.add(a); |
253 | } |
254 | ret l; |
255 | } on fail { |
256 | print("Arguments parsed so far: " + l); |
257 | } |
258 | } |
259 | |
260 | S consumeMultiTokenLiteral() { |
261 | ret consumeUntilSpaceOr(-> atCmdEnd()); |
262 | } |
263 | |
264 | bool atCmdEnd() { |
265 | ret |
266 | parenLevels == 0 && atEndOrLineBreak() |
267 | || is(";") || is("}") || is(")"); |
268 | } |
269 | |
270 | void assertCmdEnd() { if (!atCmdEnd()) fail("Expected end of command"); } |
271 | |
272 | Evaluable parseCall(Evaluable target) { |
273 | if (atCmdEnd() || !isIdentifier()) ret target; |
274 | |
275 | var start = ptr(); |
276 | S methodName = tpp(); |
277 | var args = parseArguments(); |
278 | |
279 | if (nempty(args)) |
280 | ret new Toolbox.CallMethod(target, methodName, args); |
281 | else |
282 | ret src(start, new Toolbox.CallMethodOrGetField(target, methodName)); |
283 | } |
284 | |
285 | <A> A src(TokPtr start, A a) { |
286 | if (a cast IHasTokenRangeWithSrc) |
287 | a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr())); |
288 | ret a; |
289 | } |
290 | |
291 | // can return MethodOnObject |
292 | swappable O findExternalObject(S name) ctex { |
293 | //try object findClassThroughDefaultClassFinder(name); |
294 | //try object findClassInStandardImports(name); |
295 | |
296 | S fullName = globalClassNames().get(name); |
297 | if (fullName != null) |
298 | ret Class.forName(fullName); |
299 | |
300 | fOr (container : functionContainers) |
301 | if (hasMethodNamed(container, name)) |
302 | ret new MethodOnObject(container, name); |
303 | null; |
304 | } |
305 | |
306 | selfType allowTheWorld() { ret allowTheWorld(mc()); } |
307 | |
308 | selfType allowTheWorld(O... functionContainers) { |
309 | this.functionContainers = asList(functionContainers); |
310 | globalClassNames_cache = null; // recalculate |
311 | this; |
312 | } |
313 | |
314 | void printFunctionDefs(Script script) { |
315 | print(values(script.functionDefs)); |
316 | } |
317 | |
318 | void parseFunctionDefinition() { |
319 | consume("def"); |
320 | S functionName = assertIdentifier(tpp()); |
321 | new LS args; |
322 | while (isIdentifier()) |
323 | args.add(tpp()); |
324 | temp tempAddAll(knownVars, args); |
325 | var functionBody = parseCurlyBlock(true); |
326 | |
327 | currentReturnableScript.functionDefs.put(functionName, |
328 | new Toolbox.FunctionDef(functionName, args, functionBody)); |
329 | } |
330 | |
331 | Evaluable parseCurlyBlock(bool returnable) { |
332 | //print(+knownVars); |
333 | consume("{"); |
334 | var script = parseScript(returnable); |
335 | consume("}"); |
336 | ret script; |
337 | } |
338 | |
339 | Evaluable parseWhileLoop() { |
340 | consume("while"); |
341 | var condition = parseExpr(); |
342 | var body = parseCurlyBlock(false); |
343 | ret new Toolbox.While(condition, body); |
344 | } |
345 | |
346 | Evaluable parseForEach() { |
347 | consume("for"); |
348 | S var = assertIdentifier(tpp()); |
349 | print("for var", var); |
350 | consume("in"); |
351 | var collection = parseExpr(); |
352 | print(+collection); |
353 | temp tempAdd(knownVars, var); |
354 | var body = parseCurlyBlock(false); |
355 | ret new Toolbox.ForEach(collection, var, body); |
356 | } |
357 | |
358 | Evaluable parseIfStatement() { |
359 | consume("if"); |
360 | var condition = parseExpr(); |
361 | var body = parseCurlyBlock(false); |
362 | ret new Toolbox.IfThen(condition, body); |
363 | } |
364 | |
365 | // declare an external variable |
366 | void addVar(S var) { knownVars.add(var); } |
367 | |
368 | // short name to full name |
369 | simplyCached SS globalClassNames() { |
370 | var packages = mapToTreeSet(importedPackages(), pkg -> pkg + "."); |
371 | |
372 | // add inner classes of function containers |
373 | var classContainers = classContainerPrefixes(); |
374 | |
375 | new SS out; |
376 | for (className : g22utils.classNameResolver().allFullyQualifiedClassNames()) { |
377 | if (!contains(className, '$')) { |
378 | S pkg = longestPrefixInTreeSet(className, packages); |
379 | if (pkg != null) { |
380 | S shortName = dropPrefix(pkg, className); |
381 | if (!shortName.contains(".")) |
382 | out.put(shortName, className); |
383 | } |
384 | } |
385 | |
386 | S container = longestPrefixInTreeSet(className, classContainers); |
387 | if (container != null) |
388 | out.put(dropPrefix(container, className), className); |
389 | } |
390 | |
391 | ret out; |
392 | } |
393 | |
394 | swappable Cl<S> importedPackages() { |
395 | ret itemPlus("java.lang", standardImports_fullyImportedPackages()); |
396 | } |
397 | |
398 | TreeSet<S> classContainerPrefixes() { |
399 | ret mapToTreeSet(functionContainers, fc -> className(fc) + "$"); |
400 | } |
401 | } |
Began life as a copy of #1033976
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1034291 |
Snippet name: | GazelleV_LeftArrowScriptParser backup before type inference |
Eternal ID of this version: | #1034291/1 |
Text MD5: | f561e55965ddeabc63f10b6754735309 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-02-01 03:08:06 |
Source code size: | 11835 bytes / 401 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 124 / 143 |
Referenced in: | [show references] |