Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

1701
LINES

< > BotCompany Repo | #1033976 // GazelleV_LeftArrowScriptParser

JavaX fragment (include) [tags: use-pretranspiled]

Uses 679K of libraries. Click here for Pure Java version (35930L/179K).

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  
// TODO: "then" is used twice which is probably not good
16  
// (for if-then and for chaining calls)
17  
18  
sclass GazelleV_LeftArrowScriptParser > SimpleLeftToRightParser {
19  
  // most important "experimental" setting (will set this to true)
20  
  settable bool magicSwitch = true;
21  
  
22  
  // New object scopes (class members without "this")
23  
  // - MAY potentially break old scripts (although probably not)
24  
  settable bool objectScopesEnabled = true;
25  
26  
  replace Toolbox with GazelleV_LeftArrowScript.
27  
  delegate Script, Evaluable, FunctionDef, FixedVarBase,
28  
    LambdaMethodOnArgument, CallOnTarget to Toolbox.
29  
  replace const with _const.
30  
  
31  
  ClassNameResolver classNameResolver;
32  
  new L functionContainers;
33  
  
34  
  settable ILASClassLoader lasClassLoader;
35  
  settable S classDefPrefix;
36  
  
37  
  // will be noted in source references
38  
  settable O sourceInfo;
39  
  
40  
  settable bool optimize = true;
41  
  settable bool useFixedVarContexts = false; // doesn't completely work yet
42  
  
43  
  Scope scope;
44  
  new LinkedHashMap<S, LASValueDescriptor> knownVars;
45  
  
46  
  //new L<FixedVarBase> varAccessesToFix;
47  
  
48  
  Set<S> closerTokens = litset(";", "}", ")");
49  
  BuildingScript currentReturnableScript;
50  
  BuildingScript currentLoop;
51  
  bool inParens;
52  
  int idCounter;
53  
  
54  
  new Map<S, LASClassDef> classDefs;
55  
  
56  
  gettable Map<S, FunctionDef> functionDefs = new Map;
57  
  
58  
  Set<S> flags = ciSet();
59  
  
60  
  settable bool internStringLiterals = true;
61  
  
62  
  // events for auto-completer
63  
  // 1. record which vars are known at current parse location
64  
  event knownVarsSnapshot(Map<S, LASValueDescriptor> knownVars);
65  
  
66  
  // 2. indicates we just parsed an expression of type type
67  
  //    (useful for listing methods+fields for auto-completion)
68  
  event typeHook(LASValueDescriptor type);
69  
  
70  
  replace print with if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print.
71  
  replace printVars with if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars.
72  
  
73  
  srecord EvaluableWrapper(Evaluable expr) {}
74  
75  
  class BuildingScript {
76  
    int id = ++idCounter;
77  
    settable bool returnable;
78  
    settable bool isLoopBody;
79  
    BuildingScript returnableParent, loopParent;
80  
    //settable LASScope scope;
81  
    
82  
    new Script script;
83  
    new L<Evaluable> steps;
84  
    Map<S, FunctionDef> functionDefs = new Map;
85  
    
86  
    *(bool *returnable) { this(); }
87  
    *(bool *returnable, bool *isLoopBody) { this(); }
88  
    *() { /*scope = currentScope();*/ }
89  
    
90  
    void add(Evaluable step) { if (step != null) steps.add(step); }
91  
92  
    Evaluable get() {
93  
      //script.scope = scope;
94  
      
95  
      // if the last command is a return from THIS script,
96  
      // convert into a simple expression
97  
      
98  
      var lastStep = last(steps);
99  
      if (lastStep cast Toolbox.ReturnFromScript)
100  
        if (lastStep.script == script)
101  
          replaceLast(steps, lastStep.value);
102  
        
103  
      // if the script is only step, is not returnable
104  
      // and defines no functions, replace it with its only step
105  
      
106  
      if (!returnable && l(steps) == 1 && empty(functionDefs))
107  
        ret first(steps);
108  
        
109  
      // make and return actual script
110  
      
111  
      if (nempty(functionDefs)) script.functionDefs = functionDefs;
112  
      script.steps = toTypedArray(Evaluable.class, steps);
113  
      ret script;
114  
    }
115  
    
116  
    S toStringLong() { ret pnlToLines(steps); }
117  
    toString {
118  
      ret formatRecordVars BuildingScript(+id, +returnable , +returnableParent, +script);
119  
    }
120  
  } // end of BuildingScript
121  
  
122  
  // methods begin here
123  
  
124  
  {
125  
    tokenize = text -> {
126  
      LS tok = tokenize_base(text);
127  
      tok = preprocess(tok);
128  
      print("=== PREPROCESSED\n" + indentx(join(tok)) + "\n===");
129  
      ret tok;
130  
    };
131  
  }
132  
  
133  
  LS preprocess(LS tok) {
134  
    tok = tokenIndexedList3(tok);
135  
    tok_ifdef(tok, l1 getFlag);
136  
    tok_pcall_script(tok);
137  
    tok_script_settable(tok);
138  
    jreplace(tok, "LS", "L<S>");
139  
    jreplace(tok, ", +<id>", ", $3 $3");
140  
    ret cloneList(tok);
141  
  }
142  
  
143  
  Script parse(S text) {
144  
    setText(text);
145  
    init();
146  
    Script script = parse();
147  
    if (!atEnd())
148  
      fail("End of script expected");
149  
    ret script;
150  
  }
151  
  
152  
  Script parse() {
153  
    Script script = parseReturnableScript();
154  
    /*for (varAccess : varAccessesToFix)
155  
      varAccess.resolve();*/
156  
    if (optimize) script = script.optimizeScript();
157  
    ret script;
158  
  }
159  
  
160  
  Script parseReturnableScript() {
161  
    ret (Script) parseScript(new BuildingScript().returnable(true));
162  
  }
163  
  
164  
  // if returnable is true, it's always a Script
165  
  Evaluable parseScript(BuildingScript script) {
166  
    ret linkToSrc(-> {
167  
      script.returnableParent = currentReturnableScript;
168  
      script.loopParent = currentLoop;
169  
      if (script.returnable)
170  
        currentReturnableScript = script;
171  
      if (script.isLoopBody)
172  
        currentLoop = script;
173  
      ret parseBuildingScript(script);
174  
    });
175  
  }
176  
    
177  
  Evaluable parseBuildingScript(BuildingScript script) {
178  
    try {
179  
      parseScript_2(script);
180  
      var builtScript = script!;
181  
      currentReturnableScript = script.returnableParent;
182  
      currentLoop = script.loopParent;
183  
      ret builtScript;
184  
    } catch e {
185  
      print("Parsed so far:\n" + script);
186  
      
187  
      // TODO: could use an exception class [like ScriptError]
188  
      throw rethrowAndAppendToMessage(e,
189  
        squareBracketed(spaceCombine(sourceInfo, lineAndColumn(-1)));
190  
    }
191  
  }
192  
  
193  
  void parseScript_2(BuildingScript script) {
194  
    temp tempRestoreMap(knownVars);
195  
    
196  
    new AssureAdvance assure;
197  
    while (assure!) {
198  
      // for auto-completer
199  
      knownVarsSnapshot(knownVars);
200  
      
201  
      print("parseScript_2: Next token is " + quote(token()));
202  
      
203  
      if (is(";")) continue with next();
204  
      if (isOneOf("}", ")")) break;
205  
      
206  
      Evaluable instruction = linkToSrc(-> parseInstruction(script));
207  
      if (instruction != null)
208  
        script.add(instruction);
209  
    }
210  
    
211  
    print("parseScript_2 done");
212  
      
213  
    knownVarsSnapshot(knownVars);
214  
  }
215  
  
216  
  Evaluable parseParamDecl(BuildingScript script) {
217  
    S var = assertIdentifier(tpp());
218  
    new LASValueDescriptor valueDescriptor;
219  
    if (is(":")) {
220  
      var type = parseColonType();
221  
      valueDescriptor = LASValueDescriptor.nonExactCanBeNull(type);
222  
    }
223  
    knownVars.put(var, valueDescriptor);
224  
    script.script.params = putOrCreateLinkedHashMap(script.script.params, var, valueDescriptor);
225  
    null;
226  
  }
227  
228  
  Evaluable parseInstruction(BuildingScript script) {
229  
    /*if (is("var") && isIdentifier(token(1))) {
230  
      consume();
231  
      ret parseAssignment();
232  
    }TODO*/
233  
      
234  
    // Script parameter (old)
235  
    if (consumeOpt("param"))
236  
      ret parseParamDecl(script);
237  
238  
    if (is("throw")) {
239  
      consume();
240  
      ret new Toolbox.Throw(parseExpr());
241  
    }
242  
    
243  
    /* Hmm. Too hard. Need to use jreplace.  
244  
    if (is("pcall")) {
245  
      consume();
246  
      Evaluable body = parseCurlyBlock();
247  
      
248  
      S var = "_pcallError";
249  
      temp tempAddKnownVars(var);
250  
      Evaluable catchBlock = parseCurlyBlock("{ pcallFail " + var + " });
251  
      ret new Toolbox.TryCatch(body, var, catchBlock);
252  
    }*/
253  
      
254  
    if (isOneOf("return", "ret")) {
255  
      consume();
256  
      Evaluable expr;
257  
      if (atCmdEnd())
258  
        expr = const(null);
259  
      else
260  
        expr = parseAssignmentOrExpr();
261  
      ret new Toolbox.ReturnFromScript(currentReturnableScript.script, expr);
262  
    }
263  
    
264  
    if (consumeOpt("continue")) {
265  
      assertCmdEnd();
266  
      if (currentLoop == null)
267  
        fail("continue outside of loop");
268  
      ret new Toolbox.Continue(currentLoop.script);
269  
    }
270  
    
271  
    /* TODO
272  
    if (consumeOpt("break")) {
273  
      assertCmdEnd();
274  
      if (currentLoop == null)
275  
        fail("break outside of loop");
276  
      ret new Toolbox.Break(currentLoop.script);
277  
    }*/
278  
    
279  
    if (is("temp")) {
280  
      consume();
281  
      Evaluable tempExpr = parseAssignmentOrExpr();
282  
      print(+tempExpr);
283  
      Evaluable body = parseScript(new BuildingScript);
284  
      print(+body);
285  
      ret new Toolbox.TempBlock(tempExpr, body);
286  
    }
287  
    
288  
    if (is("will") && is(1, "return")) {
289  
      consume(2);
290  
      Evaluable exp = parseAssignmentOrExpr();
291  
      Evaluable body = parseScript(new BuildingScript);
292  
      ret new Toolbox.WillReturn(exp, body);
293  
    }
294  
    
295  
    if (is("var") && isIdentifier(token(1))) {
296  
      consume();
297  
      S varName = consumeIdentifier();
298  
      var type = parseColonTypeOpt();
299  
300  
      if (consumeLeftArrowOpt()) {
301  
        print("Found assignment");
302  
        Evaluable rhs = parseExpr();
303  
        assertNotNull("Expression expected", rhs);
304  
        
305  
        knownVars.put(varName, type == null
306  
          ? or(rhs.returnType(), new LASValueDescriptor)
307  
          : LASValueDescriptor.nonExactCanBeNull(type));
308  
        ret new Toolbox.VarDeclaration(varName, typeToClass(type), rhs);
309  
      }
310  
    }
311  
    
312  
    ret parseAssignmentOrExpr();
313  
  }
314  
  
315  
  Evaluable parseAssignmentOrExpr() {
316  
    try object parseAssignmentOpt();
317  
    ret parseExpr();
318  
  }
319  
  
320  
  // Parse assignment starting with a simple identifier (e.g. "i <- 5")
321  
  Evaluable parseAssignmentOpt() {
322  
    S t = token();
323  
    if (isIdentifier(t)) {
324  
      Type type = null;
325  
      var ptr = ptr(); // save parsing position
326  
      
327  
      // optional type
328  
      if (is(1, ":")) {
329  
        next();
330  
        type = parseColonType();
331  
        unconsume();
332  
      }
333  
        
334  
      if (is(1, "<") && is(2, "-")) {
335  
        print("Found assignment");
336  
        next(3);
337  
        Evaluable rhs = parseExpr();
338  
        assertNotNull("Expression expected", rhs);
339  
        
340  
        bool newVar = !knownVars.containsKey(t);
341  
        printVars(+newVar, +t, +knownVars);
342  
        
343  
        if (newVar && scope != null)
344  
          try object scope.completeAssignmentOpt(t, rhs);
345  
        
346  
        knownVars.put(t, type == null
347  
          ? or(rhs.returnType(), new LASValueDescriptor)
348  
          : LASValueDescriptor.nonExactCanBeNull(type));
349  
        ret newVar
350  
          ? new Toolbox.VarDeclaration(t, null, rhs)
351  
          : new Toolbox.Assignment(t, rhs);
352  
      }
353  
      
354  
      // revert parser, it was just a typed expression
355  
      // (TODO: this technically violates the O(n) parsing principle)
356  
      ptr(ptr);
357  
    }
358  
    null;
359  
  }
360  
361  
  Evaluable parseOptionalInnerExpression() {
362  
    printVars parseOptionalInnerExpression(+token());
363  
    if (atCmdEnd() || isOneOf("{", ",", "then")) null;
364  
    ret parseInnerExpr();
365  
  }
366  
  
367  
  Evaluable const(O o) { ret new Toolbox.Const(o); }
368  
  
369  
  Evaluable parseInnerExpr() { ret parseExpr(true); }
370  
  
371  
  scaffolded Evaluable parseExpr(bool inner default false) {
372  
    Evaluable e = linkToSrc(-> inner ? parseExpr_impl(true) : parseFullExpr());
373  
    print("parseExpr done:\n" + Toolbox.indentedScriptStruct(e));
374  
    ret e;
375  
  }
376  
  
377  
  Evaluable parseFullExpr() {
378  
    ret linkToSrc(-> parseExprPlusOptionalComma());
379  
    //ret parseExprPlusOptionalCommaOrThen();
380  
  }
381  
  
382  
  /*Evaluable parseExprPlusOptionalCommaOrThen() {
383  
    Evaluable expr = parseExpr_impl(false);
384  
    
385  
    //printVars("parseExprPlusOptionalCommaOrThen", t := t());
386  
    while true {
387  
      if (is("then"))
388  
        expr = parseThenCall(expr);
389  
      else if (consumeOpt(","))
390  
        expr = parseCall_noCmdEndCheck(expr);
391  
      else
392  
        break;
393  
      //printVars("parseExprPlusOptionalCommaOrThen", t2 := t());
394  
    }
395  
    ret expr;
396  
  }*/
397  
  
398  
  // TODO: How to store the object between the two calls?
399  
  // We need to put it in the VarContext somehow.
400  
  // Or allow passing in a target to CallOnTarget
401  
  CallOnTarget parseThenCall(CallOnTarget expr) {
402  
    consume("then");
403  
    CallOnTarget secondCall = cast parseCall(null);
404  
    ret new Toolbox.Then(expr, secondCall);
405  
  }
406  
  
407  
  Evaluable parseExprPlusOptionalComma() {
408  
    Evaluable expr = parseExpr_impl(false);
409  
    
410  
    //printVars("parseExprPlusOptionalComma", t := t());
411  
    while (consumeOpt(",")) {
412  
      expr = parseCall_noCmdEndCheck(expr);
413  
      //printVars("parseExprPlusOptionalComma", t2 := t());
414  
    }
415  
    ret expr;
416  
  }
417  
  
418  
  Evaluable parseExpr_impl(bool inner) {
419  
    if (atEnd()) null;
420  
    
421  
    S t = token();
422  
    printVars parseExpr(token := t);
423  
    
424  
    if (is(";")) null; // empty command
425  
    
426  
    if (consumeOpt("try")) {
427  
      Evaluable body = parseCurlyBlock();
428  
      
429  
      while (consumeOpt("catch")) {
430  
        S var = consumeIdentifierOpt();
431  
        temp tempAddKnownVars(var);
432  
        Evaluable catchBlock = parseCurlyBlock();
433  
        body = new Toolbox.TryCatch(body, var, catchBlock);
434  
      }
435  
      
436  
      if (consumeOpt("finally")) {
437  
        Evaluable finallyBlock = parseCurlyBlock();
438  
        ret new Toolbox.TryFinally(body, finallyBlock);
439  
      }
440  
      
441  
      if (is("on", "fail")) {
442  
        consume(2);
443  
        Evaluable onFailBlock = parseCurlyBlock();
444  
        ret new Toolbox.TryOnFail(body, onFailBlock);
445  
      }
446  
      
447  
      ret body;
448  
    }
449  
    
450  
    if (is("def")) {
451  
      var fd = parseFunctionDefinition(currentReturnableScript.functionDefs);
452  
      ret const(fd);
453  
    }
454  
  
455  
    if (is("{"))
456  
      ret parseCurlyBlock();
457  
      
458  
    // int or double literal
459  
    if (is("-") && empty(nextSpace()) && startsWithDigit(token(1))
460  
      || startsWithDigit(t)) {
461  
      var e = parseNumberLiteral();
462  
      ret parseCall(inner, e);
463  
    }
464  
465  
    if (isQuoted(t)) {
466  
      var e = parseStringLiteral();
467  
      ret parseCall(inner, e);
468  
    }
469  
    
470  
    if (startsWith(t, '\'')) {
471  
      consume();
472  
      var e = const(first(unquote(t)));
473  
      ret parseCall(inner, e);
474  
    }
475  
      
476  
    if (isIdentifier(t)) {
477  
      if (consumeOpt("synchronized")) {
478  
        var target = parseExpr();
479  
        var body = parseCurlyBlock();
480  
        ret new Toolbox.Synchronized(target, body);
481  
      }
482  
  
483  
      if (is("while"))
484  
        ret parseWhileLoop();
485  
  
486  
      if (is("do"))
487  
        ret parseDoWhileLoop();
488  
  
489  
      if (is("for"))
490  
        ret parseForEach();
491  
  
492  
      if (is("if"))
493  
        ret parseIfStatement();
494  
        
495  
      if (is("repeat"))
496  
        ret parseRepeatStatement();
497  
        
498  
      if (is("outer")) {
499  
        consume();
500  
        var a = parseAssignmentOpt();
501  
        if (!a instanceof Toolbox.Assignment)
502  
          fail("Assignment expected");
503  
        cast a to Toolbox.Assignment;
504  
        ret new Toolbox.AssignmentToOuterVar(a.var, a.expression);
505  
      }
506  
        
507  
      consume();
508  
      print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner);
509  
      var e = parseExprStartingWithIdentifier(t, inner);
510  
      ret inner ? parseCall(inner, e) : e;
511  
    }
512  
    
513  
    // nested expression
514  
    
515  
    if (eq(t, "(")) {
516  
      consume();
517  
      //print("Consumed opening parens (level " + parenLevels + ")");
518  
      Evaluable e = parseExpr_inParens();
519  
      //print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")");
520  
      consume(")");
521  
      ret parseCall(inner, e);
522  
    }
523  
    
524  
    if (isOneOf("&", "|") && empty(nextSpace()) && is(1, token())) {
525  
      ret parseBinaryOperator();
526  
    }
527  
      
528  
    fail("Identifier, literal, operator or opening parentheses expected (got: " + quote(t));
529  
  }
530  
  
531  
  Evaluable parseExpr_inParens() {
532  
    bool inParensOld = inParens;
533  
    inParens = true;
534  
    try {
535  
      ret parseExpr();
536  
    } finally {
537  
      inParens = inParensOld;
538  
    }
539  
  }
540  
  
541  
  Evaluable parseNumberLiteral() {
542  
    S t = consumeMultiTokenLiteral();
543  
      
544  
    if (swic(t, "0x"))
545  
      if (ewic(t, "L")) {
546  
        t = dropLast(t);
547  
        ret const(parseHexLong(dropFirst(t, 2)));
548  
      } else
549  
        ret const(parseHexInt(dropFirst(t, 2)));
550  
        
551  
    if (swic(t, "0b")) ret const(intFromBinary(dropFirst(t, 2)));
552  
    
553  
    if (isInteger(t)) ret const(parseIntOrLong(t));
554  
    if (ewic(t, "f")) ret const(parseFloat(t));
555  
    
556  
    if (endsWith(t, "L")) ret const(parseLong(t));
557  
    
558  
    ret const(parseDouble(t));
559  
  }
560  
  
561  
  Evaluable parseBinaryOperator() {
562  
    bool and = is("&");
563  
    next(2);
564  
    Evaluable a = parseInnerExpr();
565  
    Evaluable b = parseInnerExpr();
566  
    ret and
567  
      ? new Toolbox.BoolAnd(a, b)
568  
      : new Toolbox.BoolOr(a, b);
569  
  }
570  
  
571  
  bool qualifiedNameContinues() {
572  
    ret empty(prevSpace()) && eq(token(), ".") && empty(nextSpace())
573  
      && isIdentifier(token(1));
574  
  }
575  
576  
  // assumes we have consumed the "new"  
577  
  Evaluable parseNewExpr() {
578  
    S className = assertIdentifier(tpp());
579  
    
580  
    parseTypeArguments(null);
581  
    
582  
    // script-defined class?
583  
    
584  
    LASClassDef cd = classDefs.get(className);
585  
    if (cd != null)
586  
      ret new Toolbox.NewObject_LASClass(cd.resolvable(lasClassLoader));
587  
      
588  
    // class in variable?
589  
    
590  
    var type = knownVars.get(className);
591  
    if (type != null)
592  
      ret new Toolbox.NewObject_UnknownClass(new Toolbox.GetVar(className), parseArguments());
593  
594  
    // external class
595  
      
596  
    O o = findExternalObject(className);
597  
    if (o cast Class) {
598  
      Class c = o;
599  
      
600  
      if (c == L.class) c = ArrayList.class;
601  
      else if (c == Map.class) c = HashMap.class;
602  
      else if (c == Set.class) c = HashSet.class;
603  
      
604  
      ret new Toolbox.NewObject(c, parseArguments());
605  
    }
606  
    
607  
    throw new ClassNotFound(className);
608  
  }
609  
  
610  
  // t is last consumed token (the identifier the expression starts with)
611  
  // TODO: do parseCall in only one place!
612  
  Evaluable parseExprStartingWithIdentifier(S t, bool inner) {
613  
    if (eq(t, "true")) ret const(true);
614  
    if (eq(t, "false")) ret const(false);
615  
    if (eq(t, "null")) ret const(null);
616  
    
617  
    if (eq(t, "new"))
618  
      ret parseNewExpr();
619  
      
620  
    if (eq(t, "class")) {
621  
      unconsume();
622  
      ret new Toolbox.ClassDef(parseClassDef().resolvable(lasClassLoader));
623  
    }
624  
    
625  
    if (eq(t, "list") && is("{")) {
626  
      Script block = parseReturnableCurlyBlock();
627  
      Evaluable e = new Toolbox.ListFromScript(block);
628  
      ret parseCall(inner, e);
629  
    }
630  
    
631  
    // Search in locally known variables
632  
633  
    var type = knownVars.get(t);
634  
    if (type != null) {
635  
      var e = new Toolbox.GetVar(t).returnType(type);
636  
      print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression"));
637  
      ret parseCall(inner, e);
638  
    }
639  
    
640  
    // Search in scope
641  
    if (scope != null) {
642  
      try object scope.parseExprStartingWithIdentifierOpt(t, inner);
643  
    }
644  
    
645  
    // script-defined class?
646  
    
647  
    LASClassDef cd = classDefs.get(t);
648  
    if (cd != null)
649  
      ret new Toolbox.ClassDef(cd.resolvable(lasClassLoader));
650  
651  
    if (!inner) {
652  
      var fdef = lookupFunction(t);
653  
      if (fdef != null) {
654  
        if (is("~"))
655  
          ret parseTildeCall(new Toolbox.CallFunction(fdef, new Evaluable[0]));
656  
          
657  
        ret new Toolbox.CallFunction(fdef, parseArguments());
658  
      }
659  
    }
660  
    
661  
    if (eq(t, "_context"))
662  
      ret new Toolbox.GetVarContext;
663  
      
664  
    O o = findExternalObject(t);
665  
    
666  
    var start = ptr().minus(2);
667  
    
668  
    if (o == null) {
669  
      //unconsume();
670  
      throw new UnknownObject(t);
671  
    } else if (o cast EvaluableWrapper) {
672  
      ret parseCall(inner, o.expr);
673  
    } else if (inner)
674  
      ret linkToSrc(start, const(o));
675  
    else if (o cast Class) {
676  
      ret parseExprStartingWithClass(start, o);
677  
    } else if (o cast MethodOnObject) {
678  
      // it's a function from a functionContainer
679  
      
680  
      if (inner) fail("Can't call methods in arguments"); // Does this ever happen?!
681  
      
682  
      ret new Toolbox.CallMethod(const(o.object), o.method, parseArguments());
683  
    } else
684  
      ret parseCall(const(o));
685  
  }
686  
  
687  
  Evaluable parseExprStartingWithClass(TokPtr start, Class c) {
688  
    printVars("parseExprStartingWithClass", +c);
689  
    
690  
    if (atCmdEnd())
691  
      ret linkToSrc(start, const(c));
692  
      
693  
    // new without new
694  
    if (is("("))
695  
      ret new Toolbox.NewObject(c, parseArguments());
696  
      
697  
    /* old object creation syntax (e.g. Pair new a b)
698  
    if (is("new")) {
699  
      next();
700  
      ret new Toolbox.NewObject(c, parseArguments());
701  
    } else*/
702  
    
703  
    // check if it's a lambda definition first (with ->)
704  
    try object parseLambdaOpt(c);
705  
    
706  
    if (isIdentifier()) {
707  
      S name = tpp();
708  
      // We have now parsed a class (c) plus an as yet unknown identifier (name) right next to it
709  
      
710  
      // look for a static method call
711  
      if (hasStaticMethodNamed(c, name))
712  
        ret new Toolbox.CallMethod(const(c), name, parseArguments());
713  
714  
      // if the class is an interface, it's a lambda method reference or a lambda
715  
      if (isInterface(c))
716  
        ret parseLambdaMethodRef(c, name);
717  
      
718  
      // look for field second
719  
      
720  
      var field = getField(c, name);
721  
      if (field == null)
722  
        field = findFieldInInterfaces(c, name);
723  
        
724  
      if (field != null) {
725  
        if (!isStaticField(field))
726  
          fail(field + " is not a static field");
727  
        
728  
        // check for field assignment
729  
        if (consumeLeftArrowOpt()) {
730  
          Evaluable rhs = parseExpr();
731  
          ret new Toolbox.SetStaticField(field, rhs);
732  
        }
733  
      
734  
        assertCmdEnd();
735  
        
736  
        ret new Toolbox.GetStaticField(field);
737  
      }
738  
      
739  
      fail(name + " not found in " + c + " (looked for method or field)");
740  
    } else
741  
      fail("Method name expected: " + token());
742  
  }
743  
  
744  
  // parse lambda, e.g. IF1 a -> plus a a
745  
  // returns null if it's not a lambda
746  
  // assumes that c was just parsed
747  
  Evaluable parseLambdaOpt(Class c) {
748  
    // must be an interface
749  
    if (!isInterface(c)) null;
750  
    
751  
    // speculatively parse optional type args first
752  
    var ptr = ptr();
753  
    var parameterized = parseTypeArgumentsOpt(c);
754  
    
755  
    // e.g. IF0 { 5 }
756  
    if (is("{"))
757  
      ret finishLambdaDef(c, null);
758  
      
759  
    // e.g. IF1 _ size
760  
    if (consumeOpt("_")) {
761  
      S methodName = consumeIdentifier();
762  
      ret new LambdaMethodOnArgument(c, methodName, parseArguments());
763  
    }
764  
    
765  
    new LinkedHashMap<S, LASValueDescriptor> args;
766  
    
767  
    while (isIdentifier()) {
768  
      S name = consumeIdentifier();
769  
      args.put(name, typeToValueDescriptor(parseColonTypeOpt()));
770  
    }
771  
    
772  
    // Check for lambda arrow
773  
    if (!(is("-") && is(1, ">"))) {
774  
      ptr(ptr); // revert pointer
775  
      null; // not a lambda
776  
    }
777  
    
778  
    skip(2); // skip the arrow
779  
    ret finishLambdaDef(c, args);
780  
  }
781  
  
782  
  Evaluable finishLambdaDef(Class c, LinkedHashMap<S, LASValueDescriptor> args) {
783  
    temp tempAddKnownVars(args);
784  
    knownVarsSnapshot(knownVars);
785  
786  
    Evaluable body;
787  
    
788  
    // if curly brackets: parse lambda body as returnable script
789  
    // TODO: avoid script overhead if there are no return calls inside
790  
    if (is("{"))
791  
      body = parseReturnableCurlyBlock();
792  
    else
793  
      body = parseExpr();
794  
    
795  
    // return lambda
796  
    var lambda = new Toolbox.LambdaDef(c, toStringArray(keys(args)), body);
797  
    print("finishLambdaDef done:\n" + Toolbox.indentedScriptStruct(lambda));
798  
    ret lambda;
799  
  }
800  
  
801  
  // method ref, e.g. "IF1 l" meaning "l1 l"
802  
  // also, constructor ref, e.g. "IF1 Pair" meaning (x -> new Pair(x))
803  
  //
804  
  // c must be a single method interface
805  
  Evaluable parseLambdaMethodRef(Class c, S name) {
806  
    var fdef = lookupFunction(name);
807  
    if (fdef != null) {
808  
      Evaluable[] curriedArguments = parseArguments();
809  
      ret new Toolbox.CurriedScriptFunctionLambda(c, fdef, curriedArguments);
810  
    }
811  
812  
    O function = findExternalObject(name);
813  
    
814  
    if (function == null) 
815  
      throw new UnknownObject(name);
816  
      
817  
    if (function cast MethodOnObject) {
818  
      // it's a function from a functionContainer
819  
      
820  
      O target = function.object;
821  
      S targetMethod = function.method;
822  
        
823  
      Evaluable[] curriedArguments = parseArguments();
824  
      ret new Toolbox.CurriedMethodLambda(c, target, targetMethod, curriedArguments);
825  
    } else if (function cast Class) {
826  
      Class c2 = function;
827  
      
828  
      // No currying here yet
829  
      assertCmdEnd();
830  
      
831  
      var ctors = constructorsWithNumberOfArguments(c2, 1);
832  
      if (empty(ctors))
833  
        fail("No single argument constructor found in " + c2);
834  
        
835  
      ret new Toolbox.CurriedConstructorLambda(c, toArray Constructor(ctors), null);
836  
    } else
837  
      fail(function + " is not an instantiable class or callable method");
838  
839  
  }
840  
  
841  
  FunctionDef lookupFunction(S name) {
842  
    var script = currentReturnableScript;
843  
    while (script != null) {
844  
      var f = script.functionDefs.get(name);
845  
      printVars("lookupFunction", +script, +name, +f);
846  
      if (f != null) ret f;
847  
      script = script.returnableParent;
848  
    }
849  
    
850  
    // Try parser-wide function defs
851  
    ret functionDefs.get(name);
852  
  }
853  
  
854  
  Evaluable[] parseArguments() {
855  
    new L<Evaluable> l;
856  
    try {
857  
      while (true) {
858  
        print("parseArgumentsAsList: token is " + quote(t()));
859  
        
860  
        // +id
861  
        if (is("+") && empty(nextSpace()) && isIdentifier(token(1))) {
862  
          consume();
863  
          l.add(const(token()));
864  
          
865  
          // just leave identifier unconsumed
866  
          continue;
867  
        }
868  
869  
        if (consumeOpt("<")) {
870  
          Evaluable a = parseFullExpr();
871  
          print("parseArgumentsAsList: token after expr is " + quote(t()));
872  
          if (a == null) fail("expression expected");
873  
          l.add(a);
874  
          break;
875  
        }
876  
        
877  
        Evaluable a = parseOptionalInnerExpression();
878  
        if (a == null) break;
879  
        l.add(a);
880  
      }
881  
      
882  
      ret toArrayOrNull(Evaluable.class, l);
883  
    } on fail {
884  
      print("Arguments parsed so far: " + l);
885  
    }
886  
  }
887  
  
888  
  S consumeMultiTokenLiteral() {
889  
    ret consumeUntilSpaceOr(-> atCmdEnd() || is(","));
890  
  }
891  
  
892  
  bool atCmdEnd() {
893  
    ret
894  
      !inParens && atEndOrLineBreak()
895  
      || closerTokens.contains(token());
896  
  }
897  
  
898  
  void assertCmdEnd() {
899  
    if (!atCmdEnd())
900  
      fail("Expected end of command, token is: " + quote(token()));
901  
  }
902  
  
903  
  // After we just parsed an expression, see if there is something
904  
  // to the right of it.
905  
  // Currently that can be
906  
  // -a method call
907  
  // -a field access
908  
  // -a field assignment (e.g.: myPair a <- 1)
909  
  Evaluable parseCall(bool inner default false, Evaluable target) {
910  
    returnTypeHook(target);
911  
    
912  
    // normally, do a check for end of line
913  
    if (atCmdEnd()) ret target;
914  
    
915  
    if (inner) {
916  
      try object parseTildeCall(target);
917  
      ret target;
918  
    }
919  
    
920  
    ret parseCall_noCmdEndCheck(target);
921  
  }
922  
  
923  
  // For auto-completer
924  
  void returnTypeHook(Evaluable e) {
925  
    if (e != null && e.returnType() != null)
926  
      typeHook(e.returnType());
927  
  }
928  
  
929  
  Evaluable parseCall_noCmdEndCheck(Evaluable target) {
930  
    var start = ptr();
931  
    
932  
    returnTypeHook(target);
933  
    
934  
    // We usually expect an identifier
935  
    
936  
    if (isIdentifier()) {
937  
      S name = tpp(); // the identifier
938  
      
939  
      // check for field assignment
940  
      if (consumeLeftArrowOpt()) {
941  
        Evaluable rhs = parseExpr();
942  
        ret new Toolbox.SetField(target, name, rhs);
943  
      }
944  
      
945  
      bool allowNullReference = consumeOpt("?");
946  
947  
      var args = parseArguments();
948  
      var call = finalizeCall(start, target, name, args);
949  
      set(call, +allowNullReference);
950  
      ret call;
951  
    }
952  
    
953  
    // ~ also does stuff
954  
    // (map ~a == map getOpt "a")
955  
    // (list ~0 == list listGet 0)
956  
    
957  
    try object parseTildeCall(target);
958  
    
959  
    // special syntax to get a field, bypassing a method of the same name
960  
    if (is("!") && is(1, "_getField_")) {
961  
      next(2);
962  
      S field = consumeIdentifier();
963  
      ret new Toolbox.GetField(target, field);
964  
    }
965  
    
966  
    // no call found
967  
    ret target;
968  
  }
969  
  
970  
  bool consumeLeftArrowOpt() {
971  
    if (is("<") && eq(token(1), "-"))
972  
      ret true with next(2);
973  
    false;
974  
  }
975  
  
976  
  Evaluable parseTildeCall(Evaluable target) {
977  
    var start = ptr();
978  
    
979  
    if (consumeOpt("~")) {
980  
      if (isIdentifier()) {
981  
        S key = consumeIdentifier();
982  
        please include function getOpt.
983  
        CallOnTarget call = finalizeCall1(start, target, "getOpt", const(key));
984  
        call.allowNullReference(true);
985  
        ret parseCall(finalizeCall2(call));
986  
      } else {
987  
        int idx = consumeInteger();
988  
        please include function listGet.
989  
        ret parseCall(finalizeCall(start, target, "listGet", const(idx)));
990  
      }
991  
    }
992  
    
993  
    null;
994  
  }
995  
  
996  
  Evaluable finalizeCall(TokPtr start, Evaluable target, S name, Evaluable... args) {
997  
    ret finalizeCall2(finalizeCall1(start, target, name, args));
998  
  }
999  
  
1000  
  Evaluable finalizeCall2(CallOnTarget call) {
1001  
    while (is("then"))
1002  
      call = parseThenCall(call);
1003  
    ret call;
1004  
  }
1005  
  
1006  
  CallOnTarget finalizeCall1(TokPtr start, Evaluable target, S name, Evaluable... args) {
1007  
    // Is the method name also the name of a global function?
1008  
    if (magicSwitch) {
1009  
      O ext = findExternalObject(name);
1010  
      if (ext cast MethodOnObject) {
1011  
        if (nempty(args))
1012  
          ret new Toolbox.CallMethodOrGlobalFunction(target, name, ext, args);
1013  
        else
1014  
          ret new Toolbox.CallMethodOrGetFieldOrGlobalFunction(target, name, ext);
1015  
      }
1016  
    }
1017  
    
1018  
    if (nempty(args))
1019  
      ret new Toolbox.CallMethod(target, name, args);
1020  
    else
1021  
      ret linkToSrc(start, new Toolbox.CallMethodOrGetField(target, name));
1022  
  } // end of parseCall_noCmdEndCheck
1023  
  
1024  
  // add link to source code to parsed object if it supports it
1025  
  // and doesn't have a source code link already
1026  
  <A> A linkToSrc(TokPtr start, A a) {
1027  
    if (a cast IHasTokenRangeWithSrc)
1028  
      if (a.tokenRangeWithSrc() == null)
1029  
        a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr().plus(-1)).sourceInfo(sourceInfo()));
