groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pa...@apache.org
Subject [36/50] [abbrv] groovy git commit: rename antlr4 parser to remove groovy- prefix since that is by convention for modules
Date Wed, 10 May 2017 22:27:04 GMT
http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
new file mode 100644
index 0000000..d3b7538
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
@@ -0,0 +1,63 @@
+/*
+ *  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 org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.Lexer;
+import org.antlr.v4.runtime.LexerNoViableAltException;
+import org.antlr.v4.runtime.atn.ATN;
+import org.antlr.v4.runtime.atn.LexerATNSimulator;
+import org.apache.groovy.parser.antlr4.internal.AtnManager;
+
+/**
+ * The lexer for Groovy programming language, which is based on the lexer generated by Antlr4
+ *
+ * @author <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on 2016/08/14
+ */
+public class GroovyLangLexer extends GroovyLexer {
+    public GroovyLangLexer(CharStream input) {
+        super(input);
+
+        this.setInterpreter(new PositionAdjustingLexerATNSimulator(this, new AtnManager(this).getATN()));
+    }
+
+    @Override
+    public void recover(LexerNoViableAltException e) {
+        throw e; // if some lexical error occurred, stop parsing!
+    }
+
+    @Override
+    protected void rollbackOneChar() {
+        ((PositionAdjustingLexerATNSimulator) getInterpreter()).resetAcceptPosition(getInputStream(), _tokenStartCharIndex - 1, _tokenStartLine, _tokenStartCharPositionInLine - 1);
+    }
+
+    private static class PositionAdjustingLexerATNSimulator extends LexerATNSimulator {
+        public PositionAdjustingLexerATNSimulator(Lexer recog, ATN atn) {
+            super(recog, atn);
+        }
+
+        protected void resetAcceptPosition(CharStream input, int index, int line, int charPositionInLine) {
+            input.seek(index);
+            this.line = line;
+            this.charPositionInLine = charPositionInLine;
+            this.consume(input);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java
new file mode 100644
index 0000000..fa33c6e
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java
@@ -0,0 +1,38 @@
+/*
+ *  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 org.antlr.v4.runtime.TokenStream;
+import org.antlr.v4.runtime.atn.ParserATNSimulator;
+import org.apache.groovy.parser.antlr4.internal.AtnManager;
+
+/**
+ * The parser for Groovy programming language, which is based on the parser generated by Antlr4
+ *
+ * @author  <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on    2016/08/14
+ */
+public class GroovyLangParser extends GroovyParser {
+    public GroovyLangParser(TokenStream input) {
+        super(input);
+
+        this.setInterpreter(new ParserATNSimulator(this, new AtnManager(this).getATN()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovySyntaxError.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovySyntaxError.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovySyntaxError.java
new file mode 100644
index 0000000..7ca8bba
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovySyntaxError.java
@@ -0,0 +1,53 @@
+/*
+ *  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;
+
+/**
+ * Represents a syntax error of groovy program
+ */
+public class GroovySyntaxError extends AssertionError {
+    public static final int LEXER = 0;
+    public static final int PARSER = 1;
+    private int source;
+    private int line;
+    private int column;
+
+    public GroovySyntaxError(String message, int source, int line, int column) {
+        super(message, null);
+
+        if (source != LEXER && source != PARSER) {
+            throw new IllegalArgumentException("Invalid syntax error source: " + source);
+        }
+
+        this.source = source;
+        this.line = line;
+        this.column = column;
+    }
+
+    public int getSource() {
+        return source;
+    }
+    public int getLine() {
+        return line;
+    }
+
+    public int getColumn() {
+        return column;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovydocManager.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovydocManager.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovydocManager.java
new file mode 100644
index 0000000..449b2e7
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/GroovydocManager.java
@@ -0,0 +1,183 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.parser.antlr4;
+
+import groovy.lang.Groovydoc;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+
+import java.util.List;
+
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
+
+/**
+ * A utilities for managing groovydoc, e.g.
+ * 1) extracting groovydoc from groovy AST;
+ * 2) TODO extracting tags from groovydoc;
+ * 3) attach groovydoc to AST node as metadata
+ */
+public class GroovydocManager {
+    public static final String DOC_COMMENT = "_DOC_COMMENT"; // keys for meta data
+    private static final String DOC_COMMENT_PREFIX = "/**";
+    private static final String EXTRACT_DOC_COMMENT = "groovy.extract.doc.comment";
+    private static final String TRUE_STR = "true";
+    private static final boolean EXTRACTING_DOC_COMMENT_ENABLED;
+    private static final String VALUE = "value";
+    private static final String RUNTIME_GROOVYDOC_PATTERN = "(?s)/[*][*]\\s+(\\s+[*]\\s*)*@Groovydoc\\b.+?[*]/";
+    private AstBuilder astBuilder;
+
+    static {
+        boolean edce;
+        try {
+            edce = TRUE_STR.equals(System.getProperty(EXTRACT_DOC_COMMENT));
+        } catch (Exception e) {
+            edce = false;
+        }
+
+        EXTRACTING_DOC_COMMENT_ENABLED = edce;
+    }
+
+    public GroovydocManager(AstBuilder astBuilder) {
+        this.astBuilder = astBuilder;
+    }
+
+    /**
+     * Attach doc comment to member node as meta data
+     *
+     */
+    public void handle(ASTNode node, GroovyParser.GroovyParserRuleContext ctx) {
+        if (!asBoolean(node) || !asBoolean(ctx)) {
+            return;
+        }
+
+        String docCommentNodeText = this.findDocCommentByNode(ctx);
+        if (null == docCommentNodeText) {
+            return;
+        }
+
+        attachDocCommentAsMetaData(node, docCommentNodeText);
+        attachGroovydocAnnotation(node, docCommentNodeText);
+    }
+
+    /*
+     * Attach doc comment to member node as meta data
+     */
+    private void attachDocCommentAsMetaData(ASTNode node, String docCommentNodeText) {
+        if (!EXTRACTING_DOC_COMMENT_ENABLED) {
+            return;
+        }
+
+        node.putNodeMetaData(DOC_COMMENT, docCommentNodeText);
+    }
+
+    /*
+     * Attach Groovydoc annotation to the target element
+     */
+    private void attachGroovydocAnnotation(ASTNode node, String docCommentNodeText) {
+        if (!(node instanceof AnnotatedNode)) {
+            return;
+        }
+
+        if (!docCommentNodeText.matches(RUNTIME_GROOVYDOC_PATTERN)) {
+            return;
+        }
+
+        AnnotatedNode annotatedNode = (AnnotatedNode) node;
+        AnnotationNode annotationNode = new AnnotationNode(ClassHelper.make(Groovydoc.class));
+        annotationNode.addMember(VALUE, new ConstantExpression(docCommentNodeText));
+        annotatedNode.addAnnotation(annotationNode);
+    }
+
+    private String findDocCommentByNode(ParserRuleContext node) {
+        if (!asBoolean(node)) {
+            return null;
+        }
+
+        if (node instanceof GroovyParser.ClassBodyContext) {
+            return null;
+        }
+
+        ParserRuleContext parentContext = node.getParent();
+
+        if (!asBoolean(parentContext)) {
+            return null;
+        }
+
+        String docCommentNodeText = null;
+        boolean sameTypeNodeBefore = false;
+        for (ParseTree child : parentContext.children) {
+
+            if (node == child) {
+                // if no doc comment node found and no siblings of same type before the node,
+                // try to find doc comment node of its parent
+                if (!asBoolean((Object) docCommentNodeText) && !sameTypeNodeBefore) {
+                    return findDocCommentByNode(parentContext);
+                }
+
+                return docCommentNodeText;
+            }
+
+            if (node.getClass() == child.getClass()) { // e.g. ClassBodyDeclarationContext == ClassBodyDeclarationContext
+                docCommentNodeText = null;
+                sameTypeNodeBefore = true;
+                continue;
+            }
+
+            if (!(child instanceof GroovyParser.NlsContext || child instanceof GroovyParser.SepContext)) {
+                continue;
+            }
+
+            // doc comments are treated as NL
+            List<? extends TerminalNode> nlList =
+                    child instanceof GroovyParser.NlsContext
+                            ? ((GroovyParser.NlsContext) child).NL()
+                            : ((GroovyParser.SepContext) child).NL();
+
+            int nlListSize = nlList.size();
+            if (0 == nlListSize) {
+                continue;
+            }
+
+            for (int i = nlListSize - 1; i >= 0; i--) {
+                String text = nlList.get(i).getText();
+
+                if (text.matches("\\s+")) {
+                    continue;
+                }
+
+                if (text.startsWith(DOC_COMMENT_PREFIX)) {
+                    docCommentNodeText = text;
+                } else {
+                    docCommentNodeText = null;
+                }
+
+                break;
+            }
+        }
+
+        throw new GroovyBugError("node can not be found: " + node.getText()); // The exception should never be thrown!
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierManager.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierManager.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierManager.java
new file mode 100644
index 0000000..a45e36c
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierManager.java
@@ -0,0 +1,198 @@
+/*
+ *  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 org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.apache.groovy.parser.antlr4.GroovyParser.*;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
+
+/**
+ * Process modifiers for AST nodes
+ * <p>
+ * Created by Daniel.Sun on 2016/08/27.
+ */
+class ModifierManager {
+    private static final Map<Class, List<Integer>> INVALID_MODIFIERS_MAP = Collections.unmodifiableMap(new HashMap<Class, List<Integer>>() {
+        {
+            put(ConstructorNode.class, Arrays.asList(STATIC, FINAL, ABSTRACT, NATIVE));
+            put(MethodNode.class, Arrays.asList(VOLATILE/*, TRANSIENT*/));
+        }
+    });
+    private AstBuilder astBuilder;
+    private List<ModifierNode> modifierNodeList;
+
+    public ModifierManager(AstBuilder astBuilder, List<ModifierNode> modifierNodeList) {
+        this.astBuilder = astBuilder;
+        this.validate(modifierNodeList);
+        this.modifierNodeList = Collections.unmodifiableList(asBoolean((Object) modifierNodeList) ? modifierNodeList : Collections.emptyList());
+    }
+
+    private void validate(List<ModifierNode> modifierNodeList) {
+        Map<ModifierNode, Integer> modifierNodeCounter = new LinkedHashMap<>(modifierNodeList.size());
+        int visibilityModifierCnt = 0;
+
+        for (ModifierNode modifierNode : modifierNodeList) {
+            Integer cnt = modifierNodeCounter.get(modifierNode);
+
+            if (null == cnt) {
+                modifierNodeCounter.put(modifierNode, 1);
+            } else if (1 == cnt && !modifierNode.isRepeatable()) {
+                throw astBuilder.createParsingFailedException("Cannot repeat modifier[" + modifierNode.getText() + "]", modifierNode);
+            }
+
+            if (modifierNode.isVisibilityModifier()) {
+                visibilityModifierCnt++;
+
+                if (visibilityModifierCnt > 1) {
+                    throw astBuilder.createParsingFailedException("Cannot specify modifier[" + modifierNode.getText() + "] when access scope has already been defined", modifierNode);
+                }
+            }
+        }
+    }
+
+    public void validate(MethodNode methodNode) {
+        validate(INVALID_MODIFIERS_MAP.get(MethodNode.class), methodNode);
+    }
+
+    public void validate(ConstructorNode constructorNode) {
+        validate(INVALID_MODIFIERS_MAP.get(ConstructorNode.class), constructorNode);
+    }
+
+    private void validate(List<Integer> invalidModifierList, MethodNode methodNode) {
+        modifierNodeList.forEach(e -> {
+            if (invalidModifierList.contains(e.getType())) {
+                throw astBuilder.createParsingFailedException(methodNode.getClass().getSimpleName().replace("Node", "") + " has an incorrect modifier '" + e + "'.", methodNode);
+            }
+        });
+    }
+
+    // t    1: class modifiers value; 2: class member modifiers value
+    private int calcModifiersOpValue(int t) {
+        int result = 0;
+
+        for (ModifierNode modifierNode : modifierNodeList) {
+            result |= modifierNode.getOpcode();
+        }
+
+        if (!this.containsVisibilityModifier()) {
+            if (1 == t) {
+                result |= Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC;
+            } else if (2 == t) {
+                result |= Opcodes.ACC_PUBLIC;
+            }
+        }
+
+        return result;
+    }
+
+    public int getClassModifiersOpValue() {
+        return this.calcModifiersOpValue(1);
+    }
+
+    public int getClassMemberModifiersOpValue() {
+        return this.calcModifiersOpValue(2);
+    }
+
+    public List<AnnotationNode> getAnnotations() {
+        return modifierNodeList.stream()
+                .filter(ModifierNode::isAnnotation)
+                .map(ModifierNode::getAnnotationNode)
+                .collect(Collectors.toList());
+    }
+
+    public boolean contains(int modifierType) {
+        return modifierNodeList.stream().anyMatch(e -> modifierType == e.getType());
+    }
+
+    public Optional<ModifierNode> get(int modifierType) {
+        return modifierNodeList.stream().filter(e -> modifierType == e.getType()).findFirst();
+    }
+
+    public boolean containsAnnotations() {
+        return modifierNodeList.stream().anyMatch(ModifierNode::isAnnotation);
+    }
+
+    public boolean containsVisibilityModifier() {
+        return modifierNodeList.stream().anyMatch(ModifierNode::isVisibilityModifier);
+    }
+
+    public boolean containsNonVisibilityModifier() {
+        return modifierNodeList.stream().anyMatch(ModifierNode::isNonVisibilityModifier);
+    }
+
+    public Parameter processParameter(Parameter parameter) {
+        modifierNodeList.forEach(e -> {
+            parameter.setModifiers(parameter.getModifiers() | e.getOpcode());
+
+            if (e.isAnnotation()) {
+                parameter.addAnnotation(e.getAnnotationNode());
+            }
+        });
+
+        return parameter;
+    }
+
+    public int clearVisibilityModifiers(int modifiers) {
+        return modifiers & ~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE;
+    }
+
+    public MethodNode processMethodNode(MethodNode mn) {
+        modifierNodeList.forEach(e -> {
+            mn.setModifiers((e.isVisibilityModifier() ? clearVisibilityModifiers(mn.getModifiers()) : mn.getModifiers()) | e.getOpcode());
+
+            if (e.isAnnotation()) {
+                mn.addAnnotation(e.getAnnotationNode());
+            }
+        });
+
+        return mn;
+    }
+
+    public VariableExpression processVariableExpression(VariableExpression ve) {
+        modifierNodeList.forEach(e -> {
+            ve.setModifiers(ve.getModifiers() | e.getOpcode());
+
+            // local variable does not attach annotations
+        });
+
+        return ve;
+    }
+
+    public <T extends AnnotatedNode> T attachAnnotations(T node) {
+        this.getAnnotations().forEach(node::addAnnotation);
+
+        return node;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierNode.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierNode.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierNode.java
new file mode 100644
index 0000000..5d1007b
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/ModifierNode.java
@@ -0,0 +1,164 @@
+/*
+ *  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 org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.apache.groovy.parser.antlr4.GroovyParser.*;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
+
+/**
+ * Represents a modifier, which is better to place in the package org.codehaus.groovy.ast
+ * <p>
+ * Created by Daniel.Sun on 2016/08/23.
+ */
+public class ModifierNode extends ASTNode {
+    private Integer type;
+    private Integer opcode; // ASM opcode
+    private String text;
+    private AnnotationNode annotationNode;
+    private boolean repeatable;
+
+    public static final int ANNOTATION_TYPE = -999;
+    public static final Map<Integer, Integer> MODIFIER_OPCODE_MAP = Collections.unmodifiableMap(new HashMap<Integer, Integer>() {
+        {
+            put(ANNOTATION_TYPE, 0);
+            put(DEF, 0);
+
+            put(NATIVE, Opcodes.ACC_NATIVE);
+            put(SYNCHRONIZED, Opcodes.ACC_SYNCHRONIZED);
+            put(TRANSIENT, Opcodes.ACC_TRANSIENT);
+            put(VOLATILE, Opcodes.ACC_VOLATILE);
+
+            put(PUBLIC, Opcodes.ACC_PUBLIC);
+            put(PROTECTED, Opcodes.ACC_PROTECTED);
+            put(PRIVATE, Opcodes.ACC_PRIVATE);
+            put(STATIC, Opcodes.ACC_STATIC);
+            put(ABSTRACT, Opcodes.ACC_ABSTRACT);
+            put(FINAL, Opcodes.ACC_FINAL);
+            put(STRICTFP, Opcodes.ACC_STRICT);
+            put(DEFAULT, 0); // no flag for specifying a default method in the JVM spec, hence no ACC_DEFAULT flag in ASM
+        }
+    });
+
+    public ModifierNode(Integer type) {
+        this.type = type;
+        this.opcode = MODIFIER_OPCODE_MAP.get(type);
+        this.repeatable = ANNOTATION_TYPE == type; // Only annotations are repeatable
+
+        if (!asBoolean((Object) this.opcode)) {
+            throw new IllegalArgumentException("Unsupported modifier type: " + type);
+        }
+    }
+
+    /**
+     * @param type the modifier type, which is same as the token type
+     * @param text text of the ast node
+     */
+    public ModifierNode(Integer type, String text) {
+        this(type);
+        this.text = text;
+    }
+
+    /**
+     * @param annotationNode the annotation node
+     * @param text           text of the ast node
+     */
+    public ModifierNode(AnnotationNode annotationNode, String text) {
+        this(ModifierNode.ANNOTATION_TYPE, text);
+        this.annotationNode = annotationNode;
+
+        if (!asBoolean(annotationNode)) {
+            throw new IllegalArgumentException("annotationNode can not be null");
+        }
+    }
+
+    /**
+     * Check whether the modifier is not an imagined modifier(annotation, def)
+     */
+    public boolean isModifier() {
+        return !this.isAnnotation() && !this.isDef();
+    }
+
+    public boolean isVisibilityModifier() {
+        return Objects.equals(PUBLIC, this.type)
+                || Objects.equals(PROTECTED, this.type)
+                || Objects.equals(PRIVATE, this.type);
+    }
+
+    public boolean isNonVisibilityModifier() {
+        return this.isModifier() && !this.isVisibilityModifier();
+    }
+
+    public boolean isAnnotation() {
+        return Objects.equals(ANNOTATION_TYPE, this.type);
+    }
+
+    public boolean isDef() {
+        return Objects.equals(DEF, this.type);
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public Integer getOpcode() {
+        return opcode;
+    }
+
+    public boolean isRepeatable() {
+        return repeatable;
+    }
+
+    @Override
+    public String getText() {
+        return text;
+    }
+
+    public AnnotationNode getAnnotationNode() {
+        return annotationNode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ModifierNode that = (ModifierNode) o;
+        return Objects.equals(type, that.type) &&
+                Objects.equals(text, that.text) &&
+                Objects.equals(annotationNode, that.annotationNode);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, text, annotationNode);
+    }
+
+    @Override
+    public String toString() {
+        return this.text;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/PositionInfo.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/PositionInfo.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/PositionInfo.java
new file mode 100644
index 0000000..b59aabb
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/PositionInfo.java
@@ -0,0 +1,73 @@
+/*
+ *  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 java.util.Objects;
+
+/**
+ * Created by Daniel on 2017/4/22.
+ */
+public class PositionInfo {
+    private int line;
+    private int column;
+
+    public PositionInfo(int line, int column) {
+        this.line = line;
+        this.column = column;
+    }
+
+    public int getLine() {
+        return line;
+    }
+
+    public void setLine(int line) {
+        this.line = line;
+    }
+
+    public int getColumn() {
+        return column;
+    }
+
+    public void setColumn(int column) {
+        this.column = column;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PositionInfo that = (PositionInfo) o;
+        return line == that.line &&
+                column == that.column;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(line, column);
+    }
+
+    @Override
+    public String toString() {
+        if (-1 == line || -1 == column) {
+            return "";
+        }
+
+        return " @ line " + line + ", column " + column;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
new file mode 100644
index 0000000..eb3dbd9
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
@@ -0,0 +1,152 @@
+/*
+ *  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 org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.TokenStream;
+
+import java.util.Collections;
+import java.util.Set;
+
+import static org.apache.groovy.parser.antlr4.GroovyParser.*;
+
+/**
+ * Some semantic predicates for altering the behaviour of the lexer and parser
+ *
+ * @author  <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on    2016/08/20
+ */
+public class SemanticPredicates {
+    public static boolean isFollowedByWhiteSpaces(CharStream cs) {
+        for (int index = 1, c = cs.LA(index); !('\r' == c || '\n' == c || CharStream.EOF == c); index++, c = cs.LA(index)) {
+            if (String.valueOf((char) c).matches("\\S+?")) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static boolean isFollowedBy(CharStream cs, char... chars) {
+        int c1 = cs.LA(1);
+
+        for (char c : chars) {
+            if (c1 == c) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static boolean isFollowedByJavaLetterInGString(CharStream cs) {
+        int c1 = cs.LA(1);
+
+        if ('$' == c1) { // single $ is not a valid identifier
+            return false;
+        }
+
+        String str1 = String.valueOf((char) c1);
+
+        if (str1.matches("[a-zA-Z_{]")) {
+            return true;
+        }
+
+        if (str1.matches("[^\u0000-\u007F\uD800-\uDBFF]")
+                && Character.isJavaIdentifierPart(c1)) {
+            return true;
+        }
+
+        int c2 = cs.LA(2);
+        String str2 = String.valueOf((char) c2);
+
+        if (str1.matches("[\uD800-\uDBFF]")
+                && str2.matches("[\uDC00-\uDFFF]")
+                && Character.isJavaIdentifierPart(Character.toCodePoint((char) c1, (char) c2))) {
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Check whether following a method name of command expression.
+     * Method name should not end with "2: arguments" and "3: closure"
+     *
+     * @param t the type of pathExpression
+     * @return
+     */
+    public static boolean isFollowingMethodName(int t) {
+        return !(2 == t || 3 == t);
+    }
+
+    /**
+     * Distinguish between method declaration and method call/constructor declaration
+     */
+    public static boolean isInvalidMethodDeclaration(TokenStream ts) {
+        int tokenType = ts.LT(1).getType();
+
+        return (Identifier == tokenType || CapitalizedIdentifier == tokenType || StringLiteral == tokenType)
+                && LPAREN == (ts.LT(2).getType());
+    }
+
+    private static final Set<Integer> MODIFIER_SET =
+            Collections.unmodifiableSet(ModifierNode.MODIFIER_OPCODE_MAP.keySet());
+    /**
+     * Distinguish between local variable declaration and method call, e.g. `a b`
+     */
+    public static boolean isInvalidLocalVariableDeclaration(TokenStream ts) {
+        int index = 2;
+        Token token;
+        int tokenType;
+        int tokenType2 = ts.LT(index).getType();
+        int tokenType3;
+
+        if (DOT == tokenType2) {
+            int tokeTypeN = tokenType2;
+
+            do {
+                index = index + 2;
+                tokeTypeN = ts.LT(index).getType();
+            } while (DOT == tokeTypeN);
+
+            if (LT == tokeTypeN || LBRACK == tokeTypeN) {
+                return false;
+            }
+
+            index = index - 1;
+            tokenType2 = ts.LT(index + 1).getType();
+        } else {
+            index = 1;
+        }
+
+        token = ts.LT(index);
+        tokenType = token.getType();
+        tokenType3 = ts.LT(index + 2).getType();
+
+        return //VOID == tokenType ||
+                !(BuiltInPrimitiveType == tokenType || MODIFIER_SET.contains(tokenType))
+                    && Character.isLowerCase(token.getText().codePointAt(0))
+                    && !(ASSIGN == tokenType3 || (LT == tokenType2 || LBRACK == tokenType2));
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SyntaxErrorReportable.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SyntaxErrorReportable.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SyntaxErrorReportable.java
new file mode 100644
index 0000000..021e2f5
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/SyntaxErrorReportable.java
@@ -0,0 +1,58 @@
+/*
+ *  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;
+
+/**
+ * A SyntaxErrorReportable is a recognizer that can report syntax error
+ */
+public interface SyntaxErrorReportable {
+    default void require(boolean condition, String msg, int offset, boolean toAttachPositionInfo) {
+        if (condition) {
+            return;
+        }
+
+        this.throwSyntaxError(msg, offset, toAttachPositionInfo);
+    }
+    default void require(boolean condition, String msg, boolean toAttachPositionInfo) {
+        require(condition, msg, 0, toAttachPositionInfo);
+    }
+    default void require(boolean condition, String msg, int offset) {
+        require(condition, msg, offset,false);
+    }
+    default void require(boolean condition, String msg) {
+        require(condition, msg, false);
+    }
+
+    default void throwSyntaxError(String msg, int offset, boolean toAttachPositionInfo) {
+        PositionInfo positionInfo = this.genPositionInfo(offset);
+        throw new GroovySyntaxError(msg + (toAttachPositionInfo ? positionInfo.toString() : ""),
+                this.getSyntaxErrorSource(),
+                positionInfo.getLine(),
+                positionInfo.getColumn()
+        );
+    }
+
+    int getSyntaxErrorSource();
+    default PositionInfo genPositionInfo(int offset) {
+        return new PositionInfo(getErrorLine(), getErrorColumn() + offset);
+    }
+
+    int getErrorLine();
+    int getErrorColumn();
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
new file mode 100644
index 0000000..cbe3697
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
@@ -0,0 +1,370 @@
+/*
+ *  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 org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
+
+/**
+ * Transform try-with-resources to try-catch-finally
+ * Reference JLS "14.20.3. try-with-resources"(https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html)
+ *
+ * @author <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ *         Created on 2016/11/04
+ */
+public class TryWithResourcesASTTransformation {
+    private AstBuilder astBuilder;
+
+    public TryWithResourcesASTTransformation(AstBuilder astBuilder) {
+        this.astBuilder = astBuilder;
+    }
+
+    /**
+     * Reference JLS "14.20.3. try-with-resources"(https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html)
+     *
+     * @param tryCatchStatement the try-with-resources statement to transform
+     * @return try-catch-finally statement, which contains no resources clause
+     */
+    public Statement transform(TryCatchStatement tryCatchStatement) {
+        if (!asBoolean(tryCatchStatement.getResourceStatements())) {
+            return tryCatchStatement;
+        }
+
+        if (this.isBasicTryWithResourcesStatement(tryCatchStatement)) {
+            return this.transformBasicTryWithResourcesStatement(tryCatchStatement);
+        } else {
+            return this.transformExtendedTryWithResourcesStatement(tryCatchStatement);
+        }
+    }
+
+    private boolean isBasicTryWithResourcesStatement(TryCatchStatement tryCatchStatement) {
+        if (EmptyStatement.INSTANCE.equals(tryCatchStatement.getFinallyStatement())
+                && !asBoolean(tryCatchStatement.getCatchStatements())) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private ExpressionStatement makeVariableDeclarationFinal(ExpressionStatement variableDeclaration) {
+        if (!asBoolean(variableDeclaration)) {
+            return variableDeclaration;
+        }
+
+        if (!(variableDeclaration.getExpression() instanceof DeclarationExpression)) {
+            throw new IllegalArgumentException("variableDeclaration is not a declaration statement");
+        }
+
+        DeclarationExpression declarationExpression = (DeclarationExpression) variableDeclaration.getExpression();
+        if (!(declarationExpression.getLeftExpression() instanceof VariableExpression)) {
+            throw astBuilder.createParsingFailedException("The expression statement is not a variable delcaration statement", variableDeclaration);
+        }
+
+        VariableExpression variableExpression = (VariableExpression) declarationExpression.getLeftExpression();
+        variableExpression.setModifiers(variableExpression.getModifiers() | Opcodes.ACC_FINAL);
+
+        return variableDeclaration;
+    }
+
+    private int primaryExcCnt = 0;
+    private String genPrimaryExcName() {
+        return "__$$primaryExc" + primaryExcCnt++;
+    }
+
+    /*
+     *   try ResourceSpecification
+     *       Block
+     *   Catchesopt
+     *   Finallyopt
+     *
+     *   **The above AST should be transformed to the following AST**
+     *
+     *   try {
+     *       try ResourceSpecification
+     *           Block
+     *   }
+     *   Catchesopt
+     *   Finallyopt
+     */
+    private Statement transformExtendedTryWithResourcesStatement(TryCatchStatement tryCatchStatement) {
+        /*
+         *  try ResourceSpecification
+         *      Block
+         */
+        TryCatchStatement newTryWithResourcesStatement =
+                new TryCatchStatement(
+                        tryCatchStatement.getTryStatement(),
+                        EmptyStatement.INSTANCE);
+        tryCatchStatement.getResourceStatements().forEach(newTryWithResourcesStatement::addResource);
+
+
+        /*
+         *   try {
+         *       << the following try-with-resources has been transformed >>
+         *       try ResourceSpecification
+         *           Block
+         *   }
+         *   Catchesopt
+         *   Finallyopt
+         */
+        TryCatchStatement newTryCatchStatement =
+                new TryCatchStatement(
+                        astBuilder.createBlockStatement(this.transform(newTryWithResourcesStatement)),
+                        tryCatchStatement.getFinallyStatement());
+
+        tryCatchStatement.getCatchStatements().forEach(newTryCatchStatement::addCatch);
+
+        return newTryCatchStatement;
+    }
+
+    /*
+     *   try (VariableModifiersopt R Identifier = Expression ...)
+     *      Block
+     *
+     *  **The above AST should be transformed to the following AST**
+     *
+     *   {
+     *       final VariableModifiers_minus_final R Identifier = Expression;
+     *       Throwable #primaryExc = null;
+     *
+     *       try ResourceSpecification_tail
+     *           Block
+     *       catch (Throwable #t) {
+     *           #primaryExc = #t;
+     *           throw #t;
+     *       } finally {
+     *           if (Identifier != null) {
+     *               if (#primaryExc != null) {
+     *                   try {
+     *                       Identifier.close();
+     *                   } catch (Throwable #suppressedExc) {
+     *                       #primaryExc.addSuppressed(#suppressedExc);
+     *                   }
+     *               } else {
+     *                   Identifier.close();
+     *               }
+     *           }
+     *       }
+     *   }
+     *
+     */
+    private Statement transformBasicTryWithResourcesStatement(TryCatchStatement tryCatchStatement) {
+        // { ... }
+        BlockStatement blockStatement = new BlockStatement();
+
+        // final VariableModifiers_minus_final R Identifier = Expression;
+        ExpressionStatement firstResourceStatement =
+                this.makeVariableDeclarationFinal(
+                        tryCatchStatement.getResourceStatement(0));
+        astBuilder.appendStatementsToBlockStatement(blockStatement, firstResourceStatement);
+
+        // Throwable #primaryExc = null;
+        String primaryExcName = this.genPrimaryExcName();
+        ExpressionStatement primaryExcDeclarationStatement =
+                new ExpressionStatement(
+                        new DeclarationExpression(
+                                new VariableExpression(primaryExcName, ClassHelper.make(Throwable.class)),
+                                org.codehaus.groovy.syntax.Token.newSymbol(Types.ASSIGN, -1, -1),
+                                new ConstantExpression(null)
+                        )
+                );
+        astBuilder.appendStatementsToBlockStatement(blockStatement, primaryExcDeclarationStatement);
+
+
+        // The generated try-catch-finally statement
+        String firstResourceIdentifierName =
+                ((DeclarationExpression) tryCatchStatement.getResourceStatement(0).getExpression()).getLeftExpression().getText();
+
+        TryCatchStatement newTryCatchStatement =
+                new TryCatchStatement(
+                        tryCatchStatement.getTryStatement(),
+                        this.createFinallyBlockForNewTryCatchStatement(primaryExcName, firstResourceIdentifierName));
+
+        List<ExpressionStatement> resourceStatements = tryCatchStatement.getResourceStatements();
+        // 2nd, 3rd, ..., n'th resources declared in resources
+        List<ExpressionStatement> tailResourceStatements = resourceStatements.subList(1, resourceStatements.size());
+        tailResourceStatements.stream().forEach(newTryCatchStatement::addResource);
+
+        newTryCatchStatement.addCatch(this.createCatchBlockForOuterNewTryCatchStatement(primaryExcName));
+        astBuilder.appendStatementsToBlockStatement(blockStatement, this.transform(newTryCatchStatement));
+
+        return blockStatement;
+    }
+
+    /*
+     *   catch (Throwable #t) {
+     *       #primaryExc = #t;
+     *       throw #t;
+     *   }
+     *
+     */
+    private CatchStatement createCatchBlockForOuterNewTryCatchStatement(String primaryExcName) {
+        // { ... }
+        BlockStatement blockStatement = new BlockStatement();
+        String tExcName = this.genTExcName();
+
+        // #primaryExc = #t;
+        ExpressionStatement primaryExcAssignStatement =
+                new ExpressionStatement(
+                        new BinaryExpression(
+                                new VariableExpression(primaryExcName),
+                                org.codehaus.groovy.syntax.Token.newSymbol(Types.ASSIGN, -1, -1),
+                                new VariableExpression(tExcName)));
+        astBuilder.appendStatementsToBlockStatement(blockStatement, primaryExcAssignStatement);
+
+        // throw #t;
+        ThrowStatement throwTExcStatement = new ThrowStatement(new VariableExpression(tExcName));
+        astBuilder.appendStatementsToBlockStatement(blockStatement, throwTExcStatement);
+
+        // Throwable #t
+        Parameter tExcParameter = new Parameter(ClassHelper.make(Throwable.class), tExcName);
+
+        return new CatchStatement(tExcParameter, blockStatement);
+    }
+
+    private int tExcCnt = 0;
+    private String genTExcName() {
+        return "__$$t" + tExcCnt++;
+    }
+
+    /*
+     *   finally {
+     *       if (Identifier != null) {
+     *           if (#primaryExc != null) {
+     *              try {
+     *                  Identifier.close();
+     *              } catch (Throwable #suppressedExc) {
+     *                  #primaryExc.addSuppressed(#suppressedExc);
+     *              }
+     *           } else {
+     *               Identifier.close();
+     *           }
+     *       }
+     *   }
+     *
+     * We can simplify the above code to a Groovy version as follows:
+     *
+     *   finally {
+     *      if (#primaryExc != null)
+     *         try {
+     *             Identifier?.close();
+     *         } catch (Throwable #suppressedExc) {
+     *             #primaryExc.addSuppressed(#suppressedExc);
+     *         }
+     *      else
+     *          Identifier?.close();
+     *
+     *   }
+     *
+     */
+    private BlockStatement createFinallyBlockForNewTryCatchStatement(String primaryExcName, String firstResourceIdentifierName) {
+        BlockStatement finallyBlock = new BlockStatement();
+
+        // primaryExc != null
+        BooleanExpression conditionExpression =
+                new BooleanExpression(
+                        new BinaryExpression(
+                                new VariableExpression(primaryExcName),
+                                org.codehaus.groovy.syntax.Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1),
+                                new ConstantExpression(null)));
+
+        // try-catch statement
+        TryCatchStatement newTryCatchStatement =
+                new TryCatchStatement(
+                        astBuilder.createBlockStatement(this.createCloseResourceStatement(firstResourceIdentifierName)), // { Identifier?.close(); }
+                        EmptyStatement.INSTANCE);
+
+
+        String suppressedExcName = this.genSuppressedExcName();
+        newTryCatchStatement.addCatch(
+                // catch (Throwable #suppressedExc) { .. }
+                new CatchStatement(
+                        new Parameter(ClassHelper.make(Throwable.class), suppressedExcName),
+                        astBuilder.createBlockStatement(this.createAddSuppressedStatement(primaryExcName, suppressedExcName)) // #primaryExc.addSuppressed(#suppressedExc);
+                )
+        );
+
+        // if (#primaryExc != null) { ... }
+        IfStatement ifStatement =
+                new IfStatement(
+                        conditionExpression,
+                        newTryCatchStatement,
+                        this.createCloseResourceStatement(firstResourceIdentifierName) // Identifier?.close();
+                );
+        astBuilder.appendStatementsToBlockStatement(finallyBlock, ifStatement);
+
+        return astBuilder.createBlockStatement(finallyBlock);
+    }
+
+    private int suppressedExcCnt = 0;
+    private String genSuppressedExcName() {
+        return "__$$suppressedExc" + suppressedExcCnt++;
+    }
+
+    /*
+     *  Identifier?.close();
+     */
+    private ExpressionStatement createCloseResourceStatement(String firstResourceIdentifierName) {
+        MethodCallExpression closeMethodCallExpression =
+                new MethodCallExpression(new VariableExpression(firstResourceIdentifierName), "close", new ArgumentListExpression());
+
+        closeMethodCallExpression.setImplicitThis(false);
+        closeMethodCallExpression.setSafe(true);
+
+        return new ExpressionStatement(closeMethodCallExpression);
+    }
+
+    /*
+     *  #primaryExc.addSuppressed(#suppressedExc);
+     */
+    private ExpressionStatement createAddSuppressedStatement(String primaryExcName, String suppressedExcName) {
+        MethodCallExpression addSuppressedMethodCallExpression =
+                new MethodCallExpression(
+                        new VariableExpression(primaryExcName),
+                        "addSuppressed",
+                        new ArgumentListExpression(Collections.singletonList(new VariableExpression(suppressedExcName))));
+        addSuppressedMethodCallExpression.setImplicitThis(false);
+        addSuppressedMethodCallExpression.setSafe(true);
+
+        return new ExpressionStatement(addSuppressedMethodCallExpression);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java
new file mode 100644
index 0000000..02cc152
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java
@@ -0,0 +1,107 @@
+/*
+ *  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.internal;
+
+import org.antlr.v4.runtime.atn.ATN;
+import org.apache.groovy.parser.antlr4.GroovyLangLexer;
+import org.apache.groovy.parser.antlr4.GroovyLangParser;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Manage ATN for lexer and parser to avoid memory leak
+ *
+ * @author <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on 2016/08/14
+ */
+public class AtnManager {
+    public static final ReentrantReadWriteLock RRWL = new ReentrantReadWriteLock(true);
+    private static final String CACHE_THRESHOLD_NAME = "groovy.antlr4.cache.threshold";
+    private static final int CACHE_THRESHOLD;
+    private final Class ownerClass;
+    private final ATN atn;
+    private static final Map<Class, AtnWrapper> ATN_MAP =
+            Collections.unmodifiableMap(new HashMap<Class, AtnWrapper>() {
+                {
+                    put(GroovyLangLexer.class, new AtnWrapper(GroovyLangLexer._ATN));
+                    put(GroovyLangParser.class, new AtnWrapper(GroovyLangParser._ATN));
+                }
+            });
+
+    static {
+        int t = 50;
+
+        try {
+            t = Integer.parseInt(System.getProperty(CACHE_THRESHOLD_NAME));
+
+            // cache threshold should be at least 50 for better performance
+            t = t < 50 ? 50 : t;
+        } catch (Exception e) {
+            // ignored
+        }
+
+        CACHE_THRESHOLD = t;
+    }
+
+    public AtnManager(GroovyLangLexer lexer) {
+        this.ownerClass = lexer.getClass();
+        this.atn = GroovyLangLexer._ATN; //getAtnWrapper(this.ownerClass).checkAndClear();
+    }
+
+    public AtnManager(GroovyLangParser parser) {
+        this.ownerClass = parser.getClass();
+        this.atn = getAtnWrapper(this.ownerClass).checkAndClear();
+    }
+
+    public ATN getATN() {
+        return this.atn;
+    }
+
+    private AtnWrapper getAtnWrapper(Class ownerClass) {
+        return ATN_MAP.get(ownerClass);
+    }
+
+    private static class AtnWrapper {
+        private final ATN atn;
+        private final AtomicLong counter = new AtomicLong(0);
+
+        public AtnWrapper(ATN atn) {
+            this.atn = atn;
+        }
+
+        public ATN checkAndClear() {
+            if (0 != counter.incrementAndGet() % CACHE_THRESHOLD) {
+                return atn;
+            }
+
+            RRWL.writeLock().lock();
+            try {
+                atn.clearDFA();
+            } finally {
+                RRWL.writeLock().unlock();
+            }
+
+            return atn;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
new file mode 100644
index 0000000..12081ed
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
@@ -0,0 +1,110 @@
+/*
+ *  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.internal;
+
+import org.antlr.v4.runtime.BailErrorStrategy;
+import org.antlr.v4.runtime.FailedPredicateException;
+import org.antlr.v4.runtime.InputMismatchException;
+import org.antlr.v4.runtime.NoViableAltException;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.TokenStream;
+import org.antlr.v4.runtime.atn.PredictionMode;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+
+/**
+ * Provide friendly error messages when parsing errors occurred.
+ *
+ * @author <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ *         Created on 2016/10/19
+ */
+public class DescriptiveErrorStrategy extends BailErrorStrategy {
+    @Override
+    public void recover(Parser recognizer, RecognitionException e) {
+        for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
+            context.exception = e;
+        }
+
+        if (PredictionMode.LL.equals(recognizer.getInterpreter().getPredictionMode())) {
+            if (e instanceof NoViableAltException) {
+                this.reportNoViableAlternative(recognizer, (NoViableAltException) e);
+            } else if (e instanceof InputMismatchException) {
+                this.reportInputMismatch(recognizer, (InputMismatchException) e);
+            } else if (e instanceof FailedPredicateException) {
+                this.reportFailedPredicate(recognizer, (FailedPredicateException) e);
+            }
+        }
+
+        throw new ParseCancellationException(e);
+    }
+
+    @Override
+    public Token recoverInline(Parser recognizer)
+            throws RecognitionException {
+
+        this.recover(recognizer, new InputMismatchException(recognizer)); // stop parsing
+        return null;
+    }
+
+    protected String createNoViableAlternativeErrorMessage(Parser recognizer, NoViableAltException e) {
+        TokenStream tokens = recognizer.getInputStream();
+        String input;
+        if (tokens != null) {
+            if (e.getStartToken().getType() == Token.EOF) input = "<EOF>";
+            else input = tokens.getText(e.getStartToken(), e.getOffendingToken());
+        } else {
+            input = "<unknown input>";
+        }
+
+        return "Unexpected input: " + escapeWSAndQuote(input);
+    }
+
+    @Override
+    protected void reportNoViableAlternative(@NotNull Parser recognizer,
+                                             @NotNull NoViableAltException e) {
+
+        notifyErrorListeners(recognizer, this.createNoViableAlternativeErrorMessage(recognizer, e), e);
+    }
+
+    protected String createInputMismatchErrorMessage(@NotNull Parser recognizer,
+                                                     @NotNull InputMismatchException e) {
+        return "Unexpected input: " + getTokenErrorDisplay(e.getOffendingToken(recognizer)) +
+                "; Expecting " + e.getExpectedTokens().toString(recognizer.getVocabulary());
+    }
+
+    protected void reportInputMismatch(@NotNull Parser recognizer,
+                                       @NotNull InputMismatchException e) {
+
+        notifyErrorListeners(recognizer, this.createInputMismatchErrorMessage(recognizer, e), e);
+    }
+
+
+    protected String createFailedPredicateErrorMessage(@NotNull Parser recognizer,
+                                                       @NotNull FailedPredicateException e) {
+        return e.getMessage();
+    }
+
+    protected void reportFailedPredicate(@NotNull Parser recognizer,
+                                         @NotNull FailedPredicateException e) {
+        notifyErrorListeners(recognizer, this.createFailedPredicateErrorMessage(recognizer, e), e);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
new file mode 100644
index 0000000..739a737
--- /dev/null
+++ b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
@@ -0,0 +1,149 @@
+/*
+ *  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.lang.Closure;
+import org.codehaus.groovy.runtime.StringGroovyMethods;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Utilities for handling strings
+ *
+ * @author  <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on    2016/08/20
+ */
+public class StringUtils {
+    public static String replaceHexEscapes(String text) {
+        Pattern p = Pattern.compile("(\\\\*)\\\\u+([0-9abcdefABCDEF]{4})");
+	    return StringGroovyMethods.replaceAll((CharSequence) text, p, new Closure<Void>(null, null) {
+		    Object doCall(String _0, String _1, String _2) {
+				if (isLengthOdd(_1)) {
+					return _0;
+				}
+
+			    return _1 + new String(Character.toChars(Integer.parseInt(_2, 16)));
+		    }
+	    });
+    }
+
+	public static String replaceOctalEscapes(String text) {
+	    Pattern p = Pattern.compile("(\\\\*)\\\\([0-3]?[0-7]?[0-7])");
+	    return StringGroovyMethods.replaceAll((CharSequence) text, p, new Closure<Void>(null, null) {
+		    Object doCall(String _0, String _1, String _2) {
+				if (isLengthOdd(_1)) {
+					return _0;
+				}
+
+			    return _1 + new String(Character.toChars(Integer.parseInt(_2, 8)));
+		    }
+	    });
+    }
+
+    private static Map<Character, Character> STANDARD_ESCAPES = new HashMap<Character, Character>() {
+		{
+			this.put('b', '\b');
+			this.put('t', '\t');
+			this.put('n', '\n');
+			this.put('f', '\f');
+			this.put('r', '\r');
+		}
+	};
+
+	public static String replaceStandardEscapes(String text) {
+	    Pattern p = Pattern.compile("(\\\\*)\\\\([btnfr\"'])");
+
+	    String result = StringGroovyMethods.replaceAll((CharSequence) text, p, new Closure<Void>(null, null) {
+							Object doCall(String _0, String _1, String _2) {
+								if (isLengthOdd(_1)) {
+									return _0;
+								}
+
+								Character character = STANDARD_ESCAPES.get(_2.charAt(0));
+								return _1 + (character != null ? character : _2);
+							}
+						});
+
+		return result.replace("\\\\", "\\");
+    }
+
+	public static final int NONE_SLASHY = 0;
+	public static final int SLASHY = 1;
+	public static final int DOLLAR_SLASHY = 2;
+
+	public static String replaceEscapes(String text, int slashyType) {
+		if (slashyType == SLASHY || slashyType == DOLLAR_SLASHY) {
+			text = StringUtils.replaceHexEscapes(text);
+			text = StringUtils.replaceLineEscape(text);
+
+			if (slashyType == SLASHY) {
+				text = text.replace("\\/", "/");
+			}
+
+			if (slashyType == DOLLAR_SLASHY) {
+				text = text.replace("$$", "$");
+				text = text.replace("$/", "/");
+			}
+
+		} else if (slashyType == NONE_SLASHY) {
+			text = StringUtils.replaceEscapes(text);
+		} else {
+			throw new IllegalArgumentException("Invalid slashyType: " + slashyType);
+		}
+
+		return text;
+	}
+
+	private static String replaceEscapes(String text) {
+		text = text.replace("\\$", "$");
+
+		text = StringUtils.replaceLineEscape(text);
+
+        return StringUtils.replaceStandardEscapes(replaceHexEscapes(replaceOctalEscapes(text)));
+    }
+
+	private static String replaceLineEscape(String text) {
+		Pattern p = Pattern.compile("(\\\\*)\\\\\r?\n");
+		text = StringGroovyMethods.replaceAll((CharSequence) text, p, new Closure<Void>(null, null) {
+			Object doCall(String _0, String _1) {
+				if (isLengthOdd(_1)) {
+					return _0;
+				}
+
+				return _1;
+			}
+		});
+
+		return text;
+	}
+
+	private static boolean isLengthOdd(String str) {
+		return null != str && str.length() % 2 == 1;
+	}
+
+	public static String removeCR(String text) {
+        return text.replace("\r\n", "\n");
+    }
+
+	public static long countChar(String text, char c) {
+		return text.chars().filter(e -> c == e).count();
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/2c01e99f/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
new file mode 100644
index 0000000..480d2ab
--- /dev/null
+++ b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
@@ -0,0 +1,368 @@
+/*
+ *  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 org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.FieldNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.Parameter
+import org.codehaus.groovy.ast.PropertyNode
+import org.codehaus.groovy.ast.stmt.AssertStatement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.syntax.Token
+
+import static org.apache.groovy.parser.antlr4.TestUtils.doTest
+import static org.apache.groovy.parser.antlr4.TestUtils.doRunAndTest
+
+/**
+ * Some basic test cases for the new parser
+ *
+ * @author  <a href="mailto:realbluesun@hotmail.com">Daniel.Sun</a>
+ * Created on    2016/08/14
+ */
+class GroovyParserTest extends GroovyTestCase {
+
+    void setUp() {}
+
+    void tearDown() {}
+
+    void "test groovy core - Comments"() {
+        doTest('core/Comments_01.groovy', [ExpressionStatement]);
+        doTestAttachedComments();
+    }
+
+    private static doTestAttachedComments() {
+        def (newAST, oldAST) = doTest('core/Comments_02.groovy');
+        List<ClassNode> classes = new ArrayList<>(newAST.classes).sort { c1, c2 -> c1.name <=> c2.name };
+        List<MethodNode> methods = new ArrayList<>(newAST.methods).sort { m1, m2 -> m1.name <=> m2.name };
+
+        assert classes[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')            == '/** * test class Comments */'
+        assert classes[0].fields[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')  == '/**     * test Comments.SOME_VAR     */'
+        assert classes[0].fields[1].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')  == '/**     * test Comments.SOME_VAR2     */'
+        assert classes[0].fields[2].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+        assert classes[0].fields[3].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+        assert classes[0].declaredConstructors[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '') == '/**     * test Comments.constructor1     */'
+        assert classes[0].methods[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '') == '/**     * test Comments.m1     */'
+        assert classes[0].methods[1].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+        assert classes[0].methods[2].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '') == '/**     * test Comments.m3     */'
+
+        assert classes[1].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')            == '/**     * test class InnerClazz     */'
+        assert classes[1].fields[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')  == '/**         * test InnerClazz.SOME_VAR3         */'
+        assert classes[1].fields[1].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')  == '/**         * test InnerClazz.SOME_VAR4         */'
+        assert classes[1].methods[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '') == '/**         * test Comments.m4         */'
+        assert classes[1].methods[1].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '') == '/**         * test Comments.m5         */'
+
+        assert classes[2].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')            == '/**     * test class InnerEnum     */'
+        assert classes[2].fields[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')  == '/**         * InnerEnum.NEW         */'
+        assert classes[2].fields[1].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '')  == '/**         * InnerEnum.OLD         */'
+
+        assert classes[3].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+
+        assert classes[4].fields[0].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+
+        assert classes[5].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+
+        assert methods[0].nodeMetaData[GroovydocManager.DOC_COMMENT].replaceAll(/\r?\n/, '') == '/** * test someScriptMethod1 */'
+        assert methods[1].nodeMetaData[GroovydocManager.DOC_COMMENT] == null
+    }
+
+    void "test groovy core - PackageDeclaration"() {
+        doTest('core/PackageDeclaration_01.groovy');
+        doTest('core/PackageDeclaration_02.groovy');
+        doTest('core/PackageDeclaration_03.groovy');
+        doTest('core/PackageDeclaration_04.groovy');
+        doTest('core/PackageDeclaration_05.groovy');
+        doTest('core/PackageDeclaration_06.groovy');
+    }
+
+    void "test groovy core - ImportDeclaration"() {
+        doTest('core/ImportDeclaration_01.groovy');
+        doTest('core/ImportDeclaration_02.groovy');
+        doTest('core/ImportDeclaration_03.groovy');
+        doTest('core/ImportDeclaration_04.groovy');
+        doTest('core/ImportDeclaration_05.groovy');
+        doTest('core/ImportDeclaration_06.groovy');
+        doTest('core/ImportDeclaration_07.groovy');
+        doTest('core/ImportDeclaration_08.groovy');
+    }
+
+    void "test groovy core - Annotation"() {
+        doTest('core/Annotation_01.groovy');
+        doTest('core/Annotation_02.groovy');
+        doTest('core/Annotation_03.groovy');
+        doTest('core/Annotation_04.groovy');
+        doTest('core/Annotation_05.groovy');
+        doTest('core/Annotation_06.groovy');
+        doTest('core/Annotation_07.groovy');
+        doTest('core/Annotation_08.groovy');
+        doTest('core/Annotation_09.groovy');
+        doRunAndTest('core/Annotation_10x.groovy');
+    }
+
+    void "test groovy core - Literal"() {
+        doTest('core/Literal_01.groovy');
+        doTest('core/Literal_02.groovy', [ExpressionStatement]);
+        doTest('core/Literal_03.groovy');
+    }
+
+    void "test groovy core - GString"() {
+        doTest('core/GString_01.groovy');
+        doTest('core/GString_02.groovy');
+        doTest('core/GString_03.groovy');
+        doTest('core/GString_04.groovy');
+        doTest('core/GString_05.groovy');
+        doTest('core/GString_06.groovy');
+    }
+
+    void "test groovy core - Closure"() {
+        doTest('core/Closure_01.groovy');
+        doTest('core/Closure_02.groovy');
+        doTest('core/Closure_03.groovy');
+        doTest('core/Closure_04.groovy');
+        doTest('core/Closure_05.groovy', [Parameter]);
+        doTest('core/Closure_06.groovy', [Parameter]);
+        doTest('core/Closure_07.groovy', [Parameter]);
+        doTest('core/Closure_08.groovy', [Parameter]);
+        doTest('core/Closure_09.groovy', [Parameter]);
+        doTest('core/Closure_10.groovy', [Parameter]);
+    }
+
+    void "test groovy core - Lambda"() {
+        doRunAndTest('core/Lambda_01x.groovy');
+    }
+
+    void "test groovy core - MethodReference"() {
+        doRunAndTest('core/MethodReference_01x.groovy');
+    }
+
+    void "test groovy core - MethodPointer"() {
+        doRunAndTest('core/MethodPointer_01x.groovy');
+    }
+
+    void "test groovy core - ElvisAssignment"() {
+        doRunAndTest('core/ElvisAssignment_01x.groovy');
+    }
+
+    void "test groovy core - List"() {
+        doTest('core/List_01.groovy');
+    }
+
+    void "test groovy core - Map"() {
+        doTest('core/Map_01.groovy');
+    }
+
+    void "test groovy core - Expression"() {
+        doTest('core/Expression_01.groovy');
+        doTest('core/Expression_02.groovy');
+        doTest('core/Expression_03.groovy');
+        doTest('core/Expression_04.groovy');
+        doTest('core/Expression_05.groovy');
+        doTest('core/Expression_06.groovy');
+        doTest('core/Expression_07.groovy');
+        doTest('core/Expression_08.groovy');
+        doTest('core/Expression_09.groovy');
+        doTest('core/Expression_10.groovy');
+        doTest('core/Expression_11.groovy');
+        doTest('core/Expression_12.groovy');
+        doTest('core/Expression_13.groovy');
+        doTest('core/Expression_14.groovy');
+        doTest('core/Expression_15.groovy');
+        doTest('core/Expression_16.groovy', [Parameter, ExpressionStatement]);
+        doTest('core/Expression_17.groovy');
+        doTest('core/Expression_18.groovy');
+        doTest('core/Expression_19.groovy');
+        doTest('core/Expression_20.groovy');
+        doRunAndTest('core/Expression_21x.groovy');
+        doTest('core/Expression_22x.groovy');
+        doRunAndTest('core/Expression_22x.groovy');
+        doRunAndTest('core/Expression_23x.groovy');
+    }
+
+    void "test groovy core - IdenticalOp"() {
+        doRunAndTest('core/IdenticalOp_01x.groovy');
+    }
+
+    void "test groovy core - Assert"() {
+        doTest('core/Assert_01.groovy');
+        doRunAndTest('core/Assert_02x.groovy');
+        doRunAndTest('core/Assert_03x.groovy');
+    }
+
+    void "test groovy core - IfElse"() {
+        doTest('core/IfElse_01.groovy', [AssertStatement]);
+    }
+
+    void "test groovy core - For"() {
+        doTest('core/For_01.groovy', [AssertStatement]);
+        doTest('core/For_02.groovy');
+        doTest('core/For_03.groovy');
+        doRunAndTest('core/For_04x.groovy');
+        doRunAndTest('core/For_05x.groovy');
+    }
+
+    void "test groovy core - While"() {
+        doTest('core/While_01.groovy');
+        doRunAndTest('core/While_02x.groovy');
+    }
+
+    void "test groovy core - CodeBlock"() {
+        doRunAndTest('core/CodeBlock_01x.groovy');
+    }
+
+    void "test groovy core - DoWhile"() {
+        doRunAndTest('core/DoWhile_01x.groovy');
+        doRunAndTest('core/DoWhile_02x.groovy');
+        doRunAndTest('core/DoWhile_03x.groovy');
+        doRunAndTest('core/DoWhile_04x.groovy');
+    }
+
+
+    void "test groovy core - TryCatch"() {
+        doTest('core/TryCatch_01.groovy');
+    }
+
+    void "test groovy core - TryWithResources"() {
+        doRunAndTest('core/TryWithResources_01x.groovy');
+    }
+
+    void "test groovy core - SafeIndex"() {
+        doRunAndTest('core/SafeIndex_01x.groovy');
+        doRunAndTest('core/SafeIndex_02x.groovy');
+        doRunAndTest('core/SafeIndex_03x.groovy');
+    }
+
+    void "test groovy core - NegativeRelationalOperators"() {
+        doRunAndTest('core/NegativeRelationalOperators_01x.groovy');
+        doRunAndTest('core/NegativeRelationalOperators_02x.groovy');
+    }
+
+    void "test groovy core - DefaultMethod"() {
+        doRunAndTest('core/DefaultMethod_01x.groovy');
+        doRunAndTest('core/DefaultMethod_02x.groovy');
+    }
+
+
+    void "test groovy core - Switch"() {
+        doTest('core/Switch_01.groovy');
+    }
+
+    void "test groovy core - Synchronized"() {
+        doTest('core/Synchronized_01.groovy');
+    }
+
+    void "test groovy core - Return"() {
+        doTest('core/Return_01.groovy');
+    }
+
+    void "test groovy core - Throw"() {
+        doTest('core/Throw_01.groovy');
+    }
+
+    void "test groovy core - Label"() {
+        doTest('core/Label_01.groovy');
+    }
+
+    void "test groovy core - LocalVariableDeclaration"() {
+        doTest('core/LocalVariableDeclaration_01.groovy', [Token]); // [class org.codehaus.groovy.syntax.Token][startLine]:: 9 != 8
+    }
+
+    void "test groovy core - MethodDeclaration"() {
+        doTest('core/MethodDeclaration_01.groovy');
+        doTest('core/MethodDeclaration_02.groovy');
+    }
+
+    void "test groovy core - ClassDeclaration"() {
+        doTest('core/ClassDeclaration_01.groovy');
+        doTest('core/ClassDeclaration_02.groovy');
+        doTest('core/ClassDeclaration_03.groovy');
+        doTest('core/ClassDeclaration_04.groovy', [PropertyNode, FieldNode]);
+        doTest('core/ClassDeclaration_05.groovy', [ExpressionStatement]);
+        doTest('core/ClassDeclaration_06.groovy');
+        doTest('core/ClassDeclaration_07.groovy');
+    }
+
+    void "test groovy core - InterfaceDeclaration"() {
+        doTest('core/InterfaceDeclaration_01.groovy');
+        doTest('core/InterfaceDeclaration_02.groovy');
+        doTest('core/InterfaceDeclaration_03.groovy');
+    }
+
+    void "test groovy core - EnumDeclaration"() {
+        doTest('core/EnumDeclaration_01.groovy');
+        doTest('core/EnumDeclaration_02.groovy', [ExpressionStatement]);
+        doTest('core/EnumDeclaration_03.groovy');
+        doTest('core/EnumDeclaration_04.groovy');
+        doTest('core/EnumDeclaration_05.groovy');
+    }
+
+    void "test groovy core - TraitDeclaration"() {
+        doTest('core/TraitDeclaration_01.groovy');
+        doTest('core/TraitDeclaration_02.groovy');
+        doTest('core/TraitDeclaration_03.groovy');
+        doTest('core/TraitDeclaration_04.groovy', [PropertyNode, FieldNode]);
+        doTest('core/TraitDeclaration_05.groovy');
+    }
+
+    void "test groovy core - AnnotationDeclaration"() {
+        doTest('core/AnnotationDeclaration_01.groovy');
+    }
+
+    void "test groovy core - Command"() {
+        doTest('core/Command_01.groovy');
+        doTest('core/Command_02.groovy');
+        doTest('core/Command_03.groovy', [ExpressionStatement, Parameter]);
+        doTest('core/Command_04.groovy', [ExpressionStatement]);
+        doTest('core/Command_05.groovy');
+        doRunAndTest('core/Command_06x.groovy')
+    }
+
+    void "test groovy core - Unicode"() {
+        doTest('core/Unicode_01.groovy');
+    }
+
+    void "test groovy core - BreakingChanges"() {
+        doRunAndTest('core/BreakingChange_01x.groovy');
+        doRunAndTest('core/BreakingChange_02x.groovy');
+        doRunAndTest('core/BreakingChange_03x.groovy');
+        doRunAndTest('core/BreakingChange_04x.groovy');
+    }
+
+    void "test groovy core - Array"() {
+        doRunAndTest('core/Array_01x.groovy');
+    }
+
+    void "test groovy core - Groovydoc"() {
+        doRunAndTest('core/Groovydoc_01x.groovy');
+    }
+
+    void "test groovy core - Script"() {
+        doRunAndTest('core/Script_01x.groovy');
+    }
+
+    void "test groovy core - BUG"() {
+        doRunAndTest('bugs/BUG-GROOVY-4757.groovy');
+        doRunAndTest('bugs/GROOVY-3898.groovy');
+        doRunAndTest('bugs/BUG-GROOVY-5652.groovy');
+        doRunAndTest('bugs/BUG-GROOVY-4762.groovy');
+        doRunAndTest('bugs/BUG-GROOVY-4438.groovy');
+        doRunAndTest('bugs/BUG-GROOVY-6038.groovy');
+        doRunAndTest('bugs/BUG-GROOVY-2324.groovy');
+        doTest('bugs/BUG-GROOVY-8161.groovy');
+    }
+}


Mime
View raw message