content: package com.alibaba.qlexpress4.aparser;
  
  import com.alibaba.qlexpress4.DefaultClassSupplier;
  import com.alibaba.qlexpress4.InitOptions;
  import com.alibaba.qlexpress4.aparser.compiletimefunction.CodeGenerator;
  import com.alibaba.qlexpress4.aparser.compiletimefunction.CompileTimeFunction;
  import com.alibaba.qlexpress4.exception.*;
  import com.alibaba.qlexpress4.runtime.*;
  import com.alibaba.qlexpress4.runtime.instruction.*;
  import com.alibaba.qlexpress4.runtime.operator.BinaryOperator;
  import com.alibaba.qlexpress4.runtime.operator.OperatorManager;
  import com.alibaba.qlexpress4.runtime.operator.unary.UnaryOperator;
  import com.alibaba.qlexpress4.utils.QLStringUtils;
  import org.antlr.v4.runtime.RuleContext;
  import org.antlr.v4.runtime.Token;
  import org.antlr.v4.runtime.tree.ParseTree;
  import org.antlr.v4.runtime.tree.TerminalNode;
  
  import java.lang.reflect.Array;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.util.*;
  import java.util.stream.Collectors;
  
  import static com.alibaba.qlexpress4.aparser.QLParser.*;
  
  /**
   * Author: DQinYuan
   */
  public class QvmInstructionVisitor extends QLParserBaseVisitor<Void> {
      private static final String SCOPE_SEPARATOR = "$";
      
      private static final String BLOCK_LAMBDA_NAME_PREFIX = "BLOCK_";
      
      private static final String IF_PREFIX = "IF_";
      
      private static final String THEN_SUFFIX = "_THEN";
      
      private static final String ELSE_SUFFIX = "_ELSE";
      
      private static final String MACRO_PREFIX = "MACRO_";
      
      private static final String LAMBDA_PREFIX = "LAMBDA_";
      
      private static final String TRY_PREFIX = "TRY_";
      
      private static final String CATCH_SUFFIX = "_CATCH";
      
      private static final String FINAL_SUFFIX = "_FINAL";
      
      private static final String FOR_PREFIX = "FOR_";
      
      private static final String INIT_SUFFIX = "_INIT";
      
      private static final String CONDITION_SUFFIX = "_CONDITION";
      
      private static final String UPDATE_SUFFIX = "_UPDATE";
      
      private static final String BODY_SUFFIX = "_BODY";
      
      private static final String WHILE_PREFIX = "WHILE_";
      
      private static final String LBRACE = "{";
      
      private static final String RBRACE = "}";
      
      private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
      
      private static final BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
      
      private static final BigDecimal MAX_DOUBLE = new BigDecimal(String.valueOf(Double.MAX_VALUE));
      
      private static final int TIMEOUT_CHECK_GAP = 5;
      
      public enum Context {
          BLOCK, MACRO
      }
      
      private final String script;
      
      private final ImportManager importManager;
      
      private final GeneratorScope generatorScope;
      
      private final OperatorFactory operatorFactory;
      
      private final Map<String, CompileTimeFunction> compileTimeFunctions;
      
      private final InitOptions initOptions;
      
      private final Context context;
      
      private final List<QLInstruction> instructionList = new ArrayList<>();
      
      private int stackSize;
      
      private int maxStackSize;
      
      private int ifCounter = 0;
      
      private int blockCounter = 0;
      
      private int macroCounter = 0;
      
      private int lambdaCounter = 0;
      
      private int tryCounter = 0;
      
      private int forCounter = 0;
      
      private int whileCounter = 0;
      
      private int timeoutCheckPoint = -1;
      
      /*
       * main constructor
       */
      public QvmInstructionVisitor(String script, ImportManager importManager, GeneratorScope globalScope,
          OperatorFactory operatorFactory, Map<String, CompileTimeFunction> compileTimeFunctions,
          InitOptions initOptions) {
          this.script = script;
          this.importManager = importManager;
          this.generatorScope = new GeneratorScope("main", globalScope);
          this.operatorFactory = operatorFactory;
          this.context = Context.BLOCK;
          this.compileTimeFunctions = compileTimeFunctions;
          this.initOptions = initOptions;
      }
      
      /*
       *  for recursion
       */
      public QvmInstructionVisitor(String script, ImportManager importManager, GeneratorScope generatorScope,
          OperatorFactory operatorFactory, Context context, Map<String, CompileTimeFunction> compileTimeFunctions,
          InitOptions initOptions) {
          this.script = script;
          this.importManager = importManager;
          this.generatorScope = generatorScope;
          this.operatorFactory = operatorFactory;
          this.context = context;
          this.compileTimeFunctions = compileTimeFunctions;
          this.initOptions = initOptions;
      }
      
      /*
       * visible for testing
       */
      public QvmInstructionVisitor(String script) {
          this.script = script;
          this.importManager = new ImportManager(DefaultClassSupplier.getInstance(), new ArrayList<>(), new HashMap<>());
          this.generatorScope = new GeneratorScope("test-main", null);
          this.operatorFactory = new OperatorManager();
          this.context = Context.BLOCK;
          this.compileTimeFunctions = new HashMap<>();
          this.initOptions = InitOptions.DEFAULT_OPTIONS;
      }
      
      @Override
      public Void visitImportCls(ImportClsContext ctx) {
          String importClsPath =
              ctx.varId().stream().map(VarIdContext::getStart).map(Token::getText).collect(Collectors.joining("."));
          importManager.addImport(ImportManager.importCls(importClsPath));
          return null;
      }
      
      @Override
      public Void visitImportPack(ImportPackContext ctx) {
          List<VarIdContext> importPackPathTokens = ctx.varId();
          boolean isInnerCls =
              !Character.isLowerCase(importPackPathTokens.get(importPackPathTokens.size() - 1).getText().charAt(0));
          String importPath = importPackPathTokens.stream()
              .map(VarIdContext::getStart)
              .map(Token::getText)
              .collect(Collectors.joining("."));
          importManager
              .addImport(isInnerCls ? ImportManager.importInnerCls(importPath) : ImportManager.importPack(importPath));
          return null;
      }
      
      @Override
      public Void visitBlockStatements(BlockStatementsContext blockStatementsContext) {
          boolean isPreExpress = false;
          List<BlockStatementContext> nonEmptyChildren = blockStatementsContext.blockStatement()
              .stream()
              .filter(bs -> !(bs instanceof EmptyStatementContext))
              .collect(Collectors.toList());
          
          // First pass: process macro definitions to ensure they are available for functions
          for (BlockStatementContext child : nonEmptyChildren) {
              if (child instanceof MacroStatementContext) {
                  child.accept(this);
              }
          }
          
          // Second pass: process all function definitions to support forward references
          for (BlockStatementContext child : nonEmptyChildren) {
              if (child instanceof FunctionStatementContext) {
                  child.accept(this);
              }
          }
          
          // Third pass: process all other statements
          for (BlockStatementContext child : nonEmptyChildren) {
              if (!(child instanceof FunctionStatementContext) && !(child instanceof MacroStatementContext)) {
                  if (isPreExpress) {
                      addInstruction(new PopInstruction(PureErrReporter.INSTANCE));
                  }
                  isPreExpress = handleStmt(child);
              }
          }
          
          if (context == Context.BLOCK) {
              if (isPreExpress) {
                  addInstruction(new ReturnInstruction(PureErrReporter.INSTANCE, QResult.ResultType.CONTINUE, null));
              }
          }
          return null;
      }
      
      private void visitBodyExpression(ExpressionContext expressionContext) {
          BlockExprContext blockExprContext = blockExpr(expressionContext);
          if (blockExprContext != null) {
              BlockStatementsContext blockStatementsContext = blockExprContext.blockStatements();
              if (blockStatementsContext == null) {
                  return;
              }
              blockStatementsContext.accept(this);
              return;
          }
          expressionContext.accept(this);
          addInstruction(
              new ReturnInstruction(newReporterWithToken(expressionContext.getStart()), QResult.ResultType.RETURN, null));
      }
      
      private BlockExprContext blockExpr(ExpressionContext expressionContext) {
          Token startToken = expressionContext.getStart();
          Token stopToken = expressionContext.getStop();
          if (!(LBRACE.equals(startToken.getText()) && RBRACE.equals(stopToken.getText()))) {
              // fast fail
              return null;
          }
          TernaryExprContext ternaryExprContext = expressionContext.ternaryExpr();
          if (ternaryExprContext == null) {
              return null;
          }
          if (ternaryExprContext.QUESTION() != null) {
              return null;
          }
          BaseExprContext baseExprContext = ternaryExprContext.baseExpr(0);
          if (!baseExprContext.leftAsso().isEmpty()) {
              return null;
          }
          PrimaryContext primaryContext = baseExprContext.primary();
          if (primaryContext.primaryNoFixNonPathable() != null) {
              return null;
          }
          
          PrimaryNoFixPathableContext primaryNoFixPathableContext = primaryContext.primaryNoFixPathable();
          return primaryNoFixPathableContext instanceof BlockExprContext ? (BlockExprContext)primaryNoFixPathableContext
              : null;
      }
      
      @Override
      public Void visitExpression(ExpressionContext ctx) {
          TernaryExprContext ternaryExprContext = ctx.ternaryExpr();
          if (ternaryExprContext != null) {
              ternaryExprContext.accept(this);
              return null;
          }
          
          ctx.leftHandSide().accept(this);
          ctx.expression().accept(this);
          
          AssignOperatorContext assignOperatorContext = ctx.assignOperator();
          BinaryOperator assignOperator = operatorFactory.getBinaryOperator(assignOperatorContext.getText());
          addInstruction(new OperatorInstruction(newReporterWithToken(assignOperatorContext.getStart()), assignOperator,
              assignOperatorContext.getStart().getStartIndex()));
          return null;
      }
      
      @Override
      public Void visitTraditionalForStatement(TraditionalForStatementContext ctx) {
          int forCount = forCount();
          ErrorReporter forErrReporter = newReporterWithToken(ctx.FOR().getSymbol());
          
          // for init
          ForInitContext forInitContext = ctx.forInit();
          QLambdaDefinitionInner forInitLambda =
              forInitContext == null ? null : generateForInitLambda(forCount, forInitContext);
          
          // condition
          ExpressionContext forConditionContext = ctx.forCondition;
          QLambdaDefinitionInner forConditionLambda = forConditionContext == null ? null
              : generateForExpressLambda(forCount, CONDITION_SUFFIX, forConditionContext);
          
          // for update
          ExpressionContext forUpdateContext = ctx.forUpdate;
          QLambdaDefinitionInner forUpdateLambda =
              forUpdateContext == null ? null : generateForExpressLambda(forCount, UPDATE_SUFFIX, forUpdateContext);
          
          // for body
          QLambdaDefinition forBodyLambda = loopBodyVisitorDefinition(ctx.blockStatements(),
              generatorScope.getName() + SCOPE_SEPARATOR + FOR_PREFIX + forCount + BODY_SUFFIX,
              Collections.emptyList(),
              forErrReporter);
          
          int forInitSize = forInitLambda == null ? 0 : forInitLambda.getMaxStackSize();
          int forConditionSize = forConditionLambda == null ? 0 : forConditionLambda.getMaxStackSize();
          int forUpdateSize = forUpdateLambda == null ? 0 : forUpdateLambda.getMaxStackSize();
          int forScopeMaxStackSize = Math.max(forInitSize, Math.max(forConditionSize, forUpdateSize));
          
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(new TraceEvaludatedInstruction(forErrReporter, ctx.FOR().getSymbol().getStartIndex()));
          }
          
          addInstruction(new ForInstruction(forErrReporter, forInitLambda, forConditionLambda,
              forConditionContext != null ? newReporterWithToken(forConditionContext.getStart()) : null, forUpdateLambda,
              forScopeMaxStackSize, forBodyLambda));
          return null;
      }
      
      private QLambdaDefinitionInner generateForInitLambda(int forCount, ForInitContext forInitContext) {
          if (forInitContext.localVariableDeclaration() != null) {
              String scopeName = generatorScope.getName() + SCOPE_SEPARATOR + FOR_PREFIX + forCount + INIT_SUFFIX;
              QvmInstructionVisitor subVisitor = parseWithSubVisitor(forInitContext.localVariableDeclaration(),
                  new GeneratorScope(scopeName, generatorScope),
                  Context.MACRO);
              return new QLambdaDefinitionInner(scopeName, subVisitor.getInstructions(), Collections.emptyList(),
                  subVisitor.getMaxStackSize());
          }
          else if (forInitContext.expression() != null) {
              return generateForExpressLambda(forCount, INIT_SUFFIX, forInitContext.expression());
          }
          else {
              return null;
          }
      }
      
      private QLambdaDefinitionInner generateForExpressLambda(int forCount, String scopeSuffix,
          ExpressionContext expressionContext) {
          String scopeName = generatorScope.getName() + SCOPE_SEPARATOR + FOR_PREFIX + forCount + scopeSuffix;
          QvmInstructionVisitor subVisitor = parseExprBodyWithSubVisitor(expressionContext,
              new GeneratorScope(scopeName, generatorScope),
              Context.BLOCK);
          return new QLambdaDefinitionInner(scopeName, subVisitor.getInstructions(), Collections.emptyList(),
              subVisitor.getMaxStackSize());
      }
      
      @Override
      public Void visitForEachStatement(ForEachStatementContext ctx) {
          ExpressionContext targetExprContext = ctx.expression();
          targetExprContext.accept(this);
          
          DeclTypeContext declTypeContext = ctx.declType();
          Class<?> itVarCls = declTypeContext == null ? Object.class : parseDeclType(declTypeContext);
          
          ErrorReporter forEachErrReporter = newReporterWithToken(ctx.FOR().getSymbol());
          QLambdaDefinition bodyDefinition = loopBodyVisitorDefinition(ctx.blockStatements(),
              generatorScope.getName() + SCOPE_SEPARATOR + FOR_PREFIX + forCount() + BODY_SUFFIX,
              Collections.singletonList(new QLambdaDefinitionInner.Param(ctx.varId().getText(), itVarCls)),
              forEachErrReporter);
          
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(
                  new TraceEvaludatedInstruction(forEachErrReporter, ctx.FOR().getSymbol().getStartIndex()));
          }
          
          addInstruction(new ForEachInstruction(forEachErrReporter, bodyDefinition, itVarCls,
              newReporterWithToken(targetExprContext.getStart())));
          
          return null;
      }
      
      @Override
      public Void visitWhileStatement(WhileStatementContext ctx) {
          int whileCount = whileCount();
          
          String whileConditionScope =
              generatorScope.getName() + SCOPE_SEPARATOR + WHILE_PREFIX + whileCount + CONDITION_SUFFIX;
          QvmInstructionVisitor conditionSubVisitor = parseExprBodyWithSubVisitor(ctx.expression(),
              new GeneratorScope(whileConditionScope, generatorScope),
              Context.BLOCK);
          QLambdaDefinitionInner conditionLambda = new QLambdaDefinitionInner(whileConditionScope,
              conditionSubVisitor.getInstructions(), Collections.emptyList(), conditionSubVisitor.getMaxStackSize());
          
          ErrorReporter whileErrReporter = newReporterWithToken(ctx.WHILE().getSymbol());
          QLambdaDefinition whileBodyLambda = loopBodyVisitorDefinition(ctx.blockStatements(),
              generatorScope.getName() + SCOPE_SEPARATOR + WHILE_PREFIX + whileCount + BODY_SUFFIX,
              Collections.emptyList(),
              whileErrReporter);
          
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(
                  new TraceEvaludatedInstruction(whileErrReporter, ctx.WHILE().getSymbol().getStartIndex()));
          }
          
          addInstruction(new WhileInstruction(whileErrReporter, conditionLambda, whileBodyLambda,
              whileBodyLambda instanceof QLambdaDefinitionInner
                  ? Math.max(conditionLambda.getMaxStackSize(),
                      ((QLambdaDefinitionInner)whileBodyLambda).getMaxStackSize())
                  : conditionLambda.getMaxStackSize()));
          return null;
      }
      
      private QLambdaDefinition loopBodyVisitorDefinition(BlockStatementsContext bodyCtx, String scopeName,
          List<QLambdaDefinitionInner.Param> paramsType, ErrorReporter errorReporter) {
          if (bodyCtx == null) {
              return QLambdaDefinitionEmpty.INSTANCE;
          }
          QvmInstructionVisitor bodyVisitor =
              parseWithSubVisitor(bodyCtx, new GeneratorScope(scopeName, generatorScope), Context.MACRO);
          List<QLInstruction> bodyInstructions = bodyVisitor.getInstructions();
          
          List<QLInstruction> resultInstructions = new ArrayList<>();
          resultInstructions.add(new CheckTimeOutInstruction(errorReporter));
          resultInstructions.addAll(bodyInstructions);
          
          return new QLambdaDefinitionInner(scopeName, resultInstructions, paramsType, bodyVisitor.getMaxStackSize());
      }
      
      @Override
      public Void visitThrowStatement(ThrowStatementContext ctx) {
          ctx.expression().accept(this);
          addInstruction(new ThrowInstruction(newReporterWithToken(ctx.THROW().getSymbol())));
          return null;
      }
      
      @Override
      public Void visitReturnStatement(ReturnStatementContext ctx) {
          ErrorReporter errorReporter = newReporterWithToken(ctx.getStart());
          ExpressionContext expression = ctx.expression();
          if (expression == null) {
              addInstruction(new ConstInstruction(errorReporter, null, null));
          }
          else {
              expression.accept(this);
          }
          
          addInstruction(new ReturnInstruction(errorReporter, QResult.ResultType.RETURN, ctx.getStart().getStartIndex()));
          return null;
      }
      
      @Override
      public Void visitFunctionStatement(FunctionStatementContext ctx) {
          FormalOrInferredParameterListContext formalOrInferredParameterList = ctx.formalOrInferredParameterList();
          List<QLambdaDefinitionInner.Param> params = formalOrInferredParameterList == null ? Collections.emptyList()
              : parseFormalOrInferredParameterList(formalOrInferredParameterList);
          VarIdContext functionNameCtx = ctx.varId();
          QLambdaDefinition functionDefinition = parseFunctionDefinition(functionNameCtx.getText(), ctx, params);
          
          ErrorReporter errorReporter = newReporterWithToken(functionNameCtx.getStart());
          
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(
                  new TraceEvaludatedInstruction(errorReporter, functionNameCtx.getStart().getStartIndex()));
          }
          
          addInstruction(new DefineFunctionInstruction(errorReporter, functionDefinition.getName(), functionDefinition));
          return null;
      }
      
      private QLambdaDefinition parseFunctionDefinition(String functionName, FunctionStatementContext ctx,
          List<QLambdaDefinitionInner.Param> params) {
          BlockStatementsContext blockStatementsContext = ctx.blockStatements();
          if (blockStatementsContext == null) {
              return new QLambdaDefinitionInner(functionName, Collections.emptyList(), params, 0);
          }
          
          QvmInstructionVisitor functionSubVisitor = parseWithSubVisitor(blockStatementsContext,
              new GeneratorScope(functionName, generatorScope),
              Context.BLOCK);
          return new QLambdaDefinitionInner(functionName, functionSubVisitor.getInstructions(), params,
              functionSubVisitor.getMaxStackSize());
      }
      
      @Override
      public Void visitCastExpr(CastExprContext ctx) {
          DeclTypeContext castDeclTypeContext = ctx.declType();
          Class<?> castCls = parseDeclType(castDeclTypeContext);
          ErrorReporter errorReporter = newReporterWithToken(castDeclTypeContext.getStart());
          addInstruction(new ConstInstruction(errorReporter, castCls, null));
          ctx.primary().accept(this);
          addInstruction(new CastInstruction(errorReporter));
          return null;
      }
      
      @Override
      public Void visitTernaryExpr(TernaryExprContext ctx) {
          ctx.condition.accept(this);
          
          if (ctx.QUESTION() != null) {
              QvmInstructionVisitor thenVisitor = parseWithSubVisitor(ctx.thenExpr, generatorScope, Context.MACRO);
              QvmInstructionVisitor elseVisitor = parseWithSubVisitor(ctx.elseExpr, generatorScope, Context.MACRO);
              ifElseInstructions(newReporterWithToken(ctx.QUESTION().getSymbol()),
                  thenVisitor.getInstructions(),
                  null,
                  elseVisitor.getInstructions(),
                  null,
                  ctx.QUESTION().getSymbol().getStartIndex());
          }
          
          return null;
      }
      
      @Override
      public Void visitBlockExpr(BlockExprContext ctx) {
          ErrorReporter blockErrReporter = newReporterWithToken(ctx.getStart());
          BlockStatementsContext blockStatementsContext = ctx.blockStatements();
          if (blockStatementsContext == null) {
              addInstruction(new ConstInstruction(blockErrReporter, null, null));
              return null;
          }
          
          String blockScopeName = blockScopeName();
          QvmInstructionVisitor blockSubVisitor = parseWithSubVisitor(blockStatementsContext,
              new GeneratorScope(blockScopeName, generatorScope),
              Context.MACRO);
          
          addInstruction(new NewScopeInstruction(blockErrReporter, blockScopeName));
          blockSubVisitor.getInstructions().forEach(this::addInstruction);
          addInstruction(new CloseScopeInstruction(blockErrReporter, blockScopeName));
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(new TracePeekInstruction(blockErrReporter, ctx.getStart().getStartIndex()));
          }
          return null;
      }
      
      @Override
      public Void visitQlIf(QlIfContext qlIfContext) {
          qlIfContext.condition.accept(this);
          
          int ifCount = ifCount();
          ErrorReporter ifErrorReporter = newReporterWithToken(qlIfContext.IF().getSymbol());
          String ifScopeName = generatorScope.getName() + SCOPE_SEPARATOR + IF_PREFIX + ifCount;
          addInstruction(new NewScopeInstruction(ifErrorReporter, ifScopeName));
          
          String thenScopeName = generatorScope.getName() + SCOPE_SEPARATOR + IF_PREFIX + ifCount + THEN_SUFFIX;
          ThenBodyContext thenBodyContext = qlIfContext.thenBody();
          List<QLInstruction> thenInstructions =
              parseWithSubVisitor(thenBodyContext, new GeneratorScope(thenScopeName, generatorScope), Context.MACRO)
                  .getInstructions();
          if (ifBodyFillConst(thenBodyContext.expression(),
              thenBodyContext.blockStatement(),
              thenBodyContext.blockStatements())) {
              thenInstructions.add(new ConstInstruction(ifErrorReporter, null, null));
          }
          Integer thenTraceKey = thenBodyContext.LBRACE() == null ? null : thenBodyContext.getStart().getStartIndex();
          
          String elseScopeName = generatorScope.getName() + SCOPE_SEPARATOR + IF_PREFIX + ifCount + ELSE_SUFFIX;
          ElseBodyContext elseBodyContext = qlIfContext.elseBody();
          List<QLInstruction> elseInstructions =
              elseBodyContext == null ? Collections.singletonList(new ConstInstruction(ifErrorReporter, null, null))
                  : parseWithSubVisitor(elseBodyContext, new GeneratorScope(elseScopeName, generatorScope), Context.MACRO)
                      .getInstructions();
          if (elseBodyContext != null && elseBodyContext.qlIf() == null
              && ifBodyFillConst(elseBodyContext.expression(),
                  elseBodyContext.blockStatement(),
                  elseBodyContext.blockStatements())) {
              elseInstructions.add(new ConstInstruction(ifErrorReporter, null, null));
          }
          Integer elseTraceKey = elseBodyContext == null ? null
              : (elseBodyContext.LBRACE() == null ? null : elseBodyContext.getStart().getStartIndex());
          
          ifElseInstructions(ifErrorReporter,
              thenInstructions,
              thenTraceKey,
              elseInstructions,
              elseTraceKey,
              qlIfContext.getStart().getStartIndex());
          
          addInstruction(new CloseScopeInstruction(ifErrorReporter, ifScopeName));
          return null;
      }
      
      private boolean ifBodyFillConst(ExpressionContext expressionContext, BlockStatementContext blockStatementContext,
          BlockStatementsContext blockStatementsContext) {
          if (expressionContext != null) {
              return false;
          }
          if (blockStatementContext != null) {
              return stmtFillConst(blockStatementContext);
          }
          if (blockStatementsContext != null) {
              List<BlockStatementContext> statementList = blockStatementsContext.blockStatement()
                  .stream()
                  .filter(bs -> !(bs instanceof EmptyStatementContext))
                  .collect(Collectors.toList());
              return statementList.isEmpty() || stmtFillConst(statementList.get(statementList.size() - 1));
          }
          return true;
      }
      
      private boolean stmtFillConst(BlockStatementContext blockStatementContext) {
          return !(blockStatementContext instanceof ExpressionStatementContext)
              && !(blockStatementContext instanceof ReturnStatementContext);
      }
      
      @Override
      public Void visitBreakContinueStatement(BreakContinueStatementContext ctx) {
          TerminalNode aBreak = ctx.BREAK();
          QResult qResult = aBreak == null ? QResult.LOOP_CONTINUE_RESULT : QResult.LOOP_BREAK_RESULT;
          
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(
                  new TraceEvaludatedInstruction(newReporterWithToken(ctx.getStart()), ctx.getStart().getStartIndex()));
          }
          
          addInstruction(new BreakContinueInstruction(newReporterWithToken(ctx.getStart()), qResult));
          return null;
      }
      
      @Override
      public Void visitListExpr(ListExprContext ctx) {
          visitListExprInner(ctx.listItems(), newReporterWithToken(ctx.getStart()));
          return null;
      }
      
      private void visitListExprInner(ListItemsContext listItemsContext, ErrorReporter listErrorReporter) {
          if (listItemsContext == null) {
              addInstruction(new NewListInstruction(listErrorReporter, 0));
              return;
          }
          List<ExpressionContext> expressions = listItemsContext.expression();
          for (ExpressionContext expression : expressions) {
              expression.accept(this);
          }
          addInstruction(new NewListInstruction(listErrorReporter, expressions.size()));
      }
      
      @Override
      public Void visitMapExpr(MapExprContext ctx) {
          MapEntriesContext mapEntriesContext = ctx.mapEntries();
          List<MapEntryContext> mapEntryContexts = mapEntriesContext.mapEntry();
          List<String> keys = new ArrayList<>(mapEntryContexts.size());
          Class<?> cls = null;
          for (MapEntryContext mapEntryContext : mapEntryContexts) {
              MapValueContext valueContext = mapEntryContext.mapValue();
              if (valueContext instanceof EValueContext) {
                  EValueContext eValueContext = (EValueContext)valueContext;
                  keys.add(parseMapKey(mapEntryContext.mapKey()));
                  eValueContext.expression().accept(this);
                  continue;
              }
              if (valueContext instanceof ClsValueContext) {
                  ClsValueContext clsValueContext = (ClsValueContext)valueContext;
                  TerminalNode clsLiteral = clsValueContext.QuoteStringLiteral();
                  String clsText = clsLiteral.getText();
                  String clsName = clsText.substring(1, clsText.length() - 1);
                  Class<?> mayBeCls = importManager.loadQualified(clsName);
                  if (mayBeCls == null) {
                      String clsKeyText = mapEntryContext.mapKey().getText();
                      keys.add(clsKeyText.substring(1, clsKeyText.length() - 1));
                      addInstruction(new ConstInstruction(newReporterWithToken(clsLiteral.getSymbol()),
                          QLStringUtils.parseStringEscape(clsText), null));
                      // @class override
                      cls = null;
                  }
                  else {
                      cls = mayBeCls;
                  }
              }
          }
          if (cls == null) {
              addInstruction(new NewMapInstruction(newReporterWithToken(ctx.getStart()), keys));
          }
          else {
              addInstruction(new NewFilledInstanceInstruction(newReporterWithToken(ctx.getStart()), cls, keys));
          }
          return null;
      }
      
      private String parseMapKey(MapKeyContext mapKeyContext) {
          if (mapKeyContext instanceof IdKeyContext) {
              return mapKeyContext.getText();
          }
          else if (mapKeyContext instanceof StringKeyContext || mapKeyContext instanceof QuoteStringKeyContext) {
              return QLStringUtils.parseStringEscape(mapKeyContext.getText());
          }
          // shouldn't run here
          throw new IllegalStateException();
      }
      
      @Override
      public Void visitNewObjExpr(NewObjExprContext ctx) {
          Class<?> newCls = parseClsIds(ctx.varId());
          ArgumentListContext argumentListContext = ctx.argumentList();
          if (argumentListContext != null) {
              argumentListContext.accept(this);
          }
          int argNum = argumentListContext == null ? 0 : argumentListContext.expression().size();
          addInstruction(new NewInstanceInstruction(newReporterWithToken(ctx.NEW().getSymbol()), newCls, argNum));
          return null;
      }
      
      @Override
      public Void visitNewEmptyArrExpr(NewEmptyArrExprContext ctx) {
          ctx.dimExprs().accept(this);
          int dims = ctx.dimExprs().expression().size();
          Class<?> arrCls = parseDeclTypeNoArr(ctx.declTypeNoArr());
          addInstruction(new MultiNewArrayInstruction(newReporterWithToken(ctx.NEW().getSymbol()), arrCls, dims));
          return null;
      }
      
      @Override
      public Void visitNewInitArrExpr(NewInitArrExprContext ctx) {
          Class<?> cls = parseDeclTypeNoArr(ctx.declTypeNoArr());
          ArrayInitializerContext arrayInitializerContext = ctx.arrayInitializer();
          newArrWithInitializers(embedClsInDims(cls, ctx.dims().LBRACK().size() - 1), arrayInitializerContext);
          return null;
      }
      
      private Class<?> embedClsInDims(Class<?> cls, int dims) {
          for (int i = 0; i < dims; i++) {
              cls = Array.newInstance(cls, 0).getClass();
          }
          return cls;
      }
      
      @Override
      public Void visitLambdaExpr(LambdaExprContext ctx) {
          List<QLambdaDefinitionInner.Param> lambdaParams = parseLambdaParams(ctx.lambdaParameters());
          String lambdaScopeName = lambdaScopeName();
          
          QvmInstructionVisitor subVisitor = null;
          ExpressionContext expression = ctx.expression();
          ErrorReporter arrowErrorReporter = newReporterWithToken(ctx.ARROW().getSymbol());
          if (expression != null) {
              subVisitor = parseExprBodyWithSubVisitor(expression,
                  new GeneratorScope(lambdaScopeName, generatorScope),
                  Context.BLOCK);
          }
          else {
              BlockStatementsContext blockStatementsContext = ctx.blockStatements();
              if (blockStatementsContext != null) {
                  subVisitor = parseWithSubVisitor(blockStatementsContext,
                      new GeneratorScope(lambdaScopeName, generatorScope),
                      Context.BLOCK);
              }
          }
          
          if (subVisitor == null) {
              addInstruction(new LoadLambdaInstruction(arrowErrorReporter, QLambdaDefinitionEmpty.INSTANCE));
          }
          else {
              QLambdaDefinition lambdaDefinition = new QLambdaDefinitionInner(lambdaScopeName,
                  subVisitor.getInstructions(), lambdaParams, subVisitor.getMaxStackSize());
              addInstruction(new LoadLambdaInstruction(arrowErrorReporter, lambdaDefinition));
          }
          return null;
      }
      
      private List<QLambdaDefinitionInner.Param> parseLambdaParams(LambdaParametersContext lambdaParametersContext) {
          VarIdContext varIdContext = lambdaParametersContext.varId();
          if (varIdContext != null) {
              return Collections.singletonList(new QLambdaDefinitionInner.Param(varIdContext.getText(), Object.class));
          }
          FormalOrInferredParameterListContext formalOrInferredParameterList =
              lambdaParametersContext.formalOrInferredParameterList();
          if (formalOrInferredParameterList == null) {
              return Collections.emptyList();
          }
          return formalOrInferredParameterList.formalOrInferredParameter()
              .stream()
              .map(this::formalOrInferredParameter2Param)
              .collect(Collectors.toList());
      }
      
      private List<QLambdaDefinitionInner.Param> parseFormalOrInferredParameterList(
          FormalOrInferredParameterListContext formalOrInferredParameterList) {
          return formalOrInferredParameterList.formalOrInferredParameter()
              .stream()
              .map(this::formalOrInferredParameter2Param)
              .collect(Collectors.toList());
      }
      
      private QLambdaDefinitionInner.Param formalOrInferredParameter2Param(
          FormalOrInferredParameterContext formalOrInferredParameterContext) {
          String paramName = formalOrInferredParameterContext.varId().getText();
          DeclTypeContext declTypeContext = formalOrInferredParameterContext.declType();
          Class<?> paramCls = declTypeContext == null ? Object.class : parseDeclType(declTypeContext);
          return new QLambdaDefinitionInner.Param(paramName, paramCls);
      }
      
      @Override
      public Void visitTryCatchExpr(TryCatchExprContext ctx) {
          BlockStatementsContext blockStatementsContext = ctx.blockStatements();
          if (blockStatementsContext == null) {
              addInstruction(new ConstInstruction(newReporterWithToken(ctx.TRY().getSymbol()), null,
                  ctx.getStart().getStartIndex()));
              return null;
          }
          
          int tryCount = tryCount();
          String tryScopeName = generatorScope.getName() + SCOPE_SEPARATOR + TRY_PREFIX + tryCount;
          QvmInstructionVisitor bodySubVisitor = parseWithSubVisitor(blockStatementsContext,
              new GeneratorScope(tryScopeName, generatorScope),
              Context.BLOCK);
          
          QLambdaDefinition bodyLambdaDefinition = new QLambdaDefinitionInner(tryScopeName,
              bodySubVisitor.getInstructions(), Collections.emptyList(), bodySubVisitor.getMaxStackSize());
          List<Map.Entry<Class<?>, QLambdaDefinition>> exceptionTable = parseExceptionTable(tryCount, ctx);
          QLambdaDefinition finalBodyDefinition = parseFinalBodyDefinition(tryCount, ctx);
          
          addInstruction(new TryCatchInstruction(newReporterWithToken(ctx.TRY().getSymbol()), bodyLambdaDefinition,
              exceptionTable, finalBodyDefinition));
          return null;
      }
      
      private QLambdaDefinition parseFinalBodyDefinition(int tryCount, TryCatchExprContext ctx) {
          TryFinallyContext tryFinallyContext = ctx.tryFinally();
          if (tryFinallyContext == null) {
              return null;
          }
          
          BlockStatementsContext blockStatementsContext = tryFinallyContext.blockStatements();
          if (blockStatementsContext == null) {
              return null;
          }
          
          String finalScopeName = generatorScope.getName() + SCOPE_SEPARATOR + TRY_PREFIX + tryCount + FINAL_SUFFIX;
          QvmInstructionVisitor finalBodySubVisitor = parseWithSubVisitor(blockStatementsContext,
              new GeneratorScope(finalScopeName, generatorScope),
              Context.BLOCK);
          return new QLambdaDefinitionInner(finalScopeName, finalBodySubVisitor.getInstructions(),
              Collections.emptyList(), finalBodySubVisitor.getMaxStackSize());
      }
      
      private List<Map.Entry<Class<?>, QLambdaDefinition>> parseExceptionTable(int tryCount, TryCatchExprContext ctx) {
          TryCatchesContext tryCatchesContext = ctx.tryCatches();
          if (tryCatchesContext == null) {
              return Collections.emptyList();
          }
          List<TryCatchContext> tryCatchContexts = tryCatchesContext.tryCatch();
          int catchSize = tryCatchContexts.size();
          List<Map.Entry<Class<?>, QLambdaDefinition>> exceptionTable = new ArrayList<>(catchSize);
          for (TryCatchContext tryCatchContext : tryCatchContexts) {
              CatchParamsContext catchParamsContext = tryCatchContext.catchParams();
              String eName = catchParamsContext.varId().getText();
              String catchBodyName = generatorScope.getName() + SCOPE_SEPARATOR + TRY_PREFIX + tryCount + CATCH_SUFFIX;
              QvmInstructionVisitor catchSubVisitor = tryCatchContext.blockStatements() == null ? null
                  : parseWithSubVisitor(tryCatchContext.blockStatements(),
                      new GeneratorScope(catchBodyName, generatorScope),
                      Context.BLOCK);
              
              List<DeclTypeContext> catchDeclTypes = catchParamsContext.declType();
              if (catchDeclTypes.isEmpty()) {
                  QLambdaDefinitionInner.Param param = new QLambdaDefinitionInner.Param(eName, Object.class);
                  QLambdaDefinition exceptionHandlerDefinition = catchSubVisitor == null ? QLambdaDefinitionEmpty.INSTANCE
                      : new QLambdaDefinitionInner(catchBodyName, catchSubVisitor.getInstructions(),
                          Collections.singletonList(param), catchSubVisitor.getMaxStackSize());
                  exceptionTable.add(new AbstractMap.SimpleEntry<>(Object.class, exceptionHandlerDefinition));
              }
              for (DeclTypeContext declTypeContext : catchDeclTypes) {
                  Class<?> exceptionType = parseDeclType(declTypeContext);
                  QLambdaDefinitionInner.Param param = new QLambdaDefinitionInner.Param(eName, exceptionType);
                  QLambdaDefinition exceptionHandlerDefinition = catchSubVisitor == null ? QLambdaDefinitionEmpty.INSTANCE
                      : new QLambdaDefinitionInner(catchBodyName, catchSubVisitor.getInstructions(),
                          Collections.singletonList(param), catchSubVisitor.getMaxStackSize());
                  exceptionTable.add(new AbstractMap.SimpleEntry<>(exceptionType, exceptionHandlerDefinition));
              }
          }
          return exceptionTable;
      }
      
      @Override
      public Void visitVarIdExpr(VarIdExprContext ctx) {
          addInstruction(new LoadInstruction(newReporterWithToken(ctx.getStart()), ctx.varId().getText(),
              ctx.getStart().getStartIndex()));
          return null;
      }
      
      private int parsePathHeadPart(PrimaryNoFixPathableContext primaryNoFixPathableContext,
          List<PathPartContext> pathPartContexts) {
          if (primaryNoFixPathableContext instanceof TypeExprContext) {
              Class<?> cls = BuiltInTypesSet.getCls(primaryNoFixPathableContext.getStart().getText());
              int dimPartNum = parseDimParts(0, pathPartContexts);
              addInstruction(new ConstInstruction(newReporterWithToken(primaryNoFixPathableContext.getStart()),
                  new MetaClass(dimPartNum > 0 ? wrapInArray(cls, dimPartNum) : cls), null));
              return dimPartNum;
          }
          else if (primaryNoFixPathableContext instanceof VarIdExprContext) {
              VarIdExprContext idContext = (VarIdExprContext)primaryNoFixPathableContext;
              return parseIdHeadPart(idContext.varId(), pathPartContexts);
          }
          else {
              primaryNoFixPathableContext.accept(this);
              return 0;
          }
      }
      
      private int parseIdHeadPart(VarIdContext idContext, List<PathPartContext> pathPartContexts) {
          if (!pathPartContexts.isEmpty() && pathPartContexts.get(0) instanceof CallExprContext) {
              // function call
              CallExprContext callExprContext = (CallExprContext)pathPartContexts.get(0);
              ArgumentListContext argumentListContext = callExprContext.argumentList();
              visitCallFunction(idContext, argumentListContext);
              return 1;
          }
          List<String> headPartIds = new ArrayList<>();
          headPartIds.add(idContext.getText());
          for (PathPartContext pathPartContext : pathPartContexts) {
              if (pathPartContext instanceof FieldAccessContext) {
                  headPartIds.add(parseFieldId(((FieldAccessContext)pathPartContext).fieldId()));
              }
              else {
                  break;
              }
          }
          ImportManager.LoadPartQualifiedResult loadPartQualifiedResult = importManager.loadPartQualified(headPartIds);
          if (loadPartQualifiedResult.getCls() != null) {
              int restIndex = loadPartQualifiedResult.getRestIndex() - 1;
              Token clsReportToken =
                  restIndex == 0 ? idContext.getStart() : pathPartContexts.get(restIndex - 1).getStop();
              int dimPartNum = parseDimParts(restIndex, pathPartContexts);
              Class<?> cls = dimPartNum > 0 ? wrapInArray(loadPartQualifiedResult.getCls(), dimPartNum)
                  : loadPartQualifiedResult.getCls();
              
              addInstruction(new ConstInstruction(newReporterWithToken(clsReportToken), new MetaClass(cls), null));
              return restIndex + dimPartNum;
          }
          else {
              addInstruction(new LoadInstruction(newReporterWithToken(idContext.getStart()), idContext.getText(),
                  idContext.getStart().getStartIndex()));
              return 0;
          }
      }
      
      private int parseDimParts(int start, List<PathPartContext> pathPartContexts) {
          int i = start;
          for (; i < pathPartContexts.size(); i++) {
              PathPartContext pathPartContext = pathPartContexts.get(i);
              if (!isEmptyIndex(pathPartContext)) {
                  break;
              }
          }
          return i - start;
      }
      
      private boolean isEmptyIndex(PathPartContext pathPartContext) {
          if (!(pathPartContext instanceof IndexExprContext)) {
              return false;
          }
          IndexExprContext indexExprContext = (IndexExprContext)pathPartContext;
          return indexExprContext.indexValueExpr() == null;
      }
      
      @Override
      public Void visitMethodInvoke(MethodInvokeContext ctx) {
          visitMethodInvokeInner(ctx.argumentList(), ctx.varId(), false);
          return null;
      }
      
      @Override
      public Void visitOptionalMethodInvoke(OptionalMethodInvokeContext ctx) {
          visitMethodInvokeInner(ctx.argumentList(), ctx.varId(), true);
          return null;
      }
      
      @Override
      public Void visitSpreadMethodInvoke(SpreadMethodInvokeContext ctx) {
          ArgumentListContext argumentListContext = ctx.argumentList();
          if (argumentListContext != null) {
              argumentListContext.accept(this);
          }
          VarIdContext methodName = ctx.varId();
          int argNum = argumentListContext == null ? 0 : argumentListContext.expression().size();
          addInstruction(new SpreadMethodInvokeInstruction(newReporterWithToken(methodName.getStart()),
              methodName.getText(), argNum));
          return null;
      }
      
      private void visitMethodInvokeInner(ArgumentListContext argumentListContext, VarIdContext methodName,
          boolean optional) {
          if (argumentListContext != null) {
              argumentListContext.accept(this);
          }
          int argNum = argumentListContext == null ? 0 : argumentListContext.expression().size();
          addInstruction(new MethodInvokeInstruction(newReporterWithToken(methodName.getStart()), methodName.getText(),
              argNum, optional));
      }
      
      @Override
      public Void visitFieldAccess(FieldAccessContext ctx) {
          Token fieldNameToken = ctx.getStop();
          addInstruction(
              new GetFieldInstruction(newReporterWithToken(fieldNameToken), parseFieldId(ctx.fieldId()), false));
          return null;
      }
      
      @Override
      public Void visitOptionalFieldAccess(OptionalFieldAccessContext ctx) {
          Token fieldNameToken = ctx.getStop();
          addInstruction(
              new GetFieldInstruction(newReporterWithToken(fieldNameToken), parseFieldId(ctx.fieldId()), true));
          return null;
      }
      
      @Override
      public Void visitSpreadFieldAccess(SpreadFieldAccessContext ctx) {
          Token fieldNameToken = ctx.getStop();
          addInstruction(
              new SpreadGetFieldInstruction(newReporterWithToken(fieldNameToken), parseFieldId(ctx.fieldId())));
          return null;
      }
      
      private String parseFieldId(FieldIdContext ctx) {
          TerminalNode quoteStringLiteral = ctx.QuoteStringLiteral();
          if (quoteStringLiteral != null) {
              return QLStringUtils.parseStringEscape(quoteStringLiteral.getText());
          }
          return ctx.getStart().getText();
      }
      
      @Override
      public Void visitMethodAccess(MethodAccessContext ctx) {
          VarIdContext methodName = ctx.varId();
          addInstruction(new GetMethodInstruction(newReporterWithToken(ctx.DCOLON().getSymbol()), methodName.getText()));
          return null;
      }
      
      @Override
      public Void visitCallExpr(CallExprContext ctx) {
          ArgumentListContext argumentListContext = ctx.argumentList();
          if (argumentListContext != null) {
              argumentListContext.accept(this);
          }
          int argNum = argumentListContext == null ? 0 : argumentListContext.expression().size();
          addInstruction(new CallInstruction(newReporterWithToken(ctx.getStart()), argNum));
          return null;
      }
      
      @Override
      public Void visitIndexExpr(IndexExprContext ctx) {
          IndexValueExprContext indexValueExprContext = ctx.indexValueExpr();
          if (indexValueExprContext == null) {
              throw reportParseErr(ctx.getStop(),
                  QLErrorCodes.MISSING_INDEX.name(),
                  QLErrorCodes.MISSING_INDEX.getErrorMsg());
          }
          ErrorReporter errorReporter = newReporterWithToken(ctx.getStart());
          if (indexValueExprContext instanceof SingleIndexContext) {
              ((SingleIndexContext)indexValueExprContext).expression().accept(this);
              addInstruction(new IndexInstruction(errorReporter));
          }
          else if (indexValueExprContext instanceof SliceIndexContext) {
              SliceIndexContext sliceIndexContext = (SliceIndexContext)indexValueExprContext;
              if (sliceIndexContext.start == null && sliceIndexContext.end == null) {
                  addInstruction(new SliceInstruction(errorReporter, SliceInstruction.Mode.COPY));
              }
              else if (sliceIndexContext.start == null) {
                  sliceIndexContext.end.accept(this);
                  addInstruction(new SliceInstruction(errorReporter, SliceInstruction.Mode.LEFT));
              }
              else if (sliceIndexContext.end == null) {
                  sliceIndexContext.start.accept(this);
                  addInstruction(new SliceInstruction(errorReporter, SliceInstruction.Mode.RIGHT));
              }
              else {
                  sliceIndexContext.start.accept(this);
                  sliceIndexContext.end.accept(this);
                  addInstruction(new SliceInstruction(errorReporter, SliceInstruction.Mode.BOTH));
              }
          }
          return null;
      }
      
      @Override
      public Void visitCustomPath(CustomPathContext ctx) {
          ErrorReporter errorReporter = newReporterWithToken(ctx.getStart());
          String path = ctx.varId().getText();
          addInstruction(new ConstInstruction(errorReporter, path, null));
          
          String operatorId = ctx.opId().getText();
          BinaryOperator binaryOperator = operatorFactory.getBinaryOperator(operatorId);
          addInstruction(new OperatorInstruction(errorReporter, binaryOperator, ctx.opId().getStart().getStartIndex()));
          return null;
      }
      
      @Override
      public Void visitLeftAsso(LeftAssoContext ctx) {
          BinaryopContext binaryopContext = ctx.binaryop();
          ErrorReporter opErrReporter = newReporterWithToken(binaryopContext.getStart());
          String operatorId = binaryopContext.getText();
          BaseExprContext rightExpr = ctx.baseExpr();
          // short circuit operator
          if ("&&".equals(operatorId)) {
              jumpRightIfExpect(false, opErrReporter, rightExpr, operatorId, binaryopContext.getStart().getStartIndex());
          }
          else if ("||".equals(operatorId)) {
              jumpRightIfExpect(true, opErrReporter, rightExpr, operatorId, binaryopContext.getStart().getStartIndex());
          }
          else {
              rightExpr.accept(this);
              BinaryOperator binaryOperator = operatorFactory.getBinaryOperator(operatorId);
              addInstruction(
                  new OperatorInstruction(opErrReporter, binaryOperator, binaryopContext.getStart().getStartIndex()));
          }
          return null;
      }
      
      @Override
      public Void visitMacroStatement(MacroStatementContext ctx) {
          String macroId = ctx.varId().getText();
          
          BlockStatementsContext macroBlockStatementsContext = ctx.blockStatements();
          
          BlockStatementContext lastStmt = getMacroLastStmt(ctx, macroBlockStatementsContext);
          generatorScope.defineMacro(macroId,
              new MacroDefine(getMacroInstructions(macroBlockStatementsContext),
                  lastStmt instanceof ExpressionStatementContext));
          
          if (initOptions.isTraceExpression()) {
              pureAddInstruction(new TraceEvaludatedInstruction(newReporterWithToken(ctx.varId().getStart()),
                  ctx.varId().getStart().getStartIndex()));
          }
          
          return null;
      }
      
      private BlockStatementContext getMacroLastStmt(MacroStatementContext macroCtx,
          BlockStatementsContext macroBlockStatementsCtx) {
          if (macroBlockStatementsCtx == null) {
              return macroCtx;
          }
          List<BlockStatementContext> blockStatementContexts = macroBlockStatementsCtx.blockStatement()
              .stream()
              .filter(bs -> !(bs instanceof EmptyStatementContext))
              .collect(Collectors.toList());
          return blockStatementContexts.isEmpty() ? macroCtx
              : blockStatementContexts.get(blockStatementContexts.size() - 1);
      }
      
      private List<QLInstruction> getMacroInstructions(BlockStatementsContext macroBlockStatementsContext) {
          if (macroBlockStatementsContext == null) {
              return Collections.emptyList();
          }
          else {
              QvmInstructionVisitor subVisitor = parseWithSubVisitor(macroBlockStatementsContext,
                  new GeneratorScope(macroScopeName(), generatorScope),
                  Context.MACRO);
              return subVisitor.getInstructions();
          }
      }
      
      @Override
      public Void visitLocalVariableDeclaration(LocalVariableDeclarationContext ctx) {
          if (initOptions.isTraceExpression()) {
              addInstruction(
                  new TraceEvaludatedInstruction(newReporterWithToken(ctx.getStart()), ctx.getStart().getStartIndex()));
          }
          
          Class<?> declCls = parseDeclType(ctx.declType());
          List<VariableDeclaratorContext> variableDeclaratorContexts = ctx.variableDeclaratorList().variableDeclarator();
          for (VariableDeclaratorContext variableDeclarator : variableDeclaratorContexts) {
              VariableInitializerContext variableInitializer = variableDeclarator.variableInitializer();
              if (variableInitializer == null) {
                  addInstruction(new ConstInstruction(newReporterWithToken(variableDeclarator.getStop()), null, null));
              }
              else {
                  parseInitializer(variableInitializer, declCls);
              }
              VariableDeclaratorIdContext variableDeclaratorIdContext = variableDeclarator.variableDeclaratorId();
              addInstruction(new DefineLocalInstruction(newReporterWithToken(variableDeclaratorIdContext.getStart()),
                  variableDeclaratorIdContext.getText(), declCls));
          }
          return null;
      }
      
      private void parseInitializer(VariableInitializerContext variableInitializer, Class<?> declCls) {
          ExpressionContext expression = variableInitializer.expression();
          if (expression != null) {
              expression.accept(this);
              return;
          }
          ArrayInitializerContext arrayInitializerContext = variableInitializer.arrayInitializer();
          newArrWithInitializers(declCls, arrayInitializerContext);
      }
      
      private void newArrWithInitializers(Class<?> componentClz, ArrayInitializerContext arrayInitializerContext) {
          VariableInitializerListContext variableInitializerListContext =
              arrayInitializerContext.variableInitializerList();
          List<VariableInitializerContext> initializerContexts =
              variableInitializerListContext == null ? Collections.emptyList()
                  : variableInitializerListContext.variableInitializer();
          for (VariableInitializerContext variableInitializerContext : initializerContexts) {
              variableInitializerContext.accept(this);
          }
          addInstruction(new NewArrayInstruction(newReporterWithToken(arrayInitializerContext.getStart()), componentClz,
              initializerContexts.size()));
      }
      
      @Override
      public Void visitLeftHandSide(LeftHandSideContext ctx) {
          VarIdContext idContext = ctx.varId();
          List<PathPartContext> pathPartContexts = ctx.pathPart();
          if (pathPartContexts.size() == 1 && pathPartContexts.get(0) instanceof CallExprContext) {
              CallExprContext callExprContext = (CallExprContext)pathPartContexts.get(0);
              ArgumentListContext argumentListContext = callExprContext.argumentList();
              visitCallFunction(idContext, argumentListContext);
          }
          else {
              int tailPartStart = parseIdHeadPart(idContext, pathPartContexts);
              for (int i = tailPartStart; i < pathPartContexts.size(); i++) {
                  pathPartContexts.get(i).accept(this);
              }
          }
          return null;
      }
      
      private void visitCallFunction(VarIdContext functionNameContext, ArgumentListContext argumentListContext) {
          String functionName = functionNameContext.getText();
          CompileTimeFunction compileTimeFunction = compileTimeFunctions.get(functionName);
          if (compileTimeFunction != null) {
              ErrorReporter functionNameReporter = newReporterWithToken(functionNameContext.getStart());
              compileTimeFunction.createFunctionInstruction(functionName,
                  argumentListContext == null ? Collections.emptyList() : argumentListContext.expression(),
                  operatorFactory,
                  new CodeGenerator() {
                      @Override
                      public void addInstruction(QLInstruction qlInstruction) {
                          QvmInstructionVisitor.this.addInstruction(qlInstruction);
                      }
                      
                      @Override
                      public void addInstructionsByTree(ParseTree tree) {
                          tree.accept(QvmInstructionVisitor.this);
                      }
                      
                      @Override
                      public QLSyntaxException reportParseErr(String errCode, String errReason) {
                          return QvmInstructionVisitor.this
                              .reportParseErr(functionNameContext.getStart(), errCode, errReason);
                      }
                      
                      @Override
                      public QLambdaDefinition generateLambdaDefinition(ExpressionContext expressionContext,
                          List<QLambdaDefinitionInner.Param> params) {
                          QvmInstructionVisitor subVisitor =
                              parseExprBodyWithSubVisitor(expressionContext, generatorScope, context);
                          return new QLambdaDefinitionInner(functionName, subVisitor.getInstructions(), params,
                              subVisitor.getMaxStackSize());
                      }
                      
                      @Override
                      public ErrorReporter getErrorReporter() {
                          return functionNameReporter;
                      }
                      
                      @Override
                      public ErrorReporter newReporterWithToken(Token token) {
                          return QvmInstructionVisitor.this.newReporterWithToken(token);
                      }
                  }
              
              );
              return;
          }
          
          if (argumentListContext != null) {
              argumentListContext.accept(this);
          }
          int argSize = argumentListContext == null ? 0 : argumentListContext.expression().size();
          addInstruction(new CallFunctionInstruction(newReporterWithToken(functionNameContext.getStart()), functionName,
              argSize, functionNameContext.getStart().getStartIndex()));
      }
      
      @Override
      public Void visitPrimary(PrimaryContext ctx) {
          if (ctx.primaryNoFixNonPathable() != null) {
              ctx.primaryNoFixNonPathable().accept(this);
              return null;
          }
          PrimaryNoFixPathableContext primaryNoFixPathableContext = ctx.primaryNoFixPathable();
          List<PathPartContext> pathPartContexts = ctx.pathPart();
          
          // path
          // head part
          int tailPartStart = parsePathHeadPart(primaryNoFixPathableContext, pathPartContexts);
          
          // tail part
          for (int i = tailPartStart; i < pathPartContexts.size(); i++) {
              pathPartContexts.get(i).accept(this);
          }
          
          SuffixExpressContext suffixExpressContext = ctx.suffixExpress();
          if (suffixExpressContext != null) {
              String suffixOperator = suffixExpressContext.getText();
              UnaryOperator suffixUnaryOperator = operatorFactory.getSuffixUnaryOperator(suffixOperator);
              addInstruction(new UnaryInstruction(newReporterWithToken(suffixExpressContext.getStart()),
                  suffixUnaryOperator, suffixExpressContext.getStart().getStartIndex()));
          }
          
          PrefixExpressContext prefixExpressContext = ctx.prefixExpress();
          if (prefixExpressContext != null) {
              String prefixOperator = prefixExpressContext.getText();
              UnaryOperator prefixUnaryOperator = operatorFactory.getPrefixUnaryOperator(prefixOperator);
              addInstruction(new UnaryInstruction(newReporterWithToken(prefixExpressContext.getStart()),
                  prefixUnaryOperator, prefixExpressContext.getStart().getStartIndex()));
          }
          return null;
      }
      
      @Override
      public Void visitTypeExpr(TypeExprContext ctx) {
          Class<?> cls = BuiltInTypesSet.getCls(ctx.primitiveType().getText());
          addInstruction(new ConstInstruction(newReporterWithToken(ctx.getStart()), new MetaClass(cls), null));
          return null;
      }
      
      @Override
      public Void visitArrayInitializer(ArrayInitializerContext ctx) {
          return super.visitArrayInitializer(ctx);
      }
      
      @Override
      public Void visitConstExpr(ConstExprContext ctx) {
          ctx.literal().accept(this);
          return null;
      }
      
      @Override
      public Void visitContextSelectExpr(ContextSelectExprContext ctx) {
          String variableName = ctx.SelectorVariable_VANME().getText().trim();
          addInstruction(
              new LoadInstruction(newReporterWithToken(ctx.getStart()), variableName, ctx.getStart().getStartIndex()));
          return null;
      }
      
      @Override
      public Void visitLiteral(LiteralContext literal) {
          TerminalNode integerLiteral = literal.IntegerLiteral();
          if (integerLiteral != null) {
              try {
                  Number intResult = parseInteger(remove(integerLiteral.getText(), '_'));
                  addInstruction(new ConstInstruction(newReporterWithToken(integerLiteral.getSymbol()), intResult,
                      integerLiteral.getSymbol().getStartIndex()));
              }
              catch (NumberFormatException nfe) {
                  throw reportParseErr(integerLiteral.getSymbol(),
                      QLErrorCodes.INVALID_NUMBER.name(),
                      QLErrorCodes.INVALID_NUMBER.getErrorMsg());
              }
              return null;
          }
          TerminalNode floatingPointLiteral = literal.FloatingPointLiteral();
          if (floatingPointLiteral != null) {
              try {
                  Number floatingResult = parseFloating(remove(floatingPointLiteral.getText(), '_'));
                  addInstruction(new ConstInstruction(newReporterWithToken(floatingPointLiteral.getSymbol()),
                      floatingResult, floatingPointLiteral.getSymbol().getStartIndex()));
              }
              catch (NumberFormatException nfe) {
                  throw reportParseErr(floatingPointLiteral.getSymbol(),
                      QLErrorCodes.INVALID_NUMBER.name(),
                      QLErrorCodes.INVALID_NUMBER.getErrorMsg());
              }
              return null;
          }
          TerminalNode integerOrFloatingLiteral = literal.IntegerOrFloatingLiteral();
          if (integerOrFloatingLiteral != null) {
              try {
                  String numberText = integerOrFloatingLiteral.getText();
                  Number numberResult = numberText.contains(".") ? parseFloating(remove(numberText, '_'))
                      : parseInteger(remove(numberText, '_'));
                  addInstruction(new ConstInstruction(newReporterWithToken(integerOrFloatingLiteral.getSymbol()),
                      numberResult, integerOrFloatingLiteral.getSymbol().getStartIndex()));
              }
              catch (NumberFormatException nfe) {
                  throw reportParseErr(integerOrFloatingLiteral.getSymbol(),
                      QLErrorCodes.INVALID_NUMBER.name(),
                      QLErrorCodes.INVALID_NUMBER.getErrorMsg());
              }
              return null;
          }
          BoolenLiteralContext booleanLiteral = literal.boolenLiteral();
          if (booleanLiteral != null) {
              boolean boolValue = Boolean.parseBoolean(booleanLiteral.getText());
              addInstruction(new ConstInstruction(newReporterWithToken(booleanLiteral.getStart()), boolValue,
                  booleanLiteral.getStart().getStartIndex()));
              return null;
          }
          TerminalNode quoteStringLiteral = literal.QuoteStringLiteral();
          if (quoteStringLiteral != null) {
              String escapedStr = QLStringUtils.parseStringEscape(quoteStringLiteral.getText());
              addInstruction(new ConstInstruction(newReporterWithToken(quoteStringLiteral.getSymbol()), escapedStr,
                  quoteStringLiteral.getSymbol().getStartIndex()));
              return null;
          }
          DoubleQuoteStringLiteralContext doubleQuoteStringLiteral = literal.doubleQuoteStringLiteral();
          if (doubleQuoteStringLiteral != null) {
              visitDoubleQuoteStringLiteral(doubleQuoteStringLiteral);
              return null;
          }
          TerminalNode nullLiteral = literal.NULL();
          if (nullLiteral != null) {
              addInstruction(new ConstInstruction(newReporterWithToken(nullLiteral.getSymbol()), null,
                  nullLiteral.getSymbol().getStartIndex()));
              return null;
          }
          return null;
      }
      
      @Override
      public Void visitDoubleQuoteStringLiteral(DoubleQuoteStringLiteralContext ctx) {
          if (initOptions.getInterpolationMode() == InterpolationMode.DISABLE) {
              TerminalNode characters = ctx.StaticStringCharacters();
              if (characters == null) {
                  addInstruction(new ConstInstruction(newReporterWithToken(ctx.getStart()), "", null));
                  return null;
              }
              String originText = characters.getText();
              addInstruction(new ConstInstruction(newReporterWithToken(ctx.getStart()),
                  QLStringUtils.parseStringEscapeStartEnd(originText, 0, originText.length()), null));
              return null;
          }
          int childCount = ctx.getChildCount();
          for (int i = 1; i < childCount - 1; i++) {
              ParseTree child = ctx.getChild(i);
              if (child instanceof StringExpressionContext) {
                  StringExpressionContext stringExpression = (StringExpressionContext)child;
                  ExpressionContext expression = stringExpression.expression();
                  if (expression != null) {
                      // SCRIPT
                      visitExpression(expression);
                  }
                  else {
                      // VARIABLE
                      TerminalNode varTerminalNode = stringExpression.SelectorVariable_VANME();
                      String varName = varTerminalNode.getText().trim();
                      addInstruction(
                          new LoadInstruction(newReporterWithToken(varTerminalNode.getSymbol()), varName, null));
                  }
              }
              else if (child instanceof TerminalNode) {
                  TerminalNode terminalNode = (TerminalNode)child;
                  String originStr = terminalNode.getText();
                  addInstruction(new ConstInstruction(newReporterWithToken(terminalNode.getSymbol()),
                      QLStringUtils.parseStringEscapeStartEnd(originStr, 0, originStr.length()),
                      ctx.getStart().getStartIndex()));
              }
          }
          addInstruction(new StringJoinInstruction(newReporterWithToken(ctx.getStart()), childCount - 2));
          return null;
      }
      
      private Number parseFloating(String floatingText) {
          char floatingTypeFlag = floatingText.charAt(floatingText.length() - 1);
          switch (floatingTypeFlag) {
              case 'f':
              case 'F':
                  return new BigDecimal(floatingText.substring(0, floatingText.length() - 1)).floatValue();
              case 'd':
              case 'D':
                  return new BigDecimal(floatingText.substring(0, floatingText.length() - 1)).doubleValue();
              default:
                  BigDecimal baseDecimal = new BigDecimal(floatingText);
                  return baseDecimal.compareTo(MAX_DOUBLE) <= 0 ? maybePresentWithDouble(baseDecimal) : baseDecimal;
          }
      }
      
      private Number maybePresentWithDouble(BigDecimal origin) {
          double doubleValue = origin.doubleValue();
          BigDecimal reference = new BigDecimal(doubleValue);
          return reference.compareTo(origin) == 0 ? doubleValue : origin;
      }
      
      private Number parseInteger(String intText) {
          char intTypeFlag = intText.charAt(intText.length() - 1);
          switch (intTypeFlag) {
              case 'l':
              case 'L':
                  String baseIntText = intText.substring(0, intText.length() - 1);
                  BigInteger baseInt = parseBaseInteger(baseIntText);
                  return baseInt.longValue();
              default:
                  // auto type
                  baseInt = parseBaseInteger(intText);
                  if (baseInt.compareTo(MAX_INTEGER) <= 0) {
                      return baseInt.intValue();
                  }
                  else if (baseInt.compareTo(MAX_LONG) <= 0) {
                      return baseInt.longValue();
                  }
                  else {
                      return baseInt;
                  }
          }
      }
      
      private BigInteger parseBaseInteger(String intText) {
          String radixPrefix = subString(intText, 2);
          switch (radixPrefix) {
              case "0x":
              case "0X":
                  // radix 16
                  return new BigInteger(intText.substring(2), 16);
              case "0b":
              case "0B":
                  // radix 2
                  return new BigInteger(intText.substring(2), 2);
              default:
                  if (radixPrefix.startsWith("0")) {
                      // radix 8
                      return new BigInteger(intText, 8);
                  }
                  else {
                      // radix 10
                      return new BigInteger(intText);
                  }
          }
      }
      
      private Class<?> parseDeclTypeNoArr(DeclTypeNoArrContext declTypeNoArrContext) {
          PrimitiveTypeContext primitiveTypeContext = declTypeNoArrContext.primitiveType();
          if (primitiveTypeContext != null) {
              return BuiltInTypesSet.getCls(primitiveTypeContext.getText());
          }
          
          ClsTypeContext clsTypeContext = declTypeNoArrContext.clsType();
          return parseClsIds(clsTypeContext.varId());
      }
      
      private Class<?> parseDeclType(DeclTypeContext declTypeContext) {
          Class<?> baseCls = parseDeclBaseCls(declTypeContext);
          DimsContext dims = declTypeContext.dims();
          int layers = dims == null ? 0 : dims.LBRACK().size();
          return wrapInArray(baseCls, layers);
      }
      
      private Class<?> parseDeclBaseCls(DeclTypeContext declTypeContext) {
          PrimitiveTypeContext primitiveTypeContext = declTypeContext.primitiveType();
          if (primitiveTypeContext != null) {
              return BuiltInTypesSet.getCls(primitiveTypeContext.getText());
          }
          ClsTypeContext clsTypeContext = declTypeContext.clsType();
          return parseClsIds(clsTypeContext.varId());
      }
      
      private String remove(String target, char c) {
          StringBuilder builder = new StringBuilder(target.length());
          for (int i = 0; i < target.length(); i++) {
              char iChar = target.charAt(i);
              if (iChar != c) {
                  builder.append(iChar);
              }
          }
          return builder.toString();
      }
      
      private String subString(String target, int end) {
          if (end > target.length()) {
              return target;
          }
          return target.substring(0, end);
      }
      
      private Class<?> wrapInArray(Class<?> baseType, int layers) {
          for (int i = 0; i < layers; i++) {
              baseType = Array.newInstance(baseType, 0).getClass();
          }
          return baseType;
      }
      
      private Class<?> parseClsIds(List<VarIdContext> varIdContexts) {
          List<String> fieldIds = varIdContexts.stream().map(RuleContext::getText).collect(Collectors.toList());
          ImportManager.LoadPartQualifiedResult loadPartQualifiedResult = importManager.loadPartQualified(fieldIds);
          if (loadPartQualifiedResult.getCls() == null || loadPartQualifiedResult.getRestIndex() != fieldIds.size()) {
              Token lastIdToken = varIdContexts.get(varIdContexts.size() - 1).getStart();
              throw reportParseErr(lastIdToken,
                  QLErrorCodes.CLASS_NOT_FOUND.name(),
                  String.format(QLErrorCodes.CLASS_NOT_FOUND.getErrorMsg(), String.join(".", fieldIds)));
          }
          return loadPartQualifiedResult.getCls();
      }
      
      private QLSyntaxException reportParseErr(Token token, String errCode, String errReason) {
          return QLException.reportScannerErr(script,
              token.getStartIndex(),
              token.getLine(),
              token.getCharPositionInLine(),
              token.getText(),
              errCode,
              errReason);
      }
      
      public List<QLInstruction> getInstructions() {
          return instructionList;
      }
      
      public int getMaxStackSize() {
          return maxStackSize;
      }
      
      private void ifElseInstructions(ErrorReporter conditionReporter, List<QLInstruction> thenInstructions,
          Integer thenTraceKey, List<QLInstruction> elseInstructions, Integer elseTraceKey, int traceKey) {
          JumpIfPopInstruction jumpIf = new JumpIfPopInstruction(conditionReporter, false, -1);
          pureAddInstruction(jumpIf);
          int jumpStart = instructionList.size();
          thenInstructions.forEach(this::pureAddInstruction);
          if (initOptions.isTraceExpression()) {
              if (thenTraceKey != null) {
                  pureAddInstruction(new TracePeekInstruction(conditionReporter, thenTraceKey));
              }
              pureAddInstruction(new TracePeekInstruction(conditionReporter, traceKey));
          }
          addTimeoutInstruction();
          
          JumpInstruction jump = new JumpInstruction(conditionReporter, -1);
          pureAddInstruction(jump);
          
          jumpIf.setPosition(instructionList.size() - jumpStart);
          
          jumpStart = instructionList.size();
          elseInstructions.forEach(this::pureAddInstruction);
          if (initOptions.isTraceExpression()) {
              if (elseTraceKey != null) {
                  pureAddInstruction(new TracePeekInstruction(conditionReporter, elseTraceKey));
              }
              pureAddInstruction(new TracePeekInstruction(conditionReporter, traceKey));
          }
          addTimeoutInstruction();
          jump.setPosition(instructionList.size() - jumpStart);
      }
      
      private void jumpRightIfExpect(boolean expect, ErrorReporter opErrReporter, RuleContext right, String operatorId,
          int traceKey) {
          QvmInstructionVisitor rightVisitor = parseWithSubVisitor(right, generatorScope, Context.MACRO);
          List<QLInstruction> rightInstructions = rightVisitor.getInstructions();
          
          JumpIfInstruction jumpIf = new JumpIfInstruction(opErrReporter, expect, -1, traceKey);
          pureAddInstruction(jumpIf);
          
          int jumpStart = instructionList.size();
          
          rightInstructions.forEach(this::pureAddInstruction);
          BinaryOperator binaryOperator = operatorFactory.getBinaryOperator(operatorId);
          addInstruction(new OperatorInstruction(opErrReporter, binaryOperator, traceKey));
          addTimeoutInstruction();
          
          jumpIf.setPosition(instructionList.size() - jumpStart);
      }
      
      private QvmInstructionVisitor parseWithSubVisitor(RuleContext ruleContext, GeneratorScope generatorScope,
          Context context) {
          QvmInstructionVisitor subVisitor = new QvmInstructionVisitor(script, importManager, generatorScope,
              operatorFactory, context, compileTimeFunctions, initOptions);
          ruleContext.accept(subVisitor);
          return subVisitor;
      }
      
      private QvmInstructionVisitor parseExprBodyWithSubVisitor(ExpressionContext expressionContext,
          GeneratorScope generatorScope, Context context) {
          QvmInstructionVisitor subVisitor = new QvmInstructionVisitor(script, importManager, generatorScope,
              operatorFactory, context, compileTimeFunctions, initOptions);
          // reduce the level of syntax tree when expression is a block
          subVisitor.visitBodyExpression(expressionContext);
          return subVisitor;
      }
      
      private boolean handleStmt(BlockStatementContext statementContext) {
          if (maybeMacroCall(statementContext)) {
              String macroName = statementContext.getStart().getText();
              MacroDefine macroDefine = generatorScope.getMacroInstructions(macroName);
              if (macroDefine != null) {
                  macroDefine.getMacroInstructions().forEach(this::pureAddInstruction);
                  addTimeoutInstruction();
                  return macroDefine.isLastStmtExpress();
              }
          }
          statementContext.accept(this);
          return statementContext instanceof ExpressionStatementContext;
      }
      
      private boolean maybeMacroCall(BlockStatementContext statementContext) {
          if (statementContext instanceof ExpressionStatementContext) {
              ExpressionContext expressionContext = ((ExpressionStatementContext)statementContext).expression();
              if (expressionContext != null) {
                  if (expressionContext.getStart() == expressionContext.getStop()) {
                      return expressionContext.getStart().getType() == QLexer.ID;
                  }
              }
          }
          return false;
      }
      
      private void pureAddInstruction(QLInstruction qlInstruction) {
          int stackExpandSize = qlInstruction.stackOutput() - qlInstruction.stackInput();
          expandStackSize(stackExpandSize);
          instructionList.add(qlInstruction);
      }
      
      private void addInstruction(QLInstruction qlInstruction) {
          if (instructionList.size() - timeoutCheckPoint > TIMEOUT_CHECK_GAP) {
              addTimeoutInstruction();
          }
          pureAddInstruction(qlInstruction);
          if (qlInstruction instanceof MethodInvokeInstruction || qlInstruction instanceof CallFunctionInstruction
              || qlInstruction instanceof CallConstInstruction || qlInstruction instanceof CallInstruction) {
              addTimeoutInstruction();
          }
      }
      
      private void addTimeoutInstruction() {
          QLInstruction lastInstruction = instructionList.get(instructionList.size() - 1);
          if (lastInstruction instanceof CheckTimeOutInstruction) {
              return;
          }
          this.timeoutCheckPoint = instructionList.size();
          instructionList.add(new CheckTimeOutInstruction(lastInstruction.getErrorReporter()));
      }
      
      private void expandStackSize(int stackExpandSize) {
          stackSize += stackExpandSize;
          if (stackSize > maxStackSize) {
              maxStackSize = stackSize;
          }
      }
      
      private ErrorReporter newReporterWithToken(Token token) {
          return new DefaultErrReporter(script, token.getStartIndex(), token.getLine(), token.getCharPositionInLine(),
              token.getText());
      }
      
      private int whileCount() {
          return whileCounter++;
      }
      
      private int forCount() {
          return forCounter++;
      }
      
      private int ifCount() {
          return ifCounter++;
      }
      
      private int tryCount() {
          return tryCounter++;
      }
      
      private String blockScopeName() {
          return generatorScope.getName() + SCOPE_SEPARATOR + BLOCK_LAMBDA_NAME_PREFIX + blockCounter++;
      }
      
      private String macroScopeName() {
          return generatorScope.getName() + SCOPE_SEPARATOR + MACRO_PREFIX + macroCounter++;
      }
      
      private String lambdaScopeName() {
          return generatorScope.getName() + SCOPE_SEPARATOR + LAMBDA_PREFIX + lambdaCounter++;
      }
      
  }
  