1030  
    ret a;
1031  
  }
1032  
  
1033  
  // lambda version of linkToSrc
1034  
  <A> A linkToSrc(IF0<A> a) {
1035  
    var start = ptr();
1036  
    ret linkToSrc(start, a!);
1037  
  }
1038  
  
1039  
  // can return MethodOnObject
1040  
  // Note: Assumes we just parsed the name
1041  
  // TODO: rename this part of the method
1042  
  swappable O findExternalObject(S name) {
1043  
    //try object findClassThroughDefaultClassFinder(name);
1044  
    //try object findClassInStandardImports(name);
1045  
    
1046  
    if (eq(name, "void")) ret void.class;
1047  
    try object parsePrimitiveType(name);
1048  
      
1049  
    if (qualifiedNameContinues()) {
1050  
      int idx = idx()-2;
1051  
      do 
1052  
        next(2);
1053  
      while (qualifiedNameContinues());
1054  
1055  
      S fqn = joinSubList(tok, idx, idx()-1);
1056  
      ret classForName(fqn);
1057  
    }
1058  
    
1059  
    ret findExternalObject2(name);
1060  
  }
1061  
  
1062  
  swappable O findExternalObject2(S name) {
1063  
    S fullName = globalClassNames().get(name);
1064  
    if (fullName != null)
1065  
      ret classForName(fullName);
1066  
      
1067  
    try object classForNameOpt_noCache(name);
1068  
    
1069  
    fOr (container : functionContainers) {
1070  
      if (hasMethodNamed(container, name)
1071  
        && !isBlockedFunctionContainerMethod(container, name)) {
1072  
        var moo = new MethodOnObject(container, name);
1073  
        ifdef debugMethodFinding _print(+moo); endifdef
1074  
        ret moo;
1075  
      }
1076  
      
1077  
      var field = getField(container, name);
1078  
      if (field != null && isStaticField(field))
1079  
        ret new EvaluableWrapper(new Toolbox.GetStaticField(field));
1080  
    }
1081  
    
1082  
    null;
1083  
  }
