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

1695
LINES

< > BotCompany Repo | #1033976 // GazelleV_LeftArrowScriptParser

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

Transpiled version (35642L) is out of date.

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  
      ret body;
442  
    }
443  
    
444  
    if (is("def")) {
445  
      var fd = parseFunctionDefinition(currentReturnableScript.functionDefs);
446  
      ret const(fd);
447  
    }
448  
  
449  
    if (is("{"))
450  
      ret parseCurlyBlock();
451  
      
452  
    // int or double literal
453  
    if (is("-") && empty(nextSpace()) && startsWithDigit(token(1))
454  
      || startsWithDigit(t)) {
455  
      var e = parseNumberLiteral();
456  
      ret parseCall(inner, e);
457  
    }
458  
459  
    if (isQuoted(t)) {
460  
      var e = parseStringLiteral();
461  
      ret parseCall(inner, e);
462  
    }
463  
    
464  
    if (startsWith(t, '\'')) {
465  
      consume();
466  
      var e = const(first(unquote(t)));
467  
      ret parseCall(inner, e);
468  
    }
469  
      
470  
    if (isIdentifier(t)) {
471  
      if (consumeOpt("synchronized")) {
472  
        var target = parseExpr();
473  
        var body = parseCurlyBlock();
474  
        ret new Toolbox.Synchronized(target, body);
475  
      }
476  
  
477  
      if (is("while"))
478  
        ret parseWhileLoop();
479  
  
480  
      if (is("do"))
481  
        ret parseDoWhileLoop();
482  
  
483  
      if (is("for"))
484  
        ret parseForEach();
485  
  
486  
      if (is("if"))
487  
        ret parseIfStatement();
488  
        
489  
      if (is("repeat"))
490  
        ret parseRepeatStatement();
491  
        
492  
      if (is("outer")) {
493  
        consume();
494  
        var a = parseAssignmentOpt();
495  
        if (!a instanceof Toolbox.Assignment)
496  
          fail("Assignment expected");
497  
        cast a to Toolbox.Assignment;
498  
        ret new Toolbox.AssignmentToOuterVar(a.var, a.expression);
499  
      }
500  
        
501  
      consume();
502  
      print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner);
503  
      var e = parseExprStartingWithIdentifier(t, inner);
504  
      ret inner ? parseCall(inner, e) : e;
505  
    }
506  
    
507  
    // nested expression
508  
    
509  
    if (eq(t, "(")) {
510  
      consume();
511  
      //print("Consumed opening parens (level " + parenLevels + ")");
512  
      Evaluable e = parseExpr_inParens();
513  
      //print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")");
514  
      consume(")");
515  
      ret parseCall(inner, e);
516  
    }
517  
    
518  
    if (isOneOf("&", "|") && empty(nextSpace()) && is(1, token())) {
519  
      ret parseBinaryOperator();
520  
    }
521  
      
522  
    fail("Identifier, literal, operator or opening parentheses expected (got: " + quote(t));
523  
  }
524  
  
525  
  Evaluable parseExpr_inParens() {
526  
    bool inParensOld = inParens;
527  
    inParens = true;
528  
    try {
529  
      ret parseExpr();
530  
    } finally {
531  
      inParens = inParensOld;
532  
    }
533  
  }
534  
  
535  
  Evaluable parseNumberLiteral() {
536  
    S t = consumeMultiTokenLiteral();
537  
      
538  
    if (swic(t, "0x"))
539  
      if (ewic(t, "L")) {
540  
        t = dropLast(t);
541  
        ret const(parseHexLong(dropFirst(t, 2)));
542  
      } else
543  
        ret const(parseHexInt(dropFirst(t, 2)));
544  
        
545  
    if (swic(t, "0b")) ret const(intFromBinary(dropFirst(t, 2)));
546  
    
547  
    if (isInteger(t)) ret const(parseIntOrLong(t));
548  
    if (ewic(t, "f")) ret const(parseFloat(t));
549  
    
550  
    if (endsWith(t, "L")) ret const(parseLong(t));
551  
    
552  
    ret const(parseDouble(t));
553  
  }
554  
  
555  
  Evaluable parseBinaryOperator() {
556  
    bool and = is("&");
557  
    next(2);
558  
    Evaluable a = parseInnerExpr();
559  
    Evaluable b = parseInnerExpr();
560  
    ret and
561  
      ? new Toolbox.BoolAnd(a, b)
562  
      : new Toolbox.BoolOr(a, b);
563  
  }
564  
  
565  
  bool qualifiedNameContinues() {
566  
    ret empty(prevSpace()) && eq(token(), ".") && empty(nextSpace())
567  
      && isIdentifier(token(1));
568  
  }
569  
570  
  // assumes we have consumed the "new"  
571  
  Evaluable parseNewExpr() {
572  
    S className = assertIdentifier(tpp());
573  
    
574  
    parseTypeArguments(null);
575  
    
576  
    // script-defined class?
577  
    
578  
    LASClassDef cd = classDefs.get(className);
579  
    if (cd != null)
580  
      ret new Toolbox.NewObject_LASClass(cd.resolvable(lasClassLoader));
581  
      
582  
    // class in variable?
583  
    
584  
    var type = knownVars.get(className);
585  
    if (type != null)
586  
      ret new Toolbox.NewObject_UnknownClass(new Toolbox.GetVar(className), parseArguments());
587  
588  
    // external class
589  
      
590  
    O o = findExternalObject(className);
591  
    if (o cast Class) {
592  
      Class c = o;
593  
      
594  
      if (c == L.class) c = ArrayList.class;
595  
      else if (c == Map.class) c = HashMap.class;
596  
      else if (c == Set.class) c = HashSet.class;
597  
      
598  
      ret new Toolbox.NewObject(c, parseArguments());
599  
    }
600  
    
601  
    throw new ClassNotFound(className);
602  
  }
603  
  
604  
  // t is last consumed token (the identifier the expression starts with)
605  
  // TODO: do parseCall in only one place!
