All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.aviator.parser.ExpressionParser Maven / Gradle / Ivy

/**
 * Copyright (C) 2010 dennis zhuang ([email protected])
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/
package com.googlecode.aviator.parser;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Feature;
import com.googlecode.aviator.Options;
import com.googlecode.aviator.code.CodeGenerator;
import com.googlecode.aviator.exception.ExpressionRuntimeException;
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
import com.googlecode.aviator.exception.UnsupportedFeatureException;
import com.googlecode.aviator.lexer.ExpressionLexer;
import com.googlecode.aviator.lexer.SymbolTable;
import com.googlecode.aviator.lexer.token.CharToken;
import com.googlecode.aviator.lexer.token.DelegateToken;
import com.googlecode.aviator.lexer.token.DelegateToken.DelegateTokenType;
import com.googlecode.aviator.lexer.token.NumberToken;
import com.googlecode.aviator.lexer.token.PatternToken;
import com.googlecode.aviator.lexer.token.StringToken;
import com.googlecode.aviator.lexer.token.Token;
import com.googlecode.aviator.lexer.token.Token.TokenType;
import com.googlecode.aviator.lexer.token.Variable;
import com.googlecode.aviator.runtime.FunctionArgument;
import com.googlecode.aviator.runtime.FunctionParam;
import com.googlecode.aviator.utils.Constants;


/**
 * Syntex parser for expression
 *
 * @author dennis
 *
 */
public class ExpressionParser implements Parser {
  private final ExpressionLexer lexer;

  /*
   * Lookhead token
   */
  private Token lookhead;

  private final ArrayDeque> prevTokens = new ArrayDeque<>();

  private CodeGenerator codeGenerator;

  private ScopeInfo scope;

  private int parsedTokens;


  private boolean inPattern = false;

  private final AviatorEvaluatorInstance instance;

  private final boolean captureFuncArgs;

  private final Set featureSet;



  public Token getPrevToken() {
    return this.prevTokens.peek();
  }



  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.aviator.parser.Parser#getCodeGenerator()
   */
  @Override
  public CodeGenerator getCodeGenerator() {
    return this.codeGenerator;
  }



  public Token getLookhead() {
    return this.lookhead;
  }