1084  
  
1085  
  swappable bool isBlockedFunctionContainerMethod(O container, S name) {
1086  
    false;
1087  
  }
1088  
  
1089  
  selfType allowTheWorld() { ret allowTheWorld(mc()); }
1090  
  
1091  
  // first containers within argument list have priority
1092  
  // last calls to allowTheWorld/first arguments passed have priority
1093  
  selfType allowTheWorld(O... functionContainers) {
1094  
    fOr (O o : reversed(functionContainers))
1095  
      if (!contains(this.functionContainers, o)) {
1096  
        this.functionContainers.add(0, o);
1097  
        globalClassNames_cache = null; // recalculate
1098  
      }
1099  
    this;
1100  
  }
1101  
  
1102  
  void printFunctionDefs(Script script) {
1103  
    print(values(script.functionDefs));
1104  
  }
1105  
  
1106  
  AutoCloseable tempAddKnownVars(S... vars) {
1107  
    ret tempAddKnownVars(nonNulls(vars));
1108  
  }
1109  
  
1110  
  AutoCloseable tempAddKnownTypedVars(Iterable<TypedVar> vars) {
1111  
    ret tempAddKnownVars(mapToMap(vars, v -> pair(v.name, v.type)));
1112  
  }
1113  
    
1114  
  AutoCloseable tempAddKnownVars(Iterable<S> vars) {
1115  
    var newVars = mapWithSingleValue(vars, new LASValueDescriptor);
1116  
    ret tempAddKnownVars(newVars);
1117  
  }
