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

1669
LINES

< > BotCompany Repo | #1033976 // GazelleV_LeftArrowScriptParser

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

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