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