606  
  Evaluable parseExprStartingWithIdentifier(S t, bool inner) {
607  
    if (eq(t, "true")) ret const(true);
608  
    if (eq(t, "false")) ret const(false);
609  
    if (eq(t, "null")) ret const(null);
610  
    
611  
    if (eq(t, "new"))
612  
      ret parseNewExpr();
613  
      
614  
    if (eq(t, "class")) {
615  
      unconsume();
616  
      ret new Toolbox.ClassDef(parseClassDef().resolvable(lasClassLoader));
617  
    }
618  
    
619  
    if (eq(t, "list") && is("{")) {
620  
      Script block = parseReturnableCurlyBlock();
621  
      Evaluable e = new Toolbox.ListFromScript(block);
622  
      ret parseCall(inner, e);
623  
    }
624  
    
625  
    // Search in locally known variables
626  
627  
    var type = knownVars.get(t);
628  
    if (type != null) {
629  
      var e = new Toolbox.GetVar(t).returnType(type);
630  
      print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression"));
631  
      ret parseCall(inner, e);
632  
    }
633  
    
634  
    // Search in scope
635  
    if (scope != null) {
636  
      try object scope.parseExprStartingWithIdentifierOpt(t, inner);
637  
    }
638  
    
639  
    // script-defined class?
640  
    
641  
    LASClassDef cd = classDefs.get(t);
642  
    if (cd != null)
643  
      ret new Toolbox.ClassDef(cd.resolvable(lasClassLoader));
644  
645  
    if (!inner) {
646  
      var fdef = lookupFunction(t);
647  
      if (fdef != null) {
648  
        if (is("~"))
649  
          ret parseTildeCall(new Toolbox.CallFunction(fdef, new Evaluable[0]));
650  
          
651  
        ret new Toolbox.CallFunction(fdef, parseArguments());
652  
      }
653  
    }
654  
    
655  
    if (eq(t, "_context"))
656  
      ret new Toolbox.GetVarContext;
657  
      
658  
    O o = findExternalObject(t);
659  
    
660  
    var start = ptr().minus(2);
661  
    
662  
    if (o == null) {
663  
      //unconsume();
664  
      throw new UnknownObject(t);
665  
    } else if (o cast EvaluableWrapper) {
666  
      ret parseCall(inner, o.expr);
667  
    } else if (inner)
668  
      ret linkToSrc(start, const(o));
669  
    else if (o cast Class) {
670  
      ret parseExprStartingWithClass(start, o);
671  
    } else if (o cast MethodOnObject) {
672  
      // it's a function from a functionContainer
673  
      
674  
      if (inner) fail("Can't call methods in arguments"); // Does this ever happen?!
675  
      
676  
      ret new Toolbox.CallMethod(const(o.object), o.method, parseArguments());
677  
    } else
678  
      ret parseCall(const(o));
679  
  }
680  
  
681  
  Evaluable parseExprStartingWithClass(TokPtr start, Class c) {
682  
    printVars("parseExprStartingWithClass", +c);
683  
    
684  
    if (atCmdEnd())
685  
      ret linkToSrc(start, const(c));
686  
      
687  
    // new without new
688  
    if (is("("))
689  
      ret new Toolbox.NewObject(c, parseArguments());
690  
      
691  
    /* old object creation syntax (e.g. Pair new a b)
692  
    if (is("new")) {
693  
      next();
694  
      ret new Toolbox.NewObject(c, parseArguments());
695  
    } else*/
696  
    
697  
    // check if it's a lambda definition first (with ->)
698  
    try object parseLambdaOpt(c);
699  
    
700  
    if (isIdentifier()) {
701  
      S name = tpp();
702  
      // We have now parsed a class (c) plus an as yet unknown identifier (name) right next to it
703  
      
704  
      // look for a static method call
705  
      if (hasStaticMethodNamed(c, name))
706  
        ret new Toolbox.CallMethod(const(c), name, parseArguments());
707  
708  
      // if the class is an interface, it's a lambda method reference or a lambda
709  
      if (isInterface(c))
710  
        ret parseLambdaMethodRef(c, name);
711  
      
712  
      // look for field second
713  
      
714  
      var field = getField(c, name);
715  
      if (field == null)
716  
        field = findFieldInInterfaces(c, name);
717  
        
718  
      if (field != null) {
719  
        if (!isStaticField(field))
720  
          fail(field + " is not a static field");
721  
        
722  
        // check for field assignment
723  
        if (consumeLeftArrowOpt()) {
724  
          Evaluable rhs = parseExpr();
725  
          ret new Toolbox.SetStaticField(field, rhs);
726  
        }
727  
      
728  
        assertCmdEnd();
729  
        
730  
        ret new Toolbox.GetStaticField(field);
731  
      }
732  
      
733  
      fail(name + " not found in " + c + " (looked for method or field)");
734  
    } else
735  
      fail("Method name expected: " + token());
736  
  }
737  
  
738  
  // parse lambda, e.g. IF1 a -> plus a a
739  
  // returns null if it's not a lambda
740  
  // assumes that c was just parsed
741  
  Evaluable parseLambdaOpt(Class c) {
742  
    // must be an interface
743  
    if (!isInterface(c)) null;
744  
    
745  
    // speculatively parse optional type args first
746  
    var ptr = ptr();
747  
    var parameterized = parseTypeArgumentsOpt(c);
748  
    
749  
    // e.g. IF0 { 5 }
750  
    if (is("{"))
751  
      ret finishLambdaDef(c, null);
752  
      
753  
    // e.g. IF1 _ size
754  
    if (consumeOpt("_")) {
755  
      S methodName = consumeIdentifier();
756  
      ret new LambdaMethodOnArgument(c, methodName, parseArguments());
757  
    }
758  
    
759  
    new LinkedHashMap<S, LASValueDescriptor> args;
760  
    
761  
    while (isIdentifier()) {
762  
      S name = consumeIdentifier();
763  
      args.put(name, typeToValueDescriptor(parseColonTypeOpt()));
764  
    }
765  
    
766  
    // Check for lambda arrow
767  
    if (!(is("-") && is(1, ">"))) {
768  
      ptr(ptr); // revert pointer
769  
      null; // not a lambda
770  
    }
771  
    
772  
    skip(2); // skip the arrow
773  
    ret finishLambdaDef(c, args);
774  
  }