1118  
    
1119  
  AutoCloseable tempAddKnownVar(S var, LASValueDescriptor type) {
1120  
    ret tempAddKnownVars(litmap(var, type));
1121  
  }
1122  
  
1123  
  AutoCloseable tempAddKnownVars(Map<S, LASValueDescriptor> newVars) {
1124  
    /*if (scope != null)
1125  
      for (name, type : newVars)
1126  
        scope.addDeclaredVar(name, type);*/
1127  
    ret tempMapPutAll(knownVars, newVars);
1128  
  }
1129  
  
1130  
  S parseFunctionDefArg(Map<S, LASValueDescriptor> argsOut) {
1131  
    if (consumeOpt("(")) {
1132  
      S result = parseFunctionDefArg(argsOut);
1133  
      consume(")");
1134  
      ret result;
1135  
    }
1136  
    
1137  
    if (isIdentifier()) {
1138  
      S arg = tpp();
1139  
      var type = typeToValueDescriptor(parseColonTypeOpt());
1140  
      argsOut.put(arg, type);
1141  
      ret arg;
1142  
    }
1143  
    
1144  
    null;
1145  
  }
1146  
1147  
  
1148  
  Toolbox.FunctionDef parseFunctionDefinition(Map<S, FunctionDef> functionDefsToAddTo) {
1149  
    var start = ptr();
1150  
    consume("def");
1151  
    bool synthetic = consumeOpt("!");
1152  
    if (synthetic) consume("synthetic");
1153  
    S functionName = assertIdentifier(tpp());
1154  
    print("parseFunctionDefinition " + functionName);
1155  
    ret parseFunctionDefinition_step2(functionDefsToAddTo, start, functionName)
1156  
      .synthetic(synthetic);
1157  
  }
