http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/gradle/docs.gradle
----------------------------------------------------------------------
diff --git a/gradle/docs.gradle b/gradle/docs.gradle
index e80dcd4..d10b9d5 100644
--- a/gradle/docs.gradle
+++ b/gradle/docs.gradle
@@ -44,7 +44,7 @@ def javadocSpec = {
         encoding = 'UTF-8'
         author = true
         version = true
-        overview = rootProject.file('src/main/overviewj.html')
+        overview = rootProject.file('src/main/java/overviewj.html')
         footer = doc.footer
         source = '1.8'
         links('http://docs.oracle.com/javase/8/docs/api/', 
'http://docs.oracle.com/javaee/7/api/',
@@ -55,13 +55,13 @@ def javadocSpec = {
 
 def groovydocSpec = {
     use = true
-    if (project != rootProject) source = project.sourceSets.main.allSource
+    source = project.sourceSets.main.allSource
     classpath = javadoc.classpath
     ext.windowtitle = doc.title
     ext.doctitle = doc.title
     header = doc.title
     footer = doc.footer
-    overviewText = 
rootProject.resources.text.fromFile('src/main/overview.html')
+    overviewText = 
rootProject.resources.text.fromFile('src/main/java/overview.html')
     includePrivate = false
     link 'http://docs.oracle.com/javaee/7/api/', 'javax.servlet.', 
'javax.management.'
     link 'http://docs.oracle.com/javase/8/docs/api/', 'java.', 'org.xml.', 
'javax.', 'org.w3c.'

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/gradle/test.gradle
----------------------------------------------------------------------
diff --git a/gradle/test.gradle b/gradle/test.gradle
index df0ff06..b138af1 100644
--- a/gradle/test.gradle
+++ b/gradle/test.gradle
@@ -77,7 +77,7 @@ tasks.withType(Test) {
     if (testdb) {
         systemProperties 'groovy.testdb.props': testdb
     }
-    systemProperties 'apple.awt.UIElement': 'true', 
'javadocAssertion.src.dir': './src/main'
+    systemProperties 'apple.awt.UIElement': 'true', 
'javadocAssertion.src.dir': './src/main/java'
     systemProperties 'gradle.home': gradle.gradleHomeDir // this is needed by 
the security.policy
 
     classpath = files('src/test') + classpath

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/ASTTestTransformation.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/ASTTestTransformation.groovy 
b/src/main/groovy/ASTTestTransformation.groovy
new file mode 100644
index 0000000..dcfe314
--- /dev/null
+++ b/src/main/groovy/ASTTestTransformation.groovy
@@ -0,0 +1,233 @@
+/*
+ *  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.transform
+
+import groovy.transform.CompilationUnitAware
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.expr.ClosureExpression
+import org.codehaus.groovy.ast.expr.PropertyExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.ErrorCollector
+import org.codehaus.groovy.control.Janitor
+import org.codehaus.groovy.control.ProcessingUnit
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.codehaus.groovy.control.io.ReaderSource
+import org.codehaus.groovy.runtime.MethodClosure
+import org.codehaus.groovy.syntax.SyntaxException
+import org.codehaus.groovy.tools.Utilities
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
+
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+class ASTTestTransformation extends AbstractASTTransformation implements 
CompilationUnitAware {
+    private CompilationUnit compilationUnit
+
+    void visit(final ASTNode[] nodes, final SourceUnit source) {
+        AnnotationNode annotationNode = nodes[0]
+        def member = annotationNode.getMember('phase')
+        def phase = null
+        if (member) {
+            if (member instanceof VariableExpression) {
+                phase = CompilePhase.valueOf(member.text)
+            } else if (member instanceof PropertyExpression) {
+                phase = CompilePhase.valueOf(member.propertyAsString)
+            }
+            annotationNode.setMember('phase', 
propX(classX(ClassHelper.make(CompilePhase)), phase.toString()))
+        }
+        member = annotationNode.getMember('value')
+        if (member && !(member instanceof ClosureExpression)) {
+            throw new SyntaxException("ASTTest value must be a closure", 
member.getLineNumber(), member.getColumnNumber())
+        }
+        if (!member && !annotationNode.getNodeMetaData(ASTTestTransformation)) 
{
+            throw new SyntaxException("Missing test expression", 
annotationNode.getLineNumber(), annotationNode.getColumnNumber())
+        }
+        // convert value into node metadata so that the expression doesn't mix 
up with other AST xforms like type checking
+        annotationNode.putNodeMetaData(ASTTestTransformation, member)
+        annotationNode.getMembers().remove('value')
+
+        def pcallback = compilationUnit.progressCallback
+        def callback = new CompilationUnit.ProgressCallback() {
+            Binding binding = new Binding([:].withDefault {null})
+
+            @Override
+            void call(final ProcessingUnit context, final int phaseRef) {
+                if (phase==null ||  phaseRef == phase.phaseNumber) {
+                    ClosureExpression testClosure = 
nodes[0].getNodeMetaData(ASTTestTransformation)
+                    StringBuilder sb = new StringBuilder()
+                    for (int i = testClosure.lineNumber; i <= 
testClosure.lastLineNumber; i++) {
+                        sb.append(source.source.getLine(i, new 
Janitor())).append('\n')
+                    }
+                    def testSource = sb.substring(testClosure.columnNumber + 
1, sb.length())
+                    testSource = testSource.substring(0, 
testSource.lastIndexOf('}'))
+                    CompilerConfiguration config = new CompilerConfiguration()
+                    def customizer = new ImportCustomizer()
+                    config.addCompilationCustomizers(customizer)
+                    binding['sourceUnit'] = source
+                    binding['node'] = nodes[1]
+                    binding['lookup'] = new MethodClosure(LabelFinder, 
"lookup").curry(nodes[1])
+                    binding['compilationUnit'] = compilationUnit
+                    binding['compilePhase'] = 
CompilePhase.fromPhaseNumber(phaseRef)
+
+                    GroovyShell shell = new GroovyShell(binding, config)
+
+                    source.AST.imports.each {
+                        customizer.addImport(it.alias, it.type.name)
+                    }
+                    source.AST.starImports.each {
+                        customizer.addStarImports(it.packageName)
+                    }
+                    source.AST.staticImports.each {
+                        customizer.addStaticImport(it.value.alias, 
it.value.type.name, it.value.fieldName)
+                    }
+                    source.AST.staticStarImports.each {
+                        customizer.addStaticStars(it.value.className)
+                    }
+                    shell.evaluate(testSource)
+                }
+            }
+        }
+        
+        if (pcallback!=null) {
+            if (pcallback instanceof ProgressCallbackChain) {
+                pcallback.addCallback(callback)                
+            } else {
+                pcallback = new ProgressCallbackChain(pcallback, callback)
+            }
+            callback = pcallback
+        }
+        
+        compilationUnit.setProgressCallback(callback)
+
+    }
+
+    void setCompilationUnit(final CompilationUnit unit) {
+        this.compilationUnit = unit
+    }
+
+    private static class AssertionSourceDelegatingSourceUnit extends 
SourceUnit {
+        private final ReaderSource delegate
+
+        AssertionSourceDelegatingSourceUnit(final String name, final 
ReaderSource source, final CompilerConfiguration flags, final GroovyClassLoader 
loader, final ErrorCollector er) {
+            super(name, '', flags, loader, er)
+            delegate = source
+        }
+
+        @Override
+        String getSample(final int line, final int column, final Janitor 
janitor) {
+            String sample = null;
+            String text = delegate.getLine(line, janitor);
+
+            if (text != null) {
+                if (column > 0) {
+                    String marker = Utilities.repeatString(" ", column - 1) + 
"^";
+
+                    if (column > 40) {
+                        int start = column - 30 - 1;
+                        int end = (column + 10 > text.length() ? text.length() 
: column + 10 - 1);
+                        sample = "   " + text.substring(start, end) + 
Utilities.eol() + "   " +
+                                marker.substring(start, marker.length());
+                    } else {
+                        sample = "   " + text + Utilities.eol() + "   " + 
marker;
+                    }
+                } else {
+                    sample = text;
+                }
+            }
+
+            return sample;
+
+        }
+
+    }
+    
+    private static class ProgressCallbackChain extends 
CompilationUnit.ProgressCallback {
+
+        private final List<CompilationUnit.ProgressCallback> chain = new 
LinkedList<CompilationUnit.ProgressCallback>()
+
+        ProgressCallbackChain(CompilationUnit.ProgressCallback... callbacks) {
+            if (callbacks!=null) {
+                callbacks.each { addCallback(it) }
+            }
+        }
+
+        public void addCallback(CompilationUnit.ProgressCallback callback) {
+            chain << callback
+        }
+        
+        @Override
+        void call(final ProcessingUnit context, final int phase) {
+            chain*.call(context, phase)
+        }
+    }
+
+    public static class LabelFinder extends ClassCodeVisitorSupport {
+
+        public static List<Statement> lookup(MethodNode node, String label) {
+            LabelFinder finder = new LabelFinder(label, null)
+            node.code.visit(finder)
+
+            finder.targets
+        }
+
+        public static List<Statement> lookup(ClassNode node, String label) {
+            LabelFinder finder = new LabelFinder(label, null)
+            node.methods*.code*.visit(finder)
+            node.declaredConstructors*.code*.visit(finder)
+
+            finder.targets
+        }
+
+        private final String label
+        private final SourceUnit unit
+
+        private final List<Statement> targets = new LinkedList<Statement>();
+
+        LabelFinder(final String label, final SourceUnit unit) {
+            this.label = label
+            this.unit = unit;
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            unit
+        }
+
+        @Override
+        protected void visitStatement(final Statement statement) {
+            super.visitStatement(statement)
+            if (statement.statementLabel==label) targets << statement
+        }
+
+        List<Statement> getTargets() {
+            return Collections.unmodifiableList(targets)
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/ASTTransformationCustomizer.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/ASTTransformationCustomizer.groovy 
b/src/main/groovy/ASTTransformationCustomizer.groovy
new file mode 100644
index 0000000..7e84968
--- /dev/null
+++ b/src/main/groovy/ASTTransformationCustomizer.groovy
@@ -0,0 +1,301 @@
+/*
+ *  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.control.customizers
+
+import groovy.transform.CompilationUnitAware
+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.expr.ClassExpression
+import org.codehaus.groovy.ast.expr.ClosureExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.ListExpression
+import org.codehaus.groovy.classgen.GeneratorContext
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.transform.GroovyASTTransformation
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+
+import java.lang.annotation.Annotation
+
+/**
+ * This customizer allows applying an AST transformation to a source unit with
+ * several strategies.
+ *
+ * Creating a customizer with the {@link 
ASTTransformationCustomizer#ASTTransformationCustomizer(Class)
+ * class constructor} will trigger an AST transformation for
+ * each class node of a source unit. However, you cannot pass parameters to 
the annotation so the default values
+ * will be used. Writing :
+ * <pre>
+ *     def configuration = new CompilerConfiguration()
+ *     configuration.addCompilationCustomizers(new 
ASTTransformationCustomizer(Log))
+ *     def shell = new GroovyShell(configuration)
+ *     shell.evaluate("""
+ *        class MyClass {
+ *
+ *        }""")
+ * </pre>
+ *
+ * is equivalent to :
+ *
+ * <pre>
+ *     def shell = new GroovyShell()
+ *     shell.evaluate("""
+ *        &#64;Log
+ *        class MyClass {
+ *
+ *        }""")
+ * </pre>
+ *
+ * The class passed as a constructor parameter must be an AST transformation 
annotation.
+ *
+ * Alternatively, you can apply a global AST transformation by calling the
+ * {@link 
ASTTransformationCustomizer#ASTTransformationCustomizer(ASTTransformation) AST 
transformation
+ * constructor}. In that case, the transformation is applied once for the 
whole source unit.
+ *
+ * Unlike a global AST transformation declared in the 
META-INF/services/org.codehaus.groovy.transform.ASTTransformation
+ * file, which are applied if the file is in the classpath, using this 
customizer you'll have the choice to apply
+ * your transformation selectively. It can also be useful to debug global AST 
transformations without having to
+ * package your annotation in a jar file.
+ *
+ * @author Cedric Champeau
+ *
+ * @since 1.8.0
+ * 
+ */
+class ASTTransformationCustomizer extends CompilationCustomizer implements 
CompilationUnitAware {
+    private final AnnotationNode annotationNode;
+    final ASTTransformation transformation
+
+    protected CompilationUnit compilationUnit;
+    private boolean applied = false; // used for global AST transformations
+
+    /**
+     * Creates an AST transformation customizer using the specified 
annotation. The transformation classloader can
+     * be used if the transformation class cannot be loaded from the same 
class loader as the annotation class.
+     * It's assumed that the annotation is not annotated with {@code 
GroovyASTTransformationClass} and so the
+     * second argument supplies the link to the ASTTransformation class that 
should be used.
+     * @param transformationAnnotation
+     * @param astTransformationClassName
+     * @param transformationClassLoader
+     */
+    ASTTransformationCustomizer(final Class<? extends Annotation> 
transformationAnnotation, String astTransformationClassName, ClassLoader 
transformationClassLoader) {
+        super(findPhase(transformationAnnotation, astTransformationClassName, 
transformationClassLoader))
+        final Class<ASTTransformation> clazz = 
findASTTranformationClass(transformationAnnotation, astTransformationClassName, 
transformationClassLoader)
+        this.transformation = clazz.newInstance()
+        this.annotationNode = new 
AnnotationNode(ClassHelper.make(transformationAnnotation))
+    }
+
+    /**
+     * Creates an AST transformation customizer using the specified 
annotation. It's assumed that the annotation
+     * is not annotated with {@code GroovyASTTransformationClass} and so the 
second argument supplies the link to
+     * the ASTTransformation class that should be used.
+     * @param transformationAnnotation
+     * @param astTransformationClassName
+     */
+    ASTTransformationCustomizer(final Class<? extends Annotation> 
transformationAnnotation, String astTransformationClassName) {
+        this(transformationAnnotation, astTransformationClassName, 
transformationAnnotation.classLoader)
+    }
+
+    /**
+     * Creates an AST transformation customizer using the specified 
annotation. The transformation classloader can
+     * be used if the transformation class cannot be loaded from the same 
class loader as the annotation class.
+     * Additionally, you can pass a map of parameters that will be used to 
parameterize the annotation.
+     * It's assumed that the annotation is not annotated with {@code 
GroovyASTTransformationClass} and so the
+     * second argument supplies the link to the ASTTransformation class that 
should be used.
+     * @param transformationAnnotation
+     * @param astTransformationClassName
+     * @param transformationClassLoader
+     */
+    ASTTransformationCustomizer(final Map annotationParams, final Class<? 
extends Annotation> transformationAnnotation, String 
astTransformationClassName, ClassLoader transformationClassLoader) {
+        super(findPhase(transformationAnnotation, astTransformationClassName, 
transformationClassLoader))
+        final Class<ASTTransformation> clazz = 
findASTTranformationClass(transformationAnnotation, astTransformationClassName, 
transformationClassLoader)
+        this.transformation = clazz.newInstance()
+        this.annotationNode = new 
AnnotationNode(ClassHelper.make(transformationAnnotation))
+        setAnnotationParameters(annotationParams)
+    }
+
+    ASTTransformationCustomizer(final Map annotationParams, final Class<? 
extends Annotation> transformationAnnotation, String 
astTransformationClassName) {
+        this(annotationParams, transformationAnnotation, 
transformationAnnotation.classLoader)
+    }
+
+    /**
+     * Creates an AST transformation customizer using the specified 
annotation. The transformation classloader can
+     * be used if the transformation class cannot be loaded from the same 
class loader as the annotation class.
+     * @param transformationAnnotation
+     * @param transformationClassLoader
+     */
+    ASTTransformationCustomizer(final Class<? extends Annotation> 
transformationAnnotation, ClassLoader transformationClassLoader) {
+        super(findPhase(transformationAnnotation, transformationClassLoader))
+        final Class<ASTTransformation> clazz = 
findASTTranformationClass(transformationAnnotation, transformationClassLoader)
+        this.transformation = clazz.newInstance()
+        this.annotationNode = new 
AnnotationNode(ClassHelper.make(transformationAnnotation))
+    }
+
+    /**
+     * Creates an AST transformation customizer using the specified annotation.
+     * @param transformationAnnotation
+     */
+    ASTTransformationCustomizer(final Class<? extends Annotation> 
transformationAnnotation) {
+        this(transformationAnnotation, transformationAnnotation.classLoader)
+    }
+
+    /**
+     * Creates an AST transformation customizer using the specified 
transformation.
+     */
+    ASTTransformationCustomizer(final ASTTransformation transformation) {
+        super(findPhase(transformation))
+        this.transformation = transformation
+        this.annotationNode = null
+    }
+
+    /**
+     * Creates an AST transformation customizer using the specified 
annotation. The transformation classloader can
+     * be used if the transformation class cannot be loaded from the same 
class loader as the annotation class.
+     * Additionally, you can pass a map of parameters that will be used to 
parameterize the annotation.
+     * @param transformationAnnotation
+     * @param transformationClassLoader
+     */
+    ASTTransformationCustomizer(final Map annotationParams, final Class<? 
extends Annotation> transformationAnnotation, ClassLoader 
transformationClassLoader) {
+        super(findPhase(transformationAnnotation, transformationClassLoader))
+        final Class<ASTTransformation> clazz = 
findASTTranformationClass(transformationAnnotation, transformationClassLoader)
+        this.transformation = clazz.newInstance()
+        this.annotationNode = new 
AnnotationNode(ClassHelper.make(transformationAnnotation))
+        setAnnotationParameters(annotationParams)
+    }
+
+    ASTTransformationCustomizer(final Map annotationParams, final Class<? 
extends Annotation> transformationAnnotation) {
+        this(annotationParams, transformationAnnotation, 
transformationAnnotation.classLoader)
+    }
+
+    ASTTransformationCustomizer(final Map annotationParams, final 
ASTTransformation transformation) {
+        this(transformation)
+        setAnnotationParameters(annotationParams)
+    }
+
+    void setCompilationUnit(CompilationUnit unit) {
+        compilationUnit = unit
+    }
+
+    private static Class<ASTTransformation> findASTTranformationClass(Class<? 
extends Annotation> anAnnotationClass, ClassLoader transformationClassLoader) {
+        final GroovyASTTransformationClass annotation = 
anAnnotationClass.getAnnotation(GroovyASTTransformationClass)
+        if (annotation==null) throw new IllegalArgumentException("Provided 
class doesn't look like an AST @interface")
+
+        Class[] classes = annotation.classes()
+        String[] classesAsStrings = annotation.value()
+        if (classes.length+classesAsStrings.length>1) {
+            throw new IllegalArgumentException("AST transformation customizer 
doesn't support AST transforms with multiple classes")
+        }
+        return classes?classes[0]:Class.forName(classesAsStrings[0], true, 
transformationClassLoader?:anAnnotationClass.classLoader)
+    }
+
+    private static Class<ASTTransformation> findASTTranformationClass(Class<? 
extends Annotation> anAnnotationClass, String astTransformationClassName, 
ClassLoader transformationClassLoader) {
+        return Class.forName(astTransformationClassName, true, 
transformationClassLoader?:anAnnotationClass.classLoader) as 
Class<ASTTransformation>
+    }
+
+    private static CompilePhase findPhase(ASTTransformation transformation) {
+        if (transformation==null) throw new IllegalArgumentException("Provided 
transformation must not be null")
+        final Class<?> clazz = transformation.class
+        final GroovyASTTransformation annotation = 
clazz.getAnnotation(GroovyASTTransformation)
+        if (annotation==null) throw new IllegalArgumentException("Provided ast 
transformation is not annotated with "+GroovyASTTransformation.name)
+
+        annotation.phase()
+    }
+
+    private static CompilePhase findPhase(Class<? extends Annotation> 
annotationClass, ClassLoader transformationClassLoader) {
+        Class<ASTTransformation> clazz = 
findASTTranformationClass(annotationClass, transformationClassLoader);
+
+        findPhase(clazz.newInstance())
+    }
+
+    private static CompilePhase findPhase(Class<? extends Annotation> 
annotationClass, String astTransformationClassName, ClassLoader 
transformationClassLoader) {
+        Class<ASTTransformation> clazz = 
findASTTranformationClass(annotationClass, astTransformationClassName, 
transformationClassLoader);
+
+        findPhase(clazz.newInstance())
+    }
+
+    /**
+     * Specify annotation parameters. For example, if the annotation is :
+     * <pre>@Log(value='logger')</pre>
+     * You could create an AST transformation customizer and specify the 
"value" parameter thanks to this method:
+     * <pre>annotationParameters = [value: 'logger']
+     *
+     * Note that you cannot specify annotation closure values directly. If the 
annotation you want to add takes
+     * a closure as an argument, you will have to set a {@link 
ClosureExpression} instead. This can be done by either
+     * creating a custom {@link ClosureExpression} from code, or using the 
{@link org.codehaus.groovy.ast.builder.AstBuilder}.
+     *
+     * Here is an example :
+     * <pre>
+     *        // add @Contract({distance >= 0 })
+     *        customizer = new ASTTransformationCustomizer(Contract)
+     *        final expression = new 
AstBuilder().buildFromCode(CompilePhase.CONVERSION) {->
+     *            distance >= 0
+     *        }.expression[0]
+     *        customizer.annotationParameters = [value: expression]</pre>
+     *
+     * @param params the annotation parameters
+     *
+     * @since 1.8.1
+     */
+    public void setAnnotationParameters(Map<String,Object> params) {
+        if (params==null || annotationNode==null) return;
+        params.each { key, value ->
+            if (!annotationNode.classNode.getMethod(key)) {
+                throw new 
IllegalArgumentException("${annotationNode.classNode.name} does not accept any 
[$key] parameter")
+            }
+            if (value instanceof Closure) {
+                throw new IllegalArgumentException("Direct usage of closure is 
not supported by the AST " +
+                "compilation customizer. Please use ClosureExpression 
instead.")
+            } else if (value instanceof Expression) {
+                // avoid NPEs due to missing source code
+                value.setLineNumber(0)
+                value.setLastLineNumber(0)
+                annotationNode.addMember(key, value)
+            } else if (value instanceof Class) {
+                annotationNode.addMember(key, new 
ClassExpression(ClassHelper.make(value)))
+            } else if (value instanceof List) {
+                annotationNode.addMember(key, new ListExpression(value.collect 
{
+                    it instanceof Class ? new 
ClassExpression(ClassHelper.make(it)) : new ConstantExpression(it)
+                }))
+            } else {
+                annotationNode.addMember(key, new ConstantExpression(value))
+            }
+        }
+    }
+
+    @Override
+    void call(SourceUnit source, GeneratorContext context, ClassNode 
classNode) {
+        if (transformation instanceof CompilationUnitAware) {
+            transformation.compilationUnit = compilationUnit
+        }
+        if (annotationNode!=null) {
+            // this is a local ast transformation which is applied on every 
class node
+            annotationNode.sourcePosition = classNode
+            transformation.visit([annotationNode, classNode] as ASTNode[], 
source)
+        } else {
+            // this is a global AST transformation
+            if (!applied) transformation.visit(null, source)
+        }
+        applied = true
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/ASTTransformationCustomizerFactory.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/ASTTransformationCustomizerFactory.groovy 
b/src/main/groovy/ASTTransformationCustomizerFactory.groovy
new file mode 100644
index 0000000..4e4f5be
--- /dev/null
+++ b/src/main/groovy/ASTTransformationCustomizerFactory.groovy
@@ -0,0 +1,60 @@
+/*
+ *  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.control.customizers.builder
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
+
+/**
+ * This factory generates an {@link ASTTransformationCustomizer ast 
transformation customizer}.
+ * <p>
+ * Simple syntax:
+ * <pre>builder.ast(ToString)</pre>
+ * With AST transformation options:
+ * <pre>builder.ast(includeNames:true, ToString)</pre>
+ *
+ * @author Cedric Champeau
+ * @since 2.1.0
+ */
+class ASTTransformationCustomizerFactory extends AbstractFactory {
+
+    @Override
+    @CompileStatic
+    public boolean isLeaf() {
+        true
+    }
+
+    @Override
+    @CompileStatic
+    public boolean onHandleNodeAttributes(final FactoryBuilderSupport builder, 
final Object node, final Map attributes) {
+        false
+    }
+
+    @Override
+    public Object newInstance(final FactoryBuilderSupport builder, final 
Object name, final Object value, final Map attributes) throws 
InstantiationException, IllegalAccessException {
+        ASTTransformationCustomizer customizer
+        if (attributes) {
+            customizer = new ASTTransformationCustomizer(attributes, value)
+        } else {
+            customizer = new ASTTransformationCustomizer(value)
+        }
+        customizer
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/AstBuilder.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/AstBuilder.groovy 
b/src/main/groovy/AstBuilder.groovy
new file mode 100644
index 0000000..06d027b
--- /dev/null
+++ b/src/main/groovy/AstBuilder.groovy
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.ast.builder
+
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.stmt.BlockStatement
+import org.codehaus.groovy.control.CompilePhase
+
+/**
+ * The AstBuilder provides several ways to build an abstract syntax tree (AST) 
of Groovy code.
+ *
+ * You can convert a String into AST using the buildFromString method.
+ * You can convert code into AST using the buildFromCode method.
+ * You can use the AST DSL with the buildFromSpec method. 
+ * 
+ * For more information, see the resources on the Groovy wiki pages. 
+ *
+ * @author Hamlet D'Arcy
+ */
+
+public class AstBuilder {
+
+    /**
+     * Builds AST based on the code within the  {@link Closure}  parameter.
+     *
+     * This method <strong>must</strong> be invoked at compile time and never 
at runtime, because
+     * an ASTTransformation must be run to support it. If you receive an 
IllegalStateException then
+     * you most likely need to add stronger typing. For instance, this will 
not work:
+     * <code>
+     *      def builder = new AstBuilder()
+     *      builder.buildFromCode {
+     *             // some code
+     *      }
+     * </code>
+     * While this code will:
+     * <code>
+     *      new AstBuilder().buildFromCode {
+     *             // some code
+     *      }
+     * </code>
+     *
+     * The compiler rewrites buildFromCode invocations into  {@link 
AstBuilder#buildFromString(CompilePhase, boolean, String)}
+     * invocations. An exception raised during AST generation will show a 
stack trace from  {@link AstBuilder#buildFromString(CompilePhase, boolean, 
String)}
+     * and not from  {@link AstBuilder#buildFromCode(CompilePhase, boolean, 
Closure)} .
+     *
+     * The compiler saves the source code of the closure as a String within 
the Java class file. The String source
+     * of the closure will be visible and un-obfuscated within the class file. 
If your Closure parameter contains
+     * sensitive data such as a hard-coded password then that data is free to 
be seen by anyone with the class file.
+     * Do not store sensitive data within the closure parameter.
+     *
+     * @param phase
+     *      the  {@link CompilePhase}  the AST will be targeted towards. 
Default is  {@link CompilePhase#CLASS_GENERATION}
+     * @param statementsOnly
+     *      when true, only the script statements are returned. WHen false, 
you will
+     *      receive back a Script class node also. Default is true.
+     * @param block
+     *      the code that will be converted
+     * @returns a List of  {@link ASTNode} .
+     * @throws IllegalStateException
+     *      this method may not be invoked at runtime. It works via a 
compile-time transformation
+     *      of the closure source code into a String, which is sent to the  
{@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
+     *      method. The buildFromCode() method must be invoked against a 
strongly typed AstBuilder.
+     */
+    List<ASTNode> buildFromCode(CompilePhase phase = 
CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, Closure block) {
+        throw new IllegalStateException("""AstBuilder.build(CompilePhase, 
boolean, Closure):List<ASTNode> should never be called at runtime.
+Are you sure you are using it correctly?
+""")
+    }
+
+
+    /**
+     * Builds AST based on the code within the String parameter.
+     *
+     * @param phase
+     *      the  {@link CompilePhase}  the AST will be targeted towards. 
Default is  {@link CompilePhase#CLASS_GENERATION}
+     * @param statementsOnly
+     *      when true, only the script statements are returned. WHen false, 
you will
+     *      receive back a Script class node also. Default is true.
+     * @param source
+     *      The source code String that will be compiled.
+     * @returns a List of  {@link ASTNode} .
+     * @throws IllegalArgumentException
+     *      if source is null or empty
+     */
+    List<ASTNode> buildFromString(CompilePhase phase = 
CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, String source) {
+        if (!source || "" == source.trim()) throw new 
IllegalArgumentException("A source must be specified")
+        return new AstStringCompiler().compile(source, phase, statementsOnly);
+    }
+
+    /**
+     * Builds AST based on the code within the String parameter. The parameter 
is assumed to be 
+     * a code block which is not legal Groovy code. A goto label is affixed to 
the block, compiled, 
+     * and the resulting BlockStatement wrapper is removed before returning a 
result. 
+     * @param phase
+     *      the  {@link CompilePhase}  the AST will be targeted towards. 
Default is  {@link CompilePhase#CLASS_GENERATION}
+     * @param statementsOnly
+     *      when true, only the script statements are returned. WHen false, 
you will
+     *      receive back a Script class node also. Default is true.
+     * @param source
+     *      The source code String that will be compiled. The string must be a 
block wrapped in curly braces. 
+     * @returns a List of  {@link ASTNode} .
+     * @throws IllegalArgumentException
+     *      if source is null or empty
+     */
+    private List<ASTNode> buildFromBlock(CompilePhase phase = 
CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, String source) {
+        if (!source || "" == source.trim()) throw new 
IllegalArgumentException("A source must be specified")
+        def labelledSource = 
"__synthesized__label__${System.currentTimeMillis()}__:" + source
+        List<ASTNode> result = new AstStringCompiler().compile(labelledSource, 
phase, statementsOnly)
+        // find the block statement from the result, and unwrap it from one 
level.
+        result.collect { node ->
+            if (node instanceof BlockStatement) {
+                ((BlockStatement)node).statements[0] //unwrap the artifact of 
pre-pending the goto label
+            } else {
+                node
+            }
+        }
+    }
+
+    /**
+     * Builds AST based on the DSL data within the Closure parameter.
+     * @param specification
+     *      the contents to create
+     */
+    List<ASTNode> buildFromSpec(@DelegatesTo(AstSpecificationCompiler) Closure 
specification) {
+        if (specification == null) throw new IllegalArgumentException('Null: 
specification')
+        def properties = new AstSpecificationCompiler(specification)
+        return properties.expression
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/AstHelper.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/AstHelper.groovy b/src/main/groovy/AstHelper.groovy
new file mode 100644
index 0000000..206b0df
--- /dev/null
+++ b/src/main/groovy/AstHelper.groovy
@@ -0,0 +1,76 @@
+/*
+ *  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.transform.tailrec
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.stmt.ContinueStatement
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.ast.stmt.ThrowStatement
+
+import java.lang.reflect.Modifier
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
+
+/**
+ * Helping to create a few standard AST constructs
+ *
+ * @author Johannes Link
+ */
+@CompileStatic
+class AstHelper {
+       static ExpressionStatement createVariableDefinition(String 
variableName, ClassNode variableType, Expression value, boolean 
variableShouldBeFinal = false ) {
+        def newVariable = varX(variableName, variableType)
+        if (variableShouldBeFinal)
+            newVariable.setModifiers(Modifier.FINAL)
+        (ExpressionStatement) declS(newVariable, value)
+       }
+
+       static ExpressionStatement createVariableAlias(String aliasName, 
ClassNode variableType, String variableName ) {
+               createVariableDefinition(aliasName, variableType, 
varX(variableName, variableType))
+       }
+
+    static VariableExpression createVariableReference(Map variableSpec) {
+        varX((String) variableSpec.name, (ClassNode) variableSpec.type)
+    }
+
+    /**
+     * This statement should make the code jump to surrounding while loop's 
start label
+     * Does not work from within Closures
+     */
+    static Statement recurStatement() {
+        //continue _RECUR_HERE_
+        new ContinueStatement(InWhileLoopWrapper.LOOP_LABEL)
+    }
+
+    /**
+     * This statement will throw exception which will be caught and redirected 
to jump to surrounding while loop's start label
+     * Also works from within Closures but is a tiny bit slower
+     */
+    static Statement recurByThrowStatement() {
+        // throw InWhileLoopWrapper.LOOP_EXCEPTION
+        new ThrowStatement(propX(classX(InWhileLoopWrapper), 'LOOP_EXCEPTION'))
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/AstSpecificationCompiler.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/AstSpecificationCompiler.groovy 
b/src/main/groovy/AstSpecificationCompiler.groovy
new file mode 100644
index 0000000..5e607b6
--- /dev/null
+++ b/src/main/groovy/AstSpecificationCompiler.groovy
@@ -0,0 +1,1080 @@
+/*
+ *  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.ast.builder
+
+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.DynamicVariable
+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.Parameter
+import org.codehaus.groovy.ast.PropertyNode
+import org.codehaus.groovy.ast.VariableScope
+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.Expression
+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.StaticMethodCallExpression
+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.runtime.MethodClosure
+import org.codehaus.groovy.syntax.Token
+import org.codehaus.groovy.syntax.Types
+
+/**
+ * Handles parsing the properties from the closure into values that can be 
referenced.
+ * 
+ * This object is very stateful and not threadsafe. It accumulates expressions 
in the 
+ * 'expression' field as they are found and executed within the DSL. 
+ * 
+ * Note: this class consists of many one-line method calls. A better 
implementation
+ * might be to take a declarative approach and replace the one-liners with map 
entries. 
+ * 
+ * @author Hamlet D'Arcy
+ */
+class AstSpecificationCompiler implements GroovyInterceptable {
+
+    private final List<ASTNode> expression = []
+
+    /**
+     * Creates the DSL compiler.
+     */
+    AstSpecificationCompiler(@DelegatesTo(AstSpecificationCompiler) Closure 
spec) {
+        spec.delegate = this
+        spec()
+    }
+
+    /**
+     * Gets the current generated expression.
+     */
+    List<ASTNode> getExpression() {
+        return expression
+    }
+
+    /**
+    * This method takes a List of Classes (a "spec"), and makes sure that the 
expression field 
+    * contains those classes. It is a safety mechanism to enforce that the DSL 
is being called
+    * properly. 
+    * 
+    * @param methodName
+    *   the name of the method within the DSL that is being invoked. Used in 
creating error messages. 
+    * @param spec
+    *   the list of Class objects that the method expects to have in the 
expression field when invoked.
+    * @return 
+    *   the portions of the expression field that adhere to the spec. 
+    */ 
+    private List<ASTNode> enforceConstraints(String methodName, List<Class> 
spec) {
+
+        // enforce that the correct # arguments was passed
+        if (spec.size() != expression.size()) {
+            throw new IllegalArgumentException("$methodName could not be 
invoked. Expected to receive parameters $spec but found ${expression?.collect { 
it.class }}")
+        }
+
+        // enforce types and collect result
+        (0..(spec.size() - 1)).collect { int it ->
+            def actualClass = expression[it].class
+            def expectedClass = spec[it]
+            if (!expectedClass.isAssignableFrom(actualClass)) {
+                throw new IllegalArgumentException("$methodName could not be 
invoked. Expected to receive parameters $spec but found ${expression?.collect { 
it.class }}")
+            }
+            expression[it]
+        }
+    }
+
+    /**
+    * This method helps you take Closure parameters to a method and bundle 
them into 
+    * constructor calls to a specific ASTNode subtype. 
+    * @param name 
+    *       name of object being constructed, used to create helpful error 
message. 
+    * @param argBlock
+    *       the actual parameters being specified for the node
+    * @param constructorStatement
+    *       the type specific construction code that will be run
+    */ 
+    private void captureAndCreateNode(String name, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock, Closure 
constructorStatement) {
+        if (!argBlock) throw new IllegalArgumentException("nodes of type $name 
require arguments to be specified")
+
+        def oldProps = new ArrayList(expression)
+        expression.clear()
+        new AstSpecificationCompiler(argBlock)
+        def result = constructorStatement(expression) // invoke custom 
constructor for node
+        expression.clear()
+        expression.addAll(oldProps)
+        expression.add(result)
+    }
+
+    /**
+    * Helper method to convert a DSL invocation into an ASTNode instance. 
+    * 
+    * @param target     
+    *       the class you are going to create
+    * @param typeAlias  
+    *       the DSL keyword that was used to invoke this type
+    * @param ctorArgs   
+    *       a specification of what arguments the constructor expects
+    * @param argBlock   
+    *       the single closure argument used during invocation
+    */ 
+    private void makeNode(Class target, String typeAlias, List<Class<? super 
ASTNode>> ctorArgs, @DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode(target.class.simpleName, argBlock) {
+            target.newInstance(*enforceConstraints(typeAlias, ctorArgs))
+        }
+    }
+
+    /**
+    * Helper method to convert a DSL invocation with a list of parameters 
specified 
+    * in a Closure into an ASTNode instance. 
+    * 
+    * @param target     
+    *       the class you are going to create
+    * @param argBlock   
+    *       the single closure argument used during invocation
+    */ 
+    private void makeNodeFromList(Class target, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        //todo: add better error handling?
+        captureAndCreateNode(target.simpleName, argBlock) {
+            target.newInstance(new ArrayList(expression))
+        }
+    }
+
+    /**
+    * Helper method to convert a DSL invocation with a String parameter into a 
List of ASTNode instances. 
+    * 
+    * @param argBlock   
+    *       the single closure argument used during invocation
+    * @param input   
+    *       the single String argument used during invocation
+    */ 
+    private void makeListOfNodes(@DelegatesTo(AstSpecificationCompiler) 
Closure argBlock, String input) {
+        captureAndCreateNode(input, argBlock) {
+            new ArrayList(expression)
+        }
+    }
+
+    /**
+    * Helper method to convert a DSL invocation with a String parameter into 
an Array of ASTNode instances. 
+    * 
+    * @param argBlock   
+    *       the single closure argument used during invocation
+    * @param target   
+    *       the target type
+    */ 
+    private void makeArrayOfNodes(Object target, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode(target.class.simpleName, argBlock) {
+            expression.toArray(target)
+        }
+    }
+    
+    /**
+    * Helper method to convert a DSL invocation into an ASTNode instance when 
a Class parameter is specified. 
+    * 
+    * @param target     
+    *       the class you are going to create
+    * @param alias  
+    *       the DSL keyword that was used to invoke this type
+    * @param spec
+    *       the list of Classes that you expect to be present as parameters
+    * @param argBlock   
+    *       the single closure argument used during invocation
+    * @param type 
+    *       a type parameter
+    */ 
+    private void makeNodeWithClassParameter(Class target, String alias, 
List<Class> spec, @DelegatesTo(AstSpecificationCompiler) Closure argBlock, 
Class type) {
+        captureAndCreateNode(target.class.simpleName, argBlock) {
+            expression.add(0, ClassHelper.make(type))
+            target.newInstance(*enforceConstraints(alias, spec))
+        }
+    }
+
+    private void makeNodeWithStringParameter(Class target, String alias, 
List<Class> spec, @DelegatesTo(AstSpecificationCompiler) Closure argBlock, 
String text) {
+        captureAndCreateNode(target.class.simpleName, argBlock) {
+            expression.add(0, text)
+            target.newInstance(*enforceConstraints(alias, spec))
+        }
+    }
+
+    /**
+     * Creates a CastExpression.
+     */
+    void cast(Class type, @DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNodeWithClassParameter(CastExpression, 'cast', [ClassNode, 
Expression], argBlock, type)
+    }
+
+    /**
+     * Creates an ConstructorCallExpression.
+     */
+    void constructorCall(Class type, @DelegatesTo(AstSpecificationCompiler) 
Closure argBlock) {
+        makeNodeWithClassParameter(ConstructorCallExpression, 
'constructorCall', [ClassNode, Expression], argBlock, type)
+    }
+
+    /**
+     * Creates a MethodCallExpression.
+     */
+    void methodCall(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(MethodCallExpression, 'methodCall', [Expression, Expression, 
Expression], argBlock)
+    }
+
+    /**
+     * Creates an AnnotationConstantExpression.
+     */
+    void annotationConstant(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(AnnotationConstantExpression, 'annotationConstant', 
[AnnotationNode], argBlock)
+    }
+
+    /**
+     * Creates a PostfixExpression.
+     */
+    void postfix(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(PostfixExpression, 'postfix', [Expression, Token], argBlock)
+    }
+
+    /**
+     * Creates a FieldExpression.
+     */
+    void field(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(FieldExpression, 'field', [FieldNode], argBlock)
+    }
+
+    /**
+     * Creates a MapExpression.
+     */
+    void map(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNodeFromList(MapExpression, argBlock)
+    }
+
+    /**
+     * Creates a TupleExpression.
+     */
+    void tuple(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNodeFromList(TupleExpression, argBlock)
+    }
+
+    /**
+     * Creates a MapEntryExpression.
+     */
+    void mapEntry(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(MapEntryExpression, 'mapEntry', [Expression, Expression], 
argBlock)
+    }
+
+    /**
+     * Creates a gString.
+     */
+    void gString(String verbatimText, @DelegatesTo(AstSpecificationCompiler) 
Closure argBlock) {
+        makeNodeWithStringParameter(GStringExpression, 'gString', [String, 
List, List], argBlock, verbatimText)
+    }
+
+
+    /**
+     * Creates a methodPointer.
+     */
+
+    void methodPointer(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(MethodPointerExpression, 'methodPointer', [Expression, 
Expression], argBlock)
+    }
+
+    /**
+     * Creates a property.
+     */
+    void property(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(PropertyExpression, 'property', [Expression, Expression], 
argBlock)
+    }
+
+    /**
+     * Creates a RangeExpression.
+     */
+    void range(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(RangeExpression, 'range', [Expression, Expression, Boolean], 
argBlock)
+    }
+
+    /**
+     * Creates EmptyStatement.
+     */
+    void empty() {
+        expression << EmptyStatement.INSTANCE
+    }
+
+    /**
+     * Creates a label.
+     */
+    void label(String label) {
+        expression << label
+    }
+
+    /**
+     * Creates an ImportNode.
+     */
+    void importNode(Class target, String alias = null) {
+        expression << new ImportNode(ClassHelper.make(target), alias)
+    }
+
+    /**
+     * Creates a CatchStatement.
+     */
+    void catchStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(CatchStatement, 'catchStatement', [Parameter, Statement], 
argBlock)
+    }
+
+    /**
+     * Creates a ThrowStatement.
+     */
+    void throwStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(ThrowStatement, 'throwStatement', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a SynchronizedStatement.
+     */
+    void synchronizedStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(SynchronizedStatement, 'synchronizedStatement', [Expression, 
Statement], argBlock)
+    }
+
+    /**
+     * Creates a ReturnStatement.
+     */
+    void returnStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(ReturnStatement, 'returnStatement', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a TernaryExpression.
+     */
+
+    private void ternary(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(TernaryExpression, 'ternary', [BooleanExpression, Expression, 
Expression], argBlock)
+    }
+
+
+    /**
+     * Creates an ElvisOperatorExpression.
+     */
+    void elvisOperator(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(ElvisOperatorExpression, 'elvisOperator', [Expression, 
Expression], argBlock)
+    }
+
+    /**
+     * Creates a BreakStatement.
+     */
+    void breakStatement(String label = null) {
+        if (label) {
+            expression << new BreakStatement(label)
+        } else {
+            expression << new BreakStatement()
+        }
+    }
+
+    /**
+     * Creates a ContinueStatement.
+     */
+    void continueStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock = null) {
+        if (!argBlock) {
+            expression << new ContinueStatement()
+        } else {
+            makeNode(ContinueStatement, 'continueStatement', [String], 
argBlock)
+        }
+    }
+
+    /**
+     * Create a CaseStatement.
+     */
+    void caseStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(CaseStatement, 'caseStatement', [Expression, Statement], 
argBlock)
+    }
+
+    /**
+     * Creates a BlockStatement.
+     */
+    void defaultCase(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        block(argBlock) // same as arg block
+    }
+
+    /**
+     * Creates a PrefixExpression.
+     */
+    void prefix(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(PrefixExpression, 'prefix', [Token, Expression], argBlock)
+    }
+
+    /**
+     * Creates a NotExpression.
+     */
+    void not(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(NotExpression, 'not', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a DynamicVariable.
+     */
+    void dynamicVariable(String variable, boolean isStatic = false) {
+        expression << new DynamicVariable(variable, isStatic)
+    }
+
+    /**
+     * Creates a ClassNode[].
+     */
+    void exceptions(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeArrayOfNodes([] as ClassNode[], argBlock)
+    }
+
+    /**
+     * Designates a list of AnnotationNodes.
+     */
+    void annotations(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<AnnotationNode>")
+    }
+
+
+    /**
+     * Designates a list of MethodNodes.
+     */
+    void methods(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<MethodNode>")
+    }
+
+    /**
+     * Designates a list of ConstructorNodes.
+     */
+    void constructors(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) 
{
+        makeListOfNodes(argBlock, "List<ConstructorNode>")
+    }
+
+    /**
+     * Designates a list of {@code PropertyNode}s.
+     */
+    void properties(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<PropertyNode>")
+    }
+
+    /**
+     * Designates a list of {@code FieldNode}s.
+     */
+    void fields(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<FieldNode>")
+    }
+
+    /**
+     * Designates a list of ConstantExpressions.
+     */
+
+    void strings(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<ConstantExpression>")
+    }
+
+    /**
+     * Designates a list of Expressions.
+     */
+
+    void values(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<Expression>")
+    }
+
+    /**
+     * Creates a boolean value.
+     */
+    void inclusive(boolean value) {
+        expression << value
+    }
+
+    /**
+     * Creates a ConstantExpression.
+     */
+    void constant(Object value) {
+        expression << new ConstantExpression(value)
+    }
+
+    /**
+     * Creates an IfStatement.
+     */
+    void ifStatement(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(IfStatement, 'ifStatement', [BooleanExpression, Statement, 
Statement], argBlock)
+    }
+
+    /**
+     * Creates a SpreadExpression.
+     */
+    void spread(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(SpreadExpression, 'spread', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a SpreadMapExpression.
+     */
+    void spreadMap(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(SpreadMapExpression, 'spreadMap', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a WhileStatement.
+     */
+    void whileStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(WhileStatement, 'whileStatement', [BooleanExpression, 
Statement], argBlock)
+    }
+
+    /**
+     * Create a ForStatement.
+     */
+    void forStatement(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) 
{
+        makeNode(ForStatement, 'forStatement', [Parameter, Expression, 
Statement], argBlock)
+    }
+
+    /**
+     * Creates a ClosureListExpression.
+     */
+    void closureList(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNodeFromList(ClosureListExpression, argBlock)
+    }
+
+    /**
+     * Creates a DeclarationExpression.
+     */
+    void declaration(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(DeclarationExpression, 'declaration', [Expression, Token, 
Expression], argBlock)
+    }
+
+    /**
+     * Creates a ListExpression.
+     */
+    void list(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNodeFromList(ListExpression, argBlock)
+    }
+
+    /**
+     * Creates a BitwiseNegationExpression.
+     */
+    void bitwiseNegation(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(BitwiseNegationExpression, 'bitwiseNegation', [Expression], 
argBlock)
+    }
+
+    /**
+     * Creates a ClosureExpression.
+     */
+    void closure(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(ClosureExpression, 'closure', [Parameter[], Statement], 
argBlock)
+    }
+
+    /**
+     * Creates a BooleanExpression.
+     */
+    void booleanExpression(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNode(BooleanExpression, 'booleanExpression', [Expression], 
argBlock)
+    }
+
+    /**
+     * Creates a BinaryExpression.
+     */
+    void binary(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(BinaryExpression, 'binary', [Expression, Token, Expression], 
argBlock)
+    }
+
+    /**
+     * Creates a UnaryPlusExpression.
+     */
+    void unaryPlus(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(UnaryPlusExpression, 'unaryPlus', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a ClassExpression.
+     */
+    void classExpression(Class type) {
+        expression << new ClassExpression(ClassHelper.make(type))
+    }
+
+    /**
+     * Creates a UnaryMinusExpression
+     */
+    void unaryMinus(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(UnaryMinusExpression, 'unaryMinus', [Expression], argBlock)
+    }
+
+    /**
+     * Creates an AttributeExpression.
+     */
+    void attribute(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(AttributeExpression, 'attribute', [Expression, Expression], 
argBlock)
+    }
+
+    /**
+     * Creates an ExpressionStatement.
+     */
+    void expression(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeNode(ExpressionStatement, 'expression', [Expression], argBlock)
+    }
+
+    /**
+     * Creates a NamedArgumentListExpression.
+     */
+    void namedArgumentList(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeNodeFromList(NamedArgumentListExpression, argBlock)
+    }
+
+    /**
+     * Creates a ClassNode[].
+     */
+    void interfaces(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<ClassNode>")
+    }
+
+    /**
+     * Creates a MixinNode[].
+     */
+    void mixins(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, "List<MixinNode>")
+    }
+
+    /**
+     * Creates a GenericsTypes[].
+     */
+    void genericsTypes(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        makeListOfNodes(argBlock, "List<GenericsTypes>")
+    }
+
+    /**
+     * Creates a ClassNode.
+     */
+    void classNode(Class target) {
+        expression << ClassHelper.make(target, false)
+    }
+
+    /**
+     * Creates a Parameter[].
+     */
+    void parameters(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeArrayOfNodes([] as Parameter[], argBlock)
+    }
+
+    /**
+     * Creates a BlockStatement.
+     */
+    void block(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("BlockStatement", argBlock) {
+            return new BlockStatement(new ArrayList(expression), new 
VariableScope())
+        }
+    }
+
+    /**
+     * Creates a Parameter.
+     */
+    void parameter(Map<String, Class> args, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock = null) {
+        if (!args) throw new IllegalArgumentException()
+        if (args.size() > 1) throw new IllegalArgumentException()
+
+        //todo: add better error handling?
+        if (argBlock) {
+            args.each {name, type ->
+                captureAndCreateNode("Parameter", argBlock) {
+                    new Parameter(ClassHelper.make(type), name, expression[0])
+                }
+            }
+        } else {
+            args.each {name, type ->
+                expression << (new Parameter(ClassHelper.make(type), name))
+            }
+        }
+    }
+
+    /**
+     * Creates an ArrayExpression.
+     */
+    void array(Class type, @DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        captureAndCreateNode("ArrayExpression", argBlock) {
+            new ArrayExpression(ClassHelper.make(type), new 
ArrayList(expression))
+        }
+    }
+
+    /**
+     * Creates a GenericsType.
+     */
+    void genericsType(Class type, @DelegatesTo(AstSpecificationCompiler) 
Closure argBlock = null) {
+        if (argBlock) {
+            captureAndCreateNode("GenericsType", argBlock) {
+                new GenericsType(ClassHelper.make(type), expression[0] as 
ClassNode[], expression[1])
+            }
+        } else {
+            expression << new GenericsType(ClassHelper.make(type))
+        }
+    }
+
+    /**
+     * Creates a list of upperBound ClassNodes.
+     */
+    void upperBound(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        makeListOfNodes(argBlock, 'List<ClassNode>')
+    }
+
+    /**
+     * Create lowerBound ClassNode.
+     */
+    void lowerBound(Class target) {
+        expression << ClassHelper.make(target)
+    }
+
+    /**
+     * Creates a 2 element list of name and Annotation. Used with Annotation 
Members.
+     */
+    void member(String name, @DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        captureAndCreateNode("Annotation Member", argBlock) {
+            [name, expression[0]]
+        }
+    }
+
+    /**
+     * Creates an ArgumentListExpression.
+     */
+    void argumentList(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) 
{
+        if (!argBlock) {
+            expression << new ArgumentListExpression()
+        } else {
+            makeNodeFromList(ArgumentListExpression, argBlock)
+        }
+    }
+
+    /**
+     * Creates an AnnotationNode.
+     */
+    void annotation(Class target, @DelegatesTo(AstSpecificationCompiler) 
Closure argBlock = null) {
+        if (argBlock) {
+            //todo: add better error handling
+            captureAndCreateNode("ArgumentListExpression", argBlock) {
+                def node = new AnnotationNode(ClassHelper.make(target))
+                expression?.each {
+                    node.addMember(it[0], it[1])
+                }
+                node
+            }
+        } else {
+            expression << new AnnotationNode(ClassHelper.make(target))
+        }
+    }
+
+    /**
+     * Creates a MixinNode.
+     */
+    void mixin(String name, int modifiers, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("AttributeExpression", argBlock) {
+            if (expression.size() > 1) {
+                new MixinNode(name, modifiers, expression[0], new 
ArrayList(expression[1]) as ClassNode[])
+            } else {
+                new MixinNode(name, modifiers, expression[0])
+            }
+        }
+    }
+
+    /**
+     * Creates a ClassNode
+     */
+    void classNode(String name, int modifiers, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("ClassNode", argBlock) {
+            def result = new ClassNode(name, modifiers,
+                    expression[0],
+                    new ArrayList(expression[1]) as ClassNode[],
+                    new ArrayList(expression[2]) as MixinNode[]
+            )
+            while (expression.size() > 3) {
+                if (!List.isAssignableFrom(expression[3].getClass())) {
+                    throw new IllegalArgumentException("Expecting to find list 
of additional items instead found: " + expression[3].getClass())
+                }
+                if (expression[3].size() > 0) {
+                    def clazz = expression[3][0].getClass()
+                    switch(clazz) {
+                        case GenericsType:
+                            result.setGenericsTypes(new 
ArrayList(expression[3]) as GenericsType[])
+                            break
+                        case MethodNode:
+                            expression[3].each{ result.addMethod(it) }
+                            break
+                        case ConstructorNode:
+                            expression[3].each{ result.addConstructor(it) }
+                            break
+                        case PropertyNode:
+                            expression[3].each{
+                                it.field.owner = result
+                                result.addProperty(it)
+                            }
+                            break
+                        case FieldNode:
+                            expression[3].each{
+                                it.owner = result
+                                result.addField(it)
+                            }
+                            break
+                        case AnnotationNode:
+                            result.addAnnotations(new ArrayList(expression[3]))
+                            break
+                        default:
+                            throw new IllegalArgumentException("Unexpected 
item found in ClassNode spec. Expecting 
[Field|Method|Property|Constructor|Annotation|GenericsType] but found: 
$clazz.name")
+                    }
+                }
+                expression.remove(3)
+            }
+            result
+        }
+    }
+
+    /**
+     * Creates an AssertStatement.
+     */
+    void assertStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        captureAndCreateNode("AssertStatement", argBlock) {
+            if (expression.size() < 2) {
+                new AssertStatement(*enforceConstraints('assertStatement', 
[BooleanExpression]))
+            } else {
+                new AssertStatement(*enforceConstraints('assertStatement', 
[BooleanExpression, Expression]))
+            }
+        }
+    }
+
+    /**
+     * Creates a TryCatchStatement.
+     */
+    void tryCatch(@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("TryCatchStatement", argBlock) {
+            def result = new TryCatchStatement(expression[0], expression[1])
+            def catchStatements = expression.tail().tail()
+            catchStatements.each {statement -> result.addCatch(statement) }
+            return result
+        }
+    }
+
+    /**
+     * Creates a VariableExpression.
+     */
+    void variable(String variable) {
+        expression << new VariableExpression(variable)
+    }
+
+    /**
+     * Creates a MethodNode.
+     */
+    void method(String name, int modifiers, Class returnType, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("MethodNode", argBlock) {
+            //todo: enforce contract
+            def result = new MethodNode(name, modifiers, 
ClassHelper.make(returnType), expression[0], expression[1], expression[2])
+            if (expression[3]) {
+                result.addAnnotations(new ArrayList(expression[3]))
+            }
+            result
+        }
+    }
+
+    /**
+     * Creates a token.
+     */
+    void token(String value) {
+        if (value == null) throw new IllegalArgumentException("Null: value")
+
+        def tokenID = Types.lookupKeyword(value)
+        if (tokenID == Types.UNKNOWN) {
+            tokenID = Types.lookupSymbol(value)
+        }
+        if (tokenID == Types.UNKNOWN) throw new 
IllegalArgumentException("could not find token for $value")
+
+        expression << new Token(tokenID, value, -1, -1)
+    }
+
+    /**
+     * Creates a RangeExpression.
+     */
+    void range(Range range) {
+        if (range == null) throw new IllegalArgumentException('Null: range')
+        expression << new RangeExpression(new 
ConstantExpression(range.getFrom()), new ConstantExpression(range.getTo()), 
true) //default is inclusive
+    }
+
+    /**
+     * Creates a SwitchStatement.
+     */
+    void switchStatement(@DelegatesTo(AstSpecificationCompiler) Closure 
argBlock) {
+        captureAndCreateNode("SwitchStatement", argBlock) {
+            def switchExpression = expression.head()
+            def caseStatements = expression.tail().tail()
+            def defaultExpression = expression.tail().head()
+            new SwitchStatement(switchExpression, caseStatements, 
defaultExpression)
+        }
+    }
+
+    /**
+     * Creates a mapEntry.
+     */
+    void mapEntry(Map map) {
+        map.entrySet().each {
+            expression << new MapEntryExpression(
+                    new ConstantExpression(it.key),
+                    new ConstantExpression(it.value))
+        }
+    }
+
+    //
+    // todo: these methods can still be reduced smaller
+    //
+
+    /**
+     * Creates a FieldNode.
+     */
+    void fieldNode(String name, int modifiers, Class type, Class owner, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("FieldNode", argBlock) {
+            def annotations = null
+            if (expression.size() > 1) {
+                annotations = expression[1]
+                expression.remove(1)
+            }
+            expression.add(0, ClassHelper.make(owner))
+            expression.add(0, ClassHelper.make(type))
+            expression.add(0, modifiers)
+            expression.add(0, name)
+            def result = new FieldNode(*enforceConstraints('fieldNode', 
[String, Integer, ClassNode, ClassNode, Expression]))
+            if (annotations) {
+                result.addAnnotations(new ArrayList(annotations))
+            }
+            result
+        }
+    }
+
+    /**
+     * Creates an inner class.
+     */
+    void innerClass(String name, int modifiers, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("InnerClassNode", argBlock) {
+            //todo: enforce contract
+            new InnerClassNode(
+                    expression[0],
+                    name,
+                    modifiers,
+                    expression[1],
+                    new ArrayList(expression[2]) as ClassNode[],
+                    new ArrayList(expression[3]) as MixinNode[])
+        }
+    }
+
+    /**
+     * Creates a PropertyNode.
+     */
+    void propertyNode(String name, int modifiers, Class type, Class owner, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        //todo: improve error handling?
+        captureAndCreateNode("PropertyNode", argBlock) {
+            def annotations = null
+            // check if the last expression looks like annotations
+            if (List.isAssignableFrom(expression[-1].getClass())) {
+                annotations = expression[-1]
+                expression.remove(expression.size() - 1)
+            }
+            def result = new PropertyNode(name, modifiers, 
ClassHelper.make(type), ClassHelper.make(owner),
+                    expression[0],  // initial value (possibly null)
+                    expression[1],  // getter block (possibly null)
+                    expression[2])  // setter block (possibly null)
+            if (annotations) {
+                result.addAnnotations(new ArrayList(annotations))
+            }
+            result
+        }
+    }
+
+    /**
+     * Creates a StaticMethodCallExpression.
+     */
+    void staticMethodCall(Class target, String name, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("StaticMethodCallExpression", argBlock) {
+            expression.add(0, name)
+            expression.add(0, ClassHelper.make(target))
+            new 
StaticMethodCallExpression(*enforceConstraints('staticMethodCall', [ClassNode, 
String, Expression]))
+        }
+    }
+
+    /**
+     * Creates a StaticMethodCallExpression.
+     */
+    void staticMethodCall(MethodClosure target, 
@DelegatesTo(AstSpecificationCompiler) Closure argBlock) {
+        captureAndCreateNode("StaticMethodCallExpression", argBlock) {
+            expression.add(0, target.method)
+            expression.add(0, 
ClassHelper.makeWithoutCaching(target.owner.class, false))
+            new 
StaticMethodCallExpression(*enforceConstraints('staticMethodCall', [ClassNode, 
String, Expression]))
+        }
+    }
+
+    /**
+     * Creates a ConstructorNode.
+     */
+    void constructor(int modifiers, @DelegatesTo(AstSpecificationCompiler) 
Closure argBlock) {
+        captureAndCreateNode("ConstructorNode", argBlock) {
+            def annotations = null
+            if (expression.size() > 3) {
+                annotations = expression[3]
+                expression.remove(3)
+            }
+            expression.add(0, modifiers)
+            def result = new 
ConstructorNode(*enforceConstraints('constructor', [Integer, Parameter[], 
ClassNode[], Statement]))
+            if (annotations) {
+                result.addAnnotations(new ArrayList(annotations))
+            }
+            result
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/AstStringCompiler.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/AstStringCompiler.groovy 
b/src/main/groovy/AstStringCompiler.groovy
new file mode 100644
index 0000000..497c125
--- /dev/null
+++ b/src/main/groovy/AstStringCompiler.groovy
@@ -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.codehaus.groovy.ast.builder
+
+import groovy.transform.PackageScope
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.ModuleNode
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.control.CompilerConfiguration
+
+/**
+ * This class handles converting Strings to ASTNode lists.
+ *
+ * @author Hamlet D'Arcy
+ */
+@PackageScope class AstStringCompiler {
+    
+    /**
+     * Performs the String source to {@link List} of {@link ASTNode}.
+     *
+     * @param script
+     *      a Groovy script in String form
+     * @param compilePhase
+     *      the int based CompilePhase to compile it to.
+     * @param statementsOnly
+     */
+    List<ASTNode> compile(String script, CompilePhase compilePhase, boolean 
statementsOnly) {
+        def scriptClassName = "script" + System.currentTimeMillis()
+        GroovyClassLoader classLoader = new GroovyClassLoader()
+        GroovyCodeSource codeSource = new GroovyCodeSource(script, 
scriptClassName + ".groovy", "/groovy/script")
+        CompilationUnit cu = new 
CompilationUnit(CompilerConfiguration.DEFAULT, codeSource.codeSource, 
classLoader)
+        cu.addSource(codeSource.getName(), script);
+        cu.compile(compilePhase.getPhaseNumber())
+        // collect all the ASTNodes into the result, possibly ignoring the 
script body if desired
+        return cu.ast.modules.inject([]) {List acc, ModuleNode node ->
+            if (node.statementBlock) acc.add(node.statementBlock)
+            node.classes?.each {
+                if (!(it.name == scriptClassName && statementsOnly)) {
+                    acc << it
+                }
+            }
+            acc
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/groovy/CollectRecursiveCalls.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/CollectRecursiveCalls.groovy 
b/src/main/groovy/CollectRecursiveCalls.groovy
new file mode 100644
index 0000000..2c7e6de
--- /dev/null
+++ b/src/main/groovy/CollectRecursiveCalls.groovy
@@ -0,0 +1,62 @@
+/*
+ *  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.transform.tailrec
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.ast.CodeVisitorSupport
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+
+/**
+ * Collect all recursive calls within method
+ *
+ * @author Johannes Link
+ */
+@CompileStatic
+class CollectRecursiveCalls extends CodeVisitorSupport {
+       MethodNode method
+       List<Expression> recursiveCalls = []
+
+       public void visitMethodCallExpression(MethodCallExpression call) {
+               if (isRecursive(call)) {
+                       recursiveCalls << call
+               }
+        super.visitMethodCallExpression(call)
+    }
+
+       public void visitStaticMethodCallExpression(StaticMethodCallExpression 
call) {
+               if (isRecursive(call)) {
+            recursiveCalls << call
+        }
+               super.visitStaticMethodCallExpression(call)
+       }
+       
+       private boolean isRecursive(call) {
+               new RecursivenessTester().isRecursive(method: method, call: 
call)
+       }
+       
+       synchronized List<Expression> collect(MethodNode method) {
+               recursiveCalls.clear()
+               this.method = method
+               this.method.code.visit(this)
+               recursiveCalls
+       }
+}

Reply via email to