You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/05/08 07:32:47 UTC

[13/23] groovy git commit: rename antlr4 parser to remove groovy- prefix since that is by convention for modules

http://git-wip-us.apache.org/repos/asf/groovy/blob/eb2f9dde/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
new file mode 100644
index 0000000..8228ed2
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -0,0 +1,4691 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.parser.antlr4;
+
+import groovy.lang.IntRange;
+import org.antlr.v4.runtime.ANTLRErrorListener;
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.atn.PredictionMode;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.apache.groovy.parser.antlr4.internal.AtnManager;
+import org.apache.groovy.parser.antlr4.internal.DescriptiveErrorStrategy;
+import org.apache.groovy.parser.antlr4.util.StringUtils;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.antlr.EnumHelper;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.EnumConstantClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.ImportNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.PackageNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.AttributeExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ClosureListExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.GStringExpression;
+import org.codehaus.groovy.ast.expr.LambdaExpression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.MethodPointerExpression;
+import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
+import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.PostfixExpression;
+import org.codehaus.groovy.ast.expr.PrefixExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.RangeExpression;
+import org.codehaus.groovy.ast.expr.SpreadExpression;
+import org.codehaus.groovy.ast.expr.SpreadMapExpression;
+import org.codehaus.groovy.ast.expr.TernaryExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.runtime.IOGroovyMethods;
+import org.codehaus.groovy.runtime.StringGroovyMethods;
+import org.codehaus.groovy.syntax.Numbers;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import static org.apache.groovy.parser.antlr4.GroovyLangParser.*;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
+
+/**
+ * Building the AST from the parse tree generated by Antlr4
+ *
+ * @author <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ *         Created on 2016/08/14
+ */
+public class AstBuilder extends GroovyParserBaseVisitor<Object> implements GroovyParserVisitor<Object> {
+
+    public AstBuilder(SourceUnit sourceUnit, ClassLoader classLoader) {
+        this.sourceUnit = sourceUnit;
+        this.moduleNode = new ModuleNode(sourceUnit);
+        this.classLoader = classLoader; // unused for the time being
+
+        this.lexer = new GroovyLangLexer(
+                new ANTLRInputStream(
+                        this.readSourceCode(sourceUnit)));
+        this.parser = new GroovyLangParser(
+                new CommonTokenStream(this.lexer));
+
+        this.parser.setErrorHandler(new DescriptiveErrorStrategy());
+
+        this.tryWithResourcesASTTransformation = new TryWithResourcesASTTransformation(this);
+        this.groovydocManager = new GroovydocManager(this);
+    }
+
+    private GroovyParserRuleContext buildCST() {
+        GroovyParserRuleContext result;
+
+        // parsing have to wait util clearing is complete.
+        AtnManager.RRWL.readLock().lock();
+        try {
+            result = buildCST(PredictionMode.SLL);
+        } catch (Throwable t) {
+            // if some syntax error occurred in the lexer, no need to retry the powerful LL mode
+            if (t instanceof GroovySyntaxError && GroovySyntaxError.LEXER == ((GroovySyntaxError) t).getSource()) {
+                throw t;
+            }
+
+            result = buildCST(PredictionMode.LL);
+        } finally {
+            AtnManager.RRWL.readLock().unlock();
+        }
+
+        return result;
+    }
+
+    private GroovyParserRuleContext buildCST(PredictionMode predictionMode) {
+        parser.getInterpreter().setPredictionMode(predictionMode);
+
+        if (PredictionMode.SLL.equals(predictionMode)) {
+            this.removeErrorListeners();
+        } else {
+            ((CommonTokenStream) parser.getInputStream()).reset();
+            this.addErrorListeners();
+        }
+
+        return parser.compilationUnit();
+    }
+
+    public ModuleNode buildAST() {
+        try {
+            return (ModuleNode) this.visit(this.buildCST());
+        } catch (Throwable t) {
+            CompilationFailedException cfe;
+
+            if (t instanceof CompilationFailedException) {
+                cfe = (CompilationFailedException) t;
+            } else if (t instanceof ParseCancellationException) {
+                cfe = createParsingFailedException(t.getCause());
+            } else {
+                cfe = createParsingFailedException(t);
+            }
+
+//            LOGGER.log(Level.SEVERE, "Failed to build AST", cfe);
+
+            throw cfe;
+        }
+    }
+
+    @Override
+    public ModuleNode visitCompilationUnit(CompilationUnitContext ctx) {
+        this.visit(ctx.packageDeclaration());
+
+        for (StatementContext s : ctx.statement()) {
+            Object e = this.visit(s);
+            if (e instanceof DeclarationListStatement) { // local variable declaration
+                for (Statement ds : ((DeclarationListStatement) e).getDeclarationStatements()) {
+                    moduleNode.addStatement(ds);
+                }
+            } else if (e instanceof Statement) {
+                moduleNode.addStatement((Statement) e);
+            } else if (e instanceof MethodNode) { // script method
+                moduleNode.addMethod((MethodNode) e);
+            }
+        }
+        for (ClassNode cl : this.classNodeList) {
+            moduleNode.addClass(cl);
+        }
+        if (this.isPackageInfoDeclaration()) {
+            this.addPackageInfoClassNode();
+        } else {
+            // if groovy source file only contains blank(including EOF), add "return null" to the AST
+            if (this.isBlankScript(ctx)) {
+                this.addEmptyReturnStatement();
+            }
+        }
+
+        this.configureScriptClassNode();
+
+        return moduleNode;
+    }
+
+    @Override
+    public PackageNode visitPackageDeclaration(PackageDeclarationContext ctx) {
+        String packageName = this.visitQualifiedName(ctx.qualifiedName());
+        moduleNode.setPackageName(packageName + DOT_STR);
+
+        PackageNode packageNode = moduleNode.getPackage();
+
+        for (AnnotationNode e : this.visitAnnotationsOpt(ctx.annotationsOpt())) {
+            packageNode.addAnnotation(e);
+        }
+
+        return this.configureAST(packageNode, ctx);
+    }
+
+    @Override
+    public ImportNode visitImportDeclaration(ImportDeclarationContext ctx) {
+        ImportNode importNode;
+
+        boolean hasStatic = asBoolean(ctx.STATIC());
+        boolean hasStar = asBoolean(ctx.MUL());
+        boolean hasAlias = asBoolean(ctx.alias);
+
+        List<AnnotationNode> annotationNodeList = this.visitAnnotationsOpt(ctx.annotationsOpt());
+
+        if (hasStatic) {
+            if (hasStar) { // e.g. import static java.lang.Math.*
+                String qualifiedName = this.visitQualifiedName(ctx.qualifiedName());
+                ClassNode type = ClassHelper.make(qualifiedName);
+
+
+                moduleNode.addStaticStarImport(type.getText(), type, annotationNodeList);
+
+                importNode = last(moduleNode.getStaticStarImports().values());
+            } else { // e.g. import static java.lang.Math.pow
+                List<QualifiedNameElementContext> identifierList = new LinkedList<>(ctx.qualifiedName().qualifiedNameElement());
+                int identifierListSize = identifierList.size();
+                String name = identifierList.get(identifierListSize - 1).getText();
+                StringBuilder builder = new StringBuilder();
+                long limit = identifierListSize - 1;
+                for (GroovyParserRuleContext groovyParserRuleContext : identifierList) {
+                    if (limit-- == 0) break;
+                    String text = groovyParserRuleContext.getText();
+                    if (builder.length() > 0) {
+                        builder.append(DOT_STR);
+                    }
+                    builder.append(text);
+                }
+                ClassNode classNode =
+                        ClassHelper.make(
+                                builder.toString());
+                String alias = hasAlias
+                        ? ctx.alias.getText()
+                        : name;
+
+                moduleNode.addStaticImport(classNode, name, alias, annotationNodeList);
+
+                importNode = last(moduleNode.getStaticImports().values());
+            }
+        } else {
+            if (hasStar) { // e.g. import java.util.*
+                String qualifiedName = this.visitQualifiedName(ctx.qualifiedName());
+
+                moduleNode.addStarImport(qualifiedName + DOT_STR, annotationNodeList);
+
+                importNode = last(moduleNode.getStarImports());
+            } else { // e.g. import java.util.Map
+                String qualifiedName = this.visitQualifiedName(ctx.qualifiedName());
+                String name = last(ctx.qualifiedName().qualifiedNameElement()).getText();
+                ClassNode classNode = ClassHelper.make(qualifiedName);
+                String alias = hasAlias
+                        ? ctx.alias.getText()
+                        : name;
+
+                moduleNode.addImport(alias, classNode, annotationNodeList);
+
+                importNode = last(moduleNode.getImports());
+            }
+        }
+
+        return this.configureAST(importNode, ctx);
+    }
+
+    // statement {    --------------------------------------------------------------------
+    @Override
+    public AssertStatement visitAssertStatement(AssertStatementContext ctx) {
+        Expression conditionExpression = (Expression) this.visit(ctx.ce);
+        BooleanExpression booleanExpression =
+                this.configureAST(
+                        new BooleanExpression(conditionExpression), conditionExpression);
+
+        if (!asBoolean(ctx.me)) {
+            return this.configureAST(
+                    new AssertStatement(booleanExpression), ctx);
+        }
+
+        return this.configureAST(new AssertStatement(booleanExpression,
+                        (Expression) this.visit(ctx.me)),
+                ctx);
+    }
+
+    @Override
+    public AssertStatement visitAssertStmtAlt(AssertStmtAltContext ctx) {
+        return this.configureAST(this.visitAssertStatement(ctx.assertStatement()), ctx);
+    }
+
+    @Override
+    public IfStatement visitIfElseStmtAlt(IfElseStmtAltContext ctx) {
+        Expression conditionExpression = this.visitParExpression(ctx.parExpression());
+        BooleanExpression booleanExpression =
+                this.configureAST(
+                        new BooleanExpression(conditionExpression), conditionExpression);
+
+        Statement ifBlock =
+                this.unpackStatement(
+                        (Statement) this.visit(ctx.tb));
+        Statement elseBlock =
+                this.unpackStatement(
+                        asBoolean(ctx.ELSE())
+                                ? (Statement) this.visit(ctx.fb)
+                                : EmptyStatement.INSTANCE);
+
+        return this.configureAST(new IfStatement(booleanExpression, ifBlock, elseBlock), ctx);
+    }
+
+    @Override
+    public Statement visitLoopStmtAlt(LoopStmtAltContext ctx) {
+        return this.configureAST((Statement) this.visit(ctx.loopStatement()), ctx);
+    }
+
+    @Override
+    public ForStatement visitForStmtAlt(ForStmtAltContext ctx) {
+        Pair<Parameter, Expression> controlPair = this.visitForControl(ctx.forControl());
+
+        Statement loopBlock = this.unpackStatement((Statement) this.visit(ctx.statement()));
+
+        return this.configureAST(
+                new ForStatement(controlPair.getKey(), controlPair.getValue(), asBoolean(loopBlock) ? loopBlock : EmptyStatement.INSTANCE),
+                ctx);
+    }
+
+    @Override
+    public Pair<Parameter, Expression> visitForControl(ForControlContext ctx) {
+        if (asBoolean(ctx.enhancedForControl())) { // e.g. for(int i in 0..<10) {}
+            return this.visitEnhancedForControl(ctx.enhancedForControl());
+        }
+
+        if (asBoolean(ctx.SEMI())) { // e.g. for(int i = 0; i < 10; i++) {}
+            ClosureListExpression closureListExpression = new ClosureListExpression();
+
+            closureListExpression.addExpression(this.visitForInit(ctx.forInit()));
+            closureListExpression.addExpression(asBoolean(ctx.expression()) ? (Expression) this.visit(ctx.expression()) : EmptyExpression.INSTANCE);
+            closureListExpression.addExpression(this.visitForUpdate(ctx.forUpdate()));
+
+            return new Pair<>(ForStatement.FOR_LOOP_DUMMY, (Expression)closureListExpression);
+        }
+
+        throw createParsingFailedException("Unsupported for control: " + ctx.getText(), ctx);
+    }
+
+    @Override
+    public Expression visitForInit(ForInitContext ctx) {
+        if (!asBoolean(ctx)) {
+            return EmptyExpression.INSTANCE;
+        }
+
+        if (asBoolean(ctx.localVariableDeclaration())) {
+            DeclarationListStatement declarationListStatement = this.visitLocalVariableDeclaration(ctx.localVariableDeclaration());
+            List<?> declarationExpressionList = declarationListStatement.getDeclarationExpressions();
+
+            if (declarationExpressionList.size() == 1) {
+                return this.configureAST((Expression) declarationExpressionList.get(0), ctx);
+            } else {
+                return this.configureAST(new ClosureListExpression((List<Expression>) declarationExpressionList), ctx);
+            }
+        }
+
+        if (asBoolean(ctx.expressionList())) {
+            return this.translateExpressionList(ctx.expressionList());
+        }
+
+        throw createParsingFailedException("Unsupported for init: " + ctx.getText(), ctx);
+    }
+
+    @Override
+    public Expression visitForUpdate(ForUpdateContext ctx) {
+        if (!asBoolean(ctx)) {
+            return EmptyExpression.INSTANCE;
+        }
+
+        return this.translateExpressionList(ctx.expressionList());
+    }
+
+    private Expression translateExpressionList(ExpressionListContext ctx) {
+        List<Expression> expressionList = this.visitExpressionList(ctx);
+
+        if (expressionList.size() == 1) {
+            return this.configureAST(expressionList.get(0), ctx);
+        } else {
+            return this.configureAST(new ClosureListExpression(expressionList), ctx);
+        }
+    }
+
+    @Override
+    public Pair<Parameter, Expression> visitEnhancedForControl(EnhancedForControlContext ctx) {
+        Parameter parameter = this.configureAST(
+                new Parameter(this.visitType(ctx.type()), this.visitVariableDeclaratorId(ctx.variableDeclaratorId()).getName()),
+                ctx.variableDeclaratorId());
+
+        // FIXME Groovy will ignore variableModifier of parameter in the for control
+        // In order to make the new parser behave same with the old one, we do not process variableModifier*
+
+        return new Pair<>(parameter, (Expression) this.visit(ctx.expression()));
+    }
+
+    @Override
+    public Pair<Parameter, Expression> visitClassicalForControl(ClassicalForControlContext ctx) {
+        ClosureListExpression closureListExpression = new ClosureListExpression();
+
+        closureListExpression.addExpression(this.visitForInit(ctx.forInit()));
+        closureListExpression.addExpression(asBoolean(ctx.expression()) ? (Expression) this.visit(ctx.expression()) : EmptyExpression.INSTANCE);
+        closureListExpression.addExpression(this.visitForUpdate(ctx.forUpdate()));
+
+        return new Pair<>(ForStatement.FOR_LOOP_DUMMY, closureListExpression);
+    }
+
+    @Override
+    public WhileStatement visitWhileStmtAlt(WhileStmtAltContext ctx) {
+        Expression conditionExpression = this.visitParExpression(ctx.parExpression());
+        BooleanExpression booleanExpression =
+                this.configureAST(
+                        new BooleanExpression(conditionExpression), conditionExpression);
+
+        Statement loopBlock = this.unpackStatement((Statement) this.visit(ctx.statement()));
+
+        return this.configureAST(
+                new WhileStatement(booleanExpression, asBoolean(loopBlock) ? loopBlock : EmptyStatement.INSTANCE),
+                ctx);
+    }
+
+    @Override
+    public DoWhileStatement visitDoWhileStmtAlt(DoWhileStmtAltContext ctx) {
+        Expression conditionExpression = this.visitParExpression(ctx.parExpression());
+
+        BooleanExpression booleanExpression =
+                this.configureAST(
+                        new BooleanExpression(conditionExpression),
+                        conditionExpression
+                );
+
+        Statement loopBlock = this.unpackStatement((Statement) this.visit(ctx.statement()));
+
+        return this.configureAST(
+                new DoWhileStatement(booleanExpression, asBoolean(loopBlock) ? loopBlock : EmptyStatement.INSTANCE),
+                ctx);
+    }
+
+    @Override
+    public Statement visitTryCatchStmtAlt(TryCatchStmtAltContext ctx) {
+        return this.configureAST(this.visitTryCatchStatement(ctx.tryCatchStatement()), ctx);
+    }
+
+    @Override
+    public Statement visitTryCatchStatement(TryCatchStatementContext ctx) {
+        TryCatchStatement tryCatchStatement =
+                new TryCatchStatement((Statement) this.visit(ctx.block()),
+                        this.visitFinallyBlock(ctx.finallyBlock()));
+
+        if (asBoolean(ctx.resources())) {
+            for (ExpressionStatement e : this.visitResources(ctx.resources())) {
+                tryCatchStatement.addResource(e);
+            }
+        }
+
+        for (CatchClauseContext cc : ctx.catchClause()) {
+            List<CatchStatement> list = this.visitCatchClause(cc);
+            for (CatchStatement cs : list) {
+                tryCatchStatement.addCatch(cs);
+            }
+        }
+        return this.configureAST(
+                tryWithResourcesASTTransformation.transform(
+                        this.configureAST(tryCatchStatement, ctx)),
+                ctx);
+    }
+
+
+    @Override
+    public List<ExpressionStatement> visitResources(ResourcesContext ctx) {
+        return this.visitResourceList(ctx.resourceList());
+    }
+
+    @Override
+    public List<ExpressionStatement> visitResourceList(ResourceListContext ctx) {
+        List<ExpressionStatement> list = new ArrayList<>();
+        for (ResourceContext resourceContext : ctx.resource()) {
+            ExpressionStatement expressionStatement = visitResource(resourceContext);
+            list.add(expressionStatement);
+        }
+        return list;
+    }
+
+    @Override
+    public ExpressionStatement visitResource(ResourceContext ctx) {
+        if (asBoolean(ctx.localVariableDeclaration())) {
+            List<ExpressionStatement> declarationStatements = this.visitLocalVariableDeclaration(ctx.localVariableDeclaration()).getDeclarationStatements();
+
+            if (declarationStatements.size() > 1) {
+                throw createParsingFailedException("Multi resources can not be declared in one statement", ctx);
+            }
+
+            return declarationStatements.get(0);
+        } else if (asBoolean(ctx.expression())) {
+            Expression expression = (Expression) this.visit(ctx.expression());
+            if (!(expression instanceof BinaryExpression
+                    && Types.ASSIGN == ((BinaryExpression) expression).getOperation().getType()
+                    && ((BinaryExpression) expression).getLeftExpression() instanceof VariableExpression)) {
+
+                throw createParsingFailedException("Only variable declarations are allowed to declare resource", ctx);
+            }
+
+            BinaryExpression assignmentExpression = (BinaryExpression) expression;
+
+            return this.configureAST(
+                    new ExpressionStatement(
+                            this.configureAST(
+                                    new DeclarationExpression(
+                                            this.configureAST(
+                                                    new VariableExpression(assignmentExpression.getLeftExpression().getText()),
+                                                    assignmentExpression.getLeftExpression()
+                                            ),
+                                            assignmentExpression.getOperation(),
+                                            assignmentExpression.getRightExpression()
+                                    ), ctx)
+                    ), ctx);
+        }
+
+        throw createParsingFailedException("Unsupported resource declaration: " + ctx.getText(), ctx);
+    }
+
+    /**
+     * Multi-catch(1..*) clause will be unpacked to several normal catch clauses, so the return type is List
+     *
+     * @param ctx the parse tree
+     * @return
+     */
+    @Override
+    public List<CatchStatement> visitCatchClause(CatchClauseContext ctx) {
+        // FIXME Groovy will ignore variableModifier of parameter in the catch clause
+        // In order to make the new parser behave same with the old one, we do not process variableModifier*
+
+        List<CatchStatement> list = new ArrayList<>();
+        for (ClassNode e : this.visitCatchType(ctx.catchType())) {
+            CatchStatement catchStatement = this.configureAST(
+                    new CatchStatement(
+                            // FIXME The old parser does not set location info for the parameter of the catch clause.
+                            // we could make it better
+                            //this.configureAST(new Parameter(e, this.visitIdentifier(ctx.identifier())), ctx.Identifier()),
+
+                            new Parameter(e, this.visitIdentifier(ctx.identifier())),
+                            this.visitBlock(ctx.block())),
+                    ctx.block());
+            list.add(catchStatement);
+        }
+        return list;
+    }
+
+    @Override
+    public List<ClassNode> visitCatchType(CatchTypeContext ctx) {
+        if (!asBoolean(ctx)) {
+            return Collections.singletonList(ClassHelper.OBJECT_TYPE);
+        }
+
+        List<ClassNode> list = new ArrayList<>();
+        for (QualifiedClassNameContext qualifiedClassNameContext : ctx.qualifiedClassName()) {
+            ClassNode classNode = visitQualifiedClassName(qualifiedClassNameContext);
+            list.add(classNode);
+        }
+        return list;
+    }
+
+
+    @Override
+    public Statement visitFinallyBlock(FinallyBlockContext ctx) {
+        if (!asBoolean(ctx)) {
+            return EmptyStatement.INSTANCE;
+        }
+
+        return this.configureAST(
+                this.createBlockStatement((Statement) this.visit(ctx.block())),
+                ctx);
+    }
+
+    @Override
+    public SwitchStatement visitSwitchStmtAlt(SwitchStmtAltContext ctx) {
+        return this.configureAST(this.visitSwitchStatement(ctx.switchStatement()), ctx);
+    }
+
+    public SwitchStatement visitSwitchStatement(SwitchStatementContext ctx) {
+        List<Statement> statementList = new LinkedList<>();
+        for (SwitchBlockStatementGroupContext c : ctx.switchBlockStatementGroup()) {
+            statementList.addAll(this.visitSwitchBlockStatementGroup(c));
+        }
+
+        List<CaseStatement> caseStatementList = new LinkedList<>();
+        List<Statement> defaultStatementList = new LinkedList<>();
+
+        for (Statement e : statementList) {
+            if (e instanceof CaseStatement) {
+                caseStatementList.add((CaseStatement) e);
+            } else if (isTrue(e, IS_SWITCH_DEFAULT)) {
+                defaultStatementList.add(e);
+            }
+        }
+
+        int defaultStatementListSize = defaultStatementList.size();
+        if (defaultStatementListSize > 1) {
+            throw createParsingFailedException("switch statement should have only one default case, which should appear at last", defaultStatementList.get(0));
+        }
+
+        if (defaultStatementListSize > 0 && last(statementList) instanceof CaseStatement) {
+            throw createParsingFailedException("default case should appear at last", defaultStatementList.get(0));
+        }
+
+        return this.configureAST(
+                new SwitchStatement(
+                        this.visitParExpression(ctx.parExpression()),
+                        caseStatementList,
+                        defaultStatementListSize == 0 ? EmptyStatement.INSTANCE : defaultStatementList.get(0)
+                ),
+                ctx);
+
+    }
+
+
+    @Override
+    @SuppressWarnings({"unchecked"})
+    public List<Statement> visitSwitchBlockStatementGroup(SwitchBlockStatementGroupContext ctx) {
+        int labelCnt = ctx.switchLabel().size();
+        List<Token> firstLabelHolder = new ArrayList<>(1);
+
+        List<Statement> statementList = new ArrayList<>(4);
+        for (SwitchLabelContext e : ctx.switchLabel()) {
+            Pair<Token, Expression> pair = this.visitSwitchLabel(e);
+
+            boolean isLast = labelCnt - 1 == statementList.size();
+
+            switch (pair.getKey().getType()) {
+                case CASE: {
+                    if (!asBoolean(statementList)) {
+                        firstLabelHolder.add(pair.getKey());
+                    }
+
+                    statementList.add(
+                            this.configureAST(
+                                    new CaseStatement(
+                                            pair.getValue(),
+
+                                            // check whether processing the last label. if yes, block statement should be attached.
+                                            isLast ? this.visitBlockStatements(ctx.blockStatements())
+                                                    : EmptyStatement.INSTANCE
+                                    ),
+                                    firstLabelHolder.get(0)));
+
+                    break;
+                }
+                case DEFAULT: {
+
+                    BlockStatement blockStatement = this.visitBlockStatements(ctx.blockStatements());
+                    blockStatement.putNodeMetaData(IS_SWITCH_DEFAULT, true);
+
+                    statementList.add(
+                            // this.configureAST(blockStatement, pair.getKey())
+                            blockStatement
+                    );
+
+                    break;
+                }
+            }
+        }
+        return statementList;
+    }
+
+    @Override
+    public Pair<Token, Expression> visitSwitchLabel(SwitchLabelContext ctx) {
+        if (asBoolean(ctx.CASE())) {
+            return new Pair<>(ctx.CASE().getSymbol(), (Expression) this.visit(ctx.expression()));
+        } else if (asBoolean(ctx.DEFAULT())) {
+            return new Pair<>(ctx.DEFAULT().getSymbol(), (Expression) EmptyExpression.INSTANCE);
+        }
+
+        throw createParsingFailedException("Unsupported switch label: " + ctx.getText(), ctx);
+    }
+
+
+    @Override
+    public SynchronizedStatement visitSynchronizedStmtAlt(SynchronizedStmtAltContext ctx) {
+        return this.configureAST(
+                new SynchronizedStatement(this.visitParExpression(ctx.parExpression()), this.visitBlock(ctx.block())),
+                ctx);
+    }
+
+
+    @Override
+    public ExpressionStatement visitExpressionStmtAlt(ExpressionStmtAltContext ctx) {
+        return (ExpressionStatement) this.visit(ctx.statementExpression());
+    }
+
+    @Override
+    public ReturnStatement visitReturnStmtAlt(ReturnStmtAltContext ctx) {
+        return this.configureAST(new ReturnStatement(asBoolean(ctx.expression())
+                        ? (Expression) this.visit(ctx.expression())
+                        : ConstantExpression.EMPTY_EXPRESSION),
+                ctx);
+    }
+
+    @Override
+    public ThrowStatement visitThrowStmtAlt(ThrowStmtAltContext ctx) {
+        return this.configureAST(
+                new ThrowStatement((Expression) this.visit(ctx.expression())),
+                ctx);
+    }
+
+    @Override
+    public Statement visitLabeledStmtAlt(LabeledStmtAltContext ctx) {
+        Statement statement = (Statement) this.visit(ctx.statement());
+
+        statement.addStatementLabel(this.visitIdentifier(ctx.identifier()));
+
+        return statement; // this.configureAST(statement, ctx);
+    }
+
+    @Override
+    public BreakStatement visitBreakStatement(BreakStatementContext ctx) {
+        String label = asBoolean(ctx.identifier())
+                ? this.visitIdentifier(ctx.identifier())
+                : null;
+
+        return this.configureAST(new BreakStatement(label), ctx);
+    }
+
+    @Override
+    public BreakStatement visitBreakStmtAlt(BreakStmtAltContext ctx) {
+        return this.configureAST(this.visitBreakStatement(ctx.breakStatement()), ctx);
+    }
+
+    @Override
+    public ContinueStatement visitContinueStatement(ContinueStatementContext ctx) {
+        String label = asBoolean(ctx.identifier())
+                ? this.visitIdentifier(ctx.identifier())
+                : null;
+
+        return this.configureAST(new ContinueStatement(label), ctx);
+
+    }
+
+    @Override
+    public ContinueStatement visitContinueStmtAlt(ContinueStmtAltContext ctx) {
+        return this.configureAST(this.visitContinueStatement(ctx.continueStatement()), ctx);
+    }
+
+    @Override
+    public ImportNode visitImportStmtAlt(ImportStmtAltContext ctx) {
+        return this.configureAST(this.visitImportDeclaration(ctx.importDeclaration()), ctx);
+    }
+
+    @Override
+    public ClassNode visitTypeDeclarationStmtAlt(TypeDeclarationStmtAltContext ctx) {
+        return this.configureAST(this.visitTypeDeclaration(ctx.typeDeclaration()), ctx);
+    }
+
+
+    @Override
+    public Statement visitLocalVariableDeclarationStmtAlt(LocalVariableDeclarationStmtAltContext ctx) {
+        return this.configureAST(this.visitLocalVariableDeclaration(ctx.localVariableDeclaration()), ctx);
+    }
+
+    @Override
+    public MethodNode visitMethodDeclarationStmtAlt(MethodDeclarationStmtAltContext ctx) {
+        return this.configureAST(this.visitMethodDeclaration(ctx.methodDeclaration()), ctx);
+    }
+
+    // } statement    --------------------------------------------------------------------
+
+    @Override
+    public ClassNode visitTypeDeclaration(TypeDeclarationContext ctx) {
+        if (asBoolean(ctx.classDeclaration())) { // e.g. class A {}
+            ctx.classDeclaration().putNodeMetaData(TYPE_DECLARATION_MODIFIERS, this.visitClassOrInterfaceModifiersOpt(ctx.classOrInterfaceModifiersOpt()));
+            return this.configureAST(this.visitClassDeclaration(ctx.classDeclaration()), ctx);
+        }
+
+        throw createParsingFailedException("Unsupported type declaration: " + ctx.getText(), ctx);
+    }
+
+    private void initUsingGenerics(ClassNode classNode) {
+        if (classNode.isUsingGenerics()) {
+            return;
+        }
+
+        if (!classNode.isEnum()) {
+            classNode.setUsingGenerics(classNode.getSuperClass().isUsingGenerics());
+        }
+
+        if (!classNode.isUsingGenerics() && asBoolean((Object) classNode.getInterfaces())) {
+            for (ClassNode anInterface : classNode.getInterfaces()) {
+                classNode.setUsingGenerics(classNode.isUsingGenerics() || anInterface.isUsingGenerics());
+
+                if (classNode.isUsingGenerics())
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public ClassNode visitClassDeclaration(ClassDeclarationContext ctx) {
+        String packageName = moduleNode.getPackageName();
+        packageName = asBoolean((Object) packageName) ? packageName : "";
+
+        List<ModifierNode> modifierNodeList = ctx.getNodeMetaData(TYPE_DECLARATION_MODIFIERS);
+        Objects.requireNonNull(modifierNodeList, "modifierNodeList should not be null");
+
+        ModifierManager modifierManager = new ModifierManager(this, modifierNodeList);
+        int modifiers = modifierManager.getClassModifiersOpValue();
+
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+
+        final ClassNode outerClass = classNodeStack.peek();
+        ClassNode classNode;
+        String className = this.visitIdentifier(ctx.identifier());
+        if (asBoolean(ctx.ENUM())) {
+            classNode =
+                    EnumHelper.makeEnumNode(
+                            asBoolean(outerClass) ? className : packageName + className,
+                            modifiers, null, outerClass);
+        } else {
+            if (asBoolean(outerClass)) {
+                classNode =
+                        new InnerClassNode(
+                                outerClass,
+                                outerClass.getName() + "$" + className,
+                                modifiers | (outerClass.isInterface() ? Opcodes.ACC_STATIC : 0),
+                                ClassHelper.OBJECT_TYPE);
+            } else {
+                classNode =
+                        new ClassNode(
+                                packageName + className,
+                                modifiers,
+                                ClassHelper.OBJECT_TYPE);
+            }
+
+        }
+
+        this.configureAST(classNode, ctx);
+        classNode.putNodeMetaData(CLASS_NAME, className);
+        classNode.setSyntheticPublic(syntheticPublic);
+
+        if (asBoolean(ctx.TRAIT())) {
+            classNode.addAnnotation(new AnnotationNode(ClassHelper.make(GROOVY_TRANSFORM_TRAIT)));
+        }
+        classNode.addAnnotations(modifierManager.getAnnotations());
+        classNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters()));
+
+        boolean isInterface = asBoolean(ctx.INTERFACE()) && !asBoolean(ctx.AT());
+        boolean isInterfaceWithDefaultMethods = false;
+
+        // declaring interface with default method
+        if (isInterface && this.containsDefaultMethods(ctx)) {
+            isInterfaceWithDefaultMethods = true;
+            classNode.addAnnotation(new AnnotationNode(ClassHelper.make(GROOVY_TRANSFORM_TRAIT)));
+            classNode.putNodeMetaData(IS_INTERFACE_WITH_DEFAULT_METHODS, true);
+        }
+
+        if (asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT()) || isInterfaceWithDefaultMethods) { // class OR trait OR interface with default methods
+            classNode.setSuperClass(this.visitType(ctx.sc));
+            classNode.setInterfaces(this.visitTypeList(ctx.is));
+
+            this.initUsingGenerics(classNode);
+        } else if (isInterface) { // interface(NOT annotation)
+            classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT);
+
+            classNode.setSuperClass(ClassHelper.OBJECT_TYPE);
+            classNode.setInterfaces(this.visitTypeList(ctx.scs));
+
+            this.initUsingGenerics(classNode);
+
+            this.hackMixins(classNode);
+        } else if (asBoolean(ctx.ENUM())) { // enum
+            classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL);
+
+            classNode.setInterfaces(this.visitTypeList(ctx.is));
+
+            this.initUsingGenerics(classNode);
+        } else if (asBoolean(ctx.AT())) { // annotation
+            classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_ANNOTATION);
+
+            classNode.addInterface(ClassHelper.Annotation_TYPE);
+
+            this.hackMixins(classNode);
+        } else {
+            throw createParsingFailedException("Unsupported class declaration: " + ctx.getText(), ctx);
+        }
+
+        // we put the class already in output to avoid the most inner classes
+        // will be used as first class later in the loader. The first class
+        // there determines what GCL#parseClass for example will return, so we
+        // have here to ensure it won't be the inner class
+        if (asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT())) {
+            classNodeList.add(classNode);
+        }
+
+        int oldAnonymousInnerClassCounter = this.anonymousInnerClassCounter;
+        classNodeStack.push(classNode);
+        ctx.classBody().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+        this.visitClassBody(ctx.classBody());
+        classNodeStack.pop();
+        this.anonymousInnerClassCounter = oldAnonymousInnerClassCounter;
+
+        if (!(asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT()))) {
+            classNodeList.add(classNode);
+        }
+
+        groovydocManager.handle(classNode, ctx);
+
+        return classNode;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private boolean containsDefaultMethods(ClassDeclarationContext ctx) {
+        for (ClassBodyDeclarationContext c : ctx.classBody().classBodyDeclaration()) {
+            MemberDeclarationContext memberDeclarationContext = c.memberDeclaration();
+            if(memberDeclarationContext != null) {
+                MethodDeclarationContext methodDeclarationContext = memberDeclarationContext.methodDeclaration();
+                if (methodDeclarationContext != null) {
+                    if (createModifierManager(methodDeclarationContext).contains(DEFAULT)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Void visitClassBody(ClassBodyContext ctx) {
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        Objects.requireNonNull(classNode, "classNode should not be null");
+
+        if (asBoolean(ctx.enumConstants())) {
+            ctx.enumConstants().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            this.visitEnumConstants(ctx.enumConstants());
+        }
+
+        for (ClassBodyDeclarationContext e : ctx.classBodyDeclaration()) {
+            e.putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            this.visitClassBodyDeclaration(e);
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<FieldNode> visitEnumConstants(EnumConstantsContext ctx) {
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        Objects.requireNonNull(classNode, "classNode should not be null");
+
+        List<FieldNode> list = new LinkedList<>();
+        for (EnumConstantContext e : ctx.enumConstant()) {
+            e.putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            list.add(visitEnumConstant(e));
+        }
+        return list;
+    }
+
+    @Override
+    public FieldNode visitEnumConstant(EnumConstantContext ctx) {
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        Objects.requireNonNull(classNode, "classNode should not be null");
+
+        InnerClassNode anonymousInnerClassNode = null;
+        if (asBoolean(ctx.anonymousInnerClassDeclaration())) {
+            ctx.anonymousInnerClassDeclaration().putNodeMetaData(ANONYMOUS_INNER_CLASS_SUPER_CLASS, classNode);
+            anonymousInnerClassNode = this.visitAnonymousInnerClassDeclaration(ctx.anonymousInnerClassDeclaration());
+        }
+
+        FieldNode enumConstant =
+                EnumHelper.addEnumConstant(
+                        classNode,
+                        this.visitIdentifier(ctx.identifier()),
+                        createEnumConstantInitExpression(ctx.arguments(), anonymousInnerClassNode));
+
+        for (AnnotationNode a : this.visitAnnotationsOpt(ctx.annotationsOpt())) {
+            enumConstant.addAnnotation(a);
+        }
+
+        groovydocManager.handle(enumConstant, ctx);
+
+        return this.configureAST(enumConstant, ctx);
+    }
+
+    private Expression createEnumConstantInitExpression(ArgumentsContext ctx, InnerClassNode anonymousInnerClassNode) {
+        if (!asBoolean(ctx) && !asBoolean(anonymousInnerClassNode)) {
+            return null;
+        }
+
+        TupleExpression argumentListExpression = (TupleExpression) this.visitArguments(ctx);
+        List<Expression> expressions = argumentListExpression.getExpressions();
+
+        if (expressions.size() == 1) {
+            Expression expression = expressions.get(0);
+
+            if (expression instanceof NamedArgumentListExpression) { // e.g. SOME_ENUM_CONSTANT(a: "1", b: "2")
+                List<MapEntryExpression> mapEntryExpressionList = ((NamedArgumentListExpression) expression).getMapEntryExpressions();
+                List<Expression> list = new ArrayList<>();
+                for (MapEntryExpression e : mapEntryExpressionList) {
+                    Expression e1 = (Expression) e;
+                    list.add(e1);
+                }
+                ListExpression listExpression =
+                        new ListExpression(
+                                list);
+
+                if (asBoolean(anonymousInnerClassNode)) {
+                    listExpression.addExpression(
+                            this.configureAST(
+                                    new ClassExpression(anonymousInnerClassNode),
+                                    anonymousInnerClassNode));
+                }
+
+                if (mapEntryExpressionList.size() > 1) {
+                    listExpression.setWrapped(true);
+                }
+
+                return this.configureAST(listExpression, ctx);
+            }
+
+            if (!asBoolean(anonymousInnerClassNode)) {
+                if (expression instanceof ListExpression) {
+                    ListExpression listExpression = new ListExpression();
+                    listExpression.addExpression(expression);
+
+                    return this.configureAST(listExpression, ctx);
+                }
+
+                return expression;
+            }
+
+            ListExpression listExpression = new ListExpression();
+
+            if (expression instanceof ListExpression) {
+                ((ListExpression) expression).getExpressions().forEach(listExpression::addExpression);
+            } else {
+                listExpression.addExpression(expression);
+            }
+
+            listExpression.addExpression(
+                    this.configureAST(
+                            new ClassExpression(anonymousInnerClassNode),
+                            anonymousInnerClassNode));
+
+            return this.configureAST(listExpression, ctx);
+        }
+
+        ListExpression listExpression = new ListExpression(expressions);
+        if (asBoolean(anonymousInnerClassNode)) {
+            listExpression.addExpression(
+                    this.configureAST(
+                            new ClassExpression(anonymousInnerClassNode),
+                            anonymousInnerClassNode));
+        }
+
+        if (asBoolean(ctx)) {
+            listExpression.setWrapped(true);
+        }
+
+        return asBoolean(ctx)
+                ? this.configureAST(listExpression, ctx)
+                : this.configureAST(listExpression, anonymousInnerClassNode);
+    }
+
+
+    @Override
+    public Void visitClassBodyDeclaration(ClassBodyDeclarationContext ctx) {
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        Objects.requireNonNull(classNode, "classNode should not be null");
+
+        if (asBoolean(ctx.memberDeclaration())) {
+            ctx.memberDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            this.visitMemberDeclaration(ctx.memberDeclaration());
+        } else if (asBoolean(ctx.block())) {
+            Statement statement = this.visitBlock(ctx.block());
+
+            if (asBoolean(ctx.STATIC())) { // e.g. static { }
+                classNode.addStaticInitializerStatements(Collections.singletonList(statement), false);
+            } else { // e.g.  { }
+                classNode.addObjectInitializerStatements(
+                        this.configureAST(
+                                this.createBlockStatement(statement),
+                                statement));
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Void visitMemberDeclaration(MemberDeclarationContext ctx) {
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        Objects.requireNonNull(classNode, "classNode should not be null");
+
+        if (asBoolean(ctx.methodDeclaration())) {
+            ctx.methodDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            this.visitMethodDeclaration(ctx.methodDeclaration());
+        } else if (asBoolean(ctx.fieldDeclaration())) {
+            ctx.fieldDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            this.visitFieldDeclaration(ctx.fieldDeclaration());
+        } else if (asBoolean(ctx.classDeclaration())) {
+            ctx.classDeclaration().putNodeMetaData(TYPE_DECLARATION_MODIFIERS, this.visitModifiersOpt(ctx.modifiersOpt()));
+            ctx.classDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+            this.visitClassDeclaration(ctx.classDeclaration());
+        }
+
+        return null;
+    }
+
+    @Override
+    public GenericsType[] visitTypeParameters(TypeParametersContext ctx) {
+        if (!asBoolean(ctx)) {
+            return null;
+        }
+
+        List<GenericsType> list = new ArrayList<>();
+        for (TypeParameterContext typeParameterContext : ctx.typeParameter()) {
+            GenericsType genericsType = visitTypeParameter(typeParameterContext);
+            list.add(genericsType);
+        }
+        return list.toArray(new GenericsType[0]);
+    }
+
+    @Override
+    public GenericsType visitTypeParameter(TypeParameterContext ctx) {
+        return this.configureAST(
+                new GenericsType(
+                        ClassHelper.make(this.visitClassName(ctx.className())),
+                        this.visitTypeBound(ctx.typeBound()),
+                        null
+                ),
+                ctx);
+    }
+
+    @Override
+    public ClassNode[] visitTypeBound(TypeBoundContext ctx) {
+        if (!asBoolean(ctx)) {
+            return null;
+        }
+
+        List<ClassNode> list = new ArrayList<>();
+        for (TypeContext typeContext : ctx.type()) {
+            ClassNode classNode = visitType(typeContext);
+            list.add(classNode);
+        }
+        return list.toArray(new ClassNode[0]);
+    }
+
+    @Override
+    public Void visitFieldDeclaration(FieldDeclarationContext ctx) {
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        Objects.requireNonNull(classNode, "classNode should not be null");
+
+        ctx.variableDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
+        this.visitVariableDeclaration(ctx.variableDeclaration());
+
+        return null;
+    }
+
+    private ConstructorCallExpression checkThisAndSuperConstructorCall(Statement statement) {
+        if (!(statement instanceof BlockStatement)) { // method code must be a BlockStatement
+            return null;
+        }
+
+        BlockStatement blockStatement = (BlockStatement) statement;
+        List<Statement> statementList = blockStatement.getStatements();
+
+        for (int i = 0, n = statementList.size(); i < n; i++) {
+            Statement s = statementList.get(i);
+            if (s instanceof ExpressionStatement) {
+                Expression expression = ((ExpressionStatement) s).getExpression();
+                if ((expression instanceof ConstructorCallExpression) && 0 != i) {
+                    return (ConstructorCallExpression) expression;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private ModifierManager createModifierManager(MethodDeclarationContext ctx) {
+        List<ModifierNode> modifierNodeList = Collections.emptyList();
+
+        if (asBoolean(ctx.modifiers())) {
+            modifierNodeList = this.visitModifiers(ctx.modifiers());
+        } else if (asBoolean(ctx.modifiersOpt())) {
+            modifierNodeList = this.visitModifiersOpt(ctx.modifiersOpt());
+        }
+
+        return new ModifierManager(this, modifierNodeList);
+    }
+
+    private void validateParametersOfMethodDeclaration(Parameter[] parameters, ClassNode classNode) {
+        if (!classNode.isInterface()) {
+            return;
+        }
+
+        Arrays.stream(parameters).forEach(e -> {
+            if (e.hasInitialExpression()) {
+                throw createParsingFailedException("Cannot specify default value for method parameter '" + e.getName() + " = " + e.getInitialExpression().getText() + "' inside an interface", e);
+            }
+        });
+    }
+
+    @Override
+    public MethodNode visitMethodDeclaration(MethodDeclarationContext ctx) {
+        ModifierManager modifierManager = createModifierManager(ctx);
+        String methodName = this.visitMethodName(ctx.methodName());
+        ClassNode returnType = this.visitReturnType(ctx.returnType());
+        Parameter[] parameters = this.visitFormalParameters(ctx.formalParameters());
+        ClassNode[] exceptions = this.visitQualifiedClassNameList(ctx.qualifiedClassNameList());
+
+        anonymousInnerClassesDefinedInMethodStack.push(new LinkedList<InnerClassNode>());
+        Statement code = this.visitMethodBody(ctx.methodBody());
+        List<InnerClassNode> anonymousInnerClassList = anonymousInnerClassesDefinedInMethodStack.pop();
+
+        MethodNode methodNode;
+        // if classNode is not null, the method declaration is for class declaration
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+        if (asBoolean(classNode)) {
+            validateParametersOfMethodDeclaration(parameters, classNode);
+
+            methodNode = createConstructorOrMethodNodeForClass(ctx, modifierManager, methodName, returnType, parameters, exceptions, code, classNode);
+        } else { // script method declaration
+            methodNode = createScriptMethodNode(modifierManager, methodName, returnType, parameters, exceptions, code);
+        }
+        for (InnerClassNode e : anonymousInnerClassList) {
+            e.setEnclosingMethod(methodNode);
+        }
+
+        methodNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters()));
+        methodNode.setSyntheticPublic(
+                this.isSyntheticPublic(
+                        this.isAnnotationDeclaration(classNode),
+                        classNode instanceof EnumConstantClassNode,
+                        asBoolean(ctx.returnType()),
+                        modifierManager));
+
+        if (modifierManager.contains(STATIC)) {
+            for (Parameter e : methodNode.getParameters()) {
+                e.setInStaticContext(true);
+            }
+            methodNode.getVariableScope().setInStaticContext(true);
+        }
+
+        this.configureAST(methodNode, ctx);
+
+        validateMethodDeclaration(ctx, methodNode, modifierManager, classNode);
+
+        groovydocManager.handle(methodNode, ctx);
+
+        return methodNode;
+    }
+
+    private void validateMethodDeclaration(MethodDeclarationContext ctx, MethodNode methodNode, ModifierManager modifierManager, ClassNode classNode) {
+        boolean isAbstractMethod = methodNode.isAbstract();
+        boolean hasMethodBody = asBoolean(methodNode.getCode());
+
+        if (9 == ctx.ct) { // script
+            if (isAbstractMethod || !hasMethodBody) { // method should not be declared abstract in the script
+                throw createParsingFailedException("You can not define a " + (isAbstractMethod ? "abstract" : "") + " method[" + methodNode.getName() + "] " + (!hasMethodBody ? "without method body" : "") + " in the script. Try " + (isAbstractMethod ? "removing the 'abstract'" : "") + (isAbstractMethod && !hasMethodBody ? " and" : "") + (!hasMethodBody ? " adding a method body" : ""), methodNode);
+            }
+        } else {
+            if (!isAbstractMethod && !hasMethodBody) { // non-abstract method without body in the non-script(e.g. class, enum, trait) is not allowed!
+                throw createParsingFailedException("You defined a method[" + methodNode.getName() + "] without body. Try adding a method body, or declare it abstract", methodNode);
+            }
+
+            boolean isInterfaceOrAbstractClass = asBoolean(classNode) && classNode.isAbstract() && !classNode.isAnnotationDefinition();
+            if (isInterfaceOrAbstractClass && !modifierManager.contains(DEFAULT) && isAbstractMethod && hasMethodBody) {
+                throw createParsingFailedException("You defined an abstract method[" + methodNode.getName() + "] with body. Try removing the method body" + (classNode.isInterface() ? ", or declare it default" : ""), methodNode);
+            }
+        }
+
+        modifierManager.validate(methodNode);
+
+        if (methodNode instanceof ConstructorNode) {
+            modifierManager.validate((ConstructorNode) methodNode);
+        }
+    }
+
+    private MethodNode createScriptMethodNode(ModifierManager modifierManager, String methodName, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
+        MethodNode methodNode;
+        methodNode =
+                new MethodNode(
+                        methodName,
+                        modifierManager.contains(PRIVATE) ? Opcodes.ACC_PRIVATE : Opcodes.ACC_PUBLIC,
+                        returnType,
+                        parameters,
+                        exceptions,
+                        code);
+
+        modifierManager.processMethodNode(methodNode);
+        return methodNode;
+    }
+
+    private MethodNode createConstructorOrMethodNodeForClass(MethodDeclarationContext ctx, ModifierManager modifierManager, String methodName, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code, ClassNode classNode) {
+        MethodNode methodNode;
+        String className = classNode.getNodeMetaData(CLASS_NAME);
+        int modifiers = modifierManager.getClassMemberModifiersOpValue();
+
+        if (!asBoolean(ctx.returnType())
+                && asBoolean(ctx.methodBody())
+                && methodName.equals(className)) { // constructor declaration
+
+            methodNode = createConstructorNodeForClass(methodName, parameters, exceptions, code, classNode, modifiers);
+        } else { // class memeber method declaration
+            methodNode = createMethodNodeForClass(ctx, modifierManager, methodName, returnType, parameters, exceptions, code, classNode, modifiers);
+        }
+
+        modifierManager.attachAnnotations(methodNode);
+        return methodNode;
+    }
+
+    private MethodNode createMethodNodeForClass(MethodDeclarationContext ctx, ModifierManager modifierManager, String methodName, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code, ClassNode classNode, int modifiers) {
+        MethodNode methodNode;
+        if (asBoolean(ctx.elementValue())) { // the code of annotation method
+            code = this.configureAST(
+                    new ExpressionStatement(
+                            this.visitElementValue(ctx.elementValue())),
+                    ctx.elementValue());
+
+        }
+
+        modifiers |= !modifierManager.contains(STATIC) && (classNode.isInterface() || (isTrue(classNode, IS_INTERFACE_WITH_DEFAULT_METHODS) && !modifierManager.contains(DEFAULT))) ? Opcodes.ACC_ABSTRACT : 0;
+
+        checkWhetherMethodNodeWithSameSignatureExists(classNode, methodName, parameters, ctx);
+
+        methodNode = classNode.addMethod(methodName, modifiers, returnType, parameters, exceptions, code);
+
+        methodNode.setAnnotationDefault(asBoolean(ctx.elementValue()));
+        return methodNode;
+    }
+
+    private void checkWhetherMethodNodeWithSameSignatureExists(ClassNode classNode, String methodName, Parameter[] parameters, MethodDeclarationContext ctx) {
+        MethodNode sameSigMethodNode = classNode.getDeclaredMethod(methodName, parameters);
+
+        if (null == sameSigMethodNode) {
+            return;
+        }
+
+        throw createParsingFailedException("The method " +  sameSigMethodNode.getText() + " duplicates another method of the same signature", ctx);
+    }
+
+    private ConstructorNode createConstructorNodeForClass(String methodName, Parameter[] parameters, ClassNode[] exceptions, Statement code, ClassNode classNode, int modifiers) {
+        ConstructorCallExpression thisOrSuperConstructorCallExpression = this.checkThisAndSuperConstructorCall(code);
+        if (asBoolean(thisOrSuperConstructorCallExpression)) {
+            throw createParsingFailedException(thisOrSuperConstructorCallExpression.getText() + " should be the first statement in the constructor[" + methodName + "]", thisOrSuperConstructorCallExpression);
+        }
+
+        return classNode.addConstructor(
+                modifiers,
+                parameters,
+                exceptions,
+                code);
+    }
+
+    @Override
+    public String visitMethodName(MethodNameContext ctx) {
+        if (asBoolean(ctx.identifier())) {
+            return this.visitIdentifier(ctx.identifier());
+        }
+
+        if (asBoolean(ctx.stringLiteral())) {
+            return this.visitStringLiteral(ctx.stringLiteral()).getText();
+        }
+
+        throw createParsingFailedException("Unsupported method name: " + ctx.getText(), ctx);
+    }
+
+    @Override
+    public ClassNode visitReturnType(ReturnTypeContext ctx) {
+        if (!asBoolean(ctx)) {
+            return ClassHelper.OBJECT_TYPE;
+        }
+
+        if (asBoolean(ctx.type())) {
+            return this.visitType(ctx.type());
+        }
+
+        if (asBoolean(ctx.VOID())) {
+            return ClassHelper.VOID_TYPE;
+        }
+
+        throw createParsingFailedException("Unsupported return type: " + ctx.getText(), ctx);
+    }
+
+    @Override
+    public Statement visitMethodBody(MethodBodyContext ctx) {
+        if (!asBoolean(ctx)) {
+            return null;
+        }
+
+        return this.configureAST(this.visitBlock(ctx.block()), ctx);
+    }
+
+    @Override
+    public DeclarationListStatement visitLocalVariableDeclaration(LocalVariableDeclarationContext ctx) {
+        return this.configureAST(this.visitVariableDeclaration(ctx.variableDeclaration()), ctx);
+    }
+
+    private ModifierManager createModifierManager(VariableDeclarationContext ctx) {
+        List<ModifierNode> modifierNodeList = Collections.emptyList();
+
+        if (asBoolean(ctx.variableModifiers())) {
+            modifierNodeList = this.visitVariableModifiers(ctx.variableModifiers());
+        } else if (asBoolean(ctx.variableModifiersOpt())) {
+            modifierNodeList = this.visitVariableModifiersOpt(ctx.variableModifiersOpt());
+        } else if (asBoolean(ctx.modifiers())) {
+            modifierNodeList = this.visitModifiers(ctx.modifiers());
+        } else if (asBoolean(ctx.modifiersOpt())) {
+            modifierNodeList = this.visitModifiersOpt(ctx.modifiersOpt());
+        }
+
+        return new ModifierManager(this, modifierNodeList);
+    }
+
+    private DeclarationListStatement createMultiAssignmentDeclarationListStatement(VariableDeclarationContext ctx, ModifierManager modifierManager) {
+        if (!modifierManager.contains(DEF)) {
+            throw createParsingFailedException("keyword def is required to declare tuple, e.g. def (int a, int b) = [1, 2]", ctx);
+        }
+
+        List<Expression> list = new ArrayList<>();
+        for (Expression e : this.visitTypeNamePairs(ctx.typeNamePairs())) {
+            modifierManager.processVariableExpression((VariableExpression) e);
+            list.add(e);
+        }
+        return this.configureAST(
+                new DeclarationListStatement(
+                        this.configureAST(
+                                modifierManager.attachAnnotations(
+                                        new DeclarationExpression(
+                                                new ArgumentListExpression(
+                                                        list
+                                                ),
+                                                this.createGroovyTokenByType(ctx.ASSIGN().getSymbol(), Types.ASSIGN),
+                                                this.visitVariableInitializer(ctx.variableInitializer())
+                                        )
+                                ),
+                                ctx
+                        )
+                ),
+                ctx
+        );
+    }
+
+    @Override
+    public DeclarationListStatement visitVariableDeclaration(VariableDeclarationContext ctx) {
+        ModifierManager modifierManager = this.createModifierManager(ctx);
+
+        if (asBoolean(ctx.typeNamePairs())) { // e.g. def (int a, int b) = [1, 2]
+            return this.createMultiAssignmentDeclarationListStatement(ctx, modifierManager);
+        }
+
+        ClassNode variableType = this.visitType(ctx.type());
+        ctx.variableDeclarators().putNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE, variableType);
+        List<DeclarationExpression> declarationExpressionList = this.visitVariableDeclarators(ctx.variableDeclarators());
+
+        // if classNode is not null, the variable declaration is for class declaration. In other words, it is a field declaration
+        ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE);
+
+        if (asBoolean(classNode)) {
+            return createFieldDeclarationListStatement(ctx, modifierManager, variableType, declarationExpressionList, classNode);
+        }
+
+        for (DeclarationExpression e : declarationExpressionList) {
+            VariableExpression variableExpression = (VariableExpression) e.getLeftExpression();
+
+            modifierManager.processVariableExpression(variableExpression);
+            modifierManager.attachAnnotations(e);
+        }
+
+        int size = declarationExpressionList.size();
+        if (size > 0) {
+            DeclarationExpression declarationExpression = declarationExpressionList.get(0);
+
+            if (1 == size) {
+                this.configureAST(declarationExpression, ctx);
+            } else {
+                // Tweak start of first declaration
+                declarationExpression.setLineNumber(ctx.getStart().getLine());
+                declarationExpression.setColumnNumber(ctx.getStart().getCharPositionInLine() + 1);
+            }
+        }
+
+        return this.configureAST(new DeclarationListStatement(declarationExpressionList), ctx);
+    }
+
+    private DeclarationListStatement createFieldDeclarationListStatement(VariableDeclarationContext ctx, ModifierManager modifierManager, ClassNode variableType, List<DeclarationExpression> declarationExpressionList, ClassNode classNode) {
+        for (DeclarationExpression e : declarationExpressionList) {
+            VariableExpression variableExpression = (VariableExpression) e.getLeftExpression();
+
+            int modifiers = modifierManager.getClassMemberModifiersOpValue();
+
+            Expression initialValue = EmptyExpression.INSTANCE.equals(e.getRightExpression()) ? null : e.getRightExpression();
+            Object defaultValue = findDefaultValueByType(variableType);
+
+            if (classNode.isInterface()) {
+                if (!asBoolean(initialValue)) {
+                    initialValue = !asBoolean(defaultValue) ? null : new ConstantExpression(defaultValue);
+                }
+
+                modifiers |= Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+            }
+
+            if (classNode.isInterface() || modifierManager.containsVisibilityModifier()) {
+                FieldNode fieldNode =
+                        classNode.addField(
+                                variableExpression.getName(),
+                                modifiers,
+                                variableType,
+                                initialValue);
+                modifierManager.attachAnnotations(fieldNode);
+
+                groovydocManager.handle(fieldNode, ctx);
+
+                this.configureAST(fieldNode, ctx);
+            } else {
+                PropertyNode propertyNode =
+                        classNode.addProperty(
+                                variableExpression.getName(),
+                                modifiers | Opcodes.ACC_PUBLIC,
+                                variableType,
+                                initialValue,
+                                null,
+                                null);
+
+                FieldNode fieldNode = propertyNode.getField();
+                fieldNode.setModifiers(modifiers & ~Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE);
+                fieldNode.setSynthetic(!classNode.isInterface());
+                modifierManager.attachAnnotations(fieldNode);
+
+                groovydocManager.handle(fieldNode, ctx);
+                groovydocManager.handle(propertyNode, ctx);
+
+                this.configureAST(fieldNode, ctx);
+                this.configureAST(propertyNode, ctx);
+            }
+
+        }
+
+        return null;
+    }
+
+    @Override
+    public List<Expression> visitTypeNamePairs(TypeNamePairsContext ctx) {
+        List<Expression> list = new ArrayList<>();
+        for (TypeNamePairContext typeNamePairContext : ctx.typeNamePair()) {
+            VariableExpression variableExpression = visitTypeNamePair(typeNamePairContext);
+            list.add(variableExpression);
+        }
+        return list;
+    }
+
+    @Override
+    public VariableExpression visitTypeNamePair(TypeNamePairContext ctx) {
+        return this.configureAST(
+                new VariableExpression(
+                        this.visitVariableDeclaratorId(ctx.variableDeclaratorId()).getName(),
+                        this.visitType(ctx.type())),
+                ctx);
+    }
+
+    @Override
+    public List<DeclarationExpression> visitVariableDeclarators(VariableDeclaratorsContext ctx) {
+        ClassNode variableType = ctx.getNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE);
+        Objects.requireNonNull(variableType, "variableType should not be null");
+
+        List<DeclarationExpression> list = new LinkedList<>();
+        for (VariableDeclaratorContext e : ctx.variableDeclarator()) {
+            e.putNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE, variableType);
+            list.add(this.visitVariableDeclarator(e));
+            }
+            return list;
+    }
+
+    @Override
+    public DeclarationExpression visitVariableDeclarator(VariableDeclaratorContext ctx) {
+        ClassNode variableType = ctx.getNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE);
+        Objects.requireNonNull(variableType, "variableType should not be null");
+
+        org.codehaus.groovy.syntax.Token token;
+        if (asBoolean(ctx.ASSIGN())) {
+            token = createGroovyTokenByType(ctx.ASSIGN().getSymbol(), Types.ASSIGN);
+        } else {
+            token = new org.codehaus.groovy.syntax.Token(Types.ASSIGN, ASSIGN_STR, ctx.start.getLine(), 1);
+        }
+
+        return this.configureAST(
+                new DeclarationExpression(
+                        this.configureAST(
+                                new VariableExpression(
+                                        this.visitVariableDeclaratorId(ctx.variableDeclaratorId()).getName(),
+                                        variableType
+                                ),
+                                ctx.variableDeclaratorId()),
+                        token,
+                        this.visitVariableInitializer(ctx.variableInitializer())),
+                ctx);
+    }
+
+    @Override
+    public Expression visitVariableInitializer(VariableInitializerContext ctx) {
+        if (!asBoolean(ctx)) {
+            return EmptyExpression.INSTANCE;
+        }
+
+        if (asBoolean(ctx.statementExpression())) {
+            return this.configureAST(
+                    ((ExpressionStatement) this.visit(ctx.statementExpression())).getExpression(),
+                    ctx);
+        }
+
+        if (asBoolean(ctx.standardLambda())) {
+            return this.configureAST(this.visitStandardLambda(ctx.standardLambda()), ctx);
+        }
+
+        throw createParsingFailedException("Unsupported variable initializer: " + ctx.getText(), ctx);
+    }
+
+    @Override
+    public List<Expression> visitVariableInitializers(VariableInitializersContext ctx) {
+        if (!asBoolean(ctx)) {
+            return Collections.emptyList();
+        }
+
+        List<Expression> list = new ArrayList<>();
+        for (VariableInitializerContext variableInitializerContext : ctx.variableInitializer()) {
+            Expression expression = visitVariableInitializer(variableInitializerContext);
+            list.add(expression);
+        }
+        return list;
+    }
+
+    @Override
+    public List<Expression> visitArrayInitializer(ArrayInitializerContext ctx) {
+        if (!asBoolean(ctx)) {
+            return Collections.emptyList();
+        }
+
+        return this.visitVariableInitializers(ctx.variableInitializers());
+    }
+
+    @Override
+    public Statement visitBlock(BlockContext ctx) {
+        if (!asBoolean(ctx)) {
+            return this.createBlockStatement();
+        }
+
+        return this.configureAST(
+                this.visitBlockStatementsOpt(ctx.blockStatementsOpt()),
+                ctx);
+    }
+
+
+    @Override
+    public ExpressionStatement visitNormalExprAlt(NormalExprAltContext ctx) {
+        return this.configureAST(new ExpressionStatement((Expression) this.visit(ctx.expression())), ctx);
+    }
+
+    @Override
+    public ExpressionStatement visitCommandExprAlt(CommandExprAltContext ctx) {
+        return this.configureAST(new ExpressionStatement(this.visitCommandExpression(ctx.commandExpression())), ctx);
+    }
+
+    @Override
+    public Expression visitCommandExpression(CommandExpressionContext ctx) {
+        Expression baseExpr = this.visitPathExpression(ctx.pathExpression());
+        Expression arguments = this.visitEnhancedArgumentList(ctx.enhancedArgumentList());
+
+        MethodCallExpression methodCallExpression;
+        if (baseExpr instanceof PropertyExpression) { // e.g. obj.a 1, 2
+            methodCallExpression =
+                    this.configureAST(
+                            this.createMethodCallExpression(
+                                    (PropertyExpression) baseExpr, arguments),
+                            arguments);
+
+        } else if (baseExpr instanceof MethodCallExpression && !isTrue(baseExpr, IS_INSIDE_PARENTHESES)) { // e.g. m {} a, b  OR  m(...) a, b
+            if (asBoolean(arguments)) {
+                // The error should never be thrown.
+                throw new GroovyBugError("When baseExpr is a instance of MethodCallExpression, which should follow NO argumentList");
+            }
+
+            methodCallExpression = (MethodCallExpression) baseExpr;
+        } else if (
+                !isTrue(baseExpr, IS_INSIDE_PARENTHESES)
+                        && (baseExpr instanceof VariableExpression /* e.g. m 1, 2 */
+                        || baseExpr instanceof GStringExpression /* e.g. "$m" 1, 2 */
+                        || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING)) /* e.g. "m" 1, 2 */)
+                ) {
+            methodCallExpression =
+                    this.configureAST(
+                            this.createMethodCallExpression(baseExpr, arguments),
+                            arguments);
+        } else { // e.g. a[x] b, new A() b, etc.
+            methodCallExpression =
+                    this.configureAST(
+                            new MethodCallExpression(
+                                    baseExpr,
+                                    CALL_STR,
+                                    arguments
+                            ),
+                            arguments
+                    );
+
+            methodCallExpression.setImplicitThis(false);
+        }
+
+        if (!asBoolean(ctx.commandArgument())) {
+            return this.configureAST(methodCallExpression, ctx);
+        }
+
+        Expression r = methodCallExpression;
+        for (CommandArgumentContext commandArgumentContext : ctx.commandArgument()) {
+            commandArgumentContext.putNodeMetaData(CMD_EXPRESSION_BASE_EXPR, r);
+            r = this.visitCommandArgument(commandArgumentContext);
+        }
+
+        return this.configureAST(r, ctx);
+    }
+
+    @Override
+    public Expression visitCommandArgument(CommandArgumentContext ctx) {
+        // e.g. x y a b     we call "x y" as the base expression
+        Expression baseExpr = ctx.getNodeMetaData(CMD_EXPRESSION_BASE_EXPR);
+
+        Expression primaryExpr = (Expression) this.visit(ctx.primary());
+
+        if (asBoolean(ctx.enhancedArgumentList())) { // e.g. x y a b
+            if (baseExpr instanceof PropertyExpression) { // the branch should never reach, because a.b.c will be parsed as a path expression, not a method call
+                throw createParsingFailedException("Unsupported command argument: " + ctx.getText(), ctx);
+            }
+
+            // the following code will process "a b" of "x y a b"
+            MethodCallExpression methodCallExpression =
+                    new MethodCallExpression(
+                            baseExpr,
+                            this.createConstantExpression(primaryExpr),
+                            this.visitEnhancedArgumentList(ctx.enhancedArgumentList())
+                    );
+            methodCallExpression.setImplicitThis(false);
+
+            return this.configureAST(methodCallExpression, ctx);
+        } else if (asBoolean(ctx.pathElement())) { // e.g. x y a.b
+            Expression pathExpression =
+                    this.createPathExpression(
+                            this.configureAST(
+                                    new PropertyExpression(baseExpr, this.createConstantExpression(primaryExpr)),
+                                    primaryExpr
+                            ),
+                            ctx.pathElement()
+                    );
+
+            return this.configureAST(pathExpression, ctx);
+        }
+
+        // e.g. x y a
+        return this.configureAST(
+                new PropertyExpression(
+                        baseExpr,
+                        primaryExpr instanceof VariableExpression
+                                ? this.createConstantExpression(primaryExpr)
+                                : primaryExpr
+                ),
+                primaryExpr
+        );
+    }
+
+
+    // expression {    --------------------------------------------------------------------
+
+    @Override
+    public ClassNode visitCastParExpression(CastParExpressionContext ctx) {
+        return this.visitType(ctx.type());
+    }
+
+    @Override
+    public Expression visitParExpression(ParExpressionContext ctx) {
+        Expression expression;
+
+        if (asBoolean(ctx.statementExpression())) {
+            expression = ((ExpressionStatement) this.visit(ctx.statementExpression())).getExpression();
+        } else if (asBoolean(ctx.standardLambda())) {
+            expression = this.visitStandardLambda(ctx.standardLambda());
+        } else {
+            throw createParsingFailedException("Unsupported parentheses expression: " + ctx.getText(), ctx);
+        }
+
+        expression.putNodeMetaData(IS_INSIDE_PARENTHESES, true);
+
+        Integer insideParenLevel = expression.getNodeMetaData(INSIDE_PARENTHESES_LEVEL);
+        if (asBoolean((Object) insideParenLevel)) {
+            insideParenLevel++;
+        } else {
+            insideParenLevel = 1;
+        }
+        expression.putNodeMetaData(INSIDE_PARENTHESES_LEVEL, insideParenLevel);
+
+        return this.configureAST(expression, ctx);
+    }
+
+    @Override
+    public Expression visitPathExpression(PathExpressionContext ctx) {
+        return this.configureAST(
+                this.createPathExpression((Expression) this.visit(ctx.primary()), ctx.pathElement()),
+                ctx);
+    }
+
+    @Override
+    public Expression visitPathElement(PathElementContext ctx) {
+        Expression baseExpr = ctx.getNodeMetaData(PATH_EXPRESSION_BASE_EXPR);
+        Objects.requireNonNull(baseExpr, "baseExpr is required!");
+
+        if (asBoolean(ctx.namePart())) {
+            Expression namePartExpr = this.visitNamePart(ctx.namePart());
+            GenericsType[] genericsTypes = this.visitNonWildcardTypeArguments(ctx.nonWildcardTypeArguments());
+
+
+            if (asBoolean(ctx.DOT())) {
+                if (asBoolean(ctx.AT())) { // e.g. obj.@a
+                    return this.configureAST(new AttributeExpression(baseExpr, namePartExpr), ctx);
+                } else { // e.g. obj.p
+                    PropertyExpression propertyExpression = new PropertyExpression(baseExpr, namePartExpr);
+                    propertyExpression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes);
+
+                    return this.configureAST(propertyExpression, ctx);
+                }
+            } else if (asBoolean(ctx.SAFE_DOT())) {
+                if (asBoolean(ctx.AT())) { // e.g. obj?.@a
+                    return this.configureAST(new AttributeExpression(baseExpr, namePartExpr, true), ctx);
+                } else { // e.g. obj?.p
+                    PropertyExpression propertyExpression = new PropertyExpression(baseExpr, namePartExpr, true);
+                    propertyExpression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes);
+
+                    return this.configureAST(propertyExpression, ctx);
+                }
+            } else if (asBoolean(ctx.METHOD_POINTER())) { // e.g. obj.&m
+                return this.configureAST(new MethodPointerExpression(baseExpr, namePartExpr), ctx);
+            } else if (asBoolean(ctx.METHOD_REFERENCE())) { // e.g. obj::m
+                return this.configureAST(new MethodReferenceExpression(baseExpr, namePartExpr), ctx);
+            } else if (asBoolean(ctx.SPREAD_DOT())) {
+                if (asBoolean(ctx.AT())) { // e.g. obj*.@a
+                    AttributeExpression attributeExpression = new AttributeExpression(baseExpr, namePartExpr, true);
+
+                    attributeExpression.setSpreadSafe(true);
+
+                    return this.configureAST(attributeExpression, ctx);
+                } else { // e.g. obj*.p
+                    PropertyExpression propertyExpression = new PropertyExpression(baseExpr, namePartExpr, true);
+                    propertyExpression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes);
+
+                    propertyExpression.setSpreadSafe(true);
+
+                    return this.configureAST(propertyExpression, ctx);
+                }
+            }
+        }
+
+        if (asBoolean(ctx.indexPropertyArgs())) { // e.g. list[1, 3, 5]
+            Pair<Token, Expression> pair = this.visitIndexPropertyArgs(ctx.indexPropertyArgs());
+
+            return this.configureAST(
+                    new BinaryExpression(baseExpr, createGroovyToken(pair.getKey()), pair.getValue(), asBoolean(ctx.indexPropertyArgs().QUESTION())),
+                    ctx);
+        }
+
+        if (asBoolean(ctx.namedPropertyArgs())) { // this is a special way to new instance, e.g. Person(name: 'Daniel.Sun', location: 'Shanghai')
+            List<MapEntryExpression> mapEntryExpressionList =
+                    this.visitNamedPropertyArgs(ctx.namedPropertyArgs());
+
+            Expression right;
+            if (mapEntryExpressionList.size() == 1) {
+                MapEntryExpression mapEntryExpression = mapEntryExpressionList.get(0);
+
+                if (mapEntryExpression.getKeyExpression() instanceof SpreadMapExpression) {
+                    right = mapEntryExpression.getKeyExpression();
+                } else {
+                    right = mapEntryExpression;
+                }
+            } else {
+                List<Expression> list = new LinkedList<>();
+                for (MapEntryExpression e : mapEntryExpressionList) {
+                    list.add(e.getKeyExpression() instanceof SpreadMapExpression ?
+                            e.getKeyExpression() : e);
+                }
+                ListExpression listExpression = this.configureAST(new ListExpression(list), ctx.namedPropertyArgs());
+                listExpression.setWrapped(true);
+                right = listExpression;
+            }
+
+            return this.configureAST(
+                    new BinaryExpression(baseExpr, createGroovyToken(ctx.namedPropertyArgs().LBRACK().getSymbol()), right),
+                    ctx);
+        }
+
+        if (asBoolean(ctx.arguments())) {
+            Expression argumentsExpr = this.visitArguments(ctx.arguments());
+
+            if (isTrue(baseExpr, IS_INSIDE_PARENTHESES)) { // e.g. (obj.x)(), (obj.@x)()
+                MethodCallExpression methodCallExpression =
+                        new MethodCallExpression(
+                                baseExpr,
+                                CALL_STR,
+                                argumentsExpr
+                        );
+
+                methodCallExpression.setImplicitThis(false);
+
+                return this.configureAST(methodCallExpression, ctx);
+            }
+
+            if (baseExpr instanceof AttributeExpression) { // e.g. obj.@a(1, 2)
+                AttributeExpression attributeExpression = (AttributeExpression) baseExpr;
+                attributeExpression.setSpreadSafe(false); // whether attributeExpression is spread safe or not, we must reset it as false
+
+                MethodCallExpression methodCallExpression =
+                        new MethodCallExpression(
+                                attributeExpression,
+                                CALL_STR,
+                                argumentsExpr
+                        );
+
+                return this.configureAST(methodCallExpression, ctx);
+            }
+
+            if (baseExpr instanceof PropertyExpression) { // e.g. obj.a(1, 2)
+                MethodCallExpression methodCallExpression =
+                        this.createMethodCallExpression((PropertyExpression) baseExpr, argumentsExpr);
+
+                return this.configureAST(methodCallExpression, ctx);
+            }
+
+            if (baseExpr instanceof VariableExpression) { // void and primitive type AST node must be an instance of VariableExpression
+                String baseExprText = baseExpr.getText();
+                if (VOID_STR.equals(baseExprText)) { // e.g. void()
+                    MethodCallExpression methodCallExpression =
+                            new MethodCallExpression(
+                                    this.createConstantExpression(baseExpr),
+                                    CALL_STR,
+                                    argumentsExpr
+                            );
+
+                    methodCallExpression.setImplicitThis(false);
+
+                    return this.configureAST(methodCallExpression, ctx);
+                } else if (PRIMITIVE_TYPE_SET.contains(baseExprText)) { // e.g. int(), long(), float(), etc.
+                    throw createParsingFailedException("Primitive type literal: " + baseExprText + " cannot be used as a method name", ctx);
+                }
+            }
+
+            if (baseExpr instanceof VariableExpression
+                    || baseExpr

<TRUNCATED>