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

1683
LINES

< > BotCompany Repo | #1033976 // GazelleV_LeftArrowScriptParser

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

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

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