http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
new file mode 100644
index 0000000..8d20ad6
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangLexer.java
@@ -0,0 +1,45 @@
+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:realblue...@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/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/GroovyLangParser.java
new file mode 100644
index 0000000..fa33c6e
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/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:realblue...@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/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
new file mode 100644
index 0000000..cf620dd
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/SemanticPredicates.java
@@ -0,0 +1,94 @@
+/*
+ *  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.TokenStream;
+
+import static org.apache.groovy.parser.antlr4.GroovyParser.*;
+
+/**
+ * Some semantic predicates for altering the behaviour of the lexer and parser
+ *
+ * @author  <a href="mailto:realblue...@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 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)
+                && GroovyLangParser.LPAREN == (ts.LT(2).getType());
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
new file mode 100644
index 0000000..b249d5f
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/TryWithResourcesASTTransformation.java
@@ -0,0 +1,339 @@
+package org.apache.groovy.parser.antlr4;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+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:realblue...@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 long 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 long 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 long 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/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/AtnManager.java
new file mode 100644
index 0000000..2e6e3a8
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/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:realblue...@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 = 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/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
new file mode 100644
index 0000000..c2c5afe
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/internal/DescriptiveErrorStrategy.java
@@ -0,0 +1,102 @@
+/*
+ *  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.*;
+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:realblue...@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/91c04014/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
----------------------------------------------------------------------
diff --git 
a/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
new file mode 100644
index 0000000..1c16679
--- /dev/null
+++ 
b/subprojects/groovy-antlr4-grammar/src/main/java/org/apache/groovy/parser/antlr4/util/StringUtils.java
@@ -0,0 +1,145 @@
+/*
+ *  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:realblue...@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 (null != _1 && _1.length() % 2 == 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 (null != _1 && _1.length() % 2 == 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 (null != _1 
&& _1.length() % 2 == 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 (null != _1 && _1.length() % 2 == 1) {
+                                       return _0;
+                               }
+
+                               return _1;
+                       }
+               });
+
+               return text;
+       }
+
+       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

Reply via email to