775  
  
776  
  Evaluable finishLambdaDef(Class c, LinkedHashMap<S, LASValueDescriptor> args) {
777  
    temp tempAddKnownVars(args);
778  
    knownVarsSnapshot(knownVars);
779  
780  
    Evaluable body;
781  
    
782  
    // if curly brackets: parse lambda body as returnable script
783  
    // TODO: avoid script overhead if there are no return calls inside
784  
    if (is("{"))
785  
      body = parseReturnableCurlyBlock();
786  
    else
787  
      body = parseExpr();
788  
    
789  
    // return lambda
790  
    var lambda = new Toolbox.LambdaDef(c, toStringArray(keys(args)), body);
791  
    print("finishLambdaDef done:\n" + Toolbox.indentedScriptStruct(lambda));
792  
    ret lambda;
793  
  }
794  
  
795  
  // method ref, e.g. "IF1 l" meaning "l1 l"
796  
  // also, constructor ref, e.g. "IF1 Pair" meaning (x -> new Pair(x))
797  
  //
798  
  // c must be a single method interface
799  
  Evaluable parseLambdaMethodRef(Class c, S name) {
800  
    var fdef = lookupFunction(name);
801  
    if (fdef != null) {
802  
      Evaluable[] curriedArguments = parseArguments();
803  
      ret new Toolbox.CurriedScriptFunctionLambda(c, fdef, curriedArguments);
804  
    }
805  
806  
    O function = findExternalObject(name);
807  
    
808  
    if (function == null) 
809  
      throw new UnknownObject(name);
810  
      
811  
    if (function cast MethodOnObject) {
812  
      // it's a function from a functionContainer
813  
      
814  
      O target = function.object;
815  
      S targetMethod = function.method;
816  
        
817  
      Evaluable[] curriedArguments = parseArguments();
818  
      ret new Toolbox.CurriedMethodLambda(c, target, targetMethod, curriedArguments);
819  
    } else if (function cast Class) {
820  
      Class c2 = function;
821  
      
822  
      // No currying here yet
823  
      assertCmdEnd();
824  
      
825  
      var ctors = constructorsWithNumberOfArguments(c2, 1);
826  
      if (empty(ctors))
827  
        fail("No single argument constructor found in " + c2);
828  
        
829  
      ret new Toolbox.CurriedConstructorLambda(c, toArray Constructor(ctors), null);
830  
    } else
831  
      fail(function + " is not an instantiable class or callable method");
832  
833  
  }
834  
  
835  
  FunctionDef lookupFunction(S name) {
836  
    var script = currentReturnableScript;
837  
    while (script != null) {
838  
      var f = script.functionDefs.get(name);
839  
      printVars("lookupFunction", +script, +name, +f);
840  
      if (f != null) ret f;
841  
      script = script.returnableParent;
842  
    }
843  
    
844  
    // Try parser-wide function defs
845  
    ret functionDefs.get(name);
846  
  }
847  
  
848  
  Evaluable[] parseArguments() {
849  
    new L<Evaluable> l;
850  
    try {
851  
      while (true) {
852  
        print("parseArgumentsAsList: token is " + quote(t()));
853  
        
854  
        // +id
855  
        if (is("+") && empty(nextSpace()) && isIdentifier(token(1))) {
856  
          consume();
857  
          l.add(const(token()));
858  
          
859  
          // just leave identifier unconsumed
860  
          continue;
861  
        }
862  
863  
        if (consumeOpt("<")) {
864  
          Evaluable a = parseFullExpr();
865  
          print("parseArgumentsAsList: token after expr is " + quote(t()));
866  
          if (a == null) fail("expression expected");
867  
          l.add(a);
868  
          break;
869  
        }
870  
        
871  
        Evaluable a = parseOptionalInnerExpression();
872  
        if (a == null) break;
873  
        l.add(a);
874  
      }
875  
      
876  
      ret toArrayOrNull(Evaluable.class, l);
877  
    } on fail {
878  
      print("Arguments parsed so far: " + l);
879  
    }
880  
  }
881  
  
882  
  S consumeMultiTokenLiteral() {
883  
    ret consumeUntilSpaceOr(-> atCmdEnd() || is(","));
884  
  }
885  
  
886  
  bool atCmdEnd() {
887  
    ret
888  
      !inParens && atEndOrLineBreak()
889  
      || closerTokens.contains(token());
890  
  }
891  
  
892  
  void assertCmdEnd() {
893  
    if (!atCmdEnd())
894  
      fail("Expected end of command, token is: " + quote(token()));
895  
  }
896  
  
897  
  // After we just parsed an expression, see if there is something
898  
  // to the right of it.
899  
  // Currently that can be
900  
  // -a method call
901  
  // -a field access
902  
  // -a field assignment (e.g.: myPair a <- 1)
903  
  Evaluable parseCall(bool inner default false, Evaluable target) {
904  
    returnTypeHook(target);
905  
    
906  
    // normally, do a check for end of line
907  
    if (atCmdEnd()) ret target;
908  
    
909  
    if (inner) {
910  
      try object parseTildeCall(target);
911  
      ret target;
912  
    }
913  
    
914  
    ret parseCall_noCmdEndCheck(target);
915  
  }
916  
  
917  
  // For auto-completer
918  
  void returnTypeHook(Evaluable e) {
919  
    if (e != null && e.returnType() != null)
920  
      typeHook(e.returnType());
921  
  }
922  
  
923  
  Evaluable parseCall_noCmdEndCheck(Evaluable target) {
924  
    var start = ptr();
925  
    
926  
    returnTypeHook(target);
927  
    
928  
    // We usually expect an identifier
929  
    
930  
    if (isIdentifier()) {
931  
      S name = tpp(); // the identifier
932  
      
933  
      // check for field assignment
934  
      if (consumeLeftArrowOpt()) {
935  
        Evaluable rhs = parseExpr();
936  
        ret new Toolbox.SetField(target, name, rhs);
937  
      }
938  
      
939  
      bool allowNullReference = consumeOpt("?");
940  
941  
      var args = parseArguments();
942  
      var call = finalizeCall(start, target, name, args);
943  
      set(call, +allowNullReference);
944  
      ret call;
945  
    }
946  
    
947  
    // ~ also does stuff
948  
    // (map ~a == map getOpt "a")
949  
    // (list ~0 == list listGet 0)
950  
    
951  
    try object parseTildeCall(target);
952  
    
953  
    // special syntax to get a field, bypassing a method of the same name
954  
    if (is("!") && is(1, "_getField_")) {
955  
      next(2);
956  
      S field = consumeIdentifier();
957  
      ret new Toolbox.GetField(target, field);
958  
    }
959  
    
960  
    // no call found
961  
    ret target;
962  
  }