1158  
    
1159  
  Toolbox.FunctionDef parseFunctionDefinition_step2(Map<S, FunctionDef> functionDefsToAddTo, TokPtr start, S functionName) {
1160  
    // argument names and types
1161  
    new LinkedHashMap<S, LASValueDescriptor> args;
1162  
    
1163  
    while (parseFunctionDefArg(args) != null) {}
1164  
    
1165  
    var fd = new Toolbox.FunctionDef(functionName, keysList(args), null); // no body yet
1166  
    
1167  
    if (is(".", ".", ".")) {
1168  
      consume(3);
1169  
      fd.varargs(true);
1170  
    }
1171  
    
1172  
    var returnType = parseColonTypeOpt();
1173  
    
1174  
    fd.argTypes(valuesArray(LASValueDescriptor, args));
1175  
    functionDefsToAddTo?.put(functionName, fd);
1176  
    
1177  
    //var scope = newScope();
1178  
    //scope.useFixedVars(useFixedVarContexts);
1179  
    //temp tempScope(scope);
1180  
    temp tempAddKnownVars(args);
1181  
    print("Parsing function body");
1182  
    fd.body = parseReturnableCurlyBlock();
1183  
    
1184  
    print("Defined function " + functionName + ", added to " + functionDefsToAddTo);
1185  
    
1186  
    //fd.scope(scope);
1187  
    if (returnType != null) fd.returnType(returnType);
1188  
    ret linkToSrc(start, fd);
1189  
  }
