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: | 408 / 406 |
| Referenced in: | [show references] |