963  
  
964  
  bool consumeLeftArrowOpt() {
965  
    if (is("<") && eq(token(1), "-"))
966  
      ret true with next(2);
967  
    false;
968  
  }
969  
  
970  
  Evaluable parseTildeCall(Evaluable target) {
971  
    var start = ptr();
972  
    
973  
    if (consumeOpt("~")) {
974  
      if (isIdentifier()) {
975  
        S key = consumeIdentifier();
976  
        please include function getOpt.
977  
        CallOnTarget call = finalizeCall1(start, target, "getOpt", const(key));
978  
        call.allowNullReference(true);
979  
        ret parseCall(finalizeCall2(call));
980  
      } else {
981  
        int idx = consumeInteger();
982  
        please include function listGet.
983  
        ret parseCall(finalizeCall(start, target, "listGet", const(idx)));
984  
      }
985  
    }
986  
    
987  
    null;
988  
  }
989  
  
990  
  Evaluable finalizeCall(TokPtr start, Evaluable target, S name, Evaluable... args) {
991  
    ret finalizeCall2(finalizeCall1(start, target, name, args));
992  
  }
993  
  
994  
  Evaluable finalizeCall2(CallOnTarget call) {
995  
    while (is("then"))
996  
      call = parseThenCall(call);
997  
    ret call;
998  
  }
999  
  
1000  
  CallOnTarget finalizeCall1(TokPtr start, Evaluable target, S name, Evaluable... args) {
1001  
    // Is the method name also the name of a global function?
1002  
    if (magicSwitch) {
1003  
      O ext = findExternalObject(name);
1004  
      if (ext cast MethodOnObject) {
1005  
        if (nempty(args))
1006  
          ret new Toolbox.CallMethodOrGlobalFunction(target, name, ext, args);
1007  
        else
1008  
          ret new Toolbox.CallMethodOrGetFieldOrGlobalFunction(target, name, ext);
1009  
      }
1010  
    }
1011  
    
1012  
    if (nempty(args))
1013  
      ret new Toolbox.CallMethod(target, name, args);
1014  
    else
1015  
      ret linkToSrc(start, new Toolbox.CallMethodOrGetField(target, name));
1016  
  } // end of parseCall_noCmdEndCheck
1017  
  
1018  
  // add link to source code to parsed object if it supports it
1019  
  // and doesn't have a source code link already
1020  
  <A> A linkToSrc(TokPtr start, A a) {
1021  
    if (a cast IHasTokenRangeWithSrc)
1022  
      if (a.tokenRangeWithSrc() == null)
1023  
        a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr().plus(-1)).sourceInfo(sourceInfo()));
1024  
    ret a;
1025  
  }
1026  
  
1027  
  // lambda version of linkToSrc
1028  
  <A> A linkToSrc(IF0<A> a) {
1029  
    var start = ptr();
1030  
    ret linkToSrc(start, a!);
1031  
  }
1032  
  
1033  
  // can return MethodOnObject
1034  
  // Note: Assumes we just parsed the name
1035  
  // TODO: rename this part of the method
1036  
  swappable O findExternalObject(S name) {
1037  
    //try object findClassThroughDefaultClassFinder(name);
1038  
    //try object findClassInStandardImports(name);
1039  
    
1040  
    if (eq(name, "void")) ret void.class;
1041  
    try object parsePrimitiveType(name);
1042  
      
1043  
    if (qualifiedNameContinues()) {
1044  
      int idx = idx()-2;
1045  
      do 
1046  
        next(2);
1047  
      while (qualifiedNameContinues());
1048  
1049  
      S fqn = joinSubList(tok, idx, idx()-1);
1050  
      ret classForName(fqn);
1051  
    }
1052  
    
1053  
    ret findExternalObject2(name);
1054  
  }
1055  
  
1056  
  swappable O findExternalObject2(S name) {
1057  
    S fullName = globalClassNames().get(name);
1058  
    if (fullName != null)
1059  
      ret classForName(fullName);
1060  
      
1061  
    try object classForNameOpt_noCache(name);
1062  
    
1063  
    fOr (container : functionContainers) {
1064  
      if (hasMethodNamed(container, name)
1065  
        && !isBlockedFunctionContainerMethod(container, name)) {
1066  
        var moo = new MethodOnObject(container, name);
1067  
        ifdef debugMethodFinding _print(+moo); endifdef
1068  
        ret moo;
1069  
      }
1070  
      
1071  
      var field = getField(container, name);
1072  
      if (field != null && isStaticField(field))
1073  
        ret new EvaluableWrapper(new Toolbox.GetStaticField(field));
1074  
    }
1075  
    
1076  
    null;
1077  
  }
1078  
  
1079  
  swappable bool isBlockedFunctionContainerMethod(O container, S name) {
1080  
    false;
1081  
  }
1082  
  
1083  
  selfType allowTheWorld() { ret allowTheWorld(mc()); }
1084  
  
1085  
  // first containers within argument list have priority
1086  
  // last calls to allowTheWorld/first arguments passed have priority
1087  
  selfType allowTheWorld(O... functionContainers) {
1088  
    fOr (O o : reversed(functionContainers))
1089  
      if (!contains(this.functionContainers, o)) {
1090  
        this.functionContainers.add(0, o);
1091  
        globalClassNames_cache = null; // recalculate
1092  
      }
1093  
    this;
1094  
  }
1095  
  
1096  
  void printFunctionDefs(Script script) {
1097  
    print(values(script.functionDefs));
1098  
  }
