Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id B2B0E200CAE for ; Thu, 11 May 2017 00:26:36 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id B0FDB160BB4; Wed, 10 May 2017 22:26:36 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 833FD160BEA for ; Thu, 11 May 2017 00:26:32 +0200 (CEST) Received: (qmail 96906 invoked by uid 500); 10 May 2017 22:26:31 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 95525 invoked by uid 99); 10 May 2017 22:26:30 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 May 2017 22:26:30 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 49936DFBAB; Wed, 10 May 2017 22:26:30 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: paulk@apache.org To: commits@groovy.apache.org Date: Wed, 10 May 2017 22:27:04 -0000 Message-Id: In-Reply-To: <08576301a28a4d6c9b372a46e9c44259@git.apache.org> References: <08576301a28a4d6c9b372a46e9c44259@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [36/50] [abbrv] groovy git commit: rename antlr4 parser to remove groovy- prefix since that is by convention for modules archived-at: Wed, 10 May 2017 22:26:37 -0000 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 Daniel.Sun + * 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 Daniel.Sun + * 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 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 + *

+ * Created by Daniel.Sun on 2016/08/27. + */ +class ModifierManager { + private static final Map> INVALID_MODIFIERS_MAP = Collections.unmodifiableMap(new HashMap>() { + { + put(ConstructorNode.class, Arrays.asList(STATIC, FINAL, ABSTRACT, NATIVE)); + put(MethodNode.class, Arrays.asList(VOLATILE/*, TRANSIENT*/)); + } + }); + private AstBuilder astBuilder; + private List modifierNodeList; + + public ModifierManager(AstBuilder astBuilder, List modifierNodeList) { + this.astBuilder = astBuilder; + this.validate(modifierNodeList); + this.modifierNodeList = Collections.unmodifiableList(asBoolean((Object) modifierNodeList) ? modifierNodeList : Collections.emptyList()); + } + + private void validate(List modifierNodeList) { + Map 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 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 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 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 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 + *

+ * 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 MODIFIER_OPCODE_MAP = Collections.unmodifiableMap(new HashMap() { + { + 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 Daniel.Sun + * 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 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 Daniel.Sun + * 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 resourceStatements = tryCatchStatement.getResourceStatements(); + // 2nd, 3rd, ..., n'th resources declared in resources + List 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 Daniel.Sun + * 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 ATN_MAP = + Collections.unmodifiableMap(new HashMap() { + { + 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 Daniel.Sun + * 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 = ""; + else input = tokens.getText(e.getStartToken(), e.getOffendingToken()); + } else { + 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 Daniel.Sun + * 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(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(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 STANDARD_ESCAPES = new HashMap() { + { + 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(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(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 Daniel.Sun + * 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 classes = new ArrayList<>(newAST.classes).sort { c1, c2 -> c1.name <=> c2.name }; + List 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'); + } +}