groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sun...@apache.org
Subject [10/20] groovy git commit: Implement the new parser
Date Fri, 11 Nov 2016 13:33:37 GMT
http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/TestUtils.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/TestUtils.groovy b/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/TestUtils.groovy
new file mode 100644
index 0000000..0212ff3
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/TestUtils.groovy
@@ -0,0 +1,259 @@
+/*
+ *  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.util.logging.Log
+import org.apache.groovy.parser.AbstractParser
+import org.apache.groovy.parser.Antlr2Parser
+import org.apache.groovy.parser.Antlr4Parser
+import org.apache.groovy.parser.antlr4.util.ASTComparatorCategory
+import org.apache.groovy.parser.antlr4.util.AstDumper
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.ast.stmt.*
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.syntax.Token
+
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+
+/**
+ * Utilities for test
+ *
+ * @author  <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on    2016/09/21
+ */
+
+@Log
+class TestUtils {
+    public static final String DEFAULT_RESOURCES_PATH = 'subprojects/groovy-antlr4-grammar/src/test/resources';
+    public static final String RESOURCES_PATH = new File(DEFAULT_RESOURCES_PATH).exists() ? DEFAULT_RESOURCES_PATH : 'src/test/resources';
+
+    static doTest(String path) {
+        return doTest(path, ASTComparatorCategory.DEFAULT_CONFIGURATION)
+    }
+
+    static doTest(String path, List ignoreClazzList) {
+        return doTest(path, addIgnore(ignoreClazzList, ASTComparatorCategory.LOCATION_IGNORE_LIST))
+    }
+
+    static doTest(String path, conf) {
+        AbstractParser antlr4Parser = new Antlr4Parser()
+        AbstractParser antlr2Parser = new Antlr2Parser()
+
+        File file = new File("$RESOURCES_PATH/$path");
+        def (newAST, newElapsedTime) = profile { antlr4Parser.parse(file) }
+        def (oldAST, oldElapsedTime) = profile { antlr2Parser.parse(file) }
+
+
+        assertAST(newAST, oldAST, conf);
+
+        long diffInMillis = newElapsedTime - oldElapsedTime;
+
+        if (diffInMillis >= 500) {
+            log.warning "${path}\t\t\t\t\tdiff:${diffInMillis / 1000}s,\tnew:${newElapsedTime / 1000}s,\told:${oldElapsedTime / 1000}s."
+        }
+
+        return [newAST, oldAST]
+    }
+
+    /*
+    static unzipAndTest(String path, String entryName) {
+        unzipAndTest(path, entryName, ASTComparatorCategory.DEFAULT_CONFIGURATION)
+    }
+    */
+
+    /*
+    static unzipAndTest(String path, String entryName, List ignoreClazzList) {
+        unzipAndTest(path, entryName, addIgnore(ignoreClazzList, ASTComparatorCategory.LOCATION_IGNORE_LIST))
+    }
+    */
+
+    static unzipAndTest(String path, String entryName, conf, Map<String, String> replacementsMap=[:]) {
+        AbstractParser antlr4Parser = new Antlr4Parser()
+        AbstractParser antlr2Parser = new Antlr2Parser()
+
+        String name = "$path!$entryName";
+        String text = readZipEntry(path, entryName);
+
+        replacementsMap?.each {k, v ->
+            text = text.replace(k, v);
+        }
+
+        def (newAST, newElapsedTime) = profile { antlr4Parser.parse(name, text) }
+        def (oldAST, oldElapsedTime) = profile { antlr2Parser.parse(name, text) }
+
+
+        assertAST(newAST, oldAST, conf);
+
+        long diffInMillis = newElapsedTime - oldElapsedTime;
+
+        if (diffInMillis >= 500) {
+            log.warning "${path}!${entryName}\t\t\t\t\tdiff:${diffInMillis / 1000}s,\tnew:${newElapsedTime / 1000}s,\told:${oldElapsedTime / 1000}s."
+        }
+    }
+
+
+    static shouldFail(String path, boolean toCheckNewParserOnly = false) {
+        shouldFail(path, ASTComparatorCategory.DEFAULT_CONFIGURATION, toCheckNewParserOnly)
+    }
+
+    static shouldFail(String path, List ignoreClazzList, boolean toCheckNewParserOnly = false) {
+        shouldFail(path, addIgnore(ignoreClazzList, ASTComparatorCategory.LOCATION_IGNORE_LIST), toCheckNewParserOnly)
+    }
+
+    static shouldFail(String path, conf, boolean toCheckNewParserOnly = false) {
+        AbstractParser antlr4Parser = new Antlr4Parser()
+        AbstractParser antlr2Parser = new Antlr2Parser()
+
+        File file = new File("$RESOURCES_PATH/$path");
+        def (newAST, newElapsedTime) = profile { antlr4Parser.parse(file) }
+        def (oldAST, oldElapsedTime) = profile { antlr2Parser.parse(file) }
+
+        if (toCheckNewParserOnly) {
+            assert (newAST == null || newAST.context.errorCollector.hasErrors())
+        } else {
+            assert (newAST == null || newAST.context.errorCollector.hasErrors()) &&
+                    (oldAST == null || oldAST.context.errorCollector.hasErrors())
+        }
+
+        long diffInMillis = newElapsedTime - oldElapsedTime;
+
+        if (diffInMillis >= 500) {
+            log.warning "${path}\t\t\t\t\tdiff:${diffInMillis / 1000}s,\tnew:${newElapsedTime / 1000}s,\told:${oldElapsedTime / 1000}s."
+        }
+    }
+
+    static unzipAndFail(String path, String entryName, conf, Map<String, String> replacementsMap=[:], boolean toCheckNewParserOnly = false) {
+        AbstractParser antlr4Parser = new Antlr4Parser()
+        AbstractParser antlr2Parser = new Antlr2Parser()
+
+        String name = "$path!$entryName";
+        String text = readZipEntry(path, entryName);
+
+        replacementsMap?.each {k, v ->
+            text = text.replace(k, v);
+        }
+
+        def (newAST, newElapsedTime) = profile { antlr4Parser.parse(name, text) }
+        def (oldAST, oldElapsedTime) = profile { antlr2Parser.parse(name, text) }
+
+        if (toCheckNewParserOnly) {
+            assert (newAST == null || newAST.context.errorCollector.hasErrors())
+        } else {
+            assert (newAST == null || newAST.context.errorCollector.hasErrors()) &&
+                    (oldAST == null || oldAST.context.errorCollector.hasErrors())
+        }
+
+        long diffInMillis = newElapsedTime - oldElapsedTime;
+
+        if (diffInMillis >= 500) {
+            log.warning "${path}!${entryName}\t\t\t\t\tdiff:${diffInMillis / 1000}s,\tnew:${newElapsedTime / 1000}s,\told:${oldElapsedTime / 1000}s."
+        }
+    }
+
+
+    static assertAST(ast1, ast2, conf) {
+        assert null != ast1 && null != ast2
+
+        ASTComparatorCategory.apply(conf) {
+            assert ast1 == ast2
+        }
+
+        assert genSrc(ast1) == genSrc(ast2)
+    }
+
+    static genSrc(ModuleNode ast) {
+        return new AstDumper(ast).gen();
+    }
+
+    static profile(Closure c) {
+        long begin = System.currentTimeMillis()
+        def result = c.call()
+        long end = System.currentTimeMillis()
+
+        return [result, end - begin];
+    }
+
+    static addIgnore(Class aClass, ArrayList<String> ignore, Map<Class, List<String>> c = null) {
+        c = c ?: ASTComparatorCategory.DEFAULT_CONFIGURATION.clone() as Map<Class, List<String>>;
+        c[aClass].addAll(ignore)
+        return c
+    }
+
+    static addIgnore(Collection<Class> aClass, ArrayList<String> ignore, Map<Class, List<String>> c = null) {
+        c = c ?: ASTComparatorCategory.DEFAULT_CONFIGURATION.clone() as Map<Class, List<String>>;
+        aClass.each { c[it].addAll(ignore) }
+        return c
+    }
+
+    static readZipEntry(String path, String entryName) {
+        String result = "";
+
+        def zf = new ZipFile(new File(path));
+        try {
+            def is = new BufferedInputStream(zf.getInputStream(new ZipEntry(entryName)));
+            result = is.getText("UTF-8");
+        } catch (Exception e) {
+            log.severe(e.message);
+        } finally {
+            try {
+                zf.close();
+            } catch(Exception e) {
+                // IGNORED
+            }
+        }
+
+        return result;
+    }
+
+    static doRunAndShouldFail(String path) {
+        assert !executeScript(path);
+    }
+
+    static doRunAndTest(String path) {
+        assert executeScript(path);
+    }
+
+    static executeScript(String path) {
+        executeScript(createAntlr4Shell(), "$RESOURCES_PATH/$path")
+    }
+
+    static executeScript(gsh, String path) {
+        def file = new File(path);
+        def content = file.text;
+
+        try {
+            gsh.evaluate(content);
+//            log.info("Evaluated $file")
+            return true;
+        } catch (Throwable t) {
+            log.severe("Failed $file: ${t.getMessage()}");
+            return false;
+        }
+    }
+
+    static createAntlr4Shell() {
+        CompilerConfiguration configuration = new CompilerConfiguration(CompilerConfiguration.DEFAULT)
+        configuration.pluginFactory = new Antlr4PluginFactory()
+
+        return new GroovyShell(configuration);
+    }
+
+    public static final List COMMON_IGNORE_CLASS_LIST = Collections.unmodifiableList([AssertStatement, BreakStatement, ConstructorNode, ContinueStatement, ExpressionStatement, FieldNode, ForStatement, GenericsType, IfStatement, MethodNode, PackageNode, Parameter, PropertyNode, ReturnStatement, ThrowStatement, Token, WhileStatement]);
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy b/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy
new file mode 100644
index 0000000..47b9507
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/ASTComparatorCategory.groovy
@@ -0,0 +1,526 @@
+/*
+ *  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.util
+
+import groovy.util.logging.Log
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.ast.expr.*
+import org.codehaus.groovy.ast.stmt.*
+import org.codehaus.groovy.syntax.Token
+
+import java.util.logging.Level
+
+@Log @SuppressWarnings("GroovyUnusedDeclaration")
+class ASTComparatorCategory {
+    static { log.level = Level.WARNING }
+    static List<String> LOCATION_IGNORE_LIST = ["columnNumber", "lineNumber", "lastColumnNumber", "lastLineNumber", "startLine"]
+    static private List<String> EXPRESSION_IGNORE_LIST = ["text"] + LOCATION_IGNORE_LIST
+
+    /**
+     *  Keeps all checked object pairs and their comparison result.
+     *  Will be cleared at {@link #apply(groovy.lang.Closure)} method }
+     */
+    static objects = [:] as Map<List<Object>, Boolean>
+    static String lastName
+
+    static Map<Class, List<String>> DEFAULT_CONFIGURATION = [
+            (ClassNode): (['module', "declaredMethodsMap", "plainNodeReference", "typeClass", "allInterfaces", "orAddStaticConstructorNode", "allDeclaredMethods", "unresolvedSuperClass", "innerClasses" ] + LOCATION_IGNORE_LIST) as List<String>,
+            (ConstructorNode): ['declaringClass'],
+            (DynamicVariable): [],
+            (EnumConstantClassNode): ["typeClass"],
+            (FieldNode): ["owner", "declaringClass", "initialValueExpression", "assignToken"],
+            (GenericsType): [],
+            (ImportNode): LOCATION_IGNORE_LIST,
+            (InnerClassNode): (['module', "declaredMethodsMap", "plainNodeReference", "typeClass", "allInterfaces", "orAddStaticConstructorNode", "allDeclaredMethods", "unresolvedSuperClass", "innerClasses" ] + LOCATION_IGNORE_LIST) as List<String>,
+            (InterfaceHelperClassNode): [],
+            (MethodNode): ["text", "declaringClass"],
+            (MixinNode): [],
+            (ModuleNode): ["context"],
+            (PackageNode): [],
+            (Parameter): [],
+            (PropertyNode): ['declaringClass', 'initialValueExpression', "assignToken"],
+            (Variable): [],
+            (VariableScope): ["clazzScope", "parent", "declaredVariablesIterator"],
+            (Token): ["root", "startColumn"],
+            (AnnotationNode): (["text"] + LOCATION_IGNORE_LIST) as List<String>,
+            (AssertStatement): ["text"],
+            (BlockStatement): ["columnNumber", "lineNumber", "lastColumnNumber", "lastLineNumber", "text"],
+            (BreakStatement): ["text"],
+            (CaseStatement): ["text"],
+            (CatchStatement): (["text"] + LOCATION_IGNORE_LIST) as List<String>,
+            (ContinueStatement): ["text"],
+            (DoWhileStatement): ["text"],
+            (EmptyStatement): ["text"],
+            (ExpressionStatement): ["text"],
+            (ForStatement): ["text"],
+            (IfStatement): ["text"],
+            (LoopingStatement): ["text"],
+            (ReturnStatement): ["text"],
+            (SwitchStatement): ["columnNumber", "lineNumber", "lastColumnNumber", "lastLineNumber", "text"],
+            (SynchronizedStatement): ["text"],
+            (ThrowStatement): ["text"],
+            (TryCatchStatement): (["text"] + LOCATION_IGNORE_LIST) as List<String>,
+            (WhileStatement): ["text"],
+            (AnnotationConstantExpression): EXPRESSION_IGNORE_LIST,
+            (ArgumentListExpression): EXPRESSION_IGNORE_LIST,
+            (ArrayExpression): EXPRESSION_IGNORE_LIST,
+            (AttributeExpression): EXPRESSION_IGNORE_LIST,
+            (BinaryExpression): EXPRESSION_IGNORE_LIST,
+            (BitwiseNegationExpression): EXPRESSION_IGNORE_LIST,
+            (BooleanExpression): EXPRESSION_IGNORE_LIST,
+            (CastExpression): EXPRESSION_IGNORE_LIST,
+            (ClassExpression): EXPRESSION_IGNORE_LIST,
+            (ClosureExpression): EXPRESSION_IGNORE_LIST,
+            (ClosureListExpression): EXPRESSION_IGNORE_LIST,
+            (ConstantExpression): EXPRESSION_IGNORE_LIST,
+            (ConstructorCallExpression): EXPRESSION_IGNORE_LIST,
+            (DeclarationExpression): ["text", "columnNumber", "lineNumber", "lastColumnNumber", "lastLineNumber"],
+            (ElvisOperatorExpression): EXPRESSION_IGNORE_LIST,
+            (EmptyExpression): EXPRESSION_IGNORE_LIST,
+            (ExpressionTransformer): EXPRESSION_IGNORE_LIST,
+            (FieldExpression): EXPRESSION_IGNORE_LIST,
+            (GStringExpression): EXPRESSION_IGNORE_LIST,
+            (ListExpression): EXPRESSION_IGNORE_LIST,
+            (MapEntryExpression): EXPRESSION_IGNORE_LIST,
+            (MapExpression): EXPRESSION_IGNORE_LIST,
+            (MethodCall): EXPRESSION_IGNORE_LIST,
+            (MethodCallExpression): EXPRESSION_IGNORE_LIST,
+            (MethodPointerExpression): EXPRESSION_IGNORE_LIST,
+            (NamedArgumentListExpression): EXPRESSION_IGNORE_LIST,
+            (NotExpression): EXPRESSION_IGNORE_LIST,
+            (PostfixExpression): EXPRESSION_IGNORE_LIST,
+            (PrefixExpression): EXPRESSION_IGNORE_LIST,
+            (PropertyExpression): EXPRESSION_IGNORE_LIST,
+            (RangeExpression): EXPRESSION_IGNORE_LIST,
+            (SpreadExpression): EXPRESSION_IGNORE_LIST,
+            (SpreadMapExpression): EXPRESSION_IGNORE_LIST,
+            (StaticMethodCallExpression): EXPRESSION_IGNORE_LIST,
+            (TernaryExpression): EXPRESSION_IGNORE_LIST,
+            (TupleExpression): EXPRESSION_IGNORE_LIST,
+            (UnaryMinusExpression): EXPRESSION_IGNORE_LIST,
+            (UnaryPlusExpression): EXPRESSION_IGNORE_LIST,
+            (VariableExpression): EXPRESSION_IGNORE_LIST,
+    ];
+
+    static Map<Class, List<String>> COLLECTION_PROPERTY_CONFIGURATION = [
+            (ModuleNode): ["classes", "name"]
+    ]
+
+    static Map<Class, List<String>> configuration = DEFAULT_CONFIGURATION;
+
+    static void apply(config = DEFAULT_CONFIGURATION, Closure cl) {
+        configuration = config
+        objects.clear()
+        use(ASTComparatorCategory, cl)
+        configuration = DEFAULT_CONFIGURATION
+    }
+
+    /**
+     * Main method that makes the magic. Compares all properties for object a and object b.
+     * There is a lot of problems in this code, like omitted class checking and so on. Just belive, it will be used properly.
+     * @param a
+     * @param b
+     * @return
+     */
+    static reflexiveEquals(a, b, ignore = []) {
+
+        if (a.getClass() != b.getClass()) {
+            log.warning(" !!!! DIFFERENCE WAS FOUND! ${a.getClass()} != ${b.getClass()}")
+            return false;
+        }
+
+        def objects = [a, b]
+        Boolean res = this.objects[objects]
+        if (res != null) {
+            log.info("Skipping [$a, $b] comparison as they are ${ res ? "" : "un" }equal.")
+            return res;
+        }
+        else if (this.objects.containsKey(objects)) {
+            log.info("Skipping as they are processed at higher levels.")
+            return true
+        }
+
+        this.objects[objects] = null
+        log.info("Equals was called for ${ a.getClass() } ${ a.hashCode() }, $lastName")
+        if (a.is(b))
+            return true
+
+        def difference = a.metaClass.properties.find { MetaBeanProperty p ->
+            if (!p.getter)
+                return false
+
+            def name = p.name
+            lastName = "$name :::: ${ a.getClass() } ${ a.hashCode() }"
+
+
+            for (Map.Entry<Class, List<String>> me : COLLECTION_PROPERTY_CONFIGURATION) {
+                if (!(me.key.isCase(a) && me.key.isCase(b))) {
+                    continue;
+                }
+
+                String propName = me.value[0];
+
+                if (name != propName) {
+                    continue;
+                }
+
+                def aValue = a."${propName}"; // FIXME when the propName is "classes", a classNode will be added to moduleNode.classes
+                def bValue = b."${propName}";
+
+                String orderName = me.value[1];
+
+                return new LinkedList(aValue?.getClass()?.isArray() ? Arrays.asList(aValue) : (aValue ?: [])).sort {c1, c2 -> c1."${orderName}" <=> c2."${orderName}"} !=
+                        new LinkedList(bValue?.getClass()?.isArray() ? Arrays.asList(bValue) : (bValue ?: [])).sort {c1, c2 -> c1."${orderName}" <=> c2."${orderName}"}
+            }
+
+
+            !(name in ignore) && (name != 'nodeMetaData' && name != 'metaDataMap') && a."$name" != b."$name"
+        }
+
+        if (difference)
+            log.warning(" !!!! DIFFERENCE WAS FOUND! [${a.metaClass.hasProperty(a, 'text') ? a.text : '<NO TEXT>'}][${a.class}][${difference.name}]:: ${ a."$difference.name" } != ${ b."$difference.name" }")
+        else
+            log.info(" ==== Exit ${ a.getClass() } ${ a.hashCode() } ====== ")
+
+        res = difference == null
+        this.objects[objects] = res
+        this.objects[objects.reverse(false)] = res
+        res
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Just a bunch of copypasted methods. Maybe will wrote AST transformation for them.
+    ////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    static equals(ClassNode a, ClassNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ConstructorNode a, ConstructorNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(DynamicVariable a, DynamicVariable b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(EnumConstantClassNode a, EnumConstantClassNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(FieldNode a, FieldNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(GenericsType a, GenericsType b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ImportNode a, ImportNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(InnerClassNode a, InnerClassNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(InterfaceHelperClassNode a, InterfaceHelperClassNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MethodNode a, MethodNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MixinNode a, MixinNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ModuleNode a, ModuleNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(PackageNode a, PackageNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(Parameter a, Parameter b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(PropertyNode a, PropertyNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(Variable a, Variable b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(VariableScope a, VariableScope b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(Token a, Token b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(CompileUnit a, CompileUnit b) {
+        true
+    }
+
+    static equals(AnnotationNode a, AnnotationNode b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Statements
+    ////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    static equals(AssertStatement a, AssertStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(BlockStatement a, BlockStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(BreakStatement a, BreakStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(CaseStatement a, CaseStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(CatchStatement a, CatchStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ContinueStatement a, ContinueStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(DoWhileStatement a, DoWhileStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(EmptyStatement a, EmptyStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ExpressionStatement a, ExpressionStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ForStatement a, ForStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(IfStatement a, IfStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(LoopingStatement a, LoopingStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ReturnStatement a, ReturnStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(SwitchStatement a, SwitchStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(SynchronizedStatement a, SynchronizedStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ThrowStatement a, ThrowStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(TryCatchStatement a, TryCatchStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(WhileStatement a, WhileStatement b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    /////////////////////////////////////////////////////////////////////////////////////////////
+    // Expressions
+    /////////////////////////////////////////////////////////////////////////////////////////////
+
+    static equals(AnnotationConstantExpression a, AnnotationConstantExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ArgumentListExpression a, ArgumentListExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ArrayExpression a, ArrayExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(AttributeExpression a, AttributeExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(BinaryExpression a, BinaryExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(BitwiseNegationExpression a, BitwiseNegationExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(BooleanExpression a, BooleanExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(CastExpression a, CastExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ClassExpression a, ClassExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ClosureExpression a, ClosureExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ClosureListExpression a, ClosureListExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ConstantExpression a, ConstantExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ConstructorCallExpression a, ConstructorCallExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(DeclarationExpression a, DeclarationExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ElvisOperatorExpression a, ElvisOperatorExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(EmptyExpression a, EmptyExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ExpressionTransformer a, ExpressionTransformer b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(FieldExpression a, FieldExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(GStringExpression a, GStringExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(ListExpression a, ListExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MapEntryExpression a, MapEntryExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MapExpression a, MapExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MethodCall a, MethodCall b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MethodCallExpression a, MethodCallExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(MethodPointerExpression a, MethodPointerExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(NamedArgumentListExpression a, NamedArgumentListExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(NotExpression a, NotExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(PostfixExpression a, PostfixExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(PrefixExpression a, PrefixExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(PropertyExpression a, PropertyExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(RangeExpression a, RangeExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(SpreadExpression a, SpreadExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(SpreadMapExpression a, SpreadMapExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(StaticMethodCallExpression a, StaticMethodCallExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(TernaryExpression a, TernaryExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(TupleExpression a, TupleExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(UnaryMinusExpression a, UnaryMinusExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(UnaryPlusExpression a, UnaryPlusExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+
+    static equals(VariableExpression a, VariableExpression b) {
+        reflexiveEquals(a, b, configuration[a.class])
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/AstDumper.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/AstDumper.groovy b/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/AstDumper.groovy
new file mode 100644
index 0000000..72a64e5
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/groovy/org/apache/groovy/parser/antlr4/util/AstDumper.groovy
@@ -0,0 +1,1025 @@
+/*
+ *  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.util
+
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.ast.expr.*
+import org.codehaus.groovy.ast.stmt.*
+import org.codehaus.groovy.classgen.BytecodeExpression
+import org.codehaus.groovy.classgen.GeneratorContext
+import org.codehaus.groovy.classgen.Verifier
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.control.io.ReaderSource
+
+import java.lang.reflect.Modifier
+
+/**
+ * Generate the groovy source according to the AST.
+ * It is useful to verify the equality of new and old parser.
+ *
+ * @author  <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on    2016/08/16
+ */
+class AstDumper {
+    private ModuleNode ast;
+
+    public AstDumper(ModuleNode ast) {
+        this.ast = ast;
+    }
+
+    /**
+     * Generate the groovy source code according the AST
+     *
+     * @return the groovy source code
+     */
+    public String gen() {
+        StringWriter out = new StringWriter();
+
+        try {
+            AstNodeToScriptVisitor visitor = new AstNodeToScriptVisitor(out, true, true);
+
+            new LinkedList<ClassNode>(this.ast?.classes ?: []).sort { c1, c2 -> c1.name <=> c2.name }?.each {
+                visitor.call(new SourceUnit((String) null, (ReaderSource) null, null, null, null) {
+                    @Override
+                    public ModuleNode getAST() {
+                        return AstDumper.this.ast;
+                    }
+                }, null, it)
+            }
+
+            return out.toString().replaceAll(/([\w_$]+)@[0-9a-z]+/, '$1@<hashcode>');
+        } finally {
+            out.close();
+        }
+    }
+}
+
+/**
+ * *****************************************************
+ * In order to solve the "Egg & Chicken" problem,
+ * we have to copy the source code(instead of invoking it): subprojects/groovy-console/src/main/groovy/groovy/inspect/swingui/AstNodeToScriptAdapter.groovy
+ * *****************************************************
+ *
+ *
+ * An adapter from ASTNode tree to source code.
+ *
+ * @author Hamlet D'Arcy
+ */
+class AstNodeToScriptVisitor extends CompilationUnit.PrimaryClassNodeOperation implements GroovyCodeVisitor, GroovyClassVisitor {
+
+    private final Writer _out
+    Stack<String> classNameStack = new Stack<String>()
+    String _indent = ''
+    boolean readyToIndent = true
+    boolean showScriptFreeForm
+    boolean showScriptClass
+    boolean scriptHasBeenVisited
+
+    def AstNodeToScriptVisitor(Writer writer, boolean showScriptFreeForm = true, boolean showScriptClass = true) {
+        this._out = writer
+        this.showScriptFreeForm = showScriptFreeForm
+        this.showScriptClass = showScriptClass
+        this.scriptHasBeenVisited = false
+    }
+
+    void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
+
+        visitPackage(source?.getAST()?.getPackage())
+
+        visitAllImports(source)
+
+        if (showScriptFreeForm && !scriptHasBeenVisited) {
+            scriptHasBeenVisited = true
+            source?.getAST()?.getStatementBlock()?.visit(this)
+        }
+        if (showScriptClass || !classNode.isScript()) {
+            visitClass classNode
+        }
+    }
+
+    private def visitAllImports(SourceUnit source) {
+        boolean staticImportsPresent = false
+        boolean importsPresent = false
+
+        source?.getAST()?.getStaticImports()?.values()?.each {
+            visitImport(it)
+            staticImportsPresent = true
+        }
+        source?.getAST()?.getStaticStarImports()?.values()?.each {
+            visitImport(it)
+            staticImportsPresent = true
+        }
+
+        if (staticImportsPresent) {
+            printDoubleBreak()
+        }
+
+        source?.getAST()?.getImports()?.each {
+            visitImport(it)
+            importsPresent = true
+        }
+        source?.getAST()?.getStarImports()?.each {
+            visitImport(it)
+            importsPresent = true
+        }
+        if (importsPresent) {
+            printDoubleBreak()
+        }
+    }
+
+
+    void print(parameter) {
+        def output = parameter.toString()
+
+        if (readyToIndent) {
+            _out.print _indent
+            readyToIndent = false
+            while (output.startsWith(' ')) {
+                output = output[1..-1]  // trim left
+            }
+        }
+        if (_out.toString().endsWith(' ')) {
+            if (output.startsWith(' ')) {
+                output = output[1..-1]
+            }
+        }
+        _out.print output
+    }
+
+    def println(parameter) {
+        throw new UnsupportedOperationException('Wrong API')
+    }
+
+    def indented(Closure block) {
+        String startingIndent = _indent
+        _indent = _indent + '    '
+        block()
+        _indent = startingIndent
+    }
+
+    def printLineBreak() {
+        if (!_out.toString().endsWith('\n')) {
+            _out.print '\n'
+        }
+        readyToIndent = true
+    }
+
+    def printDoubleBreak() {
+        if (_out.toString().endsWith('\n\n')) {
+            // do nothing
+        } else if (_out.toString().endsWith('\n')) {
+            _out.print '\n'
+        } else {
+            _out.print '\n'
+            _out.print '\n'
+        }
+        readyToIndent = true
+    }
+
+    void visitPackage(PackageNode packageNode) {
+
+        if (packageNode) {
+
+            packageNode.annotations?.each {
+                visitAnnotationNode(it)
+                printLineBreak()
+            }
+
+            if (packageNode.text.endsWith('.')) {
+                print packageNode.text[0..-2]
+            } else {
+                print packageNode.text
+            }
+            printDoubleBreak()
+        }
+    }
+
+    void visitImport(ImportNode node) {
+        if (node) {
+            node.annotations?.each {
+                visitAnnotationNode(it)
+                printLineBreak()
+            }
+            print node.text
+            printLineBreak()
+        }
+    }
+
+    @Override
+    void visitClass(ClassNode node) {
+
+        classNameStack.push(node.name)
+
+        node?.annotations?.each {
+            visitAnnotationNode(it)
+            printLineBreak()
+        }
+
+        visitModifiers(node.modifiers)
+        print "class $node.name"
+        visitGenerics node?.genericsTypes
+        boolean first = true
+        node.unresolvedInterfaces?.each {
+            if (!first) {
+                print ', '
+            } else {
+                print ' implements '
+            }
+            first = false
+            visitType it
+        }
+        print ' extends '
+        visitType node.unresolvedSuperClass
+        print ' { '
+        printDoubleBreak()
+
+        indented {
+            node?.properties?.each { visitProperty(it) }
+            printLineBreak()
+            node?.fields?.each { visitField(it) }
+            printDoubleBreak()
+            node?.declaredConstructors?.each { visitConstructor(it) }
+            printLineBreak()
+            node?.methods?.each { visitMethod(it) }
+        }
+        print '}'
+        printLineBreak()
+        classNameStack.pop()
+    }
+
+    private void visitGenerics(GenericsType[] generics) {
+
+        if (generics) {
+            print '<'
+            boolean first = true
+            generics.each { GenericsType it ->
+                if (!first) {
+                    print ', '
+                }
+                first = false
+                print it.name
+                if (it.upperBounds) {
+                    print ' extends '
+                    boolean innerFirst = true
+                    it.upperBounds.each { ClassNode upperBound ->
+                        if (!innerFirst) {
+                            print ' & '
+                        }
+                        innerFirst = false
+                        visitType upperBound
+                    }
+                }
+                if (it.lowerBound) {
+                    print ' super '
+                    visitType it.lowerBound
+                }
+            }
+            print '>'
+        }
+    }
+
+    @Override
+    void visitConstructor(ConstructorNode node) {
+        visitMethod(node)
+    }
+
+    private String visitParameters(parameters) {
+        boolean first = true
+
+        parameters.each { Parameter it ->
+            if (!first) {
+                print ', '
+            }
+            first = false
+
+            it.annotations?.each {
+                visitAnnotationNode(it)
+                print(' ')
+            }
+
+            visitModifiers(it.modifiers)
+            visitType it.type
+            print ' ' + it.name
+            if (it.initialExpression && !(it.initialExpression instanceof EmptyExpression)) {
+                print ' = '
+                it.initialExpression.visit this
+            }
+        }
+    }
+
+    @Override
+    void visitMethod(MethodNode node) {
+        node?.annotations?.each {
+            visitAnnotationNode(it)
+            printLineBreak()
+        }
+
+        visitModifiers(node.modifiers)
+        if (node.name == '<init>') {
+            print "${classNameStack.peek()}("
+            visitParameters(node.parameters)
+            print ') {'
+            printLineBreak()
+        } else if (node.name == '<clinit>') {
+            print '{ ' // will already have 'static' from modifiers
+            printLineBreak()
+        } else {
+            visitType node.returnType
+            print " $node.name("
+            visitParameters(node.parameters)
+            print ')'
+            if (node.exceptions) {
+                boolean first = true
+                print ' throws '
+                node.exceptions.each {
+                    if (!first) {
+                        print ', '
+                    }
+                    first = false
+                    visitType it
+                }
+            }
+            print ' {'
+            printLineBreak()
+        }
+
+        indented {
+            node?.code?.visit(this)
+        }
+        printLineBreak()
+        print '}'
+        printDoubleBreak()
+    }
+
+    private def visitModifiers(int modifiers) {
+        if (Modifier.isAbstract(modifiers)) {
+            print 'abstract '
+        }
+        if (Modifier.isFinal(modifiers)) {
+            print 'final '
+        }
+        if (Modifier.isInterface(modifiers)) {
+            print 'interface '
+        }
+        if (Modifier.isNative(modifiers)) {
+            print 'native '
+        }
+        if (Modifier.isPrivate(modifiers)) {
+            print 'private '
+        }
+        if (Modifier.isProtected(modifiers)) {
+            print 'protected '
+        }
+        if (Modifier.isPublic(modifiers)) {
+            print 'public '
+        }
+        if (Modifier.isStatic(modifiers)) {
+            print 'static '
+        }
+        if (Modifier.isSynchronized(modifiers)) {
+            print 'synchronized '
+        }
+        if (Modifier.isTransient(modifiers)) {
+            print 'transient '
+        }
+        if (Modifier.isVolatile(modifiers)) {
+            print 'volatile '
+        }
+    }
+
+    @Override
+    void visitField(FieldNode node) {
+        node?.annotations?.each {
+            visitAnnotationNode(it)
+            printLineBreak()
+        }
+        visitModifiers(node.modifiers)
+        visitType node.type
+        print " $node.name "
+        // do not print initial expression, as this is executed as part of the constructor, unless on static constant
+        Expression exp = node.initialValueExpression
+        if (exp instanceof ConstantExpression) exp = Verifier.transformToPrimitiveConstantIfPossible(exp)
+        ClassNode type = exp?.type
+        if (Modifier.isStatic(node.modifiers) && Modifier.isFinal(node.getModifiers())
+                && exp instanceof ConstantExpression
+                && type == node.type
+                && ClassHelper.isStaticConstantInitializerType(type)) {
+            // GROOVY-5150: final constants may be initialized directly
+            print ' = '
+            if (ClassHelper.STRING_TYPE == type) {
+                print "'"+node.initialValueExpression.text.replaceAll("'", "\\\\'")+"'"
+            } else if (ClassHelper.char_TYPE == type) {
+                print "'${node.initialValueExpression.text}'"
+            } else {
+                print node.initialValueExpression.text
+            }
+        }
+        printLineBreak()
+    }
+
+    void visitAnnotationNode(AnnotationNode node) {
+        print '@' + node?.classNode?.name
+        if (node?.members) {
+            print '('
+            boolean first = true
+            node.members.each { String name, Expression value ->
+                if (first) {
+                    first = false
+                } else {
+                    print ', '
+                }
+                print name + ' = '
+                value.visit(this)
+            }
+            print ')'
+        }
+
+    }
+
+    @Override
+    void visitProperty(PropertyNode node) {
+        // is a FieldNode, avoid double dispatch
+    }
+
+    @Override
+    void visitBlockStatement(BlockStatement block) {
+        block?.statements?.each {
+            it.visit(this)
+            printLineBreak()
+        }
+        if (!_out.toString().endsWith('\n')) {
+            printLineBreak()
+        }
+    }
+
+    @Override
+    void visitForLoop(ForStatement statement) {
+
+        print 'for ('
+        if (statement?.variable != ForStatement.FOR_LOOP_DUMMY) {
+            visitParameters([statement.variable])
+            print ' : '
+        }
+
+        if (statement?.collectionExpression instanceof ListExpression) {
+            statement?.collectionExpression?.visit this
+        } else {
+            statement?.collectionExpression?.visit this
+        }
+        print ') {'
+        printLineBreak()
+        indented {
+            statement?.loopBlock?.visit this
+        }
+        print '}'
+        printLineBreak()
+    }
+
+    @Override
+    void visitIfElse(IfStatement ifElse) {
+        print 'if ('
+        ifElse?.booleanExpression?.visit this
+        print ') {'
+        printLineBreak()
+        indented {
+            ifElse?.ifBlock?.visit this
+        }
+        printLineBreak()
+        if (ifElse?.elseBlock && !(ifElse.elseBlock instanceof EmptyStatement)) {
+            print '} else {'
+            printLineBreak()
+            indented {
+                ifElse?.elseBlock?.visit this
+            }
+            printLineBreak()
+        }
+        print '}'
+        printLineBreak()
+    }
+
+    @Override
+    void visitExpressionStatement(ExpressionStatement statement) {
+        statement.expression.visit this
+    }
+
+    @Override
+    void visitReturnStatement(ReturnStatement statement) {
+        printLineBreak()
+        print 'return '
+        statement.getExpression().visit(this)
+        printLineBreak()
+    }
+
+    @Override
+    void visitSwitch(SwitchStatement statement) {
+        print 'switch ('
+        statement?.expression?.visit this
+        print ') {'
+        printLineBreak()
+        indented {
+            statement?.caseStatements?.each {
+                visitCaseStatement it
+            }
+            if (statement?.defaultStatement) {
+                print 'default: '
+                printLineBreak()
+                statement?.defaultStatement?.visit this
+            }
+        }
+        print '}'
+        printLineBreak()
+    }
+
+    @Override
+    void visitCaseStatement(CaseStatement statement) {
+        print 'case '
+        statement?.expression?.visit this
+        print ':'
+        printLineBreak()
+        indented {
+            statement?.code?.visit this
+        }
+    }
+
+    @Override
+    void visitBreakStatement(BreakStatement statement) {
+        print 'break'
+        printLineBreak()
+    }
+
+    @Override
+    void visitContinueStatement(ContinueStatement statement) {
+        print 'continue'
+        printLineBreak()
+    }
+
+    @Override
+    void visitMethodCallExpression(MethodCallExpression expression) {
+
+        Expression objectExp = expression.getObjectExpression()
+        if (objectExp instanceof VariableExpression) {
+            visitVariableExpression(objectExp, false)
+        } else {
+            objectExp.visit(this)
+        }
+        if (expression.spreadSafe) {
+            print '*'
+        }
+        if (expression.safe) {
+            print '?'
+        }
+        print '.'
+        Expression method = expression.getMethod()
+        if (method instanceof ConstantExpression) {
+            visitConstantExpression(method, true)
+        } else {
+            method.visit(this)
+        }
+        expression.getArguments().visit(this)
+    }
+
+    @Override
+    void visitStaticMethodCallExpression(StaticMethodCallExpression expression) {
+        print expression?.ownerType?.name + '.' + expression?.method
+        if (expression?.arguments instanceof VariableExpression || expression?.arguments instanceof MethodCallExpression) {
+            print '('
+            expression?.arguments?.visit this
+            print ')'
+        } else {
+            expression?.arguments?.visit this
+        }
+    }
+
+    @Override
+    void visitConstructorCallExpression(ConstructorCallExpression expression) {
+        if (expression?.isSuperCall()) {
+            print 'super'
+        } else if (expression?.isThisCall()) {
+            print 'this '
+        } else {
+            print 'new '
+            visitType expression?.type
+        }
+        expression?.arguments?.visit this
+    }
+
+    @Override
+    void visitBinaryExpression(BinaryExpression expression) {
+        expression?.leftExpression?.visit this
+        print " $expression.operation.text "
+        expression.rightExpression.visit this
+
+        if (expression?.operation?.text == '[') {
+            print ']'
+        }
+    }
+
+    @Override
+    void visitPostfixExpression(PostfixExpression expression) {
+        print '('
+        expression?.expression?.visit this
+        print ')'
+        print expression?.operation?.text
+    }
+
+    @Override
+    void visitPrefixExpression(PrefixExpression expression) {
+        print expression?.operation?.text
+        print '('
+        expression?.expression?.visit this
+        print ')'
+    }
+
+
+    @Override
+    void visitClosureExpression(ClosureExpression expression) {
+        print '{ '
+        if (expression?.parameters) {
+            visitParameters(expression?.parameters)
+            print ' ->'
+        }
+        printLineBreak()
+        indented {
+            expression?.code?.visit this
+        }
+        print '}'
+    }
+
+    @Override
+    void visitTupleExpression(TupleExpression expression) {
+        print '('
+        visitExpressionsAndCommaSeparate(expression?.expressions)
+        print ')'
+    }
+
+    @Override
+    void visitRangeExpression(RangeExpression expression) {
+        print '('
+        expression?.from?.visit this
+        print '..'
+        expression?.to?.visit this
+        print ')'
+    }
+
+    @Override
+    void visitPropertyExpression(PropertyExpression expression) {
+        expression?.objectExpression?.visit this
+        if (expression?.spreadSafe) {
+            print '*'
+        } else if (expression?.isSafe()) {
+            print '?'
+        }
+        print '.'
+        if (expression?.property instanceof ConstantExpression) {
+            visitConstantExpression(expression?.property, true)
+        } else {
+            expression?.property?.visit this
+        }
+    }
+
+    @Override
+    void visitAttributeExpression(AttributeExpression attributeExpression) {
+        visitPropertyExpression attributeExpression
+    }
+
+    @Override
+    void visitFieldExpression(FieldExpression expression) {
+        print expression?.field?.name
+    }
+
+    void visitConstantExpression(ConstantExpression expression, boolean unwrapQuotes = false) {
+        if (expression.value instanceof String && !unwrapQuotes) {
+            // string reverse escaping is very naive
+            def escaped = ((String) expression.value).replaceAll('\n', '\\\\n').replaceAll("'", "\\\\'")
+            print "'$escaped'"
+        } else {
+            print expression.value
+        }
+    }
+
+    @Override
+    void visitClassExpression(ClassExpression expression) {
+        print expression.text
+    }
+
+    void visitVariableExpression(VariableExpression expression, boolean spacePad = true) {
+
+        if (spacePad) {
+            print ' ' + expression.name + ' '
+        } else {
+            print expression.name
+        }
+    }
+
+    @Override
+    void visitDeclarationExpression(DeclarationExpression expression) {
+        // handle multiple assignment expressions
+        if (expression?.leftExpression instanceof ArgumentListExpression) {
+            print 'def '
+            visitArgumentlistExpression expression?.leftExpression, true
+            print " $expression.operation.text "
+            expression.rightExpression.visit this
+
+            if (expression?.operation?.text == '[') {
+                print ']'
+            }
+        } else {
+            visitType expression?.leftExpression?.type
+            visitBinaryExpression expression // is a BinaryExpression
+        }
+    }
+
+    @Override
+    void visitGStringExpression(GStringExpression expression) {
+        print '"' + expression.text + '"'
+    }
+
+    @Override
+    void visitSpreadExpression(SpreadExpression expression) {
+        print '*'
+        expression?.expression?.visit this
+    }
+
+    @Override
+    void visitNotExpression(NotExpression expression) {
+        print '!('
+        expression?.expression?.visit this
+        print ')'
+    }
+
+    @Override
+    void visitUnaryMinusExpression(UnaryMinusExpression expression) {
+        print '-('
+        expression?.expression?.visit this
+        print ')'
+    }
+
+    @Override
+    void visitUnaryPlusExpression(UnaryPlusExpression expression) {
+        print '+('
+        expression?.expression?.visit this
+        print ')'
+    }
+
+    @Override
+    void visitCastExpression(CastExpression expression) {
+        print '(('
+        expression?.expression?.visit this
+        print ') as '
+        visitType(expression?.type)
+        print ')'
+
+    }
+
+    /**
+     * Prints out the type, safely handling arrays.
+     * @param classNode
+     *      classnode
+     */
+    void visitType(ClassNode classNode) {
+        def name = classNode.name
+        if (name =~ /^\[+L/ && name.endsWith(';')) {
+            int numDimensions = name.indexOf('L')
+            print "${classNode.name[(numDimensions + 1)..-2]}" + ('[]' * numDimensions)
+        } else {
+            print name
+        }
+        visitGenerics classNode?.genericsTypes
+    }
+
+    void visitArgumentlistExpression(ArgumentListExpression expression, boolean showTypes = false) {
+        print '('
+        int count = expression?.expressions?.size()
+        expression.expressions.each {
+            if (showTypes) {
+                visitType it.type
+                print ' '
+            }
+            if (it instanceof VariableExpression) {
+                visitVariableExpression it, false
+            } else if (it instanceof ConstantExpression) {
+                visitConstantExpression it, false
+            } else {
+                it.visit this
+            }
+            count--
+            if (count) print ', '
+        }
+        print ')'
+    }
+
+    @Override
+    void visitBytecodeExpression(BytecodeExpression expression) {
+        print '/*BytecodeExpression*/'
+        printLineBreak()
+    }
+
+
+
+    @Override
+    void visitMapExpression(MapExpression expression) {
+        print '['
+        if (expression?.mapEntryExpressions?.size() == 0) {
+            print ':'
+        } else {
+            visitExpressionsAndCommaSeparate(expression?.mapEntryExpressions)
+        }
+        print ']'
+    }
+
+    @Override
+    void visitMapEntryExpression(MapEntryExpression expression) {
+        if (expression?.keyExpression instanceof SpreadMapExpression) {
+            print '*'            // is this correct?
+        } else {
+            expression?.keyExpression?.visit this
+        }
+        print ': '
+        expression?.valueExpression?.visit this
+    }
+
+    @Override
+    void visitListExpression(ListExpression expression) {
+        print '['
+        visitExpressionsAndCommaSeparate(expression?.expressions)
+        print ']'
+    }
+
+    @Override
+    void visitTryCatchFinally(TryCatchStatement statement) {
+        print 'try {'
+        printLineBreak()
+        indented {
+            statement?.tryStatement?.visit this
+        }
+        printLineBreak()
+        print '} '
+        printLineBreak()
+        statement?.catchStatements?.each { CatchStatement catchStatement ->
+            visitCatchStatement(catchStatement)
+        }
+        print 'finally { '
+        printLineBreak()
+        indented {
+            statement?.finallyStatement?.visit this
+        }
+        print '} '
+        printLineBreak()
+    }
+
+    @Override
+    void visitThrowStatement(ThrowStatement statement) {
+        print 'throw '
+        statement?.expression?.visit this
+        printLineBreak()
+    }
+
+    @Override
+    void visitSynchronizedStatement(SynchronizedStatement statement) {
+        print 'synchronized ('
+        statement?.expression?.visit this
+        print ') {'
+        printLineBreak()
+        indented {
+            statement?.code?.visit this
+        }
+        print '}'
+    }
+
+    @Override
+    void visitTernaryExpression(TernaryExpression expression) {
+        expression?.booleanExpression?.visit this
+        print ' ? '
+        expression?.trueExpression?.visit this
+        print ' : '
+        expression?.falseExpression?.visit this
+    }
+
+    @Override
+    void visitShortTernaryExpression(ElvisOperatorExpression expression) {
+        visitTernaryExpression(expression)
+    }
+
+    @Override
+    void visitBooleanExpression(BooleanExpression expression) {
+        expression?.expression?.visit this
+    }
+
+    @Override
+    void visitWhileLoop(WhileStatement statement) {
+        print 'while ('
+        statement?.booleanExpression?.visit this
+        print ') {'
+        printLineBreak()
+        indented {
+            statement?.loopBlock?.visit this
+        }
+        printLineBreak()
+        print '}'
+        printLineBreak()
+    }
+
+    @Override
+    void visitDoWhileLoop(DoWhileStatement statement) {
+        print 'do {'
+        printLineBreak()
+        indented {
+            statement?.loopBlock?.visit this
+        }
+        print '} while ('
+        statement?.booleanExpression?.visit this
+        print ')'
+        printLineBreak()
+    }
+
+    @Override
+    void visitCatchStatement(CatchStatement statement) {
+        print 'catch ('
+        visitParameters([statement.variable])
+        print ') {'
+        printLineBreak()
+        indented {
+            statement.code?.visit this
+        }
+        print '} '
+        printLineBreak()
+    }
+
+    @Override
+    void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
+        print '~('
+        expression?.expression?.visit this
+        print ') '
+    }
+
+    @Override
+    void visitAssertStatement(AssertStatement statement) {
+        print 'assert '
+        statement?.booleanExpression?.visit this
+        print ' : '
+        statement?.messageExpression?.visit this
+    }
+
+    @Override
+    void visitClosureListExpression(ClosureListExpression expression) {
+        boolean first = true
+        expression?.expressions?.each {
+            if (!first) {
+                print ';'
+            }
+            first = false
+            it.visit this
+        }
+    }
+
+    @Override
+    void visitMethodPointerExpression(MethodPointerExpression expression) {
+        expression?.expression?.visit this
+        print '.&'
+        expression?.methodName?.visit this
+    }
+
+    @Override
+    void visitArrayExpression(ArrayExpression expression) {
+        print 'new '
+        visitType expression?.elementType
+        print '['
+        visitExpressionsAndCommaSeparate(expression?.sizeExpression)
+        print ']'
+    }
+
+    private void visitExpressionsAndCommaSeparate(List<? super Expression> expressions) {
+        boolean first = true
+        expressions?.each {
+            if (!first) {
+                print ', '
+            }
+            first = false
+            it.visit this
+        }
+    }
+
+    @Override
+    void visitSpreadMapExpression(SpreadMapExpression expression) {
+        print '*:'
+        expression?.expression?.visit this
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/AnnotationDeclaration_01.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/AnnotationDeclaration_01.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/AnnotationDeclaration_01.groovy
new file mode 100644
index 0000000..3c5662b
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/AnnotationDeclaration_01.groovy
@@ -0,0 +1,39 @@
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+
+import java.lang.annotation.Documented
+import java.lang.annotation.ElementType
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Target
+
+@Canonical(
+        includes = ['a', 'b'], excludes = ['c']
+)
+@Documented
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.FIELD)
+@GroovyASTTransformationClass('Lulz')
+@interface FunnyAnnotation {
+    public static final String SOME_CONSTANT2 = 'SOME_CONSTANT2';
+    String SOME_CONSTANT = 'SOME_CONSTANT';
+
+    /* This is a comment
+    */
+    String name() default ""
+
+    /**
+     * This has a default, too
+     */
+    boolean synchronize() default false
+
+    boolean synchronize2() default
+            false
+}
+
+@interface a {
+
+}
+
+@interface b {
+    String name()
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_01.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_01.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_01.groovy
new file mode 100644
index 0000000..6dbf1f7
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_01.groovy
@@ -0,0 +1 @@
+@Export package core

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_02.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_02.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_02.groovy
new file mode 100644
index 0000000..d673313
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_02.groovy
@@ -0,0 +1,2 @@
+@Export
+package core

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_03.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_03.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_03.groovy
new file mode 100644
index 0000000..644de35
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_03.groovy
@@ -0,0 +1,3 @@
+@Export
+@Version
+package core

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_04.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_04.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_04.groovy
new file mode 100644
index 0000000..5edeef2
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_04.groovy
@@ -0,0 +1,2 @@
+@Export @Version
+package core

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_05.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_05.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_05.groovy
new file mode 100644
index 0000000..545d6d5
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_05.groovy
@@ -0,0 +1 @@
+@Test1 import java.util.Map

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_06.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_06.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_06.groovy
new file mode 100644
index 0000000..068d6c3
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_06.groovy
@@ -0,0 +1,2 @@
+@Test1
+import java.util.Map

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_07.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_07.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_07.groovy
new file mode 100644
index 0000000..bb777ca
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_07.groovy
@@ -0,0 +1,2 @@
+@Test1 @Test2
+import java.util.Map

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_08.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_08.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_08.groovy
new file mode 100644
index 0000000..60d51eb
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_08.groovy
@@ -0,0 +1,10 @@
+@Test1 @Test2 @Test3
+import java.util.Map
+@Test1 @Test2 @Test3
+import java.util.*
+@Test1 @Test2 @Test3
+import static java.lang.Math
+@Test1 @Test2 @Test3
+import static java.lang.Math.*
+@Test1 @Test2 @Test3
+import static java.lang.Math.pow

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_09.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_09.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_09.groovy
new file mode 100644
index 0000000..98f565a
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_09.groovy
@@ -0,0 +1,14 @@
+@Test1(12)
+@Test1(@Test1)
+@Test2(v=6)
+@Test3(v1=6, v2=8, v3=10)
+@Test4([1, 2, 3])
+@Test5(v=[1, 2, 3])
+@Test6(v1=[1, 2, 3], v2=[1], v3=6)
+package core
+
+@Grapes([
+        @Grab('xx:yy:1.0'), // xx
+        @Grab('zz:yy:1.0') /* zz */
+])
+import xx.yy.ZZ;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_10x.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_10x.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_10x.groovy
new file mode 100644
index 0000000..22cd289
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Annotation_10x.groovy
@@ -0,0 +1,20 @@
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Retention
+
+def closureClass = NestedAnnotationWithDefault.getAnnotation(AnnWithNestedAnnWithDefault).elem().elem()
+def closure = closureClass.newInstance(null, null)
+assert closure.call() == 3
+
+
+@AnnWithNestedAnnWithDefault(elem = @AnnWithDefaultValue())
+class NestedAnnotationWithDefault {}
+
+@Retention(RetentionPolicy.RUNTIME)
+@interface AnnWithDefaultValue {
+    Class elem() default { 1 + 2 }
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@interface AnnWithNestedAnnWithDefault {
+    AnnWithDefaultValue elem()
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_01.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_01.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_01.groovy
new file mode 100644
index 0000000..f9a50e0
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_01.groovy
@@ -0,0 +1,28 @@
+assert true
+assert true;
+assert true : ':(';
+assert true , ':(';
+assert true :
+        ':('
+assert true ,
+        ':(';
+
+assert true:    \
+    'hello, world'
+
+assert true,    \
+    'hello, world'
+
+assert 1 \
+        + 2 * \
+        3 :    \
+        'hello, world'
+
+assert 1 \
+        + 2 * \
+\
+        3 :    \
+\
+\
+'hello, world'
+

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_02x.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_02x.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_02x.groovy
new file mode 100644
index 0000000..887f0b9
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_02x.groovy
@@ -0,0 +1,37 @@
+import org.codehaus.groovy.runtime.powerassert.PowerAssertionError
+
+testBinaryExpression()
+
+/***********************************/
+void testBinaryExpression() {
+    isRendered """
+assert a * b
+       | | |
+       0 0 1
+        """, {
+        def a = 0
+        def b = 1
+        assert a * b
+    }
+
+    isRendered """
+assert a[b]
+       |||
+       ||0
+       |false
+       [false]
+        """, {
+        def a = [false]
+        def b = 0
+        assert a[b]
+    }
+}
+
+static isRendered(String expectedRendering, Closure failingAssertion) {
+    try {
+        failingAssertion.call();
+        assert false, "assertion should have failed but didn't"
+    } catch (PowerAssertionError e) {
+        assert expectedRendering.trim() == e.message.trim()
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_03x.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_03x.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_03x.groovy
new file mode 100644
index 0000000..75d305a
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Assert_03x.groovy
@@ -0,0 +1,26 @@
+import org.codehaus.groovy.runtime.powerassert.PowerAssertionError
+
+testPostfixExpression()
+
+/***********************************/
+
+void testPostfixExpression() {
+    isRendered """
+assert x++ == null
+       ||  |
+       |0  false
+       0
+        """, {
+        def x = 0
+        assert x++ == null
+    }
+}
+
+static isRendered(String expectedRendering, Closure failingAssertion) {
+    try {
+        failingAssertion.call();
+        assert false, "assertion should have failed but didn't"
+    } catch (PowerAssertionError e) {
+        assert expectedRendering.trim() == e.message.trim()
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_01.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_01.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_01.groovy
new file mode 100644
index 0000000..13d0b8e
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_01.groovy
@@ -0,0 +1,47 @@
+package core
+
+class A {}
+class B<T> {}
+class C<T extends A> {}
+class D<T extends A & B> {}
+class E<T extends A & B & C> {}
+class F<T extends A & B & C> extends A {}
+class F2 extends A<T> {}
+class G1<T extends A & B & C> extends A implements X {}
+class G2<T extends A & B & C> extends A<T> implements X<T> {}
+class G3                      extends A<T> implements X<T> {}
+class G4                      extends A    implements X<T> {}
+class G5                      extends A    implements X    {}
+class H<T extends A & B & C> extends A implements X, Y {}
+class I<T extends A & B & C> extends A implements X, Y, Z {}
+public class J<T extends A & B & C> extends A implements X, Y, Z {}
+@Test2 public class K<T extends A & B & C> extends A implements X, Y, Z {}
+@Test2 @Test3 public class L<T extends A & B & C> extends A implements X, Y, Z {}
+
+@Test2
+@Test3
+@Test4(value={
+        def a = someMethod()
+        assert a.result() == 'abc'
+})
+@Test5(b=2, a=1)
+@Test6(a=2, b=1)
+public
+class M
+<
+        T extends
+A &
+B &
+C
+>
+        extends
+                A
+        implements
+                X,
+                Y,
+                Z
+{
+
+}
+
+class a {}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_02.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_02.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_02.groovy
new file mode 100644
index 0000000..b8a1b40
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_02.groovy
@@ -0,0 +1,42 @@
+class AA {
+    {
+        println 123
+    }
+}
+
+class BB {
+    static {
+        println '123'
+    }
+}
+
+class CC {
+    static
+    {
+        println '123'
+    }
+}
+
+class DD {
+    static {
+        println '123'
+    }
+
+    {
+        println 'abc'
+    }
+
+    static {
+        println '234'
+    }
+
+    {
+        println 'bcd'
+    }
+}
+
+class EE {{}}
+class FF {static {}}
+class GG {static {};{}}
+
+class Iterator implements java.util.Iterator {}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_03.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_03.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_03.groovy
new file mode 100644
index 0000000..32c5c41
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_03.groovy
@@ -0,0 +1,51 @@
+import java.sql.SQLException
+
+class AAA {
+    private volatile XX xx;
+    private transient YY yy;
+
+    public AAA() {
+
+    }
+
+    public AAA(String name) {
+
+    }
+
+    @Test2
+    public AAA(String name, int age) throws Exception {
+
+    }
+
+    AAA(String name, int age, String title) throws Exception {
+
+    }
+
+    private AAA(String name, int age, String title, double income) throws Exception {
+
+    }
+
+    @Test2
+    public synchronized String sayHello(String name) {
+        return "Hello, $name";
+    }
+
+    @Test2
+    public <T> T sayHello2(T name) throws IOException, SQLException {
+        return "Hello, $name";
+    }
+
+    public static privateStaticMethod(){}
+
+    public void m(final int param) {}
+    public void m2(def param) {}
+    public void m3(final int param1, long param2, final String param3) {}
+
+    def "hello world"(p1, p2) {
+        println "$p1, $p2"
+    }
+
+    def run() {
+        this."hello world"('ab', 'bc')
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_04.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_04.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_04.groovy
new file mode 100644
index 0000000..7750cb4
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_04.groovy
@@ -0,0 +1,22 @@
+public class Person {
+    public static final SOME_CONSTANT = 'SOME_CONSTANT';
+    private String name = 'Daniel';
+    private int age;
+    @Test2
+    private String country = 'China',
+            location = 'Shanghai';
+
+    private String field, field2 = 'field2';
+    String someProperty;
+    String someProperty2 = 'someProperty2';
+    String someProperty3 = 'someProperty3',
+            someProperty4 = 'someProperty4';
+    String someProperty5, someProperty6 = 'someProperty6';
+    final String someProperty7 = 'someProperty7';
+    static final String someProperty8 = 'someProperty8';
+
+    @Test3
+    static final String someProperty9 = 'someProperty9';
+
+    protected static def protectedStaticDefField;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_05.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_05.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_05.groovy
new file mode 100644
index 0000000..7f66f72
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_05.groovy
@@ -0,0 +1,20 @@
+public class A extends B {
+    private int age;
+
+    public A() {
+        super()
+    }
+
+    public A(int age) {
+        this()
+    }
+
+    public A(String name) {
+        super(name);
+    }
+
+    public A(String name, int age) {
+        this(name);
+        this.age = age;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_06.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_06.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_06.groovy
new file mode 100644
index 0000000..95e013c
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_06.groovy
@@ -0,0 +1,83 @@
+public class OuterA {
+    class InnerB {}
+}
+
+class OuterClazz {
+    enum InnerEnum implements SomeInterface {
+        A, B
+    }
+}
+
+
+class AA {
+    class Inner {
+        public def innerMethod() {}
+    }
+    private class PrivateInner {}
+    protected final class ProtectedFinalInner {
+        ProtectedFinalInner() {
+
+        }
+
+        ProtectedFinalInner(Integer a) {
+            new A() {
+                public int method() {
+                    1
+                }
+            }
+        }
+    }
+
+    public int method() {
+        0
+    }
+}
+
+
+
+interface A {
+    static enum B {
+        static interface C {
+        }
+    }
+}
+
+interface A2 {
+    static class B2 {
+        static enum C2 {
+        }
+    }
+}
+
+enum A4 {
+    static interface B4 {
+        static class C4 {
+        }
+    }
+}
+
+
+class A3 {
+    static class B3 {
+        static enum C3 {
+        }
+    }
+}
+
+class A5 {
+    static class B5 {
+        static class C5 {
+        }
+    }
+}
+
+interface A1 {
+    static interface B1 {
+        static enum C1 {
+        }
+    }
+}
+
+
+
+

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_07.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_07.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_07.groovy
new file mode 100644
index 0000000..37cd5ee
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/ClassDeclaration_07.groovy
@@ -0,0 +1,27 @@
+new A() {}
+new A(1, '2') {
+    public void prt() {
+        new B() {}
+        new C() {
+            {
+                new D() {
+                    {
+                        new E() {}
+                        new F() {}
+                    }
+                }
+            }
+        }
+    }
+}
+
+class OuterAA {
+    public void method() {
+        new InnerBB() {}
+        new InnerCC() {{
+            new InnerDD() {}
+            new InnerEE() {}
+        }}
+        new InnerFF() {}
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_01.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_01.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_01.groovy
new file mode 100644
index 0000000..bb83ebe
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_01.groovy
@@ -0,0 +1 @@
+{->}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_02.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_02.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_02.groovy
new file mode 100644
index 0000000..3e7bc88
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_02.groovy
@@ -0,0 +1 @@
+{->12}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_03.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_03.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_03.groovy
new file mode 100644
index 0000000..61d7b27
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_03.groovy
@@ -0,0 +1 @@
+{-> {->12}}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_04.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_04.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_04.groovy
new file mode 100644
index 0000000..9c3a357
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_04.groovy
@@ -0,0 +1,3 @@
+{->
+    12
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_05.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_05.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_05.groovy
new file mode 100644
index 0000000..47686c3
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_05.groovy
@@ -0,0 +1,3 @@
+{int a ->
+    a
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_06.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_06.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_06.groovy
new file mode 100644
index 0000000..dde2725
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_06.groovy
@@ -0,0 +1,9 @@
+{int a,
+ long b, float[] c,
+ double[][] d, String e, Object[] f,
+ Object[][] g, Object[][][] h,
+ Object[]... i
+    ->
+
+    a
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_07.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_07.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_07.groovy
new file mode 100644
index 0000000..9567574
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_07.groovy
@@ -0,0 +1,3 @@
+{Object... i ->
+    i
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_08.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_08.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_08.groovy
new file mode 100644
index 0000000..3e1811d
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_08.groovy
@@ -0,0 +1,16 @@
+{ List<String> i, List<List<String>> j, List<List<List<String>>> k,
+  List<?> l, List<? extends Map> m, List<? super Map> n,
+  List<? extends Map<String, Object>> o, List<? super Map<String, Object>> p,
+  List<String>[] q,  List<? extends Map<String, Object>>[][] r,
+
+  List<
+        ? super
+                Map,
+        ? extends
+        Set,
+        List
+        > s,
+
+  List<? extends Map<String, Object>>[][]... t ->
+    i
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_09.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_09.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_09.groovy
new file mode 100644
index 0000000..8bbcee8
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_09.groovy
@@ -0,0 +1,16 @@
+{ String i,
+  final String j, final def String k,
+  @Test2 final def String l,
+  @Test2
+  final
+
+  def
+          String m,
+        final n,
+        def
+          o,
+        p, q,
+        r, s,
+  @Test2 final def String... z ->
+    i
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_10.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_10.groovy b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_10.groovy
new file mode 100644
index 0000000..010952c
--- /dev/null
+++ b/subprojects/groovy-antlr4-grammar/src/test/resources/core/Closure_10.groovy
@@ -0,0 +1,4 @@
+{ java.lang.Integer t, Integer u, int v, int[] w, int[][] x,
+    String y = 'y', long... z = 123 ->
+    i
+}
\ No newline at end of file


Mime
View raw message