1099  
  
1100  
  AutoCloseable tempAddKnownVars(S... vars) {
1101  
    ret tempAddKnownVars(nonNulls(vars));
1102  
  }
1103  
  
1104  
  AutoCloseable tempAddKnownTypedVars(Iterable<TypedVar> vars) {
1105  
    ret tempAddKnownVars(mapToMap(vars, v -> pair(v.name, v.type)));
1106  
  }
1107  
    
1108  
  AutoCloseable tempAddKnownVars(Iterable<S> vars) {
1109  
    var newVars = mapWithSingleValue(vars, new LASValueDescriptor);
1110  
    ret tempAddKnownVars(newVars);
1111  
  }
1112  
    
1113  
  AutoCloseable tempAddKnownVar(S var, LASValueDescriptor type) {
1114  
    ret tempAddKnownVars(litmap(var, type));
1115  
  }
1116  
  
1117  
  AutoCloseable tempAddKnownVars(Map<S, LASValueDescriptor> newVars) {
1118  
    /*if (scope != null)
1119  
      for (name, type : newVars)
1120  
        scope.addDeclaredVar(name, type);*/
1121  
    ret tempMapPutAll(knownVars, newVars);
1122  
  }
1123  
  
1124  
  S parseFunctionDefArg(Map<S, LASValueDescriptor> argsOut) {
1125  
    if (consumeOpt("(")) {
1126  
      S result = parseFunctionDefArg(argsOut);
1127  
      consume(")");
1128  
      ret result;
1129  
    }
1130  
    
1131  
    if (isIdentifier()) {
1132  
      S arg = tpp();
1133  
      var type = typeToValueDescriptor(parseColonTypeOpt());
1134  
      argsOut.put(arg, type);
1135  
      ret arg;
1136  
    }
1137  
    
1138  
    null;
1139  
  }
1140  
1141  
  
1142  
  Toolbox.FunctionDef parseFunctionDefinition(Map<S, FunctionDef> functionDefsToAddTo) {
1143  
    var start = ptr();
1144  
    consume("def");
1145  
    bool synthetic = consumeOpt("!");
1146  
    if (synthetic) consume("synthetic");
1147  
    S functionName = assertIdentifier(tpp());
1148  
    print("parseFunctionDefinition " + functionName);
1149  
    ret parseFunctionDefinition_step2(functionDefsToAddTo, start, functionName)
1150  
      .synthetic(synthetic);
1151  
  }
1152  
    
1153  
  Toolbox.FunctionDef parseFunctionDefinition_step2(Map<S, FunctionDef> functionDefsToAddTo, TokPtr start, S functionName) {
1154  
    // argument names and types
1155  
    new LinkedHashMap<S, LASValueDescriptor> args;
1156  
    
1157  
    while (parseFunctionDefArg(args) != null) {}
1158  
    
1159  
    var fd = new Toolbox.FunctionDef(functionName, keysList(args), null); // no body yet
1160  
    
1161  
    if (is(".", ".", ".")) {
1162  
      consume(3);
1163  
      fd.varargs(true);
1164  
    }
1165  
    
1166  
    var returnType = parseColonTypeOpt();
1167  
    
1168  
    fd.argTypes(valuesArray(LASValueDescriptor, args));
1169  
    functionDefsToAddTo?.put(functionName, fd);
1170  
    
1171  
    //var scope = newScope();
1172  
    //scope.useFixedVars(useFixedVarContexts);
1173  
    //temp tempScope(scope);
1174  
    temp tempAddKnownVars(args);
1175  
    print("Parsing function body");
1176  
    fd.body = parseReturnableCurlyBlock();
1177  
    
1178  
    print("Defined function " + functionName + ", added to " + functionDefsToAddTo);
1179  
    
1180  
    //fd.scope(scope);
1181  
    if (returnType != null) fd.returnType(returnType);
1182  
    ret linkToSrc(start, fd);
1183  
  }
1184  
  
1185  
  /*LASScope newScope() {
1186  
    ret new LASScope(scope);
1187  
  }*/
1188  
  
1189  
  Scope currentScope() { ret scope; }
1190  
  
1191  
  // enter the scope temporarily
1192  
  AutoCloseable tempScope(Scope scope) {
1193  
    var oldScope = currentScope();
1194  
    this.scope = scope;
1195  
    ret -> { this.scope = oldScope; };
1196  
  }
1197  
  
1198  
  Script parseReturnableCurlyBlock() {
1199  
    ret (Script) parseCurlyBlock(new BuildingScript().returnable(true));
1200  
  }
1201  
  
1202  
  Evaluable parseCurlyBlock(BuildingScript script default new) {
1203  
    //print(+knownVars);
1204  
    consume("{");
1205  
    bool inParensOld = inParens;
1206  
    inParens = false;
1207  
    var body = parseScript(script);
1208  
    consume("}");
1209  
    inParens = inParensOld;
1210  
    ret body;
1211  
  }
1212  
  
1213  
  Evaluable parseWhileLoop() {
1214  
    consume("while");
1215  
    var condition = parseExpr();
1216  
    var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
1217  
    ret new Toolbox.While(condition, body);
1218  
  }
1219  
  
1220  
  Evaluable parseDoWhileLoop() {
1221  
    consume("do");
1222  
    var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
1223  
    consume("while");
1224  
    var condition = parseExpr();
1225  
    ret new Toolbox.DoWhile(condition, body);
1226  
  }
1227  
  
1228  
  Evaluable parseForEach() {
1229  
    ret new ParseForEach()!;
1230  
  }
1231  
  
1232  
  class ParseForEach {
1233  
    Evaluable collection, body;
1234  
    IF0<Evaluable> finish;
1235  
    new L<TypedVar> vars;
1236  
    
1237  
    TypedVar addVar(TypedVar var) { ret addAndReturn(vars, var); }
1238  
    TypedVar consumeVar() {
1239  
      S name = consumeIdentifier();
1240  
      var type = parseColonTypeOpt();
1241  
      ret addVar(new TypedVar(name, typeToValueDescriptor(type)));
1242  
    }
1243  
    
1244  
    void parseBody {
1245  
      temp tempAddKnownTypedVars(vars);
1246  
      body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
1247  
    }
1248  
    
1249  
    Evaluable get() {
1250  
      consume("for");
1251  
      
1252  
      // for i to n { ... }
1253  
      if (is(1, "to")) {
1254  
        TypedVar var = consumeVar();
1255  
        consume("to");
1256  
        Evaluable endValue = parseExpr();
1257  
        parseBody();
1258  
        ret new Toolbox.ForIntTo(endValue, var.name, body);
1259  
      }
1260  
    
1261  
      int iIn = relativeIndexOf("in");
1262  
      if (iIn < 0) fail("for without in");
1263  
      print(+iIn);
1264  
      
1265  
      if (iIn == 1 || is(1, ":")) { // just a variable (with optional type)
1266  
        TypedVar var = consumeVar();
1267  
        finish = -> new Toolbox.ForEach(collection, var.name, body).varType(var.type);
1268  
      } else if (iIn == 2) { // for iterator (mapI)
1269  
        if (consumeOpt("iterator")) {
1270  
          TypedVar var = consumeVar();
1271  
          finish = -> new Toolbox.ForIterator(collection, var.name, body);
1272  
        } else if (consumeOpt("nested")) {
1273  
          TypedVar var = consumeVar();
1274  
          finish = -> new Toolbox.ForNested(collection, var.name, body);
1275  
        } else
1276  
          fail("Unknown pattern for 'for' loop");
1277  
      } else if (iIn == 3) {
1278  
        if (isOneOf("pair", "Pair")) {
1279  
          // "pair a b" or "Pair a b"
1280  
          consume();
1281  
          TypedVar varA = consumeVar();
1282  
          TypedVar varB = consumeVar();
1283  
          printVars(+varA, +varB);
1284  
          
1285  
          finish = -> new Toolbox.ForPairs(collection, body, varA.name, varB.name);
1286  
        } else {
1287  
          // "a, b"
1288  
          TypedVar varA = consumeVar();
1289  
          consume(",");
1290  
          TypedVar varB = consumeVar();
1291  
1292  
          finish = -> new Toolbox.ForKeyValue(collection, body, varA.name, varB.name);
1293  
        }
1294  
      } else if (iIn == 4) {
1295  
        consume("index");
1296  
        S varIndex = consumeVar().name;
1297  
        consume(",");
1298  
        S varElement = consumeVar().name;
1299  
        
1300  
        finish = -> new Toolbox.ForIndex(collection, body, varIndex, varElement);
1301  
      } else
1302  
        fail("Unknown pattern for 'for' loop");
1303  
      
1304  
      consume("in");
1305  
      collection = parseExpr_inParens();
1306  
      print(+collection);
1307  
      parseBody();
1308  
      ret finish!;
1309  
    }
1310  
  }
1311  
  
1312  
  Evaluable parseIfStatement() {
1313  
    consume("if");
1314  
    Evaluable condition, body, elseBranch = null;
1315  
    {
1316  
      temp tempAdd(closerTokens, "then");
1317  
      condition = parseExpr();
1318  
    }
1319  
    
1320  
    if (consumeOpt("then")) {
1321  
      temp tempAdd(closerTokens, "else");
1322  
      body = parseExpr();
1323  
      if (consumeOpt("else"))
1324  
        elseBranch = parseExpr();
1325  
    } else {
1326  
      body = parseCurlyBlock();
1327  
      if (consumeOpt("else")) {
1328  
        if (is("if"))
1329  
          elseBranch = parseIfStatement();
1330  
        else
1331  
          elseBranch = parseCurlyBlock();
1332  
      }
1333  
    }
1334  
    
1335  
    ret new Toolbox.IfThen(condition, body, elseBranch);
1336  
  }
1337  
  
1338  
  Evaluable parseRepeatStatement() {
1339  
    consume("repeat");
1340  
    var n = parseExpr();
1341  
    var body = parseCurlyBlock();
1342  
    ret new Toolbox.RepeatN(n, body);
1343  
  }
1344  
  
1345  
  // declare an external variable with optional type info
1346  
  void addVar(S var, LASValueDescriptor type default new) { knownVars.put(var, type); }
1347  
  void addVar(S var, Class type, bool canBeNull) {
1348  
    addVar(var, typeToValueDescriptor(type, canBeNull));
1349  
  }
1350  
  
1351  
  LASValueDescriptor typeToValueDescriptor(Type type, bool canBeNull default true) {
1352  
    ret type == null ? new LASValueDescriptor
1353  
      : new LASValueDescriptor.NonExact(typeToClass(type), canBeNull);
1354  
  }
1355  
  
1356  
  // short name to full name
1357  
  // TODO: sort by priority if classes appear twice
1358  
  simplyCached SS globalClassNames() {
1359  
    var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
1360  
    
1361  
    new SS out;
1362  
    
1363  
    fOr (name : importedClasses())
1364  
      out.put(shortenClassName(name), name);
1365  
    
1366  
    // add function containers themselves (if they're classes)
1367  
    for (fc : functionContainers)
1368  
      if (fc cast Class) {
1369  
        if (isAnonymousClass(fc)) continue;
1370  
        out.put(shortClassName(fc), className(fc));
1371  
      }
1372  
    
1373  
    // add inner classes of function containers
1374  
    var classContainers = classContainerPrefixes();
1375  
    TreeSet<S> classContainerSet = asTreeSet(classContainers);
1376  
1377  
    for (className : classNameResolver().allFullyQualifiedClassNames()) {
1378  
      if (isAnonymousClassName(className)) continue;
1379  
      if (!contains(className, '$')) {
1380  
        S pkg = longestPrefixInTreeSet(className, packages);
1381  
        if (pkg != null) {
1382  
          S shortName = dropPrefix(pkg, className);
1383  
          if (!shortName.contains(".") && !out.containsKey(shortName))
1384  
            out.put(shortName, className);
1385  
        }
1386  
      }
1387  
        
1388  
      S container = longestPrefixInTreeSet(className, classContainerSet);
1389  
      if (container != null) {
1390  
        S shortName = dropPrefix(container, className);
1391  
        S existing = out.get(shortName);
1392  
        if (existing != null) {
1393  
          int priority = indexOf(classContainers, container);
1394  
          S oldContainer = longestPrefixInTreeSet(existing, classContainerSet);
1395  
          //int oldPriority = indexOf(classContainers, oldContainer);
1396  
          int oldPriority = smartIndexOf(classContainers, oldContainer);
1397  
          printVars(+className, +shortName, +container, +priority, +existing, +oldPriority);
1398  
          if (priority > oldPriority)
1399  
            continue;
1400  
        }
1401  
        out.put(shortName, className);
1402  
      }
1403  
    }
1404  
        
1405  
    fOr (key, val : classShortcuts()) {
1406  
      S fullName = out.get(val);
1407  
      if (fullName != null)
1408  
        out.put(key, fullName);
1409  
    }
1410  
1411  
    ret out;
1412  
  }
