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

1096
LINES

< > BotCompany Repo | #1033976 // GazelleV_LeftArrowScriptParser

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

Transpiled version (31146L) is out of date.

/* 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, Evaluable, FunctionDef, FixedVarBase to Toolbox.
  replace const with _const.
  
  ClassNameResolver classNameResolver;
  new L functionContainers;
  
  settable ILASClassLoader lasClassLoader;
  settable S classDefPrefix;
  
  settable bool optimize = true;
  settable bool useFixedVarContexts = false; // doesn't completely work yet
  
  LASScope scope;
  new LinkedHashMap<S, LASValueDescriptor> knownVars;
  
  new L<FixedVarBase> varAccessesToFix;
  
  Set<S> closerTokens = litset(";", "}", ")");
  BuildingScript currentReturnableScript;
  BuildingScript currentLoop;
  bool inParens;
  int idCounter;
  
  new Map<S, LASClassDef> classDefs;
  
  // record which vars are known at current parse location
  event knownVarsSnapshot(Map<S, LASValueDescriptor> knownVars);
  
  replace print with if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print.
  replace printVars with if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars.
  
  // object can be a class
  srecord MethodOnObject(O object, S method) {}
  
  srecord EvaluableWrapper(Evaluable expr) {}

  class BuildingScript {
    int id = ++idCounter;
    settable bool returnable;
    settable bool isLoopBody;
    BuildingScript returnableParent, loopParent;
    settable LASScope scope;
    
    new Script script;
    new L<Evaluable> steps;
    Map<S, FunctionDef> functionDefs = new Map;
    
    *(bool *returnable) { this(); }
    *(bool *returnable, bool *isLoopBody) { this(); }
    *() { scope = currentScope(); }
    
    void add(Evaluable step) { if (step != null) steps.add(step); }

    Evaluable get() {
      script.scope = scope;
      
      // 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
      
      if (nempty(functionDefs)) script.functionDefs = functionDefs;
      script.steps = toTypedArray(Evaluable.class, steps);
      ret script;
    }
    
    S toStringLong() { ret pnlToLines(steps); }
    toString {
      ret formatRecordVars BuildingScript(+id, +returnable , +returnableParent, +script);
    }
  } // end of BuildingScript
  
  Script parse(S text) {
    setText(text);
    init();
    ret parse();
  }
  
  Script parse() {
    Script script = parseReturnableScript();
    for (varAccess : varAccessesToFix)
      varAccess.resolve();
    if (optimize) script = script.optimizeScript();
    ret script;
  }
  
  Script parseReturnableScript() {
    ret (Script) parseScript(new BuildingScript().returnable(true));
  }
  
  // if returnable is true, it's always a Script
  Evaluable parseScript(BuildingScript script) {
    ret linkToSrc(-> {
      script.returnableParent = currentReturnableScript;
      script.loopParent = currentLoop;
      if (script.returnable)
        currentReturnableScript = script;
      if (script.isLoopBody)
        currentLoop = script;
      ret parseBuildingScript(script);
    });
  }
    
  Evaluable parseBuildingScript(BuildingScript script) {
    try {
      parseScript_2(script);
      var builtScript = script!;
      currentReturnableScript = script.returnableParent;
      currentLoop = script.loopParent;
      ret builtScript;
    } catch e {
      print("Parsed so far:\n" + script);
      
      throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn(-1))));
    }
  }
  
  void parseScript_2(BuildingScript script) {
    temp tempRestoreMap(knownVars);
    
    new AssureAdvance assure;
    while (assure!) {
      // for auto-completer
      knownVarsSnapshot(knownVars);
      
      print("parseScript_2: Next token is " + quote(token()));
      
      if (is(";")) continue with next();
      if (isOneOf("}", ")")) break;
      
      Evaluable instruction = linkToSrc(-> parseInstruction(script));
      if (instruction != null)
        script.add(instruction);
    }
    
    print("parseScript_2 done");
      
    knownVarsSnapshot(knownVars);
  }
  
  Evaluable parseInstruction(BuildingScript script) {
    if (is("def")) {
      parseFunctionDefinition(currentReturnableScript.functionDefs);
      null;
    }
      
    if (is("param")) {
      consume();
      S var = assertIdentifier(tpp());
      new LASValueDescriptor valueDescriptor;
      if (is(":")) {
        var type = parseColonType();
        valueDescriptor = LASValueDescriptor.nonExactCanBeNull(type);
      }
      knownVars.put(var, valueDescriptor);
      null;
    }
    
    if (is("throw")) {
      consume();
      ret new Toolbox.Throw(parseExpr());
    }
    
    if (is("try")) {
      consume();
      Evaluable body = parseCurlyBlock(new BuildingScript);
      consume("catch");
      S var = consumeIdentifier();
      temp tempAddKnownVars(var);
      Evaluable catchBlock = parseCurlyBlock(new BuildingScript);
      ret new Toolbox.TryCatch(body, var, catchBlock);
    }
      
    if (isOneOf("return", "ret")) {
      consume();
      Evaluable expr;
      if (atCmdEnd())
        expr = const(null);
      else
        expr = parseAssignmentOrExpr();
      ret new Toolbox.ReturnFromScript(currentReturnableScript.script, expr);
    }
    
    if (is("continue")) {
      consume();
      assertCmdEnd();
      if (currentLoop == null)
        fail("continue outside of loop");
      ret new Toolbox.Continue(currentLoop.script);
    }
    
    if (is("temp")) {
      consume();
      Evaluable tempExpr = parseExpr();
      print(+tempExpr);
      Evaluable body = parseScript(new BuildingScript);
      print(+body);
      ret new Toolbox.TempBlock(tempExpr, body);
    }
    
    if (is("class"))
      ret new Toolbox.ClassDef(new ResolvableLASClass(lasClassLoader, parseClassDef()));
    
    ret parseAssignmentOrExpr();
  }
  
  Evaluable parseAssignmentOrExpr() {
    try object parseAssignmentOpt();
    ret parseExpr();
  }
  
  Evaluable parseAssignmentOpt() {
    S t = token();
    if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) {
      print("Found assignment");
      next(3);
      Evaluable rhs = parseExpr();
      assertNotNull("Expression expected", rhs);
      bool newVar = !knownVars.containsKey(t);
      printVars(+newVar, +t, +knownVars);
      knownVars.put(t, new LASValueDescriptor);
      ret newVar
        ? new Toolbox.VarDeclaration(t, null, rhs)
        : new Toolbox.Assignment(t, rhs);
    }
    null;
  }

  Evaluable parseOptionalInnerExpression() {
    printVars parseOptionalInnerExpression(+token());
    if (atCmdEnd() || isOneOf("{", ",")) null;
    ret parseInnerExpr();
  }
  
  Evaluable const(O o) { ret new Toolbox.Const(o); }
  
  Evaluable parseInnerExpr() { ret parseExpr(true); }
  
  scaffolded Evaluable parseExpr(bool inner default false) {
    Evaluable e = linkToSrc(-> inner ? parseExpr_impl(true) : parseExprPlusOptionalComma());
    print("parseExpr done:\n" + Toolbox.indentedScriptStruct(e));
    ret e;
  }
  
  Evaluable parseExprPlusOptionalComma() {
    Evaluable expr = parseExpr_impl(false);
    
    //printVars("parseExprPlusOptionalComma", t := t());
    while (consumeOpt(",")) {
      expr = parseCall_noCmdEndCheck(expr);
      //printVars("parseExprPlusOptionalComma", t2 := t());
    }
    ret expr;
  }
  
  Evaluable parseExpr_impl(bool inner) {
    if (atEnd()) null;
    
    S t = token();
    printVars parseExpr(token := t);
    if (is(";")) null; // empty command
  
    if (is("{"))
      ret parseCurlyBlock(new BuildingScript);
    
    // int or double literal
    if (is("-") && empty(nextSpace()) && startsWithDigit(token(1))
      || startsWithDigit(t)) {
      var e = parseNumberLiteral();
      ret inner ? e : parseCall(e);
    }

    if (isQuoted(t)) {
      consume();
      var e = const(unquote(t));
      ret inner ? e : parseCall(e);
    }
    
    if (startsWith(t, '\'')) {
      consume();
      var e = const(first(unquote(t)));
      ret inner ? e : parseCall(e);
    }
      
    if (isIdentifier(t)) {
      if (is("while"))
        ret parseWhileLoop();
  
      if (is("for"))
        ret parseForEach();
  
      if (is("if"))
        ret parseIfStatement();
        
      if (is("repeat"))
        ret parseRepeatStatement();
        
      if (is("outer")) {
        consume();
        var a = parseAssignmentOpt();
        if (!a instanceof Toolbox.Assignment)
          fail("Assignment expected");
        cast a to Toolbox.Assignment;
        ret new Toolbox.AssignmentToOuterVar(a.var, a.expression);
      }
        
      consume();
      print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner);
      ret parseExprStartingWithIdentifier(t, inner);
    }
    
    // nested expression
    
    if (eq(t, "(")) {
      bool inParensOld = inParens;
      inParens = true;
      consume();
      //print("Consumed opening parens (level " + parenLevels + ")");
      var e = parseExpr();
      //print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")");
      consume(")");
      inParens = inParensOld;
      
      ret inner ? e : parseCall(e);
    }
    
    if (isOneOf("&", "|") && empty(nextSpace()) && is(1, token())) {
      ret parseBinaryOperator();
    }
      
    fail("Identifier, literal, operator or opening parentheses expected (got: " + quote(t));
  }
  
  Evaluable parseNumberLiteral() {
    S t = consumeMultiTokenLiteral();
      
    if (swic(t, "0x")) ret const(parseHexInt(dropFirst(t, 2)));
    if (swic(t, "0b")) ret const(intFromBinary(dropFirst(t, 2)));
    
    if (isInteger(t)) ret const(parseInt(t));
    if (endsWith(t, "f")) ret const(parseFloat(t));
    
    if (endsWith(t, "L")) ret const(parseLong(t));
    
    ret const(parseDouble(t));
  }
  
  Evaluable parseBinaryOperator() {
    bool and = is("&");
    next(2);
    Evaluable a = parseInnerExpr();
    Evaluable b = parseInnerExpr();
    ret and
      ? new Toolbox.BoolAnd(a, b)
      : new Toolbox.BoolOr(a, b);
  }
  
  bool qualifiedNameContinues() {
    ret empty(prevSpace()) && eq(token(), ".") && empty(nextSpace())
      && isIdentifier(token(1));
  }
  
  // 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());
      
      parseTypeArguments(null);
      
      // script-defined class?
      
      LASClassDef cd = classDefs.get(className);
      if (cd != null)
        ret new Toolbox.NewObject_LASClass(new ResolvableLASClass(lasClassLoader, cd));
        
      // class in variable?
      
      please include function newObject.
      
      var type = knownVars.get(className);
      if (type != null)
        ret new Toolbox.NewObject_UnknownClass(new Toolbox.GetVar(className), parseArguments());

      // external class
        
      O o = findExternalObject(className);
      if (o cast Class) {
        Class c = o;
        
        if (c == L.class) c = ArrayList.class;
        else if (c == Map.class) c = HashMap.class;
        else if (c == Set.class) c = HashSet.class;
        
        ret new Toolbox.NewObject(c, parseArguments());
      }
      throw new ClassNotFound(className);
    }
    
    // First search variable in local scope if it's a fixed vars scope
    
    if (scope != null && scope.useFixedVars) {
      var type = scope.declaredVars.get(t);
      if (type != null) {
        var e = new Toolbox.GetFixedVar(scope, t);
        e.scope(scope);
        e.returnType(type);
        varAccessesToFix.add(e);
        ret inner ? e : parseCall(e);
      }
    }
    
    // Now search in outer scopes (should rework this)

    var type = knownVars.get(t);
    if (type != null) {
      var e = new Toolbox.GetVar(t).returnType(type);
      print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression"));
      ret inner ? e : parseCall(e);
    }
    
    if (!inner) {
      var fdef = lookupFunction(t);
      if (fdef != null)
        ret new Toolbox.CallFunction(fdef, parseArguments());
    }
    
    if (eq(t, "_context"))
      ret new Toolbox.GetVarContext;
      
    O o = findExternalObject(t);
    
    if (o == null) {
      //unconsume();
      throw new UnknownObject(t);
    } else if (o cast EvaluableWrapper) {
      ret inner ? o.expr : parseCall(o.expr);
    } else if (inner)
      ret const(o);
    else if (o cast Class) {
      ret parseExprStartingWithClass(o);
    } 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));
  }
  
  Evaluable parseExprStartingWithClass(Class c) {
    if (atCmdEnd())
      ret const(c);
      
    // new without new
    if (is("("))
      ret new Toolbox.NewObject(c, parseArguments());
      
    /* old object creation syntax (e.g. Pair new a b)
    if (is("new")) {
      next();
      ret new Toolbox.NewObject(c, parseArguments());
    } else*/
    
    // check if it's a lambda definition first (with ->)
    try object parseLambdaOpt(c);
    
    if (isIdentifier()) {
      S name = tpp();
      // We have now parsed a class (c) plus an as yet unknown identifier (name) right next to it
      
      // look for a static method call
      if (hasStaticMethodNamed(c, name))
        ret new Toolbox.CallMethod(const(c), name, parseArguments());

      // if the class is an interface, it's a lambda method reference or a lambda
      if (isInterface(c))
        ret parseLambdaMethodRef(c, name);
      
      // look for field second
      
      var field = getField(c, name);
      if (field != null) {
        assertCmdEnd();
        
        if (!isStaticField(field))
          fail(field + " is not a static field");
          
        ret new Toolbox.GetStaticField(field);
      }
      
      fail(name + " not found in " + c + " (looked for method or field)");
    } else
      fail("Method name expected: " + token());
  }
  
  // parse lambda, e.g. IF1 a -> plus a a
  // returns null if it's not a lambda
  Evaluable parseLambdaOpt(Class c) {
    int nArgs = 0;
    while (isIdentifier(token(nArgs))) nArgs++;
    if (!(is(nArgs, "-") && is(nArgs+1, ">"))) null; // not a lambda
    
    // read parameter names and add to known variables
    S[] argNames = consumeArray(nArgs);
    temp tempAddKnownVars(argNames);
    
    skip(2); // skip the arrow
    
    Evaluable body;
    
    // if curly brackets: parse lambda body as returnable script
    // TODO: avoid script overhead if there are no return calls inside
    if (is("{"))
      body = parseReturnableScript();
    else
      // no curly brackets => just an expression
      body = parseExpr();
    
    // return lambda
    var lambda = new Toolbox.LambdaDef(c, argNames, body);
    print("parseLambdaOpt done:\n" + Toolbox.indentedScriptStruct(lambda));
    ret lambda;
  }
  
  // method ref, e.g. "IF1 l" meaning "l1 l"
  // also, constructor ref, e.g. "IF1 Pair" meaning (x -> new Pair(x))
  //
  // c must be a single method interface
  Evaluable parseLambdaMethodRef(Class c, S name) {
    var fdef = lookupFunction(name);
    if (fdef != null) {
      Evaluable[] curriedArguments = parseArguments();
      ret new Toolbox.CurriedScriptFunctionLambda(c, fdef, curriedArguments);
    }

    O function = findExternalObject(name);
    
    if (function == null) 
      throw new UnknownObject(name);
      
    if (function cast MethodOnObject) {
      O target = function.object;
      S targetMethod = function.method;
        
      Evaluable[] curriedArguments = parseArguments();
      ret new Toolbox.CurriedMethodLambda(c, target, targetMethod, curriedArguments);
    } else if (function cast Class) {
      Class c2 = function;
      
      // No currying here yet
      assertCmdEnd();
      
      var ctors = constructorsWithNumberOfArguments(c2, 1);
      if (empty(ctors))
        fail("No single argument constructor found in " + c2);
        
      ret new Toolbox.CurriedConstructorLambda(c, toArray Constructor(ctors), null);
    } else
      fail(function + " is not an instantiable class or callable method");

  }
  
  FunctionDef lookupFunction(S name) {
    var script = currentReturnableScript;
    while (script != null) {
      var f = script.functionDefs.get(name);
      printVars("lookupFunction", +script, +name, +f);
      if (f != null) ret f;
      script = script.returnableParent;
    }
    null;
  }
  
  Evaluable[] parseArguments() {
    ret toArrayOrNull(Evaluable.class, parseArgumentsAsList());
  }
  
  L<Evaluable> parseArgumentsAsList() {
    //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() || is(","));
  }
  
  bool atCmdEnd() {
    ret
      !inParens && atEndOrLineBreak()
      || closerTokens.contains(token());
  }
  
  void assertCmdEnd() {
    if (!atCmdEnd())
      fail("Expected end of command, token is: " + quote(token()));
  }
  
  // After we just parsed an expression, see if there is something
  // to the right of it.
  // Currently that can be
  // -a method call
  // -a field access
  // -a field assignment (e.g.: myPair a <- 1)
  Evaluable parseCall(Evaluable target) {
    // normally, do a check for end of line
    if (atCmdEnd()) ret target;
    
    ret parseCall_noCmdEndCheck(target);
  }
  
  Evaluable parseCall_noCmdEndCheck(Evaluable target) {
    // in every case, we expect an identifier first
    
    if (!isIdentifier()) ret target;
    
    var start = ptr();
    S name = tpp(); // the identifier
    
    // check for field assignment
    if (eq(token(), "<") && eq(token(1), "-")) {
      next(2);
      Evaluable rhs = parseExpr();
      ret new Toolbox.SetField(target, name, rhs);
    }
    
    var args = parseArguments();
    
    if (nempty(args))
      ret new Toolbox.CallMethod(target, name, args);
    else
      ret linkToSrc(start, new Toolbox.CallMethodOrGetField(target, name));
  }
  
  // add link to source code to parsed object if it supports it
  // and doesn't have a source code link already
  <A> A linkToSrc(TokPtr start, A a) {
    if (a cast IHasTokenRangeWithSrc /*&& a.tokenRangeWithSrc() == null*/)
      a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr().plus(-1)));
    ret a;
  }
  
  // lambda version of linkToSrc
  <A> A linkToSrc(IF0<A> a) {
    var start = ptr();
    ret linkToSrc(start, a!);
  }
  
  // can return MethodOnObject
  // Note: Assumes we just parsed the name
  // TODO: rename this part of the method
  swappable O findExternalObject(S name) {
    //try object findClassThroughDefaultClassFinder(name);
    //try object findClassInStandardImports(name);
    
    try object parsePrimitiveType(name);
      
    if (qualifiedNameContinues()) {
      int idx = idx()-2;
      do 
        next(2);
      while (qualifiedNameContinues());

      S fqn = joinSubList(tok, idx, idx()-1);
      ret classForName(fqn);
    }
  
    S fullName = globalClassNames().get(name);
    if (fullName != null)
      ret classForName(fullName);
      
    fOr (container : functionContainers) {
      if (hasMethodNamed(container, name))
        ret new MethodOnObject(container, name);
      
      var field = getField(container, name);
      if (field != null && isStaticField(field))
        ret new EvaluableWrapper(new Toolbox.GetStaticField(field));
    }
    
    null;
  }
  
  selfType allowTheWorld() { ret allowTheWorld(mc()); }
  
  // first containers within argument list have priority
  // last calls to allowTheWorld/first arguments passed have priority
  selfType allowTheWorld(O... functionContainers) {
    fOr (O o : reversed(functionContainers))
      if (!contains(this.functionContainers, o)) {
        this.functionContainers.add(0, o);
        globalClassNames_cache = null; // recalculate
      }
    this;
  }
  
  void printFunctionDefs(Script script) {
    print(values(script.functionDefs));
  }
  
  AutoCloseable tempAddKnownVars(S... vars) {
    ret tempAddKnownVars(asList(vars));
  }
  
  AutoCloseable tempAddKnownVars(Iterable<S> vars) {
    var newVars = mapWithSingleValue(vars, new LASValueDescriptor);
    if (scope != null)
      for (name, type : newVars)
        scope.addDeclaredVar(name, type);
    ret tempMapPutAll(knownVars, newVars);
  }
  
  Toolbox.FunctionDef parseFunctionDefinition(Map<S, FunctionDef> functionDefsToAddTo) {
    consume("def");
    S functionName = assertIdentifier(tpp());
    print("parseFunctionDefinition " + functionName);
    new LS args;
    while (isIdentifier())
      args.add(tpp());
    var scope = newScope();
    scope.useFixedVars(useFixedVarContexts);
    temp tempScope(scope);
    temp tempAddKnownVars(args);
    print("Parsing function body");
    var functionBody = parseReturnableCurlyBlock();
    
    print("Defined function " + functionName + ", adding to " + functionDefsToAddTo);
    
    var fd = new Toolbox.FunctionDef(functionName, args, functionBody);
    fd.scope(scope);
    functionDefsToAddTo?.put(functionName, fd);
    ret fd;
  }
  
  LASScope newScope() {
    ret new LASScope(scope);
  }
  
  LASScope currentScope() { ret scope; }
  
  // enter the scope temporarily
  AutoCloseable tempScope(LASScope scope) {
    var oldScope = currentScope();
    this.scope = scope;
    ret -> {
      scope.resolve();
      this.scope = oldScope;
    };
  }
  
  Script parseReturnableCurlyBlock() {
    ret (Script) parseCurlyBlock(new BuildingScript().returnable(true));
  }
  
  Evaluable parseCurlyBlock(BuildingScript script) {
    //print(+knownVars);
    consume("{");
    bool inParensOld = inParens;
    inParens = false;
    var body = parseScript(script);
    consume("}");
    inParens = inParensOld;
    ret body;
  }
  
  Evaluable parseWhileLoop() {
    consume("while");
    var condition = parseExpr();
    var body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
    ret new Toolbox.While(condition, body);
  }
  
  Evaluable parseForEach() {
    ret new ParseForEach()!;
  }
  
  class ParseForEach {
    Evaluable collection, body;
    IF0<Evaluable> finish;
    new Set<S> vars;
    
    S addVar(S var) { ret addAndReturn(vars, var); }
    S consumeVar() { ret addVar(consumeIdentifier()); }
    
    void parseBody {
      temp tempAddKnownVars(vars);
      body = parseCurlyBlock(new BuildingScript().isLoopBody(true));
    }
    
    Evaluable get() {
      consume("for");
      
      // for i to n { ... }
      if (is(1, "to")) {
        S var = consumeVar();
        consume("to");
        Evaluable endValue = parseExpr();
        parseBody();
        ret new Toolbox.ForIntTo(endValue, var, body);
      }
    
      int iIn = relativeIndexOf("in");
      if (iIn < 0) fail("for without in");
      print(+iIn);
      
      if (iIn == 1) { // just a variable
        S var = consumeVar();
        finish = -> new Toolbox.ForEach(collection, var, body);
      } else if (iIn == 2) { // for iterator (mapI)
        if (consumeOpt("iterator")) {
          S var = consumeVar();
          finish = -> new Toolbox.ForIterator(collection, var, body);
        } else if (consumeOpt("nested")) {
          S var = consumeVar();
          finish = -> new Toolbox.ForNested(collection, var, body);
        } else
          fail("Unknown pattern for 'for' loop");
      } else if (iIn == 3) {
        if (isOneOf("pair", "Pair")) {
          // "pair a b" or "Pair a b"
          consume();
          S varA = consumeVar();
          S varB = consumeVar();
          printVars(+varA, +varB);
          
          finish = -> new Toolbox.ForPairs(collection, body, varA, varB);
        } else {
          // "a, b"
          S varA = consumeVar();
          consume(",");
          S varB = consumeVar();

          finish = -> new Toolbox.ForKeyValue(collection, body, varA, varB);
        }
      } else if (iIn == 4) {
        consume("index");
        S varIndex = consumeVar();
        consume(",");
        S varElement = consumeVar();
        
        finish = -> new Toolbox.ForIndex(collection, body, varIndex, varElement);
      } else
        fail("Unknown pattern for 'for' loop");
      
      consume("in");
      collection = parseExpr();
      print(+collection);
      parseBody();
      ret finish!;
    }
  }
  
  Evaluable parseIfStatement() {
    consume("if");
    Evaluable condition, body, elseBranch = null;
    {
      temp tempAdd(closerTokens, "then");
      condition = parseExpr();
    }
    
    if (consumeOpt("then")) {
      temp tempAdd(closerTokens, "else");
      body = parseExpr();
      if (consumeOpt("else"))
        elseBranch = parseExpr();
    } else {
      body = parseCurlyBlock(new BuildingScript);
      if (consumeOpt("else")) {
        if (is("if"))
          elseBranch = parseIfStatement();
        else
          elseBranch = parseCurlyBlock(new BuildingScript);
      }
    }
    
    ret new Toolbox.IfThen(condition, body, elseBranch);
  }
  
  Evaluable parseRepeatStatement() {
    consume("repeat");
    var n = parseExpr();
    var body = parseCurlyBlock(new BuildingScript);
    ret new Toolbox.RepeatN(n, body);
  }
  
  // declare an external variable with optional type info
  void addVar(S var, LASValueDescriptor type default new) { knownVars.put(var, type); }
  void addVar(S var, Class type, bool canBeNull) { addVar(var, new LASValueDescriptor.NonExact(type, canBeNull)); }
  
  // short name to full name
  // TODO: sort by priority if classes appear twice
  simplyCached SS globalClassNames() {
    var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
    
    new SS out;
    
    // add function containers themselves (if they're classes)
    for (fc : functionContainers)
      if (fc cast Class) {
        if (isAnonymousClass(fc)) continue;
        out.put(shortClassName(fc), className(fc));
      }
    
    // add inner classes of function containers
    var classContainers = classContainerPrefixes();
    TreeSet<S> classContainerSet = asTreeSet(classContainers);

    out.put("List", "java.util.List");
    
    for (className : classNameResolver().allFullyQualifiedClassNames()) {
      if (isAnonymousClassName(className)) continue;
      if (!contains(className, '$')) {
        S pkg = longestPrefixInTreeSet(className, packages);
        if (pkg != null) {
          S shortName = dropPrefix(pkg, className);
          if (!shortName.contains(".") && !out.containsKey(shortName))
            out.put(shortName, className);
        }
      }
        
      S container = longestPrefixInTreeSet(className, classContainerSet);
      if (container != null) {
        S shortName = dropPrefix(container, className);
        S existing = out.get(shortName);
        if (existing != null) {
          int priority = indexOf(classContainers, container);
          S oldContainer = longestPrefixInTreeSet(existing, classContainerSet);
          int oldPriority = indexOf(classContainers, oldContainer);
          printVars(+className, +shortName, +container, +priority, +existing, +oldPriority);
          if (priority > oldPriority)
            continue;
        }
        out.put(shortName, className);
      }
    }
        
    for (key, val : javaxClassShortcuts()) {
      S fullName = out.get(val);
      if (fullName != null)
        out.put(key, fullName);
    }

    ret out;
  }
  
  swappable Cl<S> importedPackages() {
    ret itemPlus("java.lang", standardImports_fullyImportedPackages());
  }
  
  void addClassAlias(S alias, S longName) {
    S fullName = globalClassNames().get(longName);
    if (fullName != null)
      globalClassNames().put(alias, fullName);
  }
  
  LS classContainerPrefixes() {
    ret map(functionContainers, fc -> className(fc) + "$");
  }
  
  // typed exceptions (unusual, I know!)
  
  srecord noeq UnknownObject(S name) > RuntimeException {
    public S getMessage() { ret "Unknown object: " + name; }
  }
  
  sclass ClassNotFound > UnknownObject {
    *(S className) { super(className); }
    
    public S getMessage() { ret "Class not found: " + name; }
  }
  
  LASClassDef parseClassDef() {
    consume("class");
    new LASClassDef classDef;
    classDef.verbose(scaffoldingEnabled());
    if (nempty(classDefPrefix))
      classDef.classDefPrefix(classDefPrefix);
    S name = consumeIdentifier();
    classDef.userGivenName(name);
    consume("{");
    
    // parse class body
    
    while (!is("}")) {
      if (is(";")) continue with next();
      
      // parse method declaration
      
      if (is("def")) {
        temp tempAddKnownVars("this");
        
        Toolbox.FunctionDef fd = parseFunctionDefinition(null);
        printVars(knownVarsAfterFunctionDef := knownVars);
        classDef.methods.add(fd);
        continue;
      }
        
      // parse field declaration
      
      new LASClassDef.FieldDef fd;
      fd.name(consumeIdentifier());
      var type = parseColonType();
      fd.type(type);
      classDef.fields.add(fd);
    }
    
    consume("}");
    classDefs.put(name, classDef);
    ret classDef;
  }
  
  Type parseColonType() {
    consume(":");
    S typeName = consumeIdentifier();
    O type = findExternalObject(typeName);
    if (!type instanceof Class)
      fail("Class not found: " + typeName);
    cast type to Type;
    ret parseTypeArguments(type);
  }
  
  Type parseTypeArguments(Type type) {
    // type arguments
    if (is("<") && isIdentifier(token(1))) {
      next();
      O arg = findExternalObject(consume());
      consume(">"); // only one parameter supported so far
      if (!arg instanceof Class)
        fail("Class not found: " + arg);
      if (type != null)
        type = new ParameterizedTypeImpl(null, type, (Class) arg);
    }
    ret type;
  }
  
  swappable Class classForName(S name) ctex {
    ret Class.forName(name);
  }
  
  // also copies the globalClassNames_cache which otherwise takes
  // a few ms to calculate
  void copyFunctionContainersFrom(GazelleV_LeftArrowScriptParser parser) {
    functionContainers = cloneList(parser.functionContainers);
    globalClassNames_cache = parser.globalClassNames();
  }
  
  ClassNameResolver classNameResolver() {
    if (classNameResolver == null)
      classNameResolver = new ClassNameResolver().byteCodePath(assertNotNull(getBytecodePathForClass(this))).init();
    ret classNameResolver;
  }
  
  selfType classNameResolver(ClassNameResolver classNameResolver) {
    this.classNameResolver = classNameResolver;
    this;
  }
} // end of GazelleV_LeftArrowScriptParser

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1033976
Snippet name: GazelleV_LeftArrowScriptParser
Eternal ID of this version: #1033976/398
Text MD5: 9a17df42cea2e7928bf7c0fd2d3673c4
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-05-14 02:57:07
Source code size: 32821 bytes / 1096 lines
Pitched / IR pitched: No / No
Views / Downloads: 823 / 3174
Version history: 397 change(s)
Referenced in: [show references]