You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:54 UTC

[43/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java
new file mode 100644
index 0000000..37a105f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java
@@ -0,0 +1,3269 @@
+/*
+ *  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.codehaus.groovy.antlr;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.TokenStreamRecognitionException;
+import antlr.collections.AST;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+import org.codehaus.groovy.antlr.treewalker.CompositeVisitor;
+import org.codehaus.groovy.antlr.treewalker.MindMapPrinter;
+import org.codehaus.groovy.antlr.treewalker.NodeAsHTMLPrinter;
+import org.codehaus.groovy.antlr.treewalker.PreOrderTraversal;
+import org.codehaus.groovy.antlr.treewalker.SourceCodeTraversal;
+import org.codehaus.groovy.antlr.treewalker.SourcePrinter;
+import org.codehaus.groovy.antlr.treewalker.Visitor;
+import org.codehaus.groovy.antlr.treewalker.VisitorAdapter;
+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.MixinNode;
+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.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.GStringExpression;
+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.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.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.ParserPlugin;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.XStreamUtils;
+import org.codehaus.groovy.syntax.ASTHelper;
+import org.codehaus.groovy.syntax.Numbers;
+import org.codehaus.groovy.syntax.ParserException;
+import org.codehaus.groovy.syntax.Reduction;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A parser plugin which adapts the JSR Antlr Parser to the Groovy runtime
+ *
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ */
+public class AntlrParserPlugin extends ASTHelper implements ParserPlugin, GroovyTokenTypes {
+
+    private static class AnonymousInnerClassCarrier extends Expression {
+        ClassNode innerClass;
+
+        public Expression transformExpression(ExpressionTransformer transformer) {
+            return null;
+        }
+
+        @Override
+        public void setSourcePosition(final ASTNode node) {
+            super.setSourcePosition(node);
+            innerClass.setSourcePosition(node);
+        }
+
+        @Override
+        public void setColumnNumber(final int columnNumber) {
+            super.setColumnNumber(columnNumber);
+            innerClass.setColumnNumber(columnNumber);
+        }
+
+        @Override
+        public void setLineNumber(final int lineNumber) {
+            super.setLineNumber(lineNumber);
+            innerClass.setLineNumber(lineNumber);
+        }
+
+        @Override
+        public void setLastColumnNumber(final int columnNumber) {
+            super.setLastColumnNumber(columnNumber);
+            innerClass.setLastColumnNumber(columnNumber);
+        }
+
+        @Override
+        public void setLastLineNumber(final int lineNumber) {
+            super.setLastLineNumber(lineNumber);
+            innerClass.setLastLineNumber(lineNumber);
+        }
+    }
+
+    protected AST ast;
+    private ClassNode classNode;
+    private MethodNode methodNode;
+    private String[] tokenNames;
+    private int innerClassCounter = 1;
+    private boolean enumConstantBeingDef = false;
+    private boolean forStatementBeingDef = false;
+    private boolean annotationBeingDef = false;
+    private boolean firstParamIsVarArg = false;
+    private boolean firstParam = false;
+
+    public /*final*/ Reduction parseCST(final SourceUnit sourceUnit, Reader reader) throws CompilationFailedException {
+        final SourceBuffer sourceBuffer = new SourceBuffer();
+        transformCSTIntoAST(sourceUnit, reader, sourceBuffer);
+        processAST();
+        return outputAST(sourceUnit, sourceBuffer);
+    }
+
+    protected void transformCSTIntoAST(SourceUnit sourceUnit, Reader reader, SourceBuffer sourceBuffer) throws CompilationFailedException {
+        ast = null;
+
+        setController(sourceUnit);
+
+        // TODO find a way to inject any GroovyLexer/GroovyRecognizer
+
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(reader, sourceBuffer);
+        UnicodeLexerSharedInputState inputState = new UnicodeLexerSharedInputState(unicodeReader);
+        GroovyLexer lexer = new GroovyLexer(inputState);
+        unicodeReader.setLexer(lexer);
+        GroovyRecognizer parser = GroovyRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+        tokenNames = parser.getTokenNames();
+        parser.setFilename(sourceUnit.getName());
+
+        // start parsing at the compilationUnit rule
+        try {
+            parser.compilationUnit();
+        }
+        catch (TokenStreamRecognitionException tsre) {
+            RecognitionException e = tsre.recog;
+            SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn());
+            se.setFatal(true);
+            sourceUnit.addError(se);
+        }
+        catch (RecognitionException e) {
+            SyntaxException se = new SyntaxException(e.getMessage(), e, e.getLine(), e.getColumn());
+            se.setFatal(true);
+            sourceUnit.addError(se);
+        }
+        catch (TokenStreamException e) {
+            sourceUnit.addException(e);
+        }
+
+        ast = parser.getAST();
+    }
+
+    protected void processAST() {
+        AntlrASTProcessor snippets = new AntlrASTProcessSnippets();
+        ast = snippets.process(ast);
+    }
+
+    public Reduction outputAST(final SourceUnit sourceUnit, final SourceBuffer sourceBuffer) {
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                outputASTInVariousFormsIfNeeded(sourceUnit, sourceBuffer);
+                return null;
+            }
+        });
+
+        return null; //new Reduction(Tpken.EOF);
+    }
+
+    private void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) {
+        // straight xstream output of AST
+        String formatProp = System.getProperty("ANTLR.AST".toLowerCase()); // uppercase to hide from jarjar
+
+        if ("xml".equals(formatProp)) {
+            saveAsXML(sourceUnit.getName(), ast);
+        }
+
+        // 'pretty printer' output of AST
+        if ("groovy".equals(formatProp)) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".pretty.groovy"));
+                Visitor visitor = new SourcePrinter(out, tokenNames);
+                AntlrASTProcessor treewalker = new SourceCodeTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".pretty.groovy");
+            }
+        }
+
+        // output AST in format suitable for opening in http://freemind.sourceforge.net
+        // which is a really nice way of seeing the AST, folding nodes etc
+        if ("mindmap".equals(formatProp)) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm"));
+                Visitor visitor = new MindMapPrinter(out, tokenNames);
+                AntlrASTProcessor treewalker = new PreOrderTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".mm");
+            }
+        }
+
+        // include original line/col info and source code on the mindmap output
+        if ("extendedMindmap".equals(formatProp)) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm"));
+                Visitor visitor = new MindMapPrinter(out, tokenNames, sourceBuffer);
+                AntlrASTProcessor treewalker = new PreOrderTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".mm");
+            }
+        }
+
+        // html output of AST
+        if ("html".equals(formatProp)) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".html"));
+                List<VisitorAdapter> v = new ArrayList<VisitorAdapter>();
+                v.add(new NodeAsHTMLPrinter(out, tokenNames));
+                v.add(new SourcePrinter(out, tokenNames));
+                Visitor visitors = new CompositeVisitor(v);
+                AntlrASTProcessor treewalker = new SourceCodeTraversal(visitors);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".html");
+            }
+        }
+    }
+
+    private static void saveAsXML(String name, AST ast) {
+        XStreamUtils.serialize(name+".antlr", ast);
+    }
+
+    public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException {
+        setClassLoader(classLoader);
+        makeModule();
+        try {
+            convertGroovy(ast);
+            if (output.getStatementBlock().isEmpty() && output.getMethods().isEmpty() && output.getClasses().isEmpty()) {
+                output.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
+            }
+
+            // set the script source position
+
+            ClassNode scriptClassNode = output.getScriptClassDummy();
+            if (scriptClassNode != null) {
+                List<Statement> statements = output.getStatementBlock().getStatements();
+                if (!statements.isEmpty()) {
+                    Statement firstStatement = statements.get(0);
+                    Statement lastStatement = statements.get(statements.size() - 1);
+
+                    scriptClassNode.setSourcePosition(firstStatement);
+                    scriptClassNode.setLastColumnNumber(lastStatement.getLastColumnNumber());
+                    scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber());
+                }
+            }
+        }
+        catch (ASTRuntimeException e) {
+            throw new ASTParserException(e.getMessage() + ". File: " + sourceUnit.getName(), e);
+        }
+        return output;
+    }
+
+    /**
+     * Converts the Antlr AST to the Groovy AST
+     */
+    protected void convertGroovy(AST node) {
+        while (node != null) {
+            int type = node.getType();
+            switch (type) {
+                case PACKAGE_DEF:
+                    packageDef(node);
+                    break;
+
+                case STATIC_IMPORT:
+                case IMPORT:
+                    importDef(node);
+                    break;
+
+                case TRAIT_DEF:
+                case CLASS_DEF:
+                    classDef(node);
+                    break;
+
+                case INTERFACE_DEF:
+                    interfaceDef(node);
+                    break;
+
+                case METHOD_DEF:
+                    methodDef(node);
+                    break;
+
+                case ENUM_DEF:
+                    enumDef(node);
+                    break;
+
+                case ANNOTATION_DEF:
+                    annotationDef(node);
+                    break;
+
+                default: {
+                    Statement statement = statement(node);
+                    output.addStatement(statement);
+                }
+            }
+            node = node.getNextSibling();
+        }
+    }
+
+    // Top level control structures
+    //-------------------------------------------------------------------------
+
+    protected void packageDef(AST packageDef) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST node = packageDef.getFirstChild();
+        if (isType(ANNOTATIONS, node)) {
+            processAnnotations(annotations, node);
+            node = node.getNextSibling();
+        }
+        String name = qualifiedName(node);
+        // TODO should we check package node doesn't already exist? conflict?
+        PackageNode packageNode = setPackage(name, annotations);
+        configureAST(packageNode, packageDef);
+    }
+
+    protected void importDef(AST importNode) {
+        try {
+            // GROOVY-6094
+            output.putNodeMetaData(ImportNode.class, ImportNode.class);
+
+            boolean isStatic = importNode.getType() == STATIC_IMPORT;
+            List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+
+            AST node = importNode.getFirstChild();
+            if (isType(ANNOTATIONS, node)) {
+                processAnnotations(annotations, node);
+                node = node.getNextSibling();
+            }
+
+            String alias = null;
+            if (isType(LITERAL_as, node)) {
+                //import is like "import Foo as Bar"
+                node = node.getFirstChild();
+                AST aliasNode = node.getNextSibling();
+                alias = identifier(aliasNode);
+            }
+
+            if (node.getNumberOfChildren() == 0) {
+                String name = identifier(node);
+                // import is like  "import Foo"
+                ClassNode type = ClassHelper.make(name);
+                configureAST(type, importNode);
+                addImport(type, name, alias, annotations);
+                return;
+            }
+
+            AST packageNode = node.getFirstChild();
+            String packageName = qualifiedName(packageNode);
+            AST nameNode = packageNode.getNextSibling();
+            if (isType(STAR, nameNode)) {
+                if (isStatic) {
+                    // import is like "import static foo.Bar.*"
+                    // packageName is actually a className in this case
+                    ClassNode type = ClassHelper.make(packageName);
+                    configureAST(type, importNode);
+                    addStaticStarImport(type, packageName, annotations);
+                } else {
+                    // import is like "import foo.*"
+                    addStarImport(packageName, annotations);
+                }
+
+                if (alias != null) throw new GroovyBugError(
+                        "imports like 'import foo.* as Bar' are not " +
+                                "supported and should be caught by the grammar");
+            } else {
+                String name = identifier(nameNode);
+                if (isStatic) {
+                    // import is like "import static foo.Bar.method"
+                    // packageName is really class name in this case
+                    ClassNode type = ClassHelper.make(packageName);
+                    configureAST(type, importNode);
+                    addStaticImport(type, name, alias, annotations);
+                } else {
+                    // import is like "import foo.Bar"
+                    ClassNode type = ClassHelper.make(packageName + "." + name);
+                    configureAST(type, importNode);
+                    addImport(type, name, alias, annotations);
+                }
+            }
+        } finally {
+            // we're using node metadata here in order to fix GROOVY-6094
+            // without breaking external APIs
+            Object node = output.getNodeMetaData(ImportNode.class);
+            if (node!=null && node!=ImportNode.class) {
+                configureAST((ImportNode)node, importNode);
+            }
+            output.removeNodeMetaData(ImportNode.class);
+        }
+    }
+
+    private void processAnnotations(List<AnnotationNode> annotations, AST node) {
+        AST child = node.getFirstChild();
+        while (child != null) {
+            if (isType(ANNOTATION, child))
+                annotations.add(annotation(child));
+            child = child.getNextSibling();
+        }
+    }
+
+    protected void annotationDef(AST classDef) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST node = classDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            checkNoInvalidModifier(classDef, "Annotation Definition", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized");
+            node = node.getNextSibling();
+        }
+        modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_ANNOTATION;
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+        ClassNode superClass = ClassHelper.OBJECT_TYPE;
+
+        GenericsType[] genericsType = null;
+        if (isType(TYPE_PARAMETERS, node)) {
+            genericsType = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (isType(EXTENDS_CLAUSE, node)) {
+            interfaces = interfaces(node);
+            node = node.getNextSibling();
+        }
+
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+        classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null);
+        classNode.setSyntheticPublic(syntheticPublic);
+        classNode.addAnnotations(annotations);
+        classNode.setGenericsTypes(genericsType);
+        classNode.addInterface(ClassHelper.Annotation_TYPE);
+        configureAST(classNode, classDef);
+
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        output.addClass(classNode);
+        classNode = null;
+    }
+
+    protected void interfaceDef(AST classDef) {
+        int oldInnerClassCounter = innerClassCounter;
+        innerInterfaceDef(classDef);
+        classNode = null;
+        innerClassCounter = oldInnerClassCounter;
+    }
+
+    protected void innerInterfaceDef(AST classDef) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST node = classDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            checkNoInvalidModifier(classDef, "Interface", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized");
+            node = node.getNextSibling();
+        }
+        modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE;
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+        ClassNode superClass = ClassHelper.OBJECT_TYPE;
+
+        GenericsType[] genericsType = null;
+        if (isType(TYPE_PARAMETERS, node)) {
+            genericsType = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (isType(EXTENDS_CLAUSE, node)) {
+            interfaces = interfaces(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode outerClass = classNode;
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+        if (classNode != null) {
+            name = classNode.getNameWithoutPackage() + "$" + name;
+            String fullName = dot(classNode.getPackageName(), name);
+            classNode = new InnerClassNode(classNode, fullName, modifiers, superClass, interfaces, null);
+        } else {
+            classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null);
+        }
+        classNode.setSyntheticPublic(syntheticPublic);
+        classNode.addAnnotations(annotations);
+        classNode.setGenericsTypes(genericsType);
+        configureAST(classNode, classDef);
+
+        int oldClassCount = innerClassCounter;
+
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        output.addClass(classNode);
+
+        classNode = outerClass;
+        innerClassCounter = oldClassCount;
+    }
+
+    protected void classDef(AST classDef) {
+        int oldInnerClassCounter = innerClassCounter;
+        innerClassDef(classDef);
+        classNode = null;
+        innerClassCounter = oldInnerClassCounter;
+    }
+
+    private ClassNode getClassOrScript(ClassNode node) {
+        if (node != null) return node;
+        return output.getScriptClassDummy();
+    }
+
+    protected Expression anonymousInnerClassDef(AST node) {
+        ClassNode oldNode = classNode;
+        ClassNode outerClass = getClassOrScript(oldNode);
+        String fullName = outerClass.getName() + '$' + innerClassCounter;
+        innerClassCounter++;
+        if (enumConstantBeingDef) {
+            classNode = new EnumConstantClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        } else {
+            classNode = new InnerClassNode(outerClass, fullName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        }
+        ((InnerClassNode) classNode).setAnonymous(true);
+        classNode.setEnclosingMethod(methodNode);
+
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        output.addClass(classNode);
+        AnonymousInnerClassCarrier ret = new AnonymousInnerClassCarrier();
+        ret.innerClass = classNode;
+        classNode = oldNode;
+
+        return ret;
+    }
+
+    protected void innerClassDef(AST classDef) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+
+        if (isType(TRAIT_DEF, classDef)) {
+            annotations.add(new AnnotationNode(ClassHelper.make("groovy.transform.Trait")));
+        }
+
+        AST node = classDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            checkNoInvalidModifier(classDef, "Class", modifiers, Opcodes.ACC_SYNCHRONIZED, "synchronized");
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+
+        GenericsType[] genericsType = null;
+        if (isType(TYPE_PARAMETERS, node)) {
+            genericsType = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode superClass = null;
+        if (isType(EXTENDS_CLAUSE, node)) {
+            superClass = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (isType(IMPLEMENTS_CLAUSE, node)) {
+            interfaces = interfaces(node);
+            node = node.getNextSibling();
+        }
+
+        // TODO read mixins
+        MixinNode[] mixins = {};
+        ClassNode outerClass = classNode;
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+        if (classNode != null) {
+            name = classNode.getNameWithoutPackage() + "$" + name;
+            String fullName = dot(classNode.getPackageName(), name);
+            if (classNode.isInterface()) {
+                modifiers |= Opcodes.ACC_STATIC;
+            }
+            classNode = new InnerClassNode(classNode, fullName, modifiers, superClass, interfaces, mixins);
+        } else {
+            classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, mixins);
+        }
+        classNode.addAnnotations(annotations);
+        classNode.setGenericsTypes(genericsType);
+        classNode.setSyntheticPublic(syntheticPublic);
+        configureAST(classNode, classDef);
+
+        // 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
+        output.addClass(classNode);
+
+        int oldClassCount = innerClassCounter;
+
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+
+        classNode = outerClass;
+        innerClassCounter = oldClassCount;
+    }
+
+    protected void objectBlock(AST objectBlock) {
+        for (AST node = objectBlock.getFirstChild(); node != null; node = node.getNextSibling()) {
+            int type = node.getType();
+            switch (type) {
+                case OBJBLOCK:
+                    objectBlock(node);
+                    break;
+
+                case ANNOTATION_FIELD_DEF:
+                case METHOD_DEF:
+                    methodDef(node);
+                    break;
+
+                case CTOR_IDENT:
+                    constructorDef(node);
+                    break;
+
+                case VARIABLE_DEF:
+                    fieldDef(node);
+                    break;
+
+                case STATIC_INIT:
+                    staticInit(node);
+                    break;
+
+                case INSTANCE_INIT:
+                    objectInit(node);
+                    break;
+
+                case ENUM_DEF:
+                    enumDef(node);
+                    break;
+
+                case ENUM_CONSTANT_DEF:
+                    enumConstantDef(node);
+                    break;
+
+                case TRAIT_DEF:
+                case CLASS_DEF:
+                    innerClassDef(node);
+                    break;
+
+                case INTERFACE_DEF:
+                    innerInterfaceDef(node);
+                    break;
+
+                default:
+                    unknownAST(node);
+            }
+        }
+    }
+
+    protected void enumDef(AST enumNode) {
+        assertNodeType(ENUM_DEF, enumNode);
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+
+        AST node = enumNode.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+
+        ClassNode[] interfaces = interfaces(node);
+        node = node.getNextSibling();
+
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+        String enumName = (classNode != null ? name : dot(getPackageName(), name));
+        ClassNode enumClass = EnumHelper.makeEnumNode(enumName, modifiers, interfaces, classNode);
+        enumClass.setSyntheticPublic(syntheticPublic);
+        ClassNode oldNode = classNode;
+        enumClass.addAnnotations(annotations);
+        classNode = enumClass;
+        configureAST(classNode, enumNode);
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        classNode = oldNode;
+
+        output.addClass(enumClass);
+    }
+
+    protected void enumConstantDef(AST node) {
+        enumConstantBeingDef = true;
+        assertNodeType(ENUM_CONSTANT_DEF, node);
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST element = node.getFirstChild();
+        if (isType(ANNOTATIONS, element)) {
+            processAnnotations(annotations, element);
+            element = element.getNextSibling();
+        }
+        String identifier = identifier(element);
+        Expression init = null;
+        element = element.getNextSibling();
+
+        if (element != null) {
+            init = expression(element);
+            ClassNode innerClass;
+            if (element.getNextSibling() == null) {
+                innerClass = getAnonymousInnerClassNode(init);
+                if (innerClass != null) {
+                    init = null;
+                }
+            } else {
+                element = element.getNextSibling();
+                Expression next = expression(element);
+                innerClass = getAnonymousInnerClassNode(next);
+            }
+
+            if (innerClass != null) {
+                // we have to handle an enum constant with a class overriding
+                // a method in which case we need to configure the inner class
+                innerClass.setSuperClass(classNode.getPlainNodeReference());
+                innerClass.setModifiers(classNode.getModifiers() | Opcodes.ACC_FINAL);
+                // we use a ClassExpression for transportation to EnumVisitor
+                Expression inner = new ClassExpression(innerClass);
+                if (init == null) {
+                    ListExpression le = new ListExpression();
+                    le.addExpression(inner);
+                    init = le;
+                } else {
+                    if (init instanceof ListExpression) {
+                        ((ListExpression) init).addExpression(inner);
+                    } else {
+                        ListExpression le = new ListExpression();
+                        le.addExpression(init);
+                        le.addExpression(inner);
+                        init = le;
+                    }
+                }
+                // and remove the final modifier from classNode to allow the sub class
+                classNode.setModifiers(classNode.getModifiers() & ~Opcodes.ACC_FINAL);
+            } else if (isType(ELIST, element)) {
+                if (init instanceof ListExpression && !((ListExpression) init).isWrapped()) {
+                    ListExpression le = new ListExpression();
+                    le.addExpression(init);
+                    init = le;
+                }
+            }
+        }
+        FieldNode enumField = EnumHelper.addEnumConstant(classNode, identifier, init);
+        enumField.addAnnotations(annotations);
+        configureAST(enumField, node);
+        enumConstantBeingDef = false;
+    }
+
+    protected void throwsList(AST node, List<ClassNode> list) {
+        String name;
+        if (isType(DOT, node)) {
+            name = qualifiedName(node);
+        } else {
+            name = identifier(node);
+        }
+        ClassNode exception = ClassHelper.make(name);
+        configureAST(exception, node);
+        list.add(exception);
+        AST next = node.getNextSibling();
+        if (next != null) throwsList(next, list);
+    }
+
+    protected void methodDef(AST methodDef) {
+        MethodNode oldNode = methodNode;
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST node = methodDef.getFirstChild();
+
+        GenericsType[] generics = null;
+        if (isType(TYPE_PARAMETERS, node)) {
+            generics = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            checkNoInvalidModifier(methodDef, "Method", modifiers, Opcodes.ACC_VOLATILE, "volatile");
+            node = node.getNextSibling();
+        }
+
+        if (isAnInterface()) {
+            modifiers |= Opcodes.ACC_ABSTRACT;
+        }
+
+        ClassNode returnType = null;
+        if (isType(TYPE, node)) {
+            returnType = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        if (classNode != null && !classNode.isAnnotationDefinition()) {
+            if (classNode.getNameWithoutPackage().equals(name)) {
+                if (isAnInterface()) {
+                    throw new ASTRuntimeException(methodDef, "Constructor not permitted within an interface.");
+                }
+                throw new ASTRuntimeException(methodDef, "Invalid constructor format. Remove '" + returnType.getName() +
+                        "' as the return type if you want a constructor, or use a different name if you want a method.");
+            }
+        }
+        node = node.getNextSibling();
+
+        Parameter[] parameters = Parameter.EMPTY_ARRAY;
+        ClassNode[] exceptions = ClassNode.EMPTY_ARRAY;
+
+        if (classNode == null || !classNode.isAnnotationDefinition()) {
+
+            assertNodeType(PARAMETERS, node);
+            parameters = parameters(node);
+            if (parameters == null) parameters = Parameter.EMPTY_ARRAY;
+            node = node.getNextSibling();
+
+            if (isType(LITERAL_throws, node)) {
+                AST throwsNode = node.getFirstChild();
+                List<ClassNode> exceptionList = new ArrayList<ClassNode>();
+                throwsList(throwsNode, exceptionList);
+                exceptions = exceptionList.toArray(exceptions);
+                node = node.getNextSibling();
+            }
+        }
+
+        boolean hasAnnotationDefault = false;
+        Statement code = null;
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+        methodNode = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
+        if ((modifiers & Opcodes.ACC_ABSTRACT) == 0) {
+            if (node == null) {
+                throw new ASTRuntimeException(methodDef, "You defined a method without body. Try adding a body, or declare it abstract.");
+            }
+            assertNodeType(SLIST, node);
+            code = statementList(node);
+        } else if (node != null && classNode.isAnnotationDefinition()) {
+            code = statement(node);
+            hasAnnotationDefault = true;
+        } else if ((modifiers & Opcodes.ACC_ABSTRACT) > 0) {
+            if (node != null) {
+                throw new ASTRuntimeException(methodDef, "Abstract methods do not define a body.");
+            }
+        }
+        methodNode.setCode(code);
+        methodNode.addAnnotations(annotations);
+        methodNode.setGenericsTypes(generics);
+        methodNode.setAnnotationDefault(hasAnnotationDefault);
+        methodNode.setSyntheticPublic(syntheticPublic);
+        configureAST(methodNode, methodDef);
+
+        if (classNode != null) {
+            classNode.addMethod(methodNode);
+        } else {
+            output.addMethod(methodNode);
+        }
+        methodNode = oldNode;
+    }
+
+    private static void checkNoInvalidModifier(AST node, String nodeType, int modifiers, int modifier, String modifierText) {
+        if ((modifiers & modifier) != 0) {
+            throw new ASTRuntimeException(node, nodeType + " has an incorrect modifier '" + modifierText + "'.");
+        }
+    }
+
+    private boolean isAnInterface() {
+        return classNode != null && (classNode.getModifiers() & Opcodes.ACC_INTERFACE) > 0;
+    }
+
+    protected void staticInit(AST staticInit) {
+        BlockStatement code = (BlockStatement) statementList(staticInit);
+        classNode.addStaticInitializerStatements(code.getStatements(), false);
+    }
+
+    protected void objectInit(AST init) {
+        BlockStatement code = (BlockStatement) statementList(init);
+        classNode.addObjectInitializerStatements(code);
+    }
+
+    protected void constructorDef(AST constructorDef) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST node = constructorDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_STATIC, "static");
+            checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_FINAL, "final");
+            checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_ABSTRACT, "abstract");
+            checkNoInvalidModifier(constructorDef, "Constructor", modifiers, Opcodes.ACC_NATIVE, "native");
+            node = node.getNextSibling();
+        }
+
+        assertNodeType(PARAMETERS, node);
+        Parameter[] parameters = parameters(node);
+        if (parameters == null) parameters = Parameter.EMPTY_ARRAY;
+        node = node.getNextSibling();
+
+        ClassNode[] exceptions = ClassNode.EMPTY_ARRAY;
+        if (isType(LITERAL_throws, node)) {
+            AST throwsNode = node.getFirstChild();
+            List<ClassNode> exceptionList = new ArrayList<ClassNode>();
+            throwsList(throwsNode, exceptionList);
+            exceptions = exceptionList.toArray(exceptions);
+            node = node.getNextSibling();
+        }
+
+        assertNodeType(SLIST, node);
+        boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
+        modifiers &= ~Opcodes.ACC_SYNTHETIC;
+        ConstructorNode constructorNode = classNode.addConstructor(modifiers, parameters, exceptions, null);
+        MethodNode oldMethod = methodNode;
+        methodNode = constructorNode;
+        Statement code = statementList(node);
+        methodNode = oldMethod;
+        constructorNode.setCode(code);
+        constructorNode.setSyntheticPublic(syntheticPublic);
+        constructorNode.addAnnotations(annotations);
+        configureAST(constructorNode, constructorDef);
+    }
+
+    protected void fieldDef(AST fieldDef) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        AST node = fieldDef.getFirstChild();
+
+        int modifiers = 0;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        if (classNode.isInterface()) {
+            modifiers |= Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+            if ((modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0) {
+                modifiers |= Opcodes.ACC_PUBLIC;
+            }
+        }
+
+        ClassNode type = null;
+        if (isType(TYPE, node)) {
+            type = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+
+        Expression initialValue = null;
+        if (node != null) {
+            assertNodeType(ASSIGN, node);
+            initialValue = expression(node.getFirstChild());
+        }
+
+        if (classNode.isInterface() && initialValue == null && type != null) {
+            initialValue = getDefaultValueForPrimitive(type);
+        }
+
+
+        FieldNode fieldNode = new FieldNode(name, modifiers, type, classNode, initialValue);
+        fieldNode.addAnnotations(annotations);
+        configureAST(fieldNode, fieldDef);
+
+        if (!hasVisibility(modifiers)) {
+            // let's set the modifiers on the field
+            int fieldModifiers = 0;
+            int flags = Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE | Opcodes.ACC_FINAL;
+
+            if (!hasVisibility(modifiers)) {
+                modifiers |= Opcodes.ACC_PUBLIC;
+                fieldModifiers |= Opcodes.ACC_PRIVATE;
+            }
+
+            // let's pass along any other modifiers we need
+            fieldModifiers |= (modifiers & flags);
+            fieldNode.setModifiers(fieldModifiers);
+            fieldNode.setSynthetic(true);
+
+            // in the case that there is already a field, we would
+            // like to use that field, instead of the default field
+            // for the property
+            FieldNode storedNode = classNode.getDeclaredField(fieldNode.getName());
+            if (storedNode != null && !classNode.hasProperty(name)) {
+                fieldNode = storedNode;
+                // we remove it here, because addProperty will add it
+                // again and we want to avoid it showing up multiple
+                // times in the fields list.
+                classNode.getFields().remove(storedNode);
+            }
+
+            PropertyNode propertyNode = new PropertyNode(fieldNode, modifiers, null, null);
+            configureAST(propertyNode, fieldDef);
+            classNode.addProperty(propertyNode);
+        } else {
+            fieldNode.setModifiers(modifiers);
+            // if there is a property of that name, then a field of that
+            // name already exists, which means this new field here should
+            // be used instead of the field the property originally has.
+            PropertyNode pn = classNode.getProperty(name);
+            if (pn != null && pn.getField().isSynthetic()) {
+                classNode.getFields().remove(pn.getField());
+                pn.setField(fieldNode);
+            }
+            classNode.addField(fieldNode);
+        }
+    }
+
+    public static Expression getDefaultValueForPrimitive(ClassNode type) {
+        if (type == ClassHelper.int_TYPE) {
+            return new ConstantExpression(0);
+        }
+        if (type == ClassHelper.long_TYPE) {
+            return new ConstantExpression(0L);
+        }
+        if (type == ClassHelper.double_TYPE) {
+            return new ConstantExpression(0.0);
+        }
+        if (type == ClassHelper.float_TYPE) {
+            return new ConstantExpression(0.0F);
+        }
+        if (type == ClassHelper.boolean_TYPE) {
+            return ConstantExpression.FALSE;
+        }
+        if (type == ClassHelper.short_TYPE) {
+            return new ConstantExpression((short) 0);
+        }
+        if (type == ClassHelper.byte_TYPE) {
+            return new ConstantExpression((byte) 0);
+        }
+        if (type == ClassHelper.char_TYPE) {
+            return new ConstantExpression((char) 0);
+        }
+        return null;
+    }
+
+    protected ClassNode[] interfaces(AST node) {
+        List<ClassNode> interfaceList = new ArrayList<ClassNode>();
+        for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) {
+            interfaceList.add(makeTypeWithArguments(implementNode));
+        }
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (!interfaceList.isEmpty()) {
+            interfaces = new ClassNode[interfaceList.size()];
+            interfaceList.toArray(interfaces);
+        }
+        return interfaces;
+    }
+
+    protected Parameter[] parameters(AST parametersNode) {
+        AST node = parametersNode.getFirstChild();
+        firstParam = false;
+        firstParamIsVarArg = false;
+        if (node == null) {
+            if (isType(IMPLICIT_PARAMETERS, parametersNode)) return Parameter.EMPTY_ARRAY;
+            return null;
+        } else {
+            List<Parameter> parameters = new ArrayList<Parameter>();
+            AST firstParameterNode = null;
+            do {
+                firstParam = (firstParameterNode == null);
+                if (firstParameterNode == null) firstParameterNode = node;
+                parameters.add(parameter(node));
+                node = node.getNextSibling();
+            }
+            while (node != null);
+
+            verifyParameters(parameters, firstParameterNode);
+
+            Parameter[] answer = new Parameter[parameters.size()];
+            parameters.toArray(answer);
+            return answer;
+        }
+    }
+
+    private void verifyParameters(List<Parameter> parameters, AST firstParameterNode) {
+        if (parameters.size() <= 1) return;
+
+        Parameter first = parameters.get(0);
+        if (firstParamIsVarArg) {
+            throw new ASTRuntimeException(firstParameterNode, "The var-arg parameter " + first.getName() + " must be the last parameter.");
+        }
+    }
+
+    protected Parameter parameter(AST paramNode) {
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        boolean variableParameterDef = isType(VARIABLE_PARAMETER_DEF, paramNode);
+        AST node = paramNode.getFirstChild();
+
+        int modifiers = 0;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        ClassNode type = ClassHelper.DYNAMIC_TYPE;
+        if (isType(TYPE, node)) {
+            type = makeTypeWithArguments(node);
+            if (variableParameterDef) type = type.makeArray();
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+
+        VariableExpression leftExpression = new VariableExpression(name, type);
+        leftExpression.setModifiers(modifiers);
+        configureAST(leftExpression, paramNode);
+
+        Parameter parameter = null;
+        if (node != null) {
+            assertNodeType(ASSIGN, node);
+            Expression rightExpression = expression(node.getFirstChild());
+            if (isAnInterface()) {
+                throw new ASTRuntimeException(node, "Cannot specify default value for method parameter '" + name + " = " + rightExpression.getText() + "' inside an interface");
+            }
+            parameter = new Parameter(type, name, rightExpression);
+        } else
+            parameter = new Parameter(type, name);
+
+        if (firstParam) firstParamIsVarArg = variableParameterDef;
+
+        configureAST(parameter, paramNode);
+        parameter.addAnnotations(annotations);
+        parameter.setModifiers(modifiers);
+        return parameter;
+    }
+
+    protected int modifiers(AST modifierNode, List<AnnotationNode> annotations, int defaultModifiers) {
+        assertNodeType(MODIFIERS, modifierNode);
+
+        boolean access = false;
+        int answer = 0;
+
+        for (AST node = modifierNode.getFirstChild(); node != null; node = node.getNextSibling()) {
+            int type = node.getType();
+            switch (type) {
+                case STATIC_IMPORT:
+                    // ignore
+                    break;
+
+                // annotations
+                case ANNOTATION:
+                    annotations.add(annotation(node));
+                    break;
+
+                // core access scope modifiers
+                case LITERAL_private:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_PRIVATE);
+                    access = setAccessTrue(node, access);
+                    break;
+
+                case LITERAL_protected:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_PROTECTED);
+                    access = setAccessTrue(node, access);
+                    break;
+
+                case LITERAL_public:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_PUBLIC);
+                    access = setAccessTrue(node, access);
+                    break;
+
+                // other modifiers
+                case ABSTRACT:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_ABSTRACT);
+                    break;
+
+                case FINAL:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_FINAL);
+                    break;
+
+                case LITERAL_native:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_NATIVE);
+                    break;
+
+                case LITERAL_static:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_STATIC);
+                    break;
+
+                case STRICTFP:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_STRICT);
+                    break;
+
+                case LITERAL_synchronized:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_SYNCHRONIZED);
+                    break;
+
+                case LITERAL_transient:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_TRANSIENT);
+                    break;
+
+                case LITERAL_volatile:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_VOLATILE);
+                    break;
+
+                default:
+                    unknownAST(node);
+            }
+        }
+        if (!access) {
+            answer |= defaultModifiers;
+            // ACC_SYNTHETIC isn't used here, use it as a special flag
+            if (defaultModifiers == Opcodes.ACC_PUBLIC) answer |= Opcodes.ACC_SYNTHETIC;
+        }
+        return answer;
+    }
+
+    protected boolean setAccessTrue(AST node, boolean access) {
+        if (!access) {
+            return true;
+        } else {
+            throw new ASTRuntimeException(node, "Cannot specify modifier: " + node.getText() + " when access scope has already been defined");
+        }
+    }
+
+    protected int setModifierBit(AST node, int answer, int bit) {
+        if ((answer & bit) != 0) {
+            throw new ASTRuntimeException(node, "Cannot repeat modifier: " + node.getText());
+        }
+        return answer | bit;
+    }
+
+    protected AnnotationNode annotation(AST annotationNode) {
+        annotationBeingDef = true;
+        AST node = annotationNode.getFirstChild();
+        String name = qualifiedName(node);
+        AnnotationNode annotatedNode = new AnnotationNode(ClassHelper.make(name));
+        configureAST(annotatedNode, annotationNode);
+        while (true) {
+            node = node.getNextSibling();
+            if (isType(ANNOTATION_MEMBER_VALUE_PAIR, node)) {
+                AST memberNode = node.getFirstChild();
+                String param = identifier(memberNode);
+                Expression expression = expression(memberNode.getNextSibling());
+                if (annotatedNode.getMember(param) != null) {
+                    throw new ASTRuntimeException(memberNode, "Annotation member '" + param + "' has already been associated with a value");
+                }
+                annotatedNode.setMember(param, expression);
+            } else {
+                break;
+            }
+        }
+        annotationBeingDef = false;
+        return annotatedNode;
+    }
+
+
+    // Statements
+    //-------------------------------------------------------------------------
+
+    protected Statement statement(AST node) {
+        Statement statement = null;
+        int type = node.getType();
+        switch (type) {
+            case SLIST:
+            case LITERAL_finally:
+                statement = statementList(node);
+                break;
+
+            case METHOD_CALL:
+                statement = methodCall(node);
+                break;
+
+            case VARIABLE_DEF:
+                statement = variableDef(node);
+                break;
+
+            case LABELED_STAT:
+                return labelledStatement(node);
+
+            case LITERAL_assert:
+                statement = assertStatement(node);
+                break;
+
+            case LITERAL_break:
+                statement = breakStatement(node);
+                break;
+
+            case LITERAL_continue:
+                statement = continueStatement(node);
+                break;
+
+            case LITERAL_if:
+                statement = ifStatement(node);
+                break;
+
+            case LITERAL_for:
+                statement = forStatement(node);
+                break;
+
+            case LITERAL_return:
+                statement = returnStatement(node);
+                break;
+
+            case LITERAL_synchronized:
+                statement = synchronizedStatement(node);
+                break;
+
+            case LITERAL_switch:
+                statement = switchStatement(node);
+                break;
+
+            case LITERAL_try:
+                statement = tryStatement(node);
+                break;
+
+            case LITERAL_throw:
+                statement = throwStatement(node);
+                break;
+
+            case LITERAL_while:
+                statement = whileStatement(node);
+                break;
+
+            default:
+                statement = new ExpressionStatement(expression(node));
+        }
+        if (statement != null) {
+            configureAST(statement, node);
+        }
+        return statement;
+    }
+
+    protected Statement statementList(AST code) {
+        return statementListNoChild(code.getFirstChild(), code);
+    }
+
+    protected Statement statementListNoChild(AST node, AST alternativeConfigureNode) {
+        BlockStatement block = new BlockStatement();
+        // alternativeConfigureNode is used only to set the source position
+        if (node != null) {
+            configureAST(block, node);
+        } else {
+            configureAST(block, alternativeConfigureNode);
+        }
+        for (; node != null; node = node.getNextSibling()) {
+            block.addStatement(statement(node));
+        }
+        return block;
+    }
+
+    protected Statement assertStatement(AST assertNode) {
+        AST node = assertNode.getFirstChild();
+        BooleanExpression booleanExpression = booleanExpression(node);
+        Expression messageExpression = null;
+
+        node = node.getNextSibling();
+        if (node != null) {
+            messageExpression = expression(node);
+        } else {
+            messageExpression = ConstantExpression.NULL;
+        }
+        AssertStatement assertStatement = new AssertStatement(booleanExpression, messageExpression);
+        configureAST(assertStatement, assertNode);
+        return assertStatement;
+    }
+
+    protected Statement breakStatement(AST node) {
+        BreakStatement breakStatement = new BreakStatement(label(node));
+        configureAST(breakStatement, node);
+        return breakStatement;
+    }
+
+    protected Statement continueStatement(AST node) {
+        ContinueStatement continueStatement = new ContinueStatement(label(node));
+        configureAST(continueStatement, node);
+        return continueStatement;
+    }
+
+    protected Statement forStatement(AST forNode) {
+        AST inNode = forNode.getFirstChild();
+        Expression collectionExpression;
+        Parameter forParameter;
+        if (isType(CLOSURE_LIST, inNode)) {
+            forStatementBeingDef = true;
+            ClosureListExpression clist = closureListExpression(inNode);
+            forStatementBeingDef = false;
+            int size = clist.getExpressions().size();
+            if (size != 3) {
+                throw new ASTRuntimeException(inNode, "3 expressions are required for the classic for loop, you gave " + size);
+            }
+            collectionExpression = clist;
+            forParameter = ForStatement.FOR_LOOP_DUMMY;
+        } else {
+            AST variableNode = inNode.getFirstChild();
+            AST collectionNode = variableNode.getNextSibling();
+
+            ClassNode type = ClassHelper.OBJECT_TYPE;
+            if (isType(VARIABLE_DEF, variableNode)) {
+                AST node = variableNode.getFirstChild();
+                // skip the final modifier if it's present
+                if (isType(MODIFIERS, node)) {
+                    int modifiersMask = modifiers(node, new ArrayList<AnnotationNode>(), 0);
+                    // only final modifier allowed
+                    if ((modifiersMask & ~Opcodes.ACC_FINAL) != 0) {
+                        throw new ASTRuntimeException(node, "Only the 'final' modifier is allowed in front of the for loop variable.");
+                    }
+                    node = node.getNextSibling();
+                }
+                type = makeTypeWithArguments(node);
+
+                variableNode = node.getNextSibling();
+            }
+            String variable = identifier(variableNode);
+
+            collectionExpression = expression(collectionNode);
+            forParameter = new Parameter(type, variable);
+            configureAST(forParameter, variableNode);
+        }
+
+        final AST node = inNode.getNextSibling();
+        Statement block;
+        if (isType(SEMI, node)) {
+            block = EmptyStatement.INSTANCE;
+        } else {
+            block = statement(node);
+        }
+        ForStatement forStatement = new ForStatement(forParameter, collectionExpression, block);
+        configureAST(forStatement, forNode);
+        return forStatement;
+    }
+
+    protected Statement ifStatement(AST ifNode) {
+        AST node = ifNode.getFirstChild();
+        assertNodeType(EXPR, node);
+        BooleanExpression booleanExpression = booleanExpression(node);
+
+        node = node.getNextSibling();
+        Statement ifBlock = statement(node);
+
+        Statement elseBlock = EmptyStatement.INSTANCE;
+        node = node.getNextSibling();
+        if (node != null) {
+            elseBlock = statement(node);
+        }
+        IfStatement ifStatement = new IfStatement(booleanExpression, ifBlock, elseBlock);
+        configureAST(ifStatement, ifNode);
+        return ifStatement;
+    }
+
+    protected Statement labelledStatement(AST labelNode) {
+        AST node = labelNode.getFirstChild();
+        String label = identifier(node);
+        Statement statement = statement(node.getNextSibling());
+        statement.addStatementLabel(label);
+        return statement;
+    }
+
+    protected Statement methodCall(AST code) {
+        Expression expression = methodCallExpression(code);
+        ExpressionStatement expressionStatement = new ExpressionStatement(expression);
+        configureAST(expressionStatement, code);
+        return expressionStatement;
+    }
+
+    protected Expression declarationExpression(AST variableDef) {
+        AST node = variableDef.getFirstChild();
+        ClassNode type = null;
+        List<AnnotationNode> annotations = new ArrayList<AnnotationNode>();
+        int modifiers = 0;
+        if (isType(MODIFIERS, node)) {
+            // force check of modifier conflicts
+            modifiers = modifiers(node, annotations, 0);
+            node = node.getNextSibling();
+        }
+        if (isType(TYPE, node)) {
+            type = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        Expression leftExpression;
+        Expression rightExpression = EmptyExpression.INSTANCE;
+        AST right;
+
+        if (isType(ASSIGN, node)) {
+            node = node.getFirstChild();
+            AST left = node.getFirstChild();
+            ArgumentListExpression alist = new ArgumentListExpression();
+            for (AST varDef = left; varDef != null; varDef = varDef.getNextSibling()) {
+                assertNodeType(VARIABLE_DEF, varDef);
+                DeclarationExpression de = (DeclarationExpression) declarationExpression(varDef);
+                alist.addExpression(de.getVariableExpression());
+            }
+            leftExpression = alist;
+            right = node.getNextSibling();
+            if (right != null) rightExpression = expression(right);
+        } else {
+            String name = identifier(node);
+            VariableExpression ve = new VariableExpression(name, type);
+            ve.setModifiers(modifiers);
+            leftExpression = ve;
+
+            right = node.getNextSibling();
+            if (right != null) {
+                assertNodeType(ASSIGN, right);
+                rightExpression = expression(right.getFirstChild());
+            }
+        }
+
+        configureAST(leftExpression, node);
+
+        Token token = makeToken(Types.ASSIGN, variableDef);
+        DeclarationExpression expression = new DeclarationExpression(leftExpression, token, rightExpression);
+        expression.addAnnotations(annotations);
+        configureAST(expression, variableDef);
+        ExpressionStatement expressionStatement = new ExpressionStatement(expression);
+        configureAST(expressionStatement, variableDef);
+        return expression;
+    }
+
+    protected Statement variableDef(AST variableDef) {
+        ExpressionStatement expressionStatement = new ExpressionStatement(declarationExpression(variableDef));
+        configureAST(expressionStatement, variableDef);
+        return expressionStatement;
+    }
+
+    protected Statement returnStatement(AST node) {
+        AST exprNode = node.getFirstChild();
+
+        // This will pick up incorrect sibling node if 'node' is a plain 'return'
+        //
+        //if (exprNode == null) {
+        //    exprNode = node.getNextSibling();
+        //}
+        Expression expression = exprNode == null ? ConstantExpression.NULL : expression(exprNode);
+        ReturnStatement returnStatement = new ReturnStatement(expression);
+        configureAST(returnStatement, node);
+        return returnStatement;
+    }
+
+    protected Statement switchStatement(AST switchNode) {
+        AST node = switchNode.getFirstChild();
+        Expression expression = expression(node);
+        Statement defaultStatement = EmptyStatement.INSTANCE;
+
+        List list = new ArrayList();
+        for (node = node.getNextSibling(); isType(CASE_GROUP, node); node = node.getNextSibling()) {
+            Statement tmpDefaultStatement;
+            AST child = node.getFirstChild();
+            if (isType(LITERAL_case, child)) {
+                List cases = new LinkedList();
+                // default statement can be grouped with previous case
+                tmpDefaultStatement = caseStatements(child, cases);
+                list.addAll(cases);
+            } else {
+                tmpDefaultStatement = statement(child.getNextSibling());
+            }
+            if (tmpDefaultStatement != EmptyStatement.INSTANCE) {
+                if (defaultStatement == EmptyStatement.INSTANCE) {
+                    defaultStatement = tmpDefaultStatement;
+                } else {
+                    throw new ASTRuntimeException(switchNode, "The default case is already defined.");
+                }
+            }
+        }
+        if (node != null) {
+            unknownAST(node);
+        }
+        SwitchStatement switchStatement = new SwitchStatement(expression, list, defaultStatement);
+        configureAST(switchStatement, switchNode);
+        return switchStatement;
+    }
+
+    protected Statement caseStatements(AST node, List cases) {
+        List<Expression> expressions = new LinkedList<Expression>();
+        Statement statement = EmptyStatement.INSTANCE;
+        Statement defaultStatement = EmptyStatement.INSTANCE;
+        AST nextSibling = node;
+        do {
+            Expression expression = expression(nextSibling.getFirstChild());
+            expressions.add(expression);
+            nextSibling = nextSibling.getNextSibling();
+        } while (isType(LITERAL_case, nextSibling));
+        if (nextSibling != null) {
+            if (isType(LITERAL_default, nextSibling)) {
+                defaultStatement = statement(nextSibling.getNextSibling());
+                statement = EmptyStatement.INSTANCE;
+            } else {
+                statement = statement(nextSibling);
+            }
+        }
+        Iterator iterator = expressions.iterator();
+        while (iterator.hasNext()) {
+            Expression expr = (Expression) iterator.next();
+            Statement stmt;
+            if (iterator.hasNext()) {
+                stmt = new CaseStatement(expr, EmptyStatement.INSTANCE);
+            } else {
+                stmt = new CaseStatement(expr, statement);
+            }
+            configureAST(stmt, node);
+            cases.add(stmt);
+        }
+        return defaultStatement;
+    }
+
+    protected Statement synchronizedStatement(AST syncNode) {
+        AST node = syncNode.getFirstChild();
+        Expression expression = expression(node);
+        Statement code = statement(node.getNextSibling());
+        SynchronizedStatement synchronizedStatement = new SynchronizedStatement(expression, code);
+        configureAST(synchronizedStatement, syncNode);
+        return synchronizedStatement;
+    }
+
+    protected Statement throwStatement(AST node) {
+        AST expressionNode = node.getFirstChild();
+        if (expressionNode == null) {
+            expressionNode = node.getNextSibling();
+        }
+        if (expressionNode == null) {
+            throw new ASTRuntimeException(node, "No expression available");
+        }
+        ThrowStatement throwStatement = new ThrowStatement(expression(expressionNode));
+        configureAST(throwStatement, node);
+        return throwStatement;
+    }
+
+    protected Statement tryStatement(AST tryStatementNode) {
+        AST tryNode = tryStatementNode.getFirstChild();
+        Statement tryStatement = statement(tryNode);
+        Statement finallyStatement = EmptyStatement.INSTANCE;
+        AST node = tryNode.getNextSibling();
+
+        // let's do the catch nodes
+        List<CatchStatement> catches = new ArrayList<CatchStatement>();
+        for (; node != null && isType(LITERAL_catch, node); node = node.getNextSibling()) {
+            final List<CatchStatement> catchStatements = catchStatement(node);
+            catches.addAll(catchStatements);
+        }
+
+        if (isType(LITERAL_finally, node)) {
+            finallyStatement = statement(node);
+            node = node.getNextSibling();
+        }
+
+        if (finallyStatement instanceof EmptyStatement && catches.isEmpty()) {
+            throw new ASTRuntimeException(tryStatementNode, "A try statement must have at least one catch or finally block.");
+        }
+
+        TryCatchStatement tryCatchStatement = new TryCatchStatement(tryStatement, finallyStatement);
+        configureAST(tryCatchStatement, tryStatementNode);
+        for (CatchStatement statement : catches) {
+            tryCatchStatement.addCatch(statement);
+        }
+        return tryCatchStatement;
+    }
+
+    protected List<CatchStatement> catchStatement(AST catchNode) {
+        AST node = catchNode.getFirstChild();
+        List<CatchStatement> catches = new LinkedList<CatchStatement>();
+        Statement code = statement(node.getNextSibling());
+        if (MULTICATCH == node.getType()) {
+            AST variableNode = node.getNextSibling();
+            final AST multicatches = node.getFirstChild();
+            if (multicatches.getType() != MULTICATCH_TYPES) {
+                // catch (e)
+                // catch (def e)
+                String variable = identifier(multicatches);
+                Parameter catchParameter = new Parameter(ClassHelper.DYNAMIC_TYPE, variable);
+                CatchStatement answer = new CatchStatement(catchParameter, code);
+                configureAST(answer, catchNode);
+                catches.add(answer);
+            } else {
+                // catch (Exception e)
+                // catch (Exception1 | Exception2 e)
+                AST exceptionNodes = multicatches.getFirstChild();
+                String variable = identifier(multicatches.getNextSibling());
+                while (exceptionNodes != null) {
+                    ClassNode exceptionType = buildName(exceptionNodes);
+                    Parameter catchParameter = new Parameter(exceptionType, variable);
+                    CatchStatement answer = new CatchStatement(catchParameter, code);
+                    configureAST(answer, catchNode);
+                    catches.add(answer);
+                    exceptionNodes = exceptionNodes.getNextSibling();
+                }
+            }
+        }
+        return catches;
+    }
+
+    protected Statement whileStatement(AST whileNode) {
+        AST node = whileNode.getFirstChild();
+        assertNodeType(EXPR, node);
+        // TODO remove this once we support declarations in the while condition
+        if (isType(VARIABLE_DEF, node.getFirstChild())) {
+            throw new ASTRuntimeException(whileNode,
+                    "While loop condition contains a declaration; this is currently unsupported.");
+        }
+        BooleanExpression booleanExpression = booleanExpression(node);
+
+        node = node.getNextSibling();
+        Statement block;
+        if (isType(SEMI, node)) {
+            block = EmptyStatement.INSTANCE;
+        } else {
+            block = statement(node);
+        }
+        WhileStatement whileStatement = new WhileStatement(booleanExpression, block);
+        configureAST(whileStatement, whileNode);
+        return whileStatement;
+    }
+
+
+    // Expressions
+    //-------------------------------------------------------------------------
+
+    protected Expression expression(AST node) {
+        return expression(node, false);
+    }
+
+    protected Expression expression(AST node, boolean convertToConstant) {
+        Expression expression = expressionSwitch(node);
+        if (convertToConstant && expression instanceof VariableExpression) {
+            // a method name can never be a VariableExpression, so it must converted
+            // to a ConstantExpression then. This is needed as the expression
+            // method doesn't know we want a ConstantExpression instead of a
+            // VariableExpression
+            VariableExpression ve = (VariableExpression) expression;
+            if (!ve.isThisExpression() && !ve.isSuperExpression()) {
+                expression = new ConstantExpression(ve.getName());
+            }
+        }
+        configureAST(expression, node);
+        return expression;
+    }
+
+    protected Expression expressionSwitch(AST node) {
+        int type = node.getType();
+        switch (type) {
+            case EXPR:
+                return expression(node.getFirstChild());
+
+            case ELIST:
+                return expressionList(node);
+
+            case SLIST:
+                return blockExpression(node);
+
+            case CLOSABLE_BLOCK:
+                return closureExpression(node);
+
+            case SUPER_CTOR_CALL:
+                return specialConstructorCallExpression(node, ClassNode.SUPER);
+
+            case METHOD_CALL:
+                return methodCallExpression(node);
+
+            case LITERAL_new:
+                return constructorCallExpression(node);
+
+            case CTOR_CALL:
+                return specialConstructorCallExpression(node, ClassNode.THIS);
+
+            case QUESTION:
+            case ELVIS_OPERATOR:
+                return ternaryExpression(node);
+
+            case OPTIONAL_DOT:
+            case SPREAD_DOT:
+            case DOT:
+                return dotExpression(node);
+
+            case IDENT:
+            case LITERAL_boolean:
+            case LITERAL_byte:
+            case LITERAL_char:
+            case LITERAL_double:
+            case LITERAL_float:
+            case LITERAL_int:
+            case LITERAL_long:
+            case LITERAL_short:
+            case LITERAL_void:
+            case LITERAL_this:
+            case LITERAL_super:
+                return variableExpression(node);
+
+            case LIST_CONSTRUCTOR:
+                return listExpression(node);
+
+            case MAP_CONSTRUCTOR:
+                return mapExpression(node);
+
+            case LABELED_ARG:
+                return mapEntryExpression(node);
+
+            case SPREAD_ARG:
+                return spreadExpression(node);
+
+            case SPREAD_MAP_ARG:
+                return spreadMapExpression(node);
+
+            // commented out of groovy.g due to non determinisms
+            //case MEMBER_POINTER_DEFAULT:
+            //    return defaultMethodPointerExpression(node);
+
+            case MEMBER_POINTER:
+                return methodPointerExpression(node);
+
+            case INDEX_OP:
+                return indexExpression(node);
+
+            case LITERAL_instanceof:
+                return instanceofExpression(node);
+
+            case LITERAL_as:
+                return asExpression(node);
+
+            case TYPECAST:
+                return castExpression(node);
+
+            // literals
+
+            case LITERAL_true:
+                return literalExpression(node, Boolean.TRUE);
+            case LITERAL_false:
+                return literalExpression(node, Boolean.FALSE);
+            case LITERAL_null:
+                return literalExpression(node, null);
+            case STRING_LITERAL:
+                return literalExpression(node, node.getText());
+
+            case STRING_CONSTRUCTOR:
+                return gstring(node);
+
+            case NUM_DOUBLE:
+            case NUM_FLOAT:
+            case NUM_BIG_DECIMAL:
+                return decimalExpression(node);
+
+            case NUM_BIG_INT:
+            case NUM_INT:
+            case NUM_LONG:
+                return integerExpression(node);
+
+            // Unary expressions
+            case LNOT:
+                NotExpression notExpression = new NotExpression(expression(node.getFirstChild()));
+                configureAST(notExpression, node);
+                return notExpression;
+
+            case UNARY_MINUS:
+                return unaryMinusExpression(node);
+
+            case BNOT:
+                BitwiseNegationExpression bitwiseNegationExpression = new BitwiseNegationExpression(expression(node.getFirstChild()));
+                configureAST(bitwiseNegationExpression, node);
+                return bitwiseNegationExpression;
+
+            case UNARY_PLUS:
+                return unaryPlusExpression(node);
+
+            // Prefix expressions
+            case INC:
+                return prefixExpression(node, Types.PLUS_PLUS);
+
+            case DEC:
+                return prefixExpression(node, Types.MINUS_MINUS);
+
+            // Postfix expressions
+            case POST_INC:
+                return postfixExpression(node, Types.PLUS_PLUS);
+
+            case POST_DEC:
+                return postfixExpression(node, Types.MINUS_MINUS);
+
+
+            // Binary expressions
+
+            case ASSIGN:
+                return binaryExpression(Types.ASSIGN, node);
+
+            case EQUAL:
+                return binaryExpression(Types.COMPARE_EQUAL, node);
+
+            case IDENTICAL:
+                return binaryExpression(Types.COMPARE_IDENTICAL, node);
+
+            case NOT_EQUAL:
+                return binaryExpression(Types.COMPARE_NOT_EQUAL, node);
+
+            case NOT_IDENTICAL:
+                return binaryExpression(Types.COMPARE_NOT_IDENTICAL, node);
+
+            case COMPARE_TO:
+                return binaryExpression(Types.COMPARE_TO, node);
+
+            case LE:
+                return binaryExpression(Types.COMPARE_LESS_THAN_EQUAL, node);
+
+            case LT:
+                return binaryExpression(Types.COMPARE_LESS_THAN, node);
+
+            case GT:
+                return binaryExpression(Types.COMPARE_GREATER_THAN, node);
+
+            case GE:
+                return binaryExpression(Types.COMPARE_GREATER_THAN_EQUAL, node);
+
+            /**
+             * TODO treble equal?
+             return binaryExpression(Types.COMPARE_IDENTICAL, node);
+
+             case ???:
+             return binaryExpression(Types.LOGICAL_AND_EQUAL, node);
+
+             case ???:
+             return binaryExpression(Types.LOGICAL_OR_EQUAL, node);
+
+             */
+
+            case LAND:
+                return binaryExpression(Types.LOGICAL_AND, node);
+
+            case LOR:
+                return binaryExpression(Types.LOGICAL_OR, node);
+
+            case BAND:
+                return binaryExpression(Types.BITWISE_AND, node);
+
+            case BAND_ASSIGN:
+                return binaryExpression(Types.BITWISE_AND_EQUAL, node);
+
+            case BOR:
+                return binaryExpression(Types.BITWISE_OR, node);
+
+            case BOR_ASSIGN:
+                return binaryExpression(Types.BITWISE_OR_EQUAL, node);
+
+            case BXOR:
+                return binaryExpression(Types.BITWISE_XOR, node);
+
+            case BXOR_ASSIGN:
+                return binaryExpression(Types.BITWISE_XOR_EQUAL, node);
+
+
+            case PLUS:
+                return binaryExpression(Types.PLUS, node);
+
+            case PLUS_ASSIGN:
+                return binaryExpression(Types.PLUS_EQUAL, node);
+
+
+            case MINUS:
+                return binaryExpression(Types.MINUS, node);
+
+            case MINUS_ASSIGN:
+                return binaryExpression(Types.MINUS_EQUAL, node);
+
+
+            case STAR:
+                return binaryExpression(Types.MULTIPLY, node);
+
+            case STAR_ASSIGN:
+                return binaryExpression(Types.MULTIPLY_EQUAL, node);
+
+
+            case STAR_STAR:
+                return binaryExpression(Types.POWER, node);
+
+            case STAR_STAR_ASSIGN:
+                return binaryExpression(Types.POWER_EQUAL, node);
+
+
+            case DIV:
+                return binaryExpression(Types.DIVIDE, node);
+
+            case DIV_ASSIGN:
+                return binaryExpression(Types.DIVIDE_EQUAL, node);
+
+
+            case MOD:
+                return binaryExpression(Types.MOD, node);
+
+            case MOD_ASSIGN:
+                return binaryExpression(Types.MOD_EQUAL, node);
+
+            case SL:
+                return binaryExpression(Types.LEFT_SHIFT, node);
+
+            case SL_ASSIGN:
+                return binaryExpression(Types.LEFT_SHIFT_EQUAL, node);
+
+            case SR:
+                return binaryExpression(Types.RIGHT_SHIFT, node);
+
+            case SR_ASSIGN:
+                return binaryExpression(Types.RIGHT_SHIFT_EQUAL, node);
+
+            case BSR:
+                return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED, node);
+
+            case BSR_ASSIGN:
+                return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED_EQUAL, node);
+
+            case VARIABLE_DEF:
+                return declarationExpression(node);
+
+            // Regex
+            case REGEX_FIND:
+                return binaryExpression(Types.FIND_REGEX, node);
+
+            case REGEX_MATCH:
+                return binaryExpression(Types.MATCH_REGEX, node);
+
+
+            // Ranges
+            case RANGE_INCLUSIVE:
+                return rangeExpression(node, true);
+
+            case RANGE_EXCLUSIVE:
+                return rangeExpression(node, false);
+
+            case DYNAMIC_MEMBER:
+                return dynamicMemberExpression(node);
+
+            case LITERAL_in:
+                return binaryExpression(Types.KEYWORD_IN, node);
+
+            case ANNOTATION:
+                return new AnnotationConstantExpression(annotation(node));
+
+            case CLOSURE_LIST:
+                return closureListExpression(node);
+
+            case LBRACK:
+            case LPAREN:
+                return tupleExpression(node);
+
+            case OBJBLOCK:
+                return anonymousInnerClassDef(node);
+
+            default:
+                unknownAST(node);
+        }
+        return null;
+    }
+
+    private TupleExpression tupleExpression(AST node) {
+        TupleExpression exp = new TupleExpression();
+        configureAST(exp, node);
+        node = node.getFirstChild();
+        while (node != null) {
+            assertNodeType(VARIABLE_DEF, node);
+            AST nameNode = node.getFirstChild().getNextSibling();
+            VariableExpression varExp = new VariableExpression(nameNode.getText());
+            configureAST(varExp, nameNode);
+            exp.addExpression(varExp);
+            node = node.getNextSibling();
+        }
+        return exp;
+    }
+
+    private ClosureListExpression closureListExpression(AST node) {
+        isClosureListExpressionAllowedHere(node);
+        AST exprNode = node.getFirstChild();
+        List<Expression> list = new LinkedList<Expression>();
+        while (exprNode != null) {
+            if (isType(EXPR, exprNode)) {
+                Expression expr = expression(exprNode);
+                configureAST(expr, exprNode);
+                list.add(expr);
+            } else {
+                assertNodeType(EMPTY_STAT, exprNode);
+                list.add(EmptyExpression.INSTANCE);
+            }
+
+            exprNode = exprNode.getNextSibling();
+        }
+        ClosureListExpression cle = new ClosureListExpression(list);
+        configureAST(cle, node);
+        return cle;
+    }
+
+    private void isClosureListExpressionAllowedHere(AST node) {
+        if (!forStatementBeingDef) {
+            throw new ASTRuntimeException(node,
+                    "Expression list of the form (a; b; c) is not supported in this context.");
+        }
+    }
+
+    protected Expression dynamicMemberExpression(AST dynamicMemberNode) {
+        AST node = dynamicMemberNode.getFirstChild();
+        return expression(node);
+    }
+
+    protected Expression ternaryExpression(AST ternaryNode) {
+        AST node = ternaryNode.getFirstChild();
+        Expression base = expression(node);
+        node = node.getNextSibling();
+        Expression left = expression(node);
+        node = node.getNextSibling();
+        Expression ret;
+        if (node == null) {
+            ret = new ElvisOperatorExpression(base, left);
+        } else {
+            Expression right = expression(node);
+            BooleanExpression booleanExpression = new BooleanExpression(base);
+            booleanExpression.setSourcePosition(base);
+            ret = new TernaryExpression(booleanExpression, left, right);
+        }
+        configureAST(ret, ternaryNode);
+        return ret;
+    }
+
+    protected Expression variableExpression(AST node) {
+        String text = node.getText();
+
+        // TODO we might wanna only try to resolve the name if we are
+        // on the left hand side of an expression or before a dot?
+        VariableExpression variableExpression = new VariableExpression(text);
+        configureAST(variableExpression, node);
+        return variableExpression;
+    }
+
+    protected Expression literalExpression(AST node, Object value) {
+        ConstantExpression constantExpression = new ConstantExpression(value, value instanceof Boolean);
+        configureAST(constantExpression, node);
+        return constantExpression;
+    }
+
+    protected Expression rangeExpression(AST rangeNode, boolean inclusive) {
+        AST node = rangeNode.getFirstChild();
+        Expression left = expression(node);
+        Expression right = expression(node.getNextSibling());
+        RangeExpression rangeExpression = new RangeExpression(left, right, inclusive);
+        configureAST(rangeExpression, rangeNode);
+        return rangeExpression;
+    }
+
+    protected Expression spreadExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        AST listNode = exprNode.getFirstChild();
+        Expression right = expression(listNode);
+        SpreadExpression spreadExpression = new SpreadExpression(right);
+        configureAST(spreadExpression, node);
+        return spreadExpression;
+    }
+
+    protected Expression spreadMapExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        Expression expr = expression(exprNode);
+        SpreadMapExpression spreadMapExpression = new SpreadMapExpression(expr);
+        configureAST(spreadMapExpression, node);
+        return spreadMapExpression;
+    }
+
+    protected Expression methodPointerExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        Expression objectExpression = expression(exprNode);
+        AST mNode = exprNode.getNextSibling();
+        Expression methodName;
+        if (isType(DYNAMIC_MEMBER, mNode)) {
+            methodName = expression(mNode);
+        } else {
+            methodName = new ConstantExpression(identifier(mNode));
+        }
+        configureAST(methodName, mNode);
+        MethodPointerExpression methodPointerExpression = new MethodPointerExpression(objectExpression, methodName);
+        configureAST(methodPointerExpression, node);
+        return methodPointerExpression;
+    }
+
+/*  commented out due to groovy.g non-determinisms
+  protected Expression defaultMethodPointerExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        String methodName = exprNode.toString();
+        MethodPointerExpression methodPointerExpression =

<TRUNCATED>