1190  
  
1191  
  /*LASScope newScope() {
1192  
    ret new LASScope(scope);
1193  
  }*/
1194  
  
1195  
  Scope currentScope() { ret scope; }
1196  
  
1197  
  // enter the scope temporarily
1198  
  AutoCloseable tempScope(Scope scope) {
1199  
    var oldScope = currentScope();
1200  
    this.scope = scope;
1201  
    ret -> { this.scope = oldScope; };
1202  
  }
1203  
  
1204  
  Script parseReturnableCurlyBlock() {
1205  
    ret (Script) parseCurlyBlock(new BuildingScript().returnable(true));
1206  
  }
1207  
  
1208  
  Evaluable parseCurlyBlock(BuildingScript script default new) {
1209  
    //print(+knownVars);
1210  
    consume("{");
1211  
    bool inParensOld = inParens;
1212  
    inParens = false;
1213  
    var body = parseScript(script);
1214  
    consume("}");
1215  
    inParens = inParensOld;
1216  
    ret body;
1217  
  }
1218  
  
1219  
  Evaluable parseWhileLoop() {
1220  
    consume("while");
1221  
    var condition = parseExpr();
1222  
    var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
1223  
    ret new Toolbox.While(condition, body);
1224  
  }
1225  
  
1226  
  Evaluable parseDoWhileLoop() {
1227  
    consume("do");
1228  
    var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
1229  
    consume("while");
1230  
    var condition = parseExpr();
1231  
    ret new Toolbox.DoWhile(condition, body);
1232  
  }
1233  
  
1234  
  Evaluable parseForEach() {
1235  
    ret new ParseForEach()!;
1236  
  }
1237  
  
1238  
  class ParseForEach {
1239  
    Evaluable collection, body;
1240  
    IF0<Evaluable> finish;
1241  
    new L<TypedVar> vars;
1242  
    
1243  
    TypedVar addVar(TypedVar var) { ret addAndReturn(vars, var); }
1244  
    TypedVar consumeVar() {
1245  
      S name = consumeIdentifier();
1246  
      var type = parseColonTypeOpt();
1247  
      ret addVar(new TypedVar(name, typeToValueDescriptor(type)));
1248  
    }
1249  
    
1250  
    void parseBody {
1251  
      temp tempAddKnownTypedVars(vars);
1252  
      body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
1253  
    }
1254  
    
1255  
    Evaluable get() {
1256  
      consume("for");
1257  
      
1258  
      // for i to n { ... }
1259  
      if (is(1, "to")) {
1260  
        TypedVar var = consumeVar();
1261  
        consume("to");
1262  
        Evaluable endValue = parseExpr();
1263  
        parseBody();
1264  
        ret new Toolbox.ForIntTo(endValue, var.name, body);
1265  
      }
1266  
    
1267  
      int iIn = relativeIndexOf("in");
1268  
      if (iIn < 0) fail("for without in");
1269  
      print(+iIn);
1270  
      
1271  
      if (iIn == 1 || is(1, ":")) { // just a variable (with optional type)
1272  
        TypedVar var = consumeVar();
1273  
        finish = -> new Toolbox.ForEach(collection, var.name, body).varType(var.type);
1274  
      } else if (iIn == 2) { // for iterator (mapI)
1275  
        if (consumeOpt("iterator")) {
1276  
          TypedVar var = consumeVar();
1277  
          finish = -> new Toolbox.ForIterator(collection, var.name, body);
1278  
        } else if (consumeOpt("nested")) {
1279  
          TypedVar var = consumeVar();
1280  
          finish = -> new Toolbox.ForNested(collection, var.name, body);
1281  
        } else
1282  
          fail("Unknown pattern for 'for' loop");
1283  
      } else if (iIn == 3) {
1284  
        if (isOneOf("pair", "Pair")) {
1285  
          // "pair a b" or "Pair a b"
1286  
          consume();
1287  
          TypedVar varA = consumeVar();
1288  
          TypedVar varB = consumeVar();
1289  
          printVars(+varA, +varB);
1290  
          
1291  
          finish = -> new Toolbox.ForPairs(collection, body, varA.name, varB.name);
1292  
        } else {
1293  
          // "a, b"
1294  
          TypedVar varA = consumeVar();
1295  
          consume(",");
1296  
          TypedVar varB = consumeVar();
1297  
1298  
          finish = -> new Toolbox.ForKeyValue(collection, body, varA.name, varB.name);
1299  
        }
1300  
      } else if (iIn == 4) {
1301  
        consume("index");
1302  
        S varIndex = consumeVar().name;
1303  
        consume(",");
1304  
        S varElement = consumeVar().name;
1305  
        
1306  
        finish = -> new Toolbox.ForIndex(collection, body, varIndex, varElement);
1307  
      } else
1308  
        fail("Unknown pattern for 'for' loop");
1309  
      
1310  
      consume("in");
1311  
      collection = parseExpr_inParens();
1312  
      print(+collection);
1313  
      parseBody();
1314  
      ret finish!;
1315  
    }
1316  
  }
1317  
  
1318  
  Evaluable parseIfStatement() {
1319  
    consume("if");
1320  
    Evaluable condition, body, elseBranch = null;
1321  
    {
1322  
      temp tempAdd(closerTokens, "then");
1323  
      condition = parseExpr();
1324  
    }
1325  
    
1326  
    if (consumeOpt("then")) {
1327  
      temp tempAdd(closerTokens, "else");
1328  
      body = parseExpr();
1329  
      if (consumeOpt("else"))
1330  
        elseBranch = parseExpr();
1331  
    } else {
1332  
      body = parseCurlyBlock();
1333  
      if (consumeOpt("else")) {
1334  
        if (is("if"))
1335  
          elseBranch = parseIfStatement();
1336  
        else
1337  
          elseBranch = parseCurlyBlock();
1338  
      }
1339  
    }
1340  
    
1341  
    ret new Toolbox.IfThen(condition, body, elseBranch);
1342  
  }
1343  
  
1344  
  Evaluable parseRepeatStatement() {
1345  
    consume("repeat");
1346  
    var n = parseExpr();
1347  
    var body = parseCurlyBlock();
1348  
    ret new Toolbox.RepeatN(n, body);
1349  
  }
1350  
  
1351  
  // declare an external variable with optional type info
1352  
  void addVar(S var, LASValueDescriptor type default new) { knownVars.put(var, type); }
1353  
  void addVar(S var, Class type, bool canBeNull) {
1354  
    addVar(var, typeToValueDescriptor(type, canBeNull));
1355  
  }
1356  
  
1357  
  LASValueDescriptor typeToValueDescriptor(Type type, bool canBeNull default true) {
1358  
    ret type == null ? new LASValueDescriptor
1359  
      : new LASValueDescriptor.NonExact(typeToClass(type), canBeNull);
1360  
  }
1361  
  
1362  
  // short name to full name
1363  
  // TODO: sort by priority if classes appear twice
1364  
  simplyCached SS globalClassNames() {
1365  
    var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
1366  
    
1367  
    new SS out;
1368  
    
1369  
    fOr (name : importedClasses())
1370  
      out.put(shortenClassName(name), name);
1371  
    
1372  
    // add function containers themselves (if they're classes)
1373  
    for (fc : functionContainers)
1374  
      if (fc cast Class) {
1375  
        if (isAnonymousClass(fc)) continue;
1376  
        out.put(shortClassName(fc), className(fc));
1377  
      }
1378  
    
1379  
    // add inner classes of function containers
1380  
    var classContainers = classContainerPrefixes();
1381  
    TreeSet<S> classContainerSet = asTreeSet(classContainers);
1382  
1383  
    for (className : classNameResolver().allFullyQualifiedClassNames()) {
1384  
      if (isAnonymousClassName(className)) continue;
1385  
      if (!contains(className, '$')) {
1386  
        S pkg = longestPrefixInTreeSet(className, packages);
1387  
        if (pkg != null) {
1388  
          S shortName = dropPrefix(pkg, className);
1389  
          if (!shortName.contains(".") && !out.containsKey(shortName))
1390  
            out.put(shortName, className);
1391  
        }
1392  
      }
1393  
        
1394  
      S container = longestPrefixInTreeSet(className, classContainerSet);
1395  
      if (container != null) {
1396  
        S shortName = dropPrefix(container, className);
1397  
        S existing = out.get(shortName);
1398  
        if (existing != null) {
1399  
          int priority = indexOf(classContainers, container);
1400  
          S oldContainer = longestPrefixInTreeSet(existing, classContainerSet);
1401  
          //int oldPriority = indexOf(classContainers, oldContainer);
1402  
          int oldPriority = smartIndexOf(classContainers, oldContainer);
1403  
          printVars(+className, +shortName, +container, +priority, +existing, +oldPriority);
1404  
          if (priority > oldPriority)
1405  
            continue;
1406  
        }
1407  
        out.put(shortName, className);
1408  
      }
1409  
    }
1410  
        
1411  
    fOr (key, val : classShortcuts()) {
1412  
      S fullName = out.get(val);
1413  
      if (fullName != null)
1414  
        out.put(key, fullName);
1415  
    }
1416  
1417  
    ret out;
1418  
  }
1419  
  
1420  
  swappable SS classShortcuts() {
1421  
    ret javaxClassShortcuts();
1422  
  }
1423  
  
1424  
  swappable Cl<S> importedPackages() {
1425  
    ret itemPlus("java.lang", standardImports_fullyImportedPackages());
1426  
  }
1427  
  
1428  
  swappable Cl<S> importedClasses() {
1429  
    ret standardImports_singleClasses();
1430  
  }
1431  
  
1432  
  void addClassAlias(S alias, S longName) {
1433  
    S fullName = globalClassNames().get(longName);
1434  
    if (fullName != null)
1435  
      globalClassNames().put(alias, fullName);
1436  
  }
1437  
  
1438  
  LS classContainerPrefixes() {
1439  
    ret map(functionContainers, fc -> className(fc) + "$");
1440  
  }
1441  
  
1442  
  // typed exceptions (unusual, I know!)
1443  
  
1444  
  srecord noeq UnknownObject(S name) > RuntimeException {
1445  
    public S getMessage() { ret "Unknown object: " + name; }
1446  
  }
1447  
  
1448  
  sclass ClassNotFound > UnknownObject {
1449  
    *(S className) { super(className); }
1450  
    
1451  
    public S getMessage() { ret "Class not found: " + name; }
1452  
  }
1453  
  
1454  
  asclass Scope {
1455  
    // Parse expression due to special rules in this scope
1456  
    // (return null if no parse)
1457  
    Evaluable parseExprStartingWithIdentifierOpt(S t, bool inner) { null; }
1458  
    
1459  
    Evaluable completeAssignmentOpt(S lhs, Evaluable rhs) { null; }
1460  
  }
1461  
  
1462  
  record ClassDefScope(LASClassDef classDef) extends Scope {
1463  
    Evaluable getThis() { ret new Toolbox.GetVar("this"); }
1464  
    
1465  
    Evaluable parseExprStartingWithIdentifierOpt(S t, bool inner) {
1466  
      printVars("ClassDefScope.parseExprStartingWithIdentifierOpt", +t);
1467  
      
1468  
      ret linkToSrc(-> {
1469  
        var field = classDef.fieldsByName.get(t);
1470  
        if (field != null) {
1471  
          print("Found field " + field);
1472  
          var e = inner
1473  
            ? new Toolbox.GetField(getThis(), field.name)
1474  
            : new Toolbox.CallMethodOrGetField(getThis(), field.name);
1475  
          ret parseCall(inner, e);
1476  
        }
1477  
        
1478  
        Class superClass = typeToClass(classDef.superClass);
1479  
        var field2 = findFieldOfClass(superClass, t);
1480  
        printVars(+field2, +superClass, +t);
1481  
        if (field2 != null) {
1482  
          print("Found field " + field2);
1483  
          var e = inner
1484  
            ? new Toolbox.GetField(getThis(), field2.getName())
1485  
            : new Toolbox.CallMethodOrGetField(getThis(), field2.getName());
1486  
          ret parseCall(inner, e);
1487  
        }
1488  
        
1489  
        if (!inner
1490  
          && (classDef.methodsByName.containsKey(t)
1491  
          || classHasMethodNamed(typeToClass(classDef.superClass), t))) {
1492  
          ret new Toolbox.CallMethod(getThis(), t, parseArguments());
1493  
        }
1494  
        
1495  
        null;
1496  
      });
1497  
    }
1498  
    
1499  
    Evaluable completeAssignmentOpt(S lhs, Evaluable rhs) {         var field = classDef.fieldsByName.get(lhs);
1500  
      if (field != null)
1501  
        ret new Toolbox.SetField(getThis(), lhs, rhs);
1502  
        
1503  
      Class superClass = typeToClass(classDef.superClass);
1504  
      var field2 = findFieldOfClass(superClass, lhs);
1505  
      if (field2 != null)
1506  
        ret new Toolbox.SetField(getThis(), lhs, rhs);
1507  
          
1508  
      null;
1509  
    }
1510  
  }
1511  
  
1512  
  LASClassDef parseClassDef() {
1513  
    ret linkToSrc(-> {
1514  
      consume("class");
1515  
      new LASClassDef classDef;
1516  
      classDef.verbose(scaffoldingEnabled());
1517  
      if (nempty(classDefPrefix))
1518  
        classDef.classDefPrefix(classDefPrefix);
1519  
      S name = consumeIdentifier();
1520  
      classDef.userGivenName(name);
1521  
      classDefs.put(name, classDef);
1522  
      temp tempMapPut(classDefs, "selfType", classDef);
1523  
      
1524  
      if (consumeOpt("extends")) {
1525  
        classDef.superClass(parseType());
1526  
      }
1527  
        
1528  
      if (consumeOpt("is"))
1529  
        classDef.interfaces.add(parseType());
1530  
  
1531  
      consume("{");
1532  
      
1533  
      // parse class body
1534  
      
1535  
      temp tempAddKnownVar("this", new LASValueDescriptor.NonExactType(classDef.resolvable(lasClassLoader), false));
1536  
      temp !objectScopesEnabled ? null : tempScope(new ClassDefScope(classDef));
1537  
      
1538  
      while (!is("}")) {
1539  
        if (is(";")) continue with next();
1540  
        
1541  
        // parse method declaration
1542  
        
1543  
        if (is("def")) {
1544  
          Toolbox.FunctionDef fd = parseFunctionDefinition(null);
1545  
          printVars(knownVarsAfterFunctionDef := knownVars);
1546  
          classDef.addMethod(fd);
1547  
          continue;
1548  
        }
1549  
        
1550  
        // parse constructor declaration
1551  
        
1552  
        var start = ptr();
1553  
        if (consumeOpt("ctor")) {
1554  
          var fd = parseFunctionDefinition_step2(null, start, "<init>");
1555  
          fd.returnType(void.class);
1556  
          classDef.addMethod(fd);
1557  
          continue;
1558  
        }
1559  
        
1560  
        // parse initializer block
1561  
        
1562  
        if (consumeOpt("initializer")) {
1563  
          classDef.initializers.add(parseCurlyBlock());
1564  
          continue;
1565  
        }
1566  
          
1567  
        // parse field declaration
1568  
        
1569  
        new LASClassDef.FieldDef fd;
1570  
        while (isOneOf("transient", "static")) { fd.addModifier(tpp()); }
1571  
        
1572  
        fd.name(consumeIdentifier());
1573  
        var type = parseColonType();
1574  
        fd.type(type);
1575  
        
1576  
        // parse field initializer
1577  
        if (consumeLeftArrowOpt())
1578  
          fd.initializer(parseExpr());
1579  
1580  
        classDef.addField(fd);
1581  
      }
1582  
      
1583  
      consume("}");
1584  
      ret classDef;
1585  
    });
1586  
  }
1587  
  
1588  
  Type parseColonTypeOpt() {
1589  
    if (is(":"))
1590  
      ret parseColonType();
1591  
    null;
1592  
  }
1593  
  
1594  
  Type parseColonType() {
1595  
    consume(":");
1596  
    ret parseType();
1597  
  }
1598  
  
1599  
  Type findType(S typeName) {
1600  
    // script-defined class?
1601  
    LASClassDef cd = classDefs.get(typeName);
1602  
    if (cd != null)
1603  
      ret cd.resolvable(lasClassLoader);
1604  
      
1605  
    O o = findExternalObject(typeName);
1606  
    if (o cast Class)
1607  
      ret o;
1608  
      
1609  
    throw new ClassNotFound(typeName);
1610  
  }
1611  
  
1612  
  Type parseType() {
1613  
    S typeName = consumeIdentifier();
1614  
    Type type = findType(typeName);
1615  
    ret parseArrayType(parseTypeArguments(type));
1616  
  }
1617  
  
1618  
  Type parseArrayType(Type type) {
1619  
    while (is("[") && is(1, "]")) {
1620  
      consume(2);
1621  
      type = new GenericArrayTypeImpl(type);
1622  
    }
1623  
    ret type;
1624  
  }
1625  
  
1626  
  Type parseTypeArguments aka parseTypeArgumentsOpt(Type type) {
1627  
    // type arguments
1628  
    if (is("<") && empty(nextSpace()) && isIdentifier(token(1))) {
1629  
      next();
1630  
      new L<Type> args;
1631  
      while true {
1632  
        args.add(parseType());
1633  
        if (is(">")) break;
1634  
        consume(",");
1635  
      }
1636  
      consume(">");
1637  
      
1638  
      if (type != null)
1639  
        type = new ParameterizedTypeImpl(null, type, toTypedArray(Type, args));
1640  
    }
1641  
    ret type;
1642  
  }
1643  
  
1644  
  swappable Class classForName(S name) ctex {
1645  
    ret Class.forName(name);
1646  
  }
1647  
  
1648  
  // also copies the globalClassNames_cache which otherwise takes
1649  
  // a few ms to calculate
1650  
  void copyFunctionContainersFrom(GazelleV_LeftArrowScriptParser parser) {
1651  
    functionContainers = cloneList(parser.functionContainers);
1652  
    globalClassNames_cache = parser.globalClassNames();
1653  
    isBlockedFunctionContainerMethod = parser.isBlockedFunctionContainerMethod;
1654  
  }
1655  
  
1656  
  ClassNameResolver classNameResolver() {
1657  
    if (classNameResolver == null)
1658  
      classNameResolver = new ClassNameResolver().byteCodePath(assertNotNull(getBytecodePathForClass(this))).init();
1659  
    ret classNameResolver;
1660  
  }
1661  
  
1662  
  selfType classNameResolver(ClassNameResolver classNameResolver) {
1663  
    this.classNameResolver = classNameResolver;
1664  
    this;
1665  
  }
1666  
1667  
  Evaluable parseStringLiteral() {  
1668  
    S s = unquote_relaxedMLS(consume());
1669  
    if (internStringLiterals)
1670  
      s = intern(s);
1671  
    ret const(s);
1672  
  }
1673  
  
1674  
  void addFunctionDefs(Map<S, FunctionDef> map) {
1675  
    putAll(functionDefs, map);
1676  
  }
1677  
  
1678  
  selfType setFlag(S flag) {
1679  
    flags.add(flag);
1680  
    this;
1681  
  }
1682  
  
1683  
  bool getFlag(S flag) {
1684  
    ret flags.contains(flag);
1685  
  }
1686  
1687  
  // forget normally imported class names (e.g. for testing)
1688  
  void noImports() {  
1689  
    importedPackages = -> null;
1690  
    importedClasses = -> null;
1691  
    classShortcuts = -> null;
1692  
  }
1693  
  
1694  
  srecord TypedVar(S name, LASValueDescriptor type) {}
1695  
  
1696  
  // parser initializer
1697  
  {
1698  
    internIdentifiers(true);
1699  
  }
1700  
  
1701  
} // end of GazelleV_LeftArrowScriptParser

download  show line numbers  debug dex  old transpilations   

Travelled to 6 computer(s): bhatertpkbcr, ekrmjmnbrukm, elmgxqgtpvxh, mowyntqkapby, mqqgnosmbjvj, wnsclhtenguj

No comments. add comment

Snippet ID: #1033976
Snippet name: GazelleV_LeftArrowScriptParser
Eternal ID of this version: #1033976/621
Text MD5: db7232a9a407ec2910095c9ef84eaddb
Transpilation MD5: c8c6776a431a292bb9073131a23a2bad
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2025-05-10 22:29:55
Source code size: 50511 bytes / 1701 lines
Pitched / IR pitched: No / No
Views / Downloads: 1598 / 5531
Version history: 620 change(s)
Referenced in: [show references]