1413  
  
1414  
  swappable SS classShortcuts() {
1415  
    ret javaxClassShortcuts();
1416  
  }
1417  
  
1418  
  swappable Cl<S> importedPackages() {
1419  
    ret itemPlus("java.lang", standardImports_fullyImportedPackages());
1420  
  }
1421  
  
1422  
  swappable Cl<S> importedClasses() {
1423  
    ret standardImports_singleClasses();
1424  
  }
1425  
  
1426  
  void addClassAlias(S alias, S longName) {
1427  
    S fullName = globalClassNames().get(longName);
1428  
    if (fullName != null)
1429  
      globalClassNames().put(alias, fullName);
1430  
  }
1431  
  
1432  
  LS classContainerPrefixes() {
1433  
    ret map(functionContainers, fc -> className(fc) + "$");
1434  
  }
1435  
  
1436  
  // typed exceptions (unusual, I know!)
1437  
  
1438  
  srecord noeq UnknownObject(S name) > RuntimeException {
1439  
    public S getMessage() { ret "Unknown object: " + name; }
1440  
  }
1441  
  
1442  
  sclass ClassNotFound > UnknownObject {
1443  
    *(S className) { super(className); }
1444  
    
1445  
    public S getMessage() { ret "Class not found: " + name; }
1446  
  }
1447  
  
1448  
  asclass Scope {
1449  
    // Parse expression due to special rules in this scope
1450  
    // (return null if no parse)
1451  
    Evaluable parseExprStartingWithIdentifierOpt(S t, bool inner) { null; }
1452  
    
1453  
    Evaluable completeAssignmentOpt(S lhs, Evaluable rhs) { null; }
1454  
  }
1455  
  
1456  
  record ClassDefScope(LASClassDef classDef) extends Scope {
1457  
    Evaluable getThis() { ret new Toolbox.GetVar("this"); }
1458  
    
1459  
    Evaluable parseExprStartingWithIdentifierOpt(S t, bool inner) {
1460  
      printVars("ClassDefScope.parseExprStartingWithIdentifierOpt", +t);
1461  
      
1462  
      ret linkToSrc(-> {
1463  
        var field = classDef.fieldsByName.get(t);
1464  
        if (field != null) {
1465  
          print("Found field " + field);
1466  
          var e = inner
1467  
            ? new Toolbox.GetField(getThis(), field.name)
1468  
            : new Toolbox.CallMethodOrGetField(getThis(), field.name);
1469  
          ret parseCall(inner, e);
1470  
        }
1471  
        
1472  
        Class superClass = typeToClass(classDef.superClass);
1473  
        var field2 = findFieldOfClass(superClass, t);
1474  
        printVars(+field2, +superClass, +t);
1475  
        if (field2 != null) {
1476  
          print("Found field " + field2);
1477  
          var e = inner
1478  
            ? new Toolbox.GetField(getThis(), field2.getName())
1479  
            : new Toolbox.CallMethodOrGetField(getThis(), field2.getName());
1480  
          ret parseCall(inner, e);
1481  
        }
1482  
        
1483  
        if (!inner
1484  
          && (classDef.methodsByName.containsKey(t)
1485  
          || classHasMethodNamed(typeToClass(classDef.superClass), t))) {
1486  
          ret new Toolbox.CallMethod(getThis(), t, parseArguments());
1487  
        }
1488  
        
1489  
        null;
1490  
      });
1491  
    }
1492  
    
1493  
    Evaluable completeAssignmentOpt(S lhs, Evaluable rhs) {         var field = classDef.fieldsByName.get(lhs);
1494  
      if (field != null)
1495  
        ret new Toolbox.SetField(getThis(), lhs, rhs);
1496  
        
1497  
      Class superClass = typeToClass(classDef.superClass);
1498  
      var field2 = findFieldOfClass(superClass, lhs);
1499  
      if (field2 != null)
1500  
        ret new Toolbox.SetField(getThis(), lhs, rhs);
1501  
          
1502  
      null;
1503  
    }
1504  
  }
1505  
  
1506  
  LASClassDef parseClassDef() {
1507  
    ret linkToSrc(-> {
1508  
      consume("class");
1509  
      new LASClassDef classDef;
1510  
      classDef.verbose(scaffoldingEnabled());
1511  
      if (nempty(classDefPrefix))
1512  
        classDef.classDefPrefix(classDefPrefix);
1513  
      S name = consumeIdentifier();
1514  
      classDef.userGivenName(name);
1515  
      classDefs.put(name, classDef);
1516  
      temp tempMapPut(classDefs, "selfType", classDef);
1517  
      
1518  
      if (consumeOpt("extends")) {
1519  
        classDef.superClass(parseType());
1520  
      }
1521  
        
1522  
      if (consumeOpt("is"))
1523  
        classDef.interfaces.add(parseType());
1524  
  
1525  
      consume("{");
1526  
      
1527  
      // parse class body
1528  
      
1529  
      temp tempAddKnownVar("this", new LASValueDescriptor.NonExactType(classDef.resolvable(lasClassLoader), false));
1530  
      temp !objectScopesEnabled ? null : tempScope(new ClassDefScope(classDef));
1531  
      
1532  
      while (!is("}")) {
1533  
        if (is(";")) continue with next();
1534  
        
1535  
        // parse method declaration
1536  
        
1537  
        if (is("def")) {
1538  
          Toolbox.FunctionDef fd = parseFunctionDefinition(null);
1539  
          printVars(knownVarsAfterFunctionDef := knownVars);
1540  
          classDef.addMethod(fd);
1541  
          continue;
1542  
        }
1543  
        
1544  
        // parse constructor declaration
1545  
        
1546  
        var start = ptr();
1547  
        if (consumeOpt("ctor")) {
1548  
          var fd = parseFunctionDefinition_step2(null, start, "<init>");
1549  
          fd.returnType(void.class);
1550  
          classDef.addMethod(fd);
1551  
          continue;
1552  
        }
1553  
        
1554  
        // parse initializer block
1555  
        
1556  
        if (consumeOpt("initializer")) {
1557  
          classDef.initializers.add(parseCurlyBlock());
1558  
          continue;
1559  
        }
1560  
          
1561  
        // parse field declaration
1562  
        
1563  
        new LASClassDef.FieldDef fd;
1564  
        while (isOneOf("transient", "static")) { fd.addModifier(tpp()); }
1565  
        
1566  
        fd.name(consumeIdentifier());
1567  
        var type = parseColonType();
1568  
        fd.type(type);
1569  
        
1570  
        // parse field initializer
1571  
        if (consumeLeftArrowOpt())
1572  
          fd.initializer(parseExpr());
1573  
1574  
        classDef.addField(fd);
1575  
      }
1576  
      
1577  
      consume("}");
1578  
      ret classDef;
1579  
    });
1580  
  }
1581  
  
1582  
  Type parseColonTypeOpt() {
1583  
    if (is(":"))
1584  
      ret parseColonType();
1585  
    null;
1586  
  }
1587  
  
1588  
  Type parseColonType() {
1589  
    consume(":");
1590  
    ret parseType();
1591  
  }
1592  
  
1593  
  Type findType(S typeName) {
1594  
    // script-defined class?
1595  
    LASClassDef cd = classDefs.get(typeName);
1596  
    if (cd != null)
1597  
      ret cd.resolvable(lasClassLoader);
1598  
      
1599  
    O o = findExternalObject(typeName);
1600  
    if (o cast Class)
1601  
      ret o;
1602  
      
1603  
    throw new ClassNotFound(typeName);
1604  
  }
1605  
  
1606  
  Type parseType() {
1607  
    S typeName = consumeIdentifier();
1608  
    Type type = findType(typeName);
1609  
    ret parseArrayType(parseTypeArguments(type));
1610  
  }
1611  
  
1612  
  Type parseArrayType(Type type) {
1613  
    while (is("[") && is(1, "]")) {
1614  
      consume(2);
1615  
      type = new GenericArrayTypeImpl(type);
1616  
    }
1617  
    ret type;
1618  
  }
1619  
  
1620  
  Type parseTypeArguments aka parseTypeArgumentsOpt(Type type) {
1621  
    // type arguments
1622  
    if (is("<") && empty(nextSpace()) && isIdentifier(token(1))) {
1623  
      next();
1624  
      new L<Type> args;
1625  
      while true {
1626  
        args.add(parseType());
1627  
        if (is(">")) break;
1628  
        consume(",");
1629  
      }
1630  
      consume(">");
1631  
      
1632  
      if (type != null)
1633  
        type = new ParameterizedTypeImpl(null, type, toTypedArray(Type, args));
1634  
    }
1635  
    ret type;
1636  
  }
1637  
  
1638  
  swappable Class classForName(S name) ctex {
1639  
    ret Class.forName(name);
1640  
  }
1641  
  
1642  
  // also copies the globalClassNames_cache which otherwise takes
1643  
  // a few ms to calculate
1644  
  void copyFunctionContainersFrom(GazelleV_LeftArrowScriptParser parser) {
1645  
    functionContainers = cloneList(parser.functionContainers);
1646  
    globalClassNames_cache = parser.globalClassNames();
1647  
    isBlockedFunctionContainerMethod = parser.isBlockedFunctionContainerMethod;
1648  
  }
1649  
  
1650  
  ClassNameResolver classNameResolver() {
1651  
    if (classNameResolver == null)
1652  
      classNameResolver = new ClassNameResolver().byteCodePath(assertNotNull(getBytecodePathForClass(this))).init();
1653  
    ret classNameResolver;
1654  
  }
1655  
  
1656  
  selfType classNameResolver(ClassNameResolver classNameResolver) {
1657  
    this.classNameResolver = classNameResolver;
1658  
    this;
1659  
  }
1660  
1661  
  Evaluable parseStringLiteral() {  
1662  
    S s = unquote_relaxedMLS(consume());
1663  
    if (internStringLiterals)
1664  
      s = intern(s);
1665  
    ret const(s);
1666  
  }
1667  
  
1668  
  void addFunctionDefs(Map<S, FunctionDef> map) {
1669  
    putAll(functionDefs, map);
1670  
  }
1671  
  
1672  
  selfType setFlag(S flag) {
1673  
    flags.add(flag);
1674  
    this;
1675  
  }
1676  
  
1677  
  bool getFlag(S flag) {
1678  
    ret flags.contains(flag);
1679  
  }
1680  
1681  
  // forget normally imported class names (e.g. for testing)
1682  
  void noImports() {  
1683  
    importedPackages = -> null;
1684  
    importedClasses = -> null;
1685  
    classShortcuts = -> null;
1686  
  }
1687  
  
1688  
  srecord TypedVar(S name, LASValueDescriptor type) {}
1689  
  
1690  
  // parser initializer
1691  
  {
1692  
    internIdentifiers(true);
1693  
  }
1694  
  
1695  
} // end of GazelleV_LeftArrowScriptParser

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1033976
Snippet name: GazelleV_LeftArrowScriptParser
Eternal ID of this version: #1033976/619
Text MD5: d5f6534dc048f7e55e00201006a5b775
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2025-01-09 18:16:05
Source code size: 50335 bytes / 1695 lines
Pitched / IR pitched: No / No
Views / Downloads: 1570 / 5467
Version history: 618 change(s)
Referenced in: [show references]