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

401
LINES

< > BotCompany Repo | #1034291 // GazelleV_LeftArrowScriptParser backup before type inference

JavaX fragment (include)

/* e.g.

  overlay <- ScreenOverlay
  bounds <- rightScreenBounds
  overlay bounds bounds
  overlay show

Can separate commands with ";" also.
For how to define functions in a script see #1033988.

Note: LeftArrowScriptAutoCompleter uses a lot of this class's internals

*/

sclass GazelleV_LeftArrowScriptParser > SimpleLeftToRightParser {
  replace Toolbox with GazelleV_LeftArrowScript.
  delegate Script to Toolbox.
  delegate Evaluable to Toolbox.
  replace const with _const.
  
  settable new G22Utils g22utils;
  L functionContainers;
  
  new LinkedHashSet<S> knownVars;
  BuildingScript currentReturnableScript;
  int parenLevels;
  
  // object can be a class
  srecord MethodOnObject(O object, S method) {}
  
  class BuildingScript {
    bool returnable;
    new Script script;
    new L<Evaluable> steps;
    Map<S, Toolbox.FunctionDef> functionDefs = new Map;
    
    *(bool *returnable) {}
    
    void add(Evaluable step) { if (step != null) steps.add(step); }

    Evaluable get() {
      // if the last command is a return from THIS script,
      // convert into a simple expression
      
      var lastStep = last(steps);
      if (lastStep cast Toolbox.ReturnFromScript)
        if (lastStep.script == script)
          replaceLast(steps, lastStep.value);
        
      // if the script is only step, is not returnable
      // and defines no functions, replace it with its only step
      
      if (!returnable && l(steps) == 1 && empty(functionDefs))
        ret first(steps);
        
      // make and return actual script
      
      script.functionDefs = functionDefs;
      script.steps = toTypedArray(Evaluable.class, steps);
      ret script;
    }
    
    S toStringLong() { ret pnlToLines(steps); }
    toString { ret formatRecordVars BuildingScript(+script, +returnable); }
  }
  
  Script parse(S text) {
    setText(text);
    ret (Script) parseScript(true);
  }
  
  // if returnable is true, it's always a Script
  Evaluable parseScript(bool returnable) {
    BuildingScript script = new(returnable);
    var lastReturnableScript = currentReturnableScript;
    if (returnable) currentReturnableScript = script;
    try {
      parseScript_2(script);
      var builtScript = script!;
      currentReturnableScript = lastReturnableScript;
      ret builtScript;
    } catch e {
      print("Parsed so far:\n" + script);
      
      throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn())));
    }
  }
  
  void parseScript_2(BuildingScript script) {
    while (mainLoop()) {
      if (is(";")) continue with next();
      if (is("}")) break;
      
      S t = token();
      print("First token of command: " + t);
      
      if (is("def"))
        continue with parseFunctionDefinition();
        
      if (is("while"))
        continue with script.add(parseWhileLoop());

      if (is("for"))
        continue with script.add(parseForEach());

      if (is("if"))
        continue with script.add(parseIfStatement());
        
      // return is just ignored for now
      if (is("return")) {
        consume();
        var expr = parseExpr();
        continue with script.add(new Toolbox.ReturnFromScript(currentReturnableScript.script, expr));
      }

      print("next tokens: " + quote(token(1)) + " " + quote(token(2)));
      if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) {
        print("Found assignment");
        next(3);
        knownVars.add(t);
        script.add(new Toolbox.Assignment(t, parseExpr()));
      } else
        script.add(parseExpr());
    }
  }
  
  Evaluable parseOptionalInnerExpression() {
    printVars parseOptionalInnerExpression(+token());
    if (atCmdEnd() || is("{")) null;
    ret parseInnerExpr();
  }
  
  Evaluable const(O o) { ret new Toolbox.Const(o); }
  
  Evaluable parseInnerExpr() { ret parseExpr(true); }
  
  Evaluable parseExpr(bool inner default false) {
    if (atEnd()) null;
    
    S t = token();
    printVars parseExpr(token := t);
    if (is(";")) null; // empty command
    
    // int or double literal
    if (is("-") && empty(nextSpace()) && startsWithDigit(token(1))
      || startsWithDigit(t)) {
      t = consumeMultiTokenLiteral();
      ret isInteger(t) ? const(parseInt(t)) : const(parseDouble(t));
    }

    if (isQuoted(t)) {
      consume();
      ret const(unquote(t));
    }
      
    if (isIdentifier(t)) {
      consume();
      print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner);
      ret parseExprStartingWithIdentifier(t, inner);
    }
    
    // nested expression
    
    if (eq(t, "(")) {
      parenLevels++;
      consume();
      print("Consumed opening parens (level " + parenLevels + ")");
      var e = parseExpr();
      print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")");
      consume(")");
      parenLevels--;
      
      ret inner ? e : parseCall(e);
    }
      
    fail("Identifier, literal or opening parentheses expected (got: " + quote(t));
  }
  
  // t is last consumed token (the identifier the expression starts with)
  Evaluable parseExprStartingWithIdentifier(S t, bool inner) {
    if (eq(t, "true")) ret const(true);
    if (eq(t, "false")) ret const(false);
    if (eq(t, "null")) ret const(null);
    
    if (eq(t, "new")) {
      S className = assertIdentifier(tpp());
      O o = findExternalObject(className);
      if (o cast Class)
        ret new Toolbox.NewObject(o, parseArguments());
      fail("Class not found: " + className);
    }

    if (knownVars.contains(t)) {
      var e = new Toolbox.GetVar(t);
      print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression"));
      ret inner ? e : parseCall(e);
    }
    
    if (!inner) {
      var fdef = currentReturnableScript.functionDefs.get(t);
      if (fdef != null)
        ret new Toolbox.CallFunction(fdef, parseArguments());
    }
      
    O o = findExternalObject(t);
    if (o == null)
      fail("Unknown object: " + t);
    else if (inner)
      ret const(o);
    else if (o cast Class) {
      /*if (atCmdEnd())
        ret new Toolbox.NewObject(o);*/
        
      /* old object creation syntax (e.g. Pair new a b)
      if (is("new")) {
        next();
        ret new Toolbox.NewObject(o, parseArguments());
      } else*/ if (isIdentifier()) {
        S name = tpp();
        
        // look for method first
        
        if (hasMethodNamed(o, name))
          ret new Toolbox.CallMethod(const(o), name, parseArguments());
          
        // look for field second
        
        var field = getField(o, name);
        if (field != null) {
          assertCmdEnd();
          ret new Toolbox.GetStaticField(field);
        }
        
        fail(name + " not found in " + o + " (looked for method or field)");
      } else
        fail("Method name expected: " + token());
    } else if (o cast MethodOnObject) {
      if (inner) fail("Can't call methods in arguments");
      ret new Toolbox.CallMethod(const(o.object), o.method, parseArguments());
    } else
      ret parseCall(const(o));
  }
  
  L<Evaluable> parseArguments() {
    //ret collectWhileNotNull(-> parseOptionalInnerExpression());
    
    new L<Evaluable> l;
    try {
      while (true) {
        Evaluable a = parseOptionalInnerExpression();
        if (a == null) break;
        l.add(a);
      }
      ret l;
    } on fail {
      print("Arguments parsed so far: " + l);
    }
  }
  
  S consumeMultiTokenLiteral() {
    ret consumeUntilSpaceOr(-> atCmdEnd());
  }
  
  bool atCmdEnd() {
    ret
      parenLevels == 0 && atEndOrLineBreak()
      || is(";") || is("}") || is(")");
  }
  
  void assertCmdEnd() { if (!atCmdEnd()) fail("Expected end of command"); }
  
  Evaluable parseCall(Evaluable target) {
    if (atCmdEnd() || !isIdentifier()) ret target;
    
    var start = ptr();
    S methodName = tpp();
    var args = parseArguments();
    
    if (nempty(args))
      ret new Toolbox.CallMethod(target, methodName, args);
    else
      ret src(start, new Toolbox.CallMethodOrGetField(target, methodName));
  }
  
  <A> A src(TokPtr start, A a) {
    if (a cast IHasTokenRangeWithSrc)
      a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr()));
    ret a;
  }
  
  // can return MethodOnObject
  swappable O findExternalObject(S name) ctex {
    //try object findClassThroughDefaultClassFinder(name);
    //try object findClassInStandardImports(name);
      
    S fullName = globalClassNames().get(name);
    if (fullName != null)
      ret Class.forName(fullName);
      
    fOr (container : functionContainers)
      if (hasMethodNamed(container, name))
        ret new MethodOnObject(container, name);
    null;
  }
  
  selfType allowTheWorld() { ret allowTheWorld(mc()); }
  
  selfType allowTheWorld(O... functionContainers) {
    this.functionContainers = asList(functionContainers);
    globalClassNames_cache = null; // recalculate
    this;
  }
  
  void printFunctionDefs(Script script) {
    print(values(script.functionDefs));
  }
  
  void parseFunctionDefinition() {
    consume("def");
    S functionName = assertIdentifier(tpp());
    new LS args;
    while (isIdentifier())
      args.add(tpp());
    temp tempAddAll(knownVars, args);
    var functionBody = parseCurlyBlock(true);
    
    currentReturnableScript.functionDefs.put(functionName,
      new Toolbox.FunctionDef(functionName, args, functionBody));
  }
  
  Evaluable parseCurlyBlock(bool returnable) {
    //print(+knownVars);
    consume("{");
    var script = parseScript(returnable);
    consume("}");
    ret script;
  }
  
  Evaluable parseWhileLoop() {
    consume("while");
    var condition = parseExpr();
    var body = parseCurlyBlock(false);
    ret new Toolbox.While(condition, body);
  }
  
  Evaluable parseForEach() {
    consume("for");
    S var = assertIdentifier(tpp());
    print("for var", var);
    consume("in");
    var collection = parseExpr();
    print(+collection);
    temp tempAdd(knownVars, var);
    var body = parseCurlyBlock(false);
    ret new Toolbox.ForEach(collection, var, body);
  }
  
  Evaluable parseIfStatement() {
    consume("if");
    var condition = parseExpr();
    var body = parseCurlyBlock(false);
    ret new Toolbox.IfThen(condition, body);
  }
  
  // declare an external variable
  void addVar(S var) { knownVars.add(var); }
  
  // short name to full name
  simplyCached SS globalClassNames() {
    var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
    
    // add inner classes of function containers
    var classContainers = classContainerPrefixes();
    
    new SS out;
    for (className : g22utils.classNameResolver().allFullyQualifiedClassNames()) {
      if (!contains(className, '$')) {
        S pkg = longestPrefixInTreeSet(className, packages);
        if (pkg != null) {
          S shortName = dropPrefix(pkg, className);
          if (!shortName.contains("."))
            out.put(shortName, className);
        }
      }
        
      S container = longestPrefixInTreeSet(className, classContainers);
      if (container != null)
        out.put(dropPrefix(container, className), className);
    }
        
    ret out;
  }
  
  swappable Cl<S> importedPackages() {
    ret itemPlus("java.lang", standardImports_fullyImportedPackages());
  }
  
  TreeSet<S> classContainerPrefixes() {
    ret mapToTreeSet(functionContainers, fc -> className(fc) + "$");
  }
}

Author comment

Began life as a copy of #1033976

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1034291
Snippet name: GazelleV_LeftArrowScriptParser backup before type inference
Eternal ID of this version: #1034291/1
Text MD5: f561e55965ddeabc63f10b6754735309
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-02-01 03:08:06
Source code size: 11835 bytes / 401 lines
Pitched / IR pitched: No / No
Views / Downloads: 68 / 79
Referenced in: [show references]