  @Override
  public SymbolTable getSymbolTable() {
    return this.lexer.getSymbolTable();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.aviator.parser.Parser#setCodeGenerator(com.googlecode.aviator.code.
   * CodeGenerator)
   */
  @Override
  public void setCodeGenerator(final CodeGenerator codeGenerator) {
    this.codeGenerator = codeGenerator;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.aviator.parser.Parser#enterScope()
   */
  @Override
  public ScopeInfo enterScope(final boolean inNewScope) {
    ScopeInfo current = this.scope;
    this.scope = new ScopeInfo(0, 0, 0, 0, inNewScope, new ArrayDeque());
    return current;
  }

  /**
   * (non-Javadoc)
   *
   * @see com.googlecode.aviator.parser.Parser#restoreScope(com.googlecode.aviator.parser.ExpressionParser.DepthInfo)
   */
  @Override
  public void restoreScope(final ScopeInfo info) {
    this.scope = info;
  }

  public ExpressionParser(final AviatorEvaluatorInstance instance, final ExpressionLexer lexer,
      final CodeGenerator codeGenerator) {
    super();
    this.scope = new ScopeInfo(0, 0, 0, 0, false, new ArrayDeque());
    this.instance = instance;
    this.captureFuncArgs = instance.getOptionValue(Options.CAPTURE_FUNCTION_ARGS).bool;
    this.lexer = lexer;
    this.lookhead = this.lexer.scan();
    if (this.lookhead != null) {
      this.parsedTokens++;
    }
    this.featureSet = this.instance.getOptionValue(Options.FEATURE_SET).featureSet;
    if (this.lookhead == null) {
      reportSyntaxError("blank script");
    }
    setCodeGenerator(codeGenerator);
    getCodeGeneratorWithTimes().setParser(this);
  }

  private void ensureFeatureEnabled(final Feature feature) {
    if (!this.featureSet.contains(feature)) {
      throw new UnsupportedFeatureException(feature);
    }
  }

  /**
   * Call __reducer_return(result)
   */
  public void returnStatement() {
    move(true);
    CodeGenerator cg = getCodeGeneratorWithTimes();
    cg.onTernaryEnd(this.lookhead);
    if (expectChar(';')) {
      // 'return;' => 'return nil;'
      if (this.scope.newLexicalScope) {
        cg.onMethodName(Constants.ReducerReturnFn);
        cg.onConstant(Variable.NIL);
        cg.onMethodParameter(this.lookhead);
        cg.onMethodInvoke(this.lookhead);
      } else {
        cg.onConstant(Variable.NIL);
      }
      move(true);
      return;
    } else {
      if (this.scope.newLexicalScope) {
        cg.onMethodName(Constants.ReducerReturnFn);
        if (!ternary()) {
          reportSyntaxError("invalid value for return, missing ';'?");
        }
        cg.onMethodParameter(this.lookhead);
        cg.onMethodInvoke(this.lookhead);
      } else {
        if (!ternary()) {
          reportSyntaxError("invalid value for return, missing ';'?");
        }
      }
    }

    if (!expectChar(';')) {
      reportSyntaxError("missing ';' for return statement");
    }
    move(true);
  }

  public boolean ternary() {
    int gcTimes = this.getCGTimes;

    if (this.lookhead == Variable.NEW) {
      newStatement();
      return true;
    }

    join();
    if (this.lookhead == null || expectChar(':') || expectChar(',')) {
      return gcTimes < this.getCGTimes;
    }
    Token opToken = this.lookhead;
    if (expectChar('?')) {
      move(true);
      CodeGenerator cg = getCodeGeneratorWithTimes();
      cg.onTernaryBoolean(opToken);
      if (!ternary()) {
        reportSyntaxError("invalid token for ternary operator");
      }
      if (expectChar(':')) {
        move(true);
        cg.onTernaryLeft(this.lookhead);
        if (!ternary()) {
          reportSyntaxError("invalid token for ternary operator");
        }
        cg.onTernaryRight(this.lookhead);
      } else {
        reportSyntaxError("expect ':'");
      }
    }
    return gcTimes < this.getCGTimes;
  }


  public void join() {
    and();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('|')) {
        getCodeGeneratorWithTimes().onJoinLeft(opToken);
        move(true);
        if (expectChar('|')) {
          move(true);
          and();
          getCodeGeneratorWithTimes().onJoinRight(opToken);
        } else {
          reportSyntaxError("expect '|'");
        }
      } else {
        if (this.lookhead == null) {
          break;
        } else {
          break;
        }
      }

    }
  }


  private boolean expectChar(final char ch) {
    if (this.lookhead == null) {
      return false;
    }
    return this.lookhead.getType() == TokenType.Char && ((CharToken) this.lookhead).getCh() == ch;
  }


  public void bitOr() {
    xor();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('|')) {
        move(true);
        if (expectChar('|')) {
          back();
          break;
        }
        xor();
        getCodeGeneratorWithTimes().onBitOr(opToken);
      } else {
        break;
      }
    }
  }


  public void xor() {
    bitAnd();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('^')) {
        move(true);
        bitAnd();
        getCodeGeneratorWithTimes().onBitXor(opToken);
      } else {
        break;
      }
    }
  }


  public void bitAnd() {
    equality();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('&')) {
        move(true);
        if (expectChar('&')) {
          back();
          break;
        }
        equality();
        getCodeGeneratorWithTimes().onBitAnd(opToken);
      } else {
        break;
      }
    }
  }


  public void and() {
    bitOr();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('&')) {
        CodeGenerator cg = getCodeGeneratorWithTimes();
        cg.onAndLeft(opToken);
        move(true);
        if (expectChar('&')) {
          move(true);
          bitOr();
          cg.onAndRight(opToken);
        } else {
          reportSyntaxError("expect '&'");
        }
      } else {
        break;
      }
    }

  }


  public void equality() {
    rel();
    while (true) {
      Token opToken = this.lookhead;
      Token prevToken = getPrevToken();
      if (expectChar('=')) {
        move(true);
        if (expectChar('=')) {
          move(true);
          rel();
          getCodeGeneratorWithTimes().onEq(opToken);
        } else if (expectChar('~')) {
          // It is a regular expression
          move(true);
          rel();
          getCodeGeneratorWithTimes().onMatch(opToken);
        } else {
          // this.back();
          // assignment

          boolean isVar = false;
          if (prevToken.getType() == TokenType.Variable) {
            isVar = true;
          } else if (prevToken.getType() == TokenType.Char
              && ((CharToken) prevToken).getCh() == ']') {
            int depth = 1;
            boolean beginSearch = false;
            boolean found = false;
            for (Token t : this.prevTokens) {
              if (!beginSearch && t == prevToken) {
                beginSearch = true;
                continue;
              }

              if (beginSearch && t.getType() == TokenType.Char) {
                CharToken chToken = (CharToken) t;
                switch (chToken.getCh()) {
                  case ']':
                    depth++;
                    break;
                  case '[':
                    depth--;
                    break;
                }
                if (depth == 0) {
                  found = true;
                  continue;
                }

              }

              if (found) {
                if (t.getType() == TokenType.Variable) {
                  t.withMeta(Constants.TYPE_META, CompileTypes.Array);
                }
                break;
              }
            }

          }

          statement();

          // try to find var(prevToken) in right statement, it's not initialized if presents.
          if (isVar) {
            checkVarIsInit(prevToken);
          }

          ensureFeatureEnabled(Feature.Assignment);
          getCodeGeneratorWithTimes().onAssignment(opToken);
        }
      } else if (expectChar('!')) {
        move(true);
        if (expectChar('=')) {
          move(true);
          rel();
          getCodeGeneratorWithTimes().onNeq(opToken);
        } else {
          reportSyntaxError("expect '='");
        }
      } else {
        break;
      }
    }
  }



  private void checkVarIsInit(final Token prevToken) {
    boolean isInit = true;
    for (Token t : this.prevTokens) {
      if (t == prevToken) {
        break;
      }
      // It's in right statement, so it's not initialized.
      if (t.getType() == TokenType.Variable && t.getLexeme().equals(prevToken.getLexeme())) {
        isInit = false;
        break;
      }
    }
    prevToken.withMeta(Constants.INIT_META, isInit);
  }


  public void rel() {
    shift();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('<')) {
        move(true);
        if (expectChar('=')) {
          move(true);
          expr();
          getCodeGeneratorWithTimes().onLe(opToken);
        } else {
          expr();
          getCodeGeneratorWithTimes().onLt(opToken);
        }
      } else if (expectChar('>')) {
        move(true);
        if (expectChar('=')) {
          move(true);
          expr();
          getCodeGeneratorWithTimes().onGe(opToken);
        } else {
          expr();
          getCodeGeneratorWithTimes().onGt(opToken);
        }
      } else {
        break;
      }
    }
  }


  public void shift() {
    expr();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('<')) {
        move(true);
        if (expectChar('<')) {
          move(true);
          expr();
          getCodeGeneratorWithTimes().onShiftLeft(opToken);
        } else {
          back();
          break;
        }
      } else if (expectChar('>')) {
        move(true);
        if (expectChar('>')) {
          move(true);
          if (expectChar('>')) {
            move(true);
            expr();
            getCodeGeneratorWithTimes().onUnsignedShiftRight(opToken);
          } else {
            expr();
            getCodeGeneratorWithTimes().onShiftRight(opToken);
          }

        } else {
          back();
          break;
        }
      } else {
        break;
      }
    }
  }


  public void expr() {
    term();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('+')) {
        move(true);
        term();
        getCodeGeneratorWithTimes().onAdd(opToken);
      } else if (expectChar('-')) {
        move(true);
        term();
        getCodeGeneratorWithTimes().onSub(opToken);
      } else {
        break;
      }
    }
  }

  public void exponent() {
    factor();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('*')) {
        move(true);
        if (expectChar('*')) {
          move(true);
          unary();
          getCodeGeneratorWithTimes().onExponent(opToken);
        } else {
          back();
          break;
        }
      } else {
        break;
      }
    }
  }


  public void term() {
    unary();
    while (true) {
      Token opToken = this.lookhead;
      if (expectChar('*')) {
        move(true);
        unary();
        getCodeGeneratorWithTimes().onMult(opToken);
      } else if (expectChar('/')) {
        move(true);
        unary();
        getCodeGeneratorWithTimes().onDiv(opToken);
      } else if (expectChar('%')) {
        move(true);
        unary();
        getCodeGeneratorWithTimes().onMod(opToken);
      } else {
        break;
      }
    }
  }


  public void unary() {
    Token opToken = this.lookhead;
    if (expectChar('!')) {
      move(true);
      // check if it is a seq function call,"!" as variable
      if (expectChar(',') || expectChar(')')) {
        back();
        exponent();
      } else {
        unary();
        getCodeGeneratorWithTimes().onNot(opToken);
      }
    } else if (expectChar('-')) {
      move(true);
      // check if it is a seq function call,"!" as variable
      if (expectChar(',') || expectChar(')')) {
        back();
        exponent();
      } else {
        unary();
        getCodeGeneratorWithTimes().onNeg(opToken);
      }
    } else if (expectChar('~')) {
      move(true);
      // check if it is a seq function call,"~" as variable
      if (expectChar(',') || expectChar(')')) {
        back();
        exponent();
      } else {
        unary();
        getCodeGeneratorWithTimes().onBitNot(opToken);
      }
    } else {
      exponent();
    }
  }

  private int getLookheadStartIndex() {
    // We should calculate the lookhead token's start index, because the token may be reserved by
    // symbol table and it's start index is wrong.
    return this.lookhead != null ? (this.lexer.getCurrentIndex() - getLookheadLexemeLength()) : -1;
  }

  private int getLookheadLexemeLength() {
    int len = this.lookhead.getLexeme().length();
    if (this.lookhead.getType() == TokenType.String) {
      // Must include quote symbols.
      len += 2;
    }
    return len;
  }

  private String getParamExp(final int lastTokenIndex) {
    if (lastTokenIndex >= 0 && getLookheadStartIndex() >= 0) {
      return this.lexer.getScanString().substring(lastTokenIndex, getLookheadStartIndex());
    } else {
      return null;
    }
  }

  public static final CharToken LEFT_PAREN = new CharToken('(', 0, -1);
  public static final CharToken RIGHT_PAREN = new CharToken(')', 0, -1);


  public boolean isOPVariable(final Token token) {
    if (token.getType() != TokenType.Char) {
      return false;
    }
    CharToken charToken = (CharToken) token;

    move(true);
    if (expectChar(',') || expectChar(')')) {
      back();
      String lexeme = String.valueOf(charToken.getCh());
      if (lexeme.equals("-")) {
        lexeme = "-sub";
      }
      return this.instance.containsFunction(lexeme);
    } else {
      back();
      return false;
    }
  }

  public void factor() {
    if (factor0()) {
      methodInvokeOrArrayAccess();
    }
  }



  private boolean factor0() {
    if (this.lookhead == null) {
      reportSyntaxError("illegal token");
    }
    if (this.lookhead == Variable.END) {
      return false;
    }
    if (expectChar('(')) {
      move(true);
      this.scope.enterParen();
      ternary();
      if (expectChar(')')) {
        move(true);
        this.scope.leaveParen();
      }
    } else if (this.lookhead.getType() == TokenType.Number
        || this.lookhead.getType() == TokenType.String
        || this.lookhead.getType() == TokenType.Variable || this.lookhead == Variable.TRUE
        || this.lookhead == Variable.FALSE || isOPVariable(this.lookhead)) {
      if (this.lookhead.getType() == TokenType.Variable) {
        checkVariableName(this.lookhead);
      }
      // binary operation as variable for seq functions
      if (this.lookhead.getType() == TokenType.Char) {
        CharToken charToken = (CharToken) this.lookhead;
        if (!ExpressionLexer.isBinaryOP(charToken.getCh())) {
          reportSyntaxError("unexpect char '" + charToken.getCh() + "'");
        }
        // make it as variable
        this.lookhead = this.lexer.getSymbolTable().reserve(
            new Variable(charToken.getLexeme(), charToken.getLineNo(), charToken.getStartIndex()));
      }
      move(true);
      // function
      Token prev = getPrevToken();
      if (prev.getType() == TokenType.Variable && expectChar('(')) {
        if (prev == Variable.LAMBDA) {
          lambda(false);
        } else if (prev == Variable.FN) {
          lambda(true);
        } else {
          method(prev);
        }
      } else if (prev.getType() == TokenType.Variable) {
        if (!arrayAccess()) {
          getCodeGeneratorWithTimes().onConstant(prev);
        }
      } else {
        getCodeGeneratorWithTimes().onConstant(prev);
      }
    } else if (expectChar('/')) {
      pattern();
    } else if (expectChar('}')) {
      return false;
    } else {
      reportSyntaxError("invalid token");
    }
    return true;
  }


  private void lambda(final boolean fn) {
    ensureFeatureEnabled(Feature.Lambda);
    this.scope.enterLambda();
    getCodeGeneratorWithTimes().onLambdaDefineStart(getPrevToken());
    this.scope.enterParen();
    move(true);
    int paramIndex = 0;
    FunctionParam lastParam = null;
    List variadicParams = new ArrayList<>(2);
    if (!expectChar(')')) {
      lastParam = lambdaArgument(paramIndex++);
      if (lastParam.isVariadic()) {
        variadicParams.add(lastParam);
      }

      while (expectChar(',')) {
        move(true);
        lastParam = lambdaArgument(paramIndex++);
        if (lastParam.isVariadic()) {
          variadicParams.add(lastParam);
        }
      }
    }

    // assert only one variadic param and it's the last one.
    if (variadicParams.size() > 1) {
      reportSyntaxError("The variadic parameter must be the last parameter: `"
          + variadicParams.get(0).getName() + "`");
    }
    if (variadicParams.size() > 0 && variadicParams.get(0) != lastParam) {
      reportSyntaxError("The variadic parameter must be the last parameter: `"
          + variadicParams.get(0).getName() + "`");
    }

    if (expectChar(')')) {
      this.scope.leaveParen();
      move(true);

      if (fn) {
        if (!expectChar('{')) {
          reportSyntaxError("expect '{'");
        }
      } else {
        if (!expectChar('-')) {
          reportSyntaxError("expect '->' for lambda body");
        }
        move(true);
        if (!expectChar('>')) {
          reportSyntaxError("expect '->' for lambda body");
        }
      }

      move(true);
      getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead);
      statements();

      if (fn) {
        if (!expectChar('}')) {
          reportSyntaxError("missing '}' to close function body");
        }
      } else {
        if (this.lookhead != Variable.END) {
          reportSyntaxError("expect lambda 'end'");
        }
      }

      getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead);
      this.scope.leaveLambda();
      move(true);
    }
  }

  private String currentTokenLexeme() {
    return this.lookhead == null ? "END_OF_STRING" : this.lookhead.getLexeme();
  }


  private FunctionParam lambdaArgument(final int index) {
    if (expectChar('&')) {
      move(true);

      if (this.lookhead.getType() != TokenType.Variable) {
        reportSyntaxError("expect argument name, but is: " + currentTokenLexeme());
      }

      return lambdaArgument0(index, true);
    } else if (this.lookhead.getType() == TokenType.Variable) {
      return lambdaArgument0(index, false);
    } else {
      reportSyntaxError("expect argument name, but is: " + currentTokenLexeme());
      return null;
    }
  }

  private FunctionParam lambdaArgument0(final int index, final boolean isVariadic) {
    if (!isJavaIdentifier(this.lookhead.getLexeme())) {
      reportSyntaxError("illegal argument name: " + currentTokenLexeme());
    }
    final FunctionParam param = new FunctionParam(index, this.lookhead.getLexeme(), isVariadic);
    getCodeGeneratorWithTimes().onLambdaArgument(this.lookhead, param);
    move(true);
    return param;
  }


  private boolean arrayAccess() {
    // check if it is a array index access
    boolean hasArray = false;
    while (expectChar('[')) {
      if (!hasArray) {
        getCodeGeneratorWithTimes().onArray(getPrevToken());
        move(true);
        hasArray = true;
      } else {
        move(true);
      }
      getCodeGeneratorWithTimes().onArrayIndexStart(getPrevToken());
      array();
    }
    return hasArray;

  }


  private void array() {
    this.scope.enterBracket();
    if (getPrevToken() == Variable.TRUE || getPrevToken() == Variable.FALSE
        || getPrevToken() == Variable.NIL) {
      reportSyntaxError(getPrevToken().getLexeme() + " could not use [] operator");
    }
    if (!ternary()) {
      reportSyntaxError("missing index for array access");
    }
    if (expectChar(']')) {
      this.scope.leaveBracket();
      move(true);
      getCodeGeneratorWithTimes().onArrayIndexEnd(this.lookhead);
    }
  }

  private void checkVariableName(final Token token) {
    if (token.getType() == TokenType.Delegate) {
      return;
    }
    if (!((Variable) token).isQuote()) {
      String[] names = token.getLexeme().split("\\.");
      for (String name : names) {
        if (!isJavaIdentifier(name)) {
          reportSyntaxError("illegal identifier: " + name);
        }
      }
    }
  }

  private void methodInvokeOrArrayAccess() {
    while (expectChar('[') || expectChar('(')) {
      if (isConstant(getPrevToken(), this.instance)) {
        break;
      }
      if (expectChar('[')) {
        arrayAccess();
      } else if (expectChar('(')) {
        method(anonymousMethodName());
      }
    }
  }

  private void method(final Token methodName) {
    if (expectChar('(')) {
      this.scope.enterParen();
      checkVariableName(methodName);
      checkFunctionName(methodName, false);
      getCodeGeneratorWithTimes().onMethodName(methodName);
      move(true);
      int paramIndex = 0;
      List params = null;
      boolean unpackArguments = false;
      if (this.captureFuncArgs) {
        params = new ArrayList<>();
      }
      int lastTokenIndex = getLookheadStartIndex();
      if (!expectChar(')')) {

        boolean isPackArgs = false;
        if (expectChar('*')) {
          move(true);
          if (expectChar('*') || expectChar(',')) {
            // binary operation as argument
            back();
          } else {
            unpackArguments = true;
            withMetaBegin();
            isPackArgs = true;
          }
        }

        ternary();

        if (isPackArgs) {
          withMetaEnd(Constants.UNPACK_ARGS, true);
        }

        getCodeGeneratorWithTimes().onMethodParameter(this.lookhead);
        if (this.captureFuncArgs) {
          params.add(new FunctionArgument(paramIndex++, getParamExp(lastTokenIndex)));
        }
        while (expectChar(',')) {
          move(true);
          isPackArgs = false;
          lastTokenIndex = getLookheadStartIndex();
          if (expectChar('*')) {
            move(true);
            if (expectChar('*') || expectChar(',')) {
              // binary operation as argument
              back();
            } else {
              unpackArguments = true;
              withMetaBegin();
              isPackArgs = true;
            }
          }

          if (!ternary()) {
            reportSyntaxError("invalid argument");
          }

          if (isPackArgs) {
            withMetaEnd(Constants.UNPACK_ARGS, true);
          }

          getCodeGeneratorWithTimes().onMethodParameter(this.lookhead);
          if (this.captureFuncArgs) {
            params.add(new FunctionArgument(paramIndex++, getParamExp(lastTokenIndex)));
          }
        }
      }
      if (unpackArguments) {
        methodName.withMeta(Constants.UNPACK_ARGS, true);
      }
      if (expectChar(')')) {
        getCodeGeneratorWithTimes()
            .onMethodInvoke(currentToken().withMeta(Constants.PARAMS_META, params));
        move(true);
        this.scope.leaveParen();
      }
    }
  }


  /**
   * Test whether a given string is a valid Java identifier.
   *
   * @param id string which should be checked
   * @return true if a valid identifier
   */
  public static final boolean isJavaIdentifier(final String id) {
    if (id == null) {
      return false;
    }

    if (id.equals("")) {
      return false;
    }

    if (!java.lang.Character.isJavaIdentifierStart(id.charAt(0))) {
      return false;
    }

    for (int i = 1; i < id.length(); i++) {
      if (!java.lang.Character.isJavaIdentifierPart(id.charAt(i))) {
        return false;
      }
    }
    if (id.equals("null")) {
      return false;
    }
    return true;
  }


  private void pattern() {
    // It is a pattern
    int startIndex = this.lookhead.getStartIndex();
    move(true);
    this.inPattern = true;
    StringBuilder sb = new StringBuilder();
    while (this.lookhead != null) {
      while (!expectChar('/')) {
        sb.append(this.lookhead.getLexeme());
        move(false);
      }
      if (getPrevToken().getType() == TokenType.Char
          && ((CharToken) getPrevToken()).getLexeme().equals("\\")) {
        sb.append("/");
        move(false);
        continue;
      }
      this.inPattern = false;
      break;
    }
    if (this.inPattern) {
      reportSyntaxError("invalid regular pattern:" + sb.toString());
    }
    getCodeGeneratorWithTimes()
        .onConstant(new PatternToken(sb.toString(), this.lexer.getLineNo(), startIndex));
    move(true);
  }


  public void reportSyntaxError(final String message) {
    int index = isValidLookhead() ? this.lookhead.getStartIndex() : this.lexer.getCurrentIndex();

    if (this.lookhead != null) {
      this.lexer.pushback(this.lookhead);
    }

    String msg = "Syntax error: " + message + //
        " at " + index + //
        ", lineNumber: " + this.lexer.getLineNo() + //
        ", token : " + //
        this.lookhead + ",\nwhile parsing expression: `\n" + //
        this.lexer.getScanString() + "^^^\n`";

    ExpressionSyntaxErrorException e = new ExpressionSyntaxErrorException(msg);
    StackTraceElement[] traces = e.getStackTrace();
    List filteredTraces = new ArrayList<>();
    for (StackTraceElement t : traces) {
      if (!this.instance.getOptionValue(Options.TRACE_EVAL).bool
          && t.getClassName().equals(this.getClass().getName())) {
        continue;
      }
      filteredTraces.add(t);
    }
    e.setStackTrace(filteredTraces.toArray(new StackTraceElement[filteredTraces.size()]));
    throw e;
  }

  private boolean isValidLookhead() {
    return this.lookhead != null && this.lookhead.getStartIndex() > 0;
  }

  public void move(final boolean analyse) {
    if (this.lookhead != null) {
      this.prevTokens.push(this.lookhead);
      this.lookhead = this.lexer.scan(analyse);
      if (this.lookhead != null) {
        this.parsedTokens++;
      }
    } else {
      reportSyntaxError("illegal token");
    }
  }

  public int getParsedTokens() {
    return this.parsedTokens;
  }

  public void back() {
    if (this.lookhead != null) {
      this.parsedTokens--;
    }
    this.lexer.pushback(this.lookhead);
    this.lookhead = getPrevToken();
  }


  public Expression parse(final boolean reportErrorIfNotEOF) {
    StatementType statementType = statements();
    if (this.lookhead != null && reportErrorIfNotEOF) {
      if (statementType == StatementType.Ternary) {
        reportSyntaxError("unexpect token '" + currentTokenLexeme()
            + "', maybe forget to insert ';' to complete last expression ");
      } else {
        reportSyntaxError("unexpect token '" + currentTokenLexeme() + "'");
      }
    }
    return getCodeGeneratorWithTimes().getResult(true);
  }


  public Expression parse() {
    return parse(true);
  }

  static enum StatementType {
    Ternary, Return, Empty, Other
  }

  /**
   * Call __reducer_break()
   */
  private void breakStatement() {
    if (!this.scope.newLexicalScope) {
      reportSyntaxError("break only can be used in for-loop");
    }
    move(true);
    getCodeGeneratorWithTimes().onMethodName(Constants.ReducerBreakFn);
    getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead);
    if (!expectChar(';')) {
      reportSyntaxError("missing ';' for break");
    }
    move(true);
  }

  /**
   * Call __reducer_cont(nil)
   */
  private void continueStatement() {
    if (!this.scope.newLexicalScope) {
      reportSyntaxError("continue only can be used in for-loop");
    }
    move(true);
    getCodeGeneratorWithTimes().onMethodName(Constants.ReducerContFn);
    getCodeGeneratorWithTimes().onConstant(Variable.NIL);
    getCodeGeneratorWithTimes().onMethodParameter(this.lookhead);
    getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead);
    if (!expectChar(';')) {
      reportSyntaxError("missing ';' for continue");
    }
    move(true);
  }


  /**
   * 
   *  while(test) {
   *     ...body...
   *  }
   *  ...statements...
   * 
* * ===> * *
   *  __reducer_callcc(__reducer_loop, lambda() ->
   *       if(test) {
   *          ...body...
   *       }else {
   *          break;
   *       }
   *  end, lambda()- >
   *       ...statements...
   *  end);
   * 
*/ private void whileStatement() { move(true); // prepare to call __reducer_callcc(LOOP, iterator, statements) getCodeGeneratorWithTimes().onMethodName(Constants.ReducerFn); getCodeGeneratorWithTimes().onConstant(Constants.REDUCER_LOOP); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); // create a lambda function wraps while body(iterator) boolean newLexicalScope = this.scope.newLexicalScope; this.scope.newLexicalScope = true; { getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); ifStatement(true, false); getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); getCodeGenerator().onMethodParameter(this.lookhead); } if (expectChar(';')) { // the statement is ended. getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { // create a lambda function wraps statements after while(statements) getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope) // .withMeta(Constants.INHERIT_ENV_META, true)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); if (statements() == StatementType.Empty) { getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); } getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); // call __reducer_callcc(LOOP, iterator, statements) getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); // restore newLexicalScope this.scope.newLexicalScope = newLexicalScope; } private void letStatement() { move(true); Token var = this.lookhead; checkVariableName(var); getCodeGenerator().onConstant(var); move(true); if (!expectChar('=')) { reportSyntaxError("expect '='"); } move(true); if (statement() == StatementType.Empty) { reportSyntaxError("invalid value to define"); } checkVarIsInit(var); ensureFeatureEnabled(Feature.Assignment); getCodeGeneratorWithTimes().onAssignment(currentToken().withMeta(Constants.DEFINE_META, true)); if (!expectChar(';')) { reportSyntaxError("missing ';' for let statement"); } move(true); } private void fnStatement() { move(true); if (expectChar('(')) { // Anonymous function lambda(true); } else { checkVariableName(this.lookhead); checkFunctionName(this.lookhead, true); getCodeGeneratorWithTimes().onConstant(this.lookhead.withMeta(Constants.INIT_META, true) .withMeta(Constants.TYPE_META, CompileTypes.Function)); move(true); if (!expectChar('(')) { reportSyntaxError("expect '(' after function name"); } lambda(true); ensureFeatureEnabled(Feature.Assignment); getCodeGeneratorWithTimes() .onAssignment(currentToken().withMeta(Constants.DEFINE_META, true)); } } private void checkFunctionName(final Token token, final boolean warnOnExists) { String fnName = token.getLexeme(); if (SymbolTable.isReservedKeyword(fnName)) { reportSyntaxError("The function name `" + fnName + "` is a reserved keyword"); } if (warnOnExists && this.instance.getFuncMap().containsKey(fnName)) { System.out.println("[Aviator WARN] The function '" + fnName + "' is already exists, but is replaced with new one."); } } private Token currentToken() { Token token = this.lookhead; if (token == null) { token = new CharToken((char) -1, this.lexer.getLineNo(), this.lexer.getCurrentIndex()); } return token; } private boolean scopeStatement() { /** *
     *   {
     *    ...body...
     *   }
     *   ...statements...
     * 
* * => * *
     *  __if_callcc((lambda() -> ...body... end)(),  lambda() ->
     *     ...statements...
     *  end);
     * 
*/ boolean hasReturn = false; boolean newLexicalScope = this.scope.newLexicalScope; this.scope.newLexicalScope = true; // prepare to call __if_callcc(result, statements) getCodeGeneratorWithTimes().onMethodName(Constants.IfReturnFn); // Create a lambda to wrap the scope body. { move(true); this.scope.enterBrace(); getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); hasReturn = statements() == StatementType.Return; getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); getCodeGeneratorWithTimes().onMethodName(anonymousMethodName()); getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); if (!expectChar('}')) { reportSyntaxError("missing '}' to close scope"); } move(true); this.scope.leaveBrace(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } if (expectChar(';')) { // the statement is ended. getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { // create a lambda function wraps statements after scope statement (statements) getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope) // .withMeta(Constants.INHERIT_ENV_META, true)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); if (statements() == StatementType.Empty) { getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); } getCodeGenerator().onMethodParameter(this.lookhead); // call __if_callcc(result, statements) getCodeGenerator().onMethodInvoke(this.lookhead); this.scope.newLexicalScope = newLexicalScope; return hasReturn; } private void tryStatement() { getCodeGeneratorWithTimes().onMethodName(Constants.TRY_VAR); move(true); if (!expectChar('{')) { reportSyntaxError("expect '{' after try"); } move(true); boolean newLexicalScope = this.scope.newLexicalScope; this.scope.newLexicalScope = true; // create a lambda function wraps try body { getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); statements(); getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } if (!expectChar('}')) { reportSyntaxError("missing '}' for try body"); } move(true); boolean hasCatch = false; boolean hasFinally = false; while (this.lookhead == Variable.CATCH) { if (!hasCatch) { // create a handler list. getCodeGeneratorWithTimes().onMethodName(Constants.SEQ_LIST_VAR); hasCatch = true; } move(true); // create a lambda function wraps catch handlers if (!expectChar('(')) { reportSyntaxError("expect '(' after catch"); } move(true); if (this.lookhead == null || this.lookhead.getType() != TokenType.Variable) { reportSyntaxError("invalid exception class name"); } checkVariableName(this.lookhead); List> exceptionClasses = new ArrayList<>(); exceptionClasses.add(this.lookhead); move(true); Token boundVar = null; if (expectChar(')')) { // catch(e) to catch all. boundVar = exceptionClasses.remove(0); exceptionClasses.add(Constants.THROWABLE_VAR); } else { // catch multi exception while (expectChar('|')) { move(true); if (this.lookhead.getType() != TokenType.Variable) { reportSyntaxError("invalid exception class to catch"); } checkVariableName(this.lookhead); exceptionClasses.add(this.lookhead); move(true); } if (this.lookhead == null || this.lookhead.getType() != TokenType.Variable) { reportSyntaxError("invalid bound variable name for exception"); } checkVariableName(this.lookhead); boundVar = this.lookhead; move(true); } if (!expectChar(')')) { reportSyntaxError("missing ')' for catch caluse"); } move(true); if (!expectChar('{')) { reportSyntaxError("missing '{' for catch block"); } move(true); { // create a catch handler getCodeGeneratorWithTimes().onMethodName(Constants.CATCH_HANDLER_VAR); getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); getCodeGeneratorWithTimes().onLambdaArgument(boundVar, new FunctionParam(0, boundVar.getLexeme(), false)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); statements(); getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); for (Token exceptionClass : exceptionClasses) { getCodeGeneratorWithTimes().onConstant(exceptionClass); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); } getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); if (!expectChar('}')) { reportSyntaxError("missing '}' for to complete catch block"); } move(true); } if (hasCatch) { // Invoke seq.list to create handler list getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } else { getCodeGeneratorWithTimes().onConstant(Variable.NIL); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } if (this.lookhead == Variable.FINALLY) { hasFinally = true; move(true); if (!expectChar('{')) { reportSyntaxError("missing '{' for finally block"); } move(true); // create a lambda to getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); statements(); getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); if (!expectChar('}')) { reportSyntaxError("missing '}' for finally block"); } move(true); } else { getCodeGeneratorWithTimes().onConstant(Variable.NIL); } if (!hasCatch && !hasFinally) { reportSyntaxError("missing catch or finally blocks for catch"); } if (expectChar(';')) { // The statement is ended. getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { // create a lambda function wraps statements after try..catch getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope) // .withMeta(Constants.INHERIT_ENV_META, true)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); if (statements() == StatementType.Empty) { getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); } getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); this.scope.newLexicalScope = newLexicalScope; getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); } private void throwStatement() { getCodeGeneratorWithTimes().onMethodName(Constants.THROW_VAR); move(true); statement(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); if (!expectChar(';')) { reportSyntaxError("missing ';' for throw statement"); } } private void newStatement() { ensureFeatureEnabled(Feature.NewInstance); getCodeGeneratorWithTimes().onMethodName(Constants.NEW_VAR); move(true); if (this.lookhead == null || this.lookhead.getType() != TokenType.Variable) { reportSyntaxError("invalid class name"); } checkVariableName(this.lookhead); getCodeGeneratorWithTimes().onConstant(this.lookhead); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); move(true); if (!expectChar('(')) { reportSyntaxError("missing '(' after class name"); } this.scope.enterParen(); move(true); if (!expectChar(')')) { ternary(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); while (expectChar(',')) { move(true); if (!ternary()) { reportSyntaxError("invalid argument"); } getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } } if (!expectChar(')')) { reportSyntaxError("missing ')' for new statement"); } getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); move(true); this.scope.leaveParen(); } private void className() { if (this.lookhead.getType() != TokenType.Variable && !expectChar('*')) { reportSyntaxError("expect variable name or * to use"); } if (expectChar('*')) { wildcard(); } else { checkVariableName(this.lookhead); getCodeGenerator().onConstant(this.lookhead); } move(true); } private void useStatement() { getCodeGeneratorWithTimes().onMethodName(Constants.USE_VAR); move(true); className(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); if (expectChar('*')) { // wildcard wildcard(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); move(true); } else if (expectChar('{')) { this.scope.enterBrace(); move(true); className(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); while (expectChar(',')) { move(true); className(); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } if (!expectChar('}')) { reportSyntaxError("expect '}' to complete use statement"); } else { move(true); this.scope.leaveBrace(); } } getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); if (!expectChar(';')) { reportSyntaxError("missing ';' for use statement"); } } private void wildcard() { getCodeGenerator() .onConstant(new Variable("*", this.lookhead.getLineNo(), this.lookhead.getStartIndex())); } private StatementType statement() { if (this.lookhead == Variable.IF) { ensureFeatureEnabled(Feature.If); if (ifStatement(false, false)) { return StatementType.Return; } else { return StatementType.Other; } } else if (this.lookhead == Variable.FOR) { ensureFeatureEnabled(Feature.ForLoop); forStatement(); return StatementType.Other; } else if (this.lookhead == Variable.RETURN) { ensureFeatureEnabled(Feature.Return); returnStatement(); return StatementType.Return; } else if (this.lookhead == Variable.BREAK) { breakStatement(); return StatementType.Return; } else if (this.lookhead == Variable.CONTINUE) { continueStatement(); return StatementType.Return; } else if (this.lookhead == Variable.LET) { ensureFeatureEnabled(Feature.Let); letStatement(); return StatementType.Other; } else if (this.lookhead == Variable.WHILE) { ensureFeatureEnabled(Feature.WhileLoop); whileStatement(); return StatementType.Other; } else if (this.lookhead == Variable.FN) { ensureFeatureEnabled(Feature.Fn); fnStatement(); return StatementType.Other; } else if (this.lookhead == Variable.TRY) { ensureFeatureEnabled(Feature.ExceptionHandle); tryStatement(); return StatementType.Other; } else if (this.lookhead == Variable.THROW) { ensureFeatureEnabled(Feature.ExceptionHandle); throwStatement(); return StatementType.Other; } else if (expectChar('{')) { ensureFeatureEnabled(Feature.LexicalScope); if (scopeStatement()) { return StatementType.Return; } else { return StatementType.Other; } } else if (this.lookhead == Variable.USE) { ensureFeatureEnabled(Feature.Use); useStatement(); return StatementType.Other; } else { if (ternary()) { return StatementType.Ternary; } else { return StatementType.Empty; } } } private void withMetaBegin() { getCodeGeneratorWithTimes().onMethodName(Constants.WithMetaFn); } private void withMetaEnd(final Object key, final Object val) { getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); getCodeGeneratorWithTimes().onConstant(value2token(key)); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); getCodeGeneratorWithTimes().onConstant(value2token(val)); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); } private Token value2token(final Object val) { if (val instanceof Token) { return (Token) val; } else if (val == null) { return Variable.NIL; } else if (val instanceof String) { return (new StringToken((String) val, this.lexer.getLineNo(), this.lookhead.getStartIndex())); } else if (val instanceof Number) { return (new NumberToken((Number) val, val.toString(), this.lexer.getLineNo(), this.lookhead.getStartIndex())); } else if (val instanceof Boolean) { return (((boolean) val) ? Variable.TRUE : Variable.FALSE); } else { throw new ExpressionRuntimeException( "Unsupported compiled-time metadata type: " + val.getClass()); } } /** *
   *  for x in coll {
   *     ...body...
   *  }
   *  ...statements...
   * 
* * ===> * *
   *  __reducer_callcc(seq, lambda(x) ->
   *      ...body...
   *  end, lambda()- >
   *       ...statements...
   *  end);
   * 
*/ private void forStatement() { move(true); List> reducerArgs = new ArrayList<>(2); while (true) { if (reducerArgs.size() > 2) { reportSyntaxError("Too many variables in for statement: " + reducerArgs.size()); } reducerArgs.add(this.lookhead); checkVariableName(this.lookhead); move(true); if (expectChar(',')) { move(true); continue; } else { break; } } if (this.lookhead == Variable.IN) { move(true); // prepare to call __reducer_callcc(seq, iterator, statements) { getCodeGeneratorWithTimes().onMethodName(Constants.ReducerFn); // The seq if (!ternary()) { reportSyntaxError("missing collection"); } getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } if (expectChar('{')) { move(true); this.scope.enterBrace(); boolean newLexicalScope = this.scope.newLexicalScope; this.scope.newLexicalScope = true; // create a lambda function wraps for-loop body(iterator) { withMetaBegin(); getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); for (Token reducerArg : reducerArgs) { getCodeGeneratorWithTimes().onLambdaArgument(reducerArg, new FunctionParam(0, reducerArg.getLexeme(), false)); } getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); statements(); getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); withMetaEnd(Constants.ARITIES_META, Long.valueOf(reducerArgs.size())); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } if (expectChar('}')) { move(true); this.scope.leaveBrace(); } else { reportSyntaxError("missing '}' in for-loop"); } if (expectChar(';')) { // The statement is ended. getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { // create a lambda function wraps statements after for-loop(statements) getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope) // .withMeta(Constants.INHERIT_ENV_META, true)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); if (statements() == StatementType.Empty) { getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); } getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); // call __reducer_callcc(seq, iterator, statements) getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); // restore newLexicalScope this.scope.newLexicalScope = newLexicalScope; } else { reportSyntaxError("expect '{' in for-loop"); } } else { reportSyntaxError("expect 'in' keyword while using for-loop"); } } private StatementType statements() { if (this.lookhead == null) { return StatementType.Empty; } StatementType stmtType = statement(); ensureDepthState(); while (expectChar(';') || stmtType == StatementType.Other || stmtType == StatementType.Return) { ensureNoStatementAfterReturn(stmtType); if (this.lookhead != null && this.lookhead != Variable.END && !expectChar('}')) { getCodeGeneratorWithTimes().onTernaryEnd(this.lookhead); } if (expectChar(';')) { move(true); } if (this.lookhead == null) { break; } StatementType nextStmtType = statement(); if (nextStmtType == StatementType.Empty) { break; } stmtType = nextStmtType; ensureDepthState(); } ensureNoStatementAfterReturn(stmtType); return stmtType; } private void ensureNoStatementAfterReturn(final StatementType statementType) { if (statementType == StatementType.Return && this.lookhead != null) { if (this.lookhead != Variable.END && !expectChar('}')) { reportSyntaxError("unreachable code"); } } } /** *
   *  if(test) {
   *     ...if-body...
   *  }else {
   *     ...else-body...
   *  }
   *  ...statements...
   * 
* * ===> * *
   *  __if_callcc(test ? (lambda() -> ...if-body... end)() :  (lambda() -> ...else-body... end)(),
   *   lambda()- >
   *       ...statements...
   *  end);
   * 
*/ private boolean ifStatement(final boolean isWhile, final boolean isElsif) { if (!isWhile) { move(true); } boolean ifBodyHasReturn = false; boolean elseBodyHasReturn = false; boolean newLexicalScope = this.scope.newLexicalScope; this.scope.newLexicalScope = true; // prepare to call __if_callcc(result, statements) getCodeGeneratorWithTimes().onMethodName(Constants.IfReturnFn); { if (!ternary()) { reportSyntaxError("missing test statement for if"); } getCodeGeneratorWithTimes().onTernaryBoolean(this.lookhead); if (expectChar('{')) { move(true); this.scope.enterBrace(); getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); ifBodyHasReturn = statements() == StatementType.Return; getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); getCodeGeneratorWithTimes().onMethodName(anonymousMethodName()); getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); getCodeGeneratorWithTimes().onTernaryLeft(this.lookhead); } else { reportSyntaxError("expect '{' for " + getLoopKeyword(isWhile) + " statement"); } if (!expectChar('}')) { reportSyntaxError("missing '}' to close " + getLoopKeyword(isWhile) + " body"); } this.scope.leaveBrace(); move(true); elseBodyHasReturn = elseStatement(isWhile, ifBodyHasReturn); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } { // if (isWhile || isElsif) { // Load ReducerEmptyVal directly. getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { if (expectChar(';')) { // the statement is ended. getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { // create a lambda function wraps statements after if statement (statements) getCodeGeneratorWithTimes().onLambdaDefineStart( getPrevToken().withMeta(Constants.SCOPE_META, this.scope.newLexicalScope) // .withMeta(Constants.INHERIT_ENV_META, true)); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); if (statements() == StatementType.Empty) { getCodeGenerator().onConstant(Constants.ReducerEmptyVal); } else { if (ifBodyHasReturn && elseBodyHasReturn && !isElsif) { reportSyntaxError("unreachable code"); } } getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); } } getCodeGenerator().onMethodParameter(this.lookhead); // call __if_callcc(result, statements) getCodeGenerator().onMethodInvoke(this.lookhead); this.scope.newLexicalScope = newLexicalScope; } return ifBodyHasReturn && elseBodyHasReturn; } private String getLoopKeyword(final boolean isWhile) { return isWhile ? "while" : "if"; } private boolean elseStatement(final boolean isWhile, final boolean ifBodyHasReturn) { if (isWhile) { // Call __reducer_break(nil) final CodeGenerator cg = getCodeGeneratorWithTimes(); cg.onMethodName(Constants.ReducerBreakFn); cg.onConstant(Variable.NIL); cg.onMethodParameter(this.lookhead); cg.onMethodInvoke(this.lookhead); cg.onTernaryRight(this.lookhead); return false; } if (expectChar(';')) { return withoutElse(); } boolean hasReturn = false; boolean hasElsif = this.lookhead == Variable.ELSIF; boolean hasElse = this.lookhead == Variable.ELSE; if (this.lookhead != null && (hasElse || hasElsif || ifBodyHasReturn)) { if (hasElse) { move(true); if (expectChar('{')) { this.scope.enterBrace(); move(true); hasReturn = elseBody(false); if (expectChar('}')) { this.scope.leaveBrace(); move(true); } else { reportSyntaxError("missing '}' to close 'else' body"); } } else { reportSyntaxError("expect '{' for else statement"); } } else if (hasElsif) { hasReturn = ifStatement(false, true); getCodeGenerator().onTernaryRight(this.lookhead); } else if (ifBodyHasReturn) { hasReturn = elseBody(true); } else { return withoutElse(); } return hasReturn; } else { // Missing else statement, always nil. return withoutElse(); } } private boolean withoutElse() { final CodeGenerator cg = getCodeGeneratorWithTimes(); cg.onConstant(Variable.NIL); cg.onTernaryRight(this.lookhead); return false; } private boolean elseBody(final boolean inheritEnv) { getCodeGeneratorWithTimes().onLambdaDefineStart(this.lookhead // .withMeta(Constants.SCOPE_META, this.scope.newLexicalScope) // .withMeta(Constants.INHERIT_ENV_META, inheritEnv) // ); getCodeGeneratorWithTimes().onLambdaBodyStart(this.lookhead); boolean hasReturn = statements() == StatementType.Return; getCodeGeneratorWithTimes().onLambdaBodyEnd(this.lookhead); getCodeGeneratorWithTimes().onMethodName(anonymousMethodName()); getCodeGeneratorWithTimes().onMethodInvoke(this.lookhead); getCodeGeneratorWithTimes().onTernaryRight(this.lookhead); return hasReturn; } private DelegateToken anonymousMethodName() { return new DelegateToken(this.lookhead, DelegateTokenType.Method_Name); } private void ensureDepthState() { DepthState state = this.scope.depthState.peekLast(); if (state != null) { back(); switch (state) { case Parent: if (this.scope.parenDepth > 0) { reportSyntaxError("insert ')' to complete statement"); } break; case Bracket: if (this.scope.bracketDepth > 0) { reportSyntaxError("insert ']' to complete statement"); } break; case Lambda: if (this.scope.lambdaDepth > 0) { reportSyntaxError("insert 'end' to complete lambda statement"); } break; case Brace: if (this.scope.braceDepth > 0) { reportSyntaxError("insert '}' to complete statement"); } break; } } } public static boolean isConstant(final Token token, final AviatorEvaluatorInstance instance) { switch (token.getType()) { case Number: case Pattern: return true; case String: return !instance.isFeatureEnabled(Feature.StringInterpolation); default: return false; } } public static boolean isLiteralToken(final Token token, final AviatorEvaluatorInstance instance) { switch (token.getType()) { case Variable: return token == Variable.TRUE || token == Variable.FALSE || token == Variable.NIL; case Char: case Number: case Pattern: return true; case String: return !instance.isFeatureEnabled(Feature.StringInterpolation); default: return false; } } private int getCGTimes; private final CodeGenerator getCodeGeneratorWithTimes() { this.getCGTimes++; return this.codeGenerator; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy