groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cchamp...@apache.org
Subject [01/37] incubator-groovy git commit: new groovy-macro subproject with code from MacroGroovy (initial contribution)
Date Wed, 07 Oct 2015 19:26:21 GMT
Repository: incubator-groovy
Updated Branches:
  refs/heads/master baba6dfdf -> c289ed419


new groovy-macro subproject with code from MacroGroovy (initial contribution)


Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/0b5f0b03
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/0b5f0b03
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/0b5f0b03

Branch: refs/heads/master
Commit: 0b5f0b03d39c10bb7071dd0eedaacc2157cdd6c0
Parents: f069c18
Author: Sergey Egorov <bsideup@gmail.com>
Authored: Thu Jul 3 00:46:13 2014 +0300
Committer: Sergei Egorov <bsideup@gmail.com>
Committed: Mon Sep 28 14:31:50 2015 +0300

----------------------------------------------------------------------
 settings.gradle                                 |   3 +-
 subprojects/groovy-macro/build.gradle           |  11 +
 .../groovy/macro/runtime/MacroBuilder.java      |  97 ++++++++
 .../macro/runtime/MacroGroovyMethods.java       |  41 ++++
 .../macro/runtime/MacroSubstitutionKey.java     |  96 ++++++++
 .../macro/transform/MacroInvocationTrap.java    | 233 +++++++++++++++++++
 .../macro/transform/MacroTransformation.java    |  70 ++++++
 ....codehaus.groovy.transform.ASTTransformation |   2 +
 .../test/groovy/groovy/SimpleMacroTest.groovy   |  90 +++++++
 9 files changed, 642 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
index eccf7e8..230d0ff 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -31,7 +31,8 @@ def subprojects = ['groovy-ant',
         'groovy-templates',
         'groovy-test',
         'groovy-testng',
-        'groovy-xml'
+        'groovy-xml',
+        'groovy-macro'
 ]
 
 if(JavaVersion.current().isJava7Compatible()) {

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/build.gradle
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/build.gradle b/subprojects/groovy-macro/build.gradle
new file mode 100644
index 0000000..2af7b10
--- /dev/null
+++ b/subprojects/groovy-macro/build.gradle
@@ -0,0 +1,11 @@
+dependencies {
+    compile rootProject
+    testCompile rootProject.sourceSets.test.runtimeClasspath
+    testCompile project(':groovy-test')
+}
+
+task moduleDescriptor(type: org.codehaus.groovy.gradle.WriteExtensionDescriptorTask) {
+    extensionClasses = 'org.codehaus.groovy.macro.runtime.MacroGroovyMethods'
+}
+
+compileJava.dependsOn moduleDescriptor
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroBuilder.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroBuilder.java
b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroBuilder.java
new file mode 100644
index 0000000..2c66f3e
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroBuilder.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed 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.macro.runtime;
+
+import groovy.lang.Closure;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.macro.transform.MacroInvocationTrap;
+import org.codehaus.groovy.macro.transform.MacroTransformation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author Sergei Egorov <bsideup@gmail.com>
+ */
+public enum MacroBuilder {
+    INSTANCE;
+
+    @SuppressWarnings("unchecked")
+    public <T> T macro(boolean asIs, String source, final Map<MacroSubstitutionKey,
Closure<Expression>> context, Class<T> resultClass) {
+        final String label = "__synthesized__label__" + System.currentTimeMillis() + "__:";
+        final String labelledSource = label + source;
+        final int linesOffset = 1;
+        final int columnsOffset = label.length() + 1; // +1 because of {
+        
+        List<ASTNode> nodes = (new AstBuilder()).buildFromString(CompilePhase.CONVERSION,
true, labelledSource);
+
+        for(ASTNode node : nodes) {
+            if (node instanceof BlockStatement) {
+
+                BlockStatement closureBlock = (BlockStatement) ((BlockStatement)node).getStatements().get(0);
+
+                (new ClassCodeExpressionTransformer() {
+                    public Expression transform(Expression expression) {
+                        if(!(expression instanceof MethodCallExpression)) {
+                            return super.transform(expression);
+                        }
+
+                        MethodCallExpression call = (MethodCallExpression) expression;
+
+                        if(!MacroInvocationTrap.isBuildInvocation(call, MacroTransformation.DOLLAR_VALUE))
{
+                            return super.transform(expression);
+                        }
+
+                        MacroSubstitutionKey key = new MacroSubstitutionKey(call, linesOffset,
columnsOffset);
+                        
+                        return context.get(key).call();
+                    }
+
+                    @Override
+                    protected SourceUnit getSourceUnit() {
+                        // Could be null if there are no errors
+                        return null;
+                    }
+                }).visitBlockStatement(closureBlock);
+
+                return (T) getMacroValue(closureBlock, asIs);
+            }
+        }
+        return null;
+    }
+
+    public static ASTNode getMacroValue(BlockStatement closureBlock, boolean asIs) {
+        if(!asIs && closureBlock.getStatements().size() == 1) {
+            Statement result = closureBlock.getStatements().get(0);
+            if(result instanceof ExpressionStatement) {
+                return ((ExpressionStatement) result).getExpression();
+            } else {
+                return result;
+            }
+        }
+        return closureBlock;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
new file mode 100644
index 0000000..578a11f
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed 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.macro.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
+
+/**
+ *
+ * @author Sergei Egorov <bsideup@gmail.com>
+ */
+public class MacroGroovyMethods {
+
+    public static class MacroValuePlaceholder {
+        public static Object $v(Closure cl) {
+            // replaced with AST transformations
+            return null; 
+        }
+    }
+
+    public static <T> T macro(Object self, @DelegatesTo(MacroValuePlaceholder.class)
Closure cl) {
+        return null;
+    }
+
+    public static <T> T macro(Object self, boolean asIs, @DelegatesTo(MacroValuePlaceholder.class)
Closure cl) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
new file mode 100644
index 0000000..e895867
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed 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.macro.runtime;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ *
+ * @author Sergei Egorov <bsideup@gmail.com>
+ */
+public class MacroSubstitutionKey {
+
+    private int startLine;
+    private int startColumn;
+    private int endLine;
+    private int endColumn;
+
+    public MacroSubstitutionKey(int startLine, int startColumn, int endLine, int endColumn)
{
+        this.startLine = startLine;
+        this.startColumn = startColumn;
+        this.endLine = endLine;
+        this.endColumn = endColumn;
+    }
+    
+    public MacroSubstitutionKey(Expression expression, int linesOffset, int columnsOffset)
{
+        this(
+                expression.getLineNumber() - linesOffset,
+                expression.getColumnNumber() - (expression.getLineNumber() == linesOffset
? columnsOffset : 0),
+                expression.getLastLineNumber() - linesOffset,
+                expression.getLastColumnNumber() - (expression.getLastLineNumber() == linesOffset
? columnsOffset : 0)
+        );
+    }
+    
+    public ConstructorCallExpression toConstructorCallExpression() {
+        return new ConstructorCallExpression(
+                ClassHelper.make(this.getClass()),
+                new ArgumentListExpression(new Expression[] {
+                        new ConstantExpression(startLine),
+                        new ConstantExpression(startColumn),
+                        new ConstantExpression(endLine),
+                        new ConstantExpression(endColumn)
+                })
+        );
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        MacroSubstitutionKey that = (MacroSubstitutionKey) o;
+
+        if (endColumn != that.endColumn) return false;
+        if (endLine != that.endLine) return false;
+        if (startColumn != that.startColumn) return false;
+        if (startLine != that.startLine) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = startLine;
+        result = 31 * result + startColumn;
+        result = 31 * result + endLine;
+        result = 31 * result + endColumn;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "SubstitutionKey{" +
+                "startLine=" + startLine +
+                ", startColumn=" + startColumn +
+                ", endLine=" + endLine +
+                ", endColumn=" + endColumn +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
new file mode 100644
index 0000000..7ac10df
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed 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.macro.transform;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.tools.ClosureUtils;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.macro.runtime.MacroBuilder;
+import org.codehaus.groovy.macro.runtime.MacroSubstitutionKey;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
+
+/**
+ *
+ * @author Sergei Egorov <bsideup@gmail.com>
+ */
+public class MacroInvocationTrap {
+
+    private final ReaderSource source;
+    private final SourceUnit sourceUnit;
+
+    /**
+     * Creates the trap and captures all macro method calls.
+     *
+     * @param source         the reader source that contains source for the SourceUnit
+     * @param sourceUnit     the source unit being compiled. Used for error messages.
+     */
+    MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
+        if (source == null) throw new IllegalArgumentException("Null: source");
+        if (sourceUnit == null) throw new IllegalArgumentException("Null: sourceUnit");
+        this.source = source;
+        this.sourceUnit = sourceUnit;
+    }
+
+    /**
+     * Reports an error back to the source unit.
+     *
+     * @param msg  the error message
+     * @param expr the expression that caused the error message.
+     */
+    private void addError(String msg, ASTNode expr) {
+        sourceUnit.getErrorCollector().addErrorAndContinue(
+                new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(),
expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), sourceUnit)
+        );
+    }
+
+    /**
+     * Attempts to find 'macro' invocations. When found, converts them into calls
+     * to the 'from string' approach.
+     *
+     * @param macroCall the method call expression that may or may not be a 'macro' invocation.
+     */
+    public void visitMethodCallExpression(final MethodCallExpression macroCall) {
+
+        if (!isBuildInvocation(macroCall, MacroTransformation.MACRO_METHOD)) {
+            return;
+        }
+
+        final ClosureExpression closureExpression = getClosureArgument(macroCall);
+        
+        if(closureExpression == null) {
+            return;
+        }
+
+        if(closureExpression.getParameters() != null && closureExpression.getParameters().length
> 0) {
+            addError("Macro closure arguments are not allowed", closureExpression);
+        }
+        
+        final MapExpression mapExpression = new MapExpression();
+        
+        (new CodeVisitorSupport() {
+            @Override
+            public void visitMethodCallExpression(MethodCallExpression call) {
+                super.visitMethodCallExpression(call);
+                
+                if(isBuildInvocation(call, MacroTransformation.DOLLAR_VALUE)) {
+                    ClosureExpression substitutionClosureExpression = getClosureArgument(call);
+                    
+                    if(substitutionClosureExpression == null) {
+                        return;
+                    }
+
+                    MacroSubstitutionKey key = new MacroSubstitutionKey(call, closureExpression.getLineNumber(),
closureExpression.getColumnNumber());
+                    
+                    mapExpression.addMapEntryExpression(key.toConstructorCallExpression(),
substitutionClosureExpression);
+                }
+            }
+        }).visitClosureExpression(closureExpression);
+        
+        String source = convertClosureToSource(this.source, closureExpression);
+
+        BlockStatement closureBlock = (BlockStatement) closureExpression.getCode();
+        
+        Boolean asIs = false;
+        
+        TupleExpression macroArguments = getMacroArguments(macroCall);
+        
+        if(macroArguments == null) {
+            return;
+        }
+
+        List<Expression> macroArgumentsExpressions = macroArguments.getExpressions();
+        
+        if(macroArgumentsExpressions.size() > 1) {
+            Expression firstArgument = macroArgumentsExpressions.get(0);
+            
+            if(!(firstArgument instanceof ConstantExpression)) {
+                addError("AsIs argument value should be constant(true or false)", firstArgument);
+                return;
+            }
+            
+            ConstantExpression asIsConstantExpression = (ConstantExpression) firstArgument;
+            
+            if(!(asIsConstantExpression.getValue() instanceof Boolean)) {
+                addError("AsIs argument value should be boolean", asIsConstantExpression);
+                return;
+            }
+
+            asIs = (Boolean) asIsConstantExpression.getValue();
+        }
+
+        List<Expression> otherArgs = new ArrayList<Expression>();
+        otherArgs.add(new ConstantExpression(asIs));
+        otherArgs.add(new ConstantExpression(source));
+        otherArgs.add(mapExpression);
+        otherArgs.add(new ClassExpression(ClassHelper.makeWithoutCaching(MacroBuilder.getMacroValue(closureBlock,
asIs).getClass(), false)));
+
+        macroCall.setArguments(new ArgumentListExpression(otherArgs));
+        macroCall.setObjectExpression(new PropertyExpression(new ClassExpression(ClassHelper.makeWithoutCaching(MacroBuilder.class,
false)), "INSTANCE"));
+        macroCall.setSpreadSafe(false);
+        macroCall.setSafe(false);
+        macroCall.setImplicitThis(false);
+    }
+    
+    protected TupleExpression getMacroArguments(MethodCallExpression call) {
+        Expression macroCallArguments = call.getArguments();
+        if (macroCallArguments == null) {
+            addError("Call should have arguments", call);
+            return null;
+        }
+
+        if(!(macroCallArguments instanceof TupleExpression)) {
+            addError("Call should have TupleExpression as arguments", macroCallArguments);
+            return null;
+        }
+
+        TupleExpression tupleArguments = (TupleExpression) macroCallArguments;
+
+        if (tupleArguments.getExpressions() == null) {
+            addError("Call arguments should have expressions", tupleArguments);
+            return null;
+        }
+        
+        return tupleArguments;
+    }
+    
+    protected ClosureExpression getClosureArgument(MethodCallExpression call) {
+        TupleExpression tupleArguments = getMacroArguments(call);
+
+        if(tupleArguments.getExpressions().size() < 1) {
+            addError("Call arguments should have at least one argument", tupleArguments);
+            return null;
+        }
+
+        Expression result = tupleArguments.getExpression(tupleArguments.getExpressions().size()
- 1);
+        if (!(result instanceof ClosureExpression)) {
+            addError("Last call argument should be a closure", result);
+            return null;
+        }
+
+        return (ClosureExpression) result;
+    }
+
+    /**
+     * Looks for 'macro' method calls.
+     *
+     * @param call the method call expression, may not be null
+     */
+    public static boolean isBuildInvocation(MethodCallExpression call, String methodName)
{
+        if (call == null) throw new IllegalArgumentException("Null: call");
+        if(methodName == null) throw new IllegalArgumentException("Null: methodName");
+        
+        if(!(call.getMethod() instanceof ConstantExpression)) {
+            return false;
+        }
+        
+        if(!(methodName.equals(call.getMethodAsString()))) {
+            return false;
+        }
+
+        // is method object correct type?
+        return call.getObjectExpression() == THIS_EXPRESSION;
+    }
+
+    /**
+     * Converts a ClosureExpression into the String source.
+     *
+     * @param expression a closure
+     * @return the source the closure was created from
+     */
+    private String convertClosureToSource(ReaderSource source, ClosureExpression expression)
{
+
+        try {
+            return ClosureUtils.convertClosureToSource(source, expression);
+        } catch (Exception e) {
+            addError(e.getMessage(), expression);
+        }
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
new file mode 100644
index 0000000..cdf0f07
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed 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.macro.transform;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.ASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+/**
+ *
+ * @author Sergei Egorov <bsideup@gmail.com>
+ */
+@GroovyASTTransformation(phase = CompilePhase.CONVERSION)
+public class MacroTransformation extends ClassCodeVisitorSupport implements ASTTransformation
{
+
+    public static final String DOLLAR_VALUE = "$v";
+    public static final String MACRO_METHOD = "macro";
+
+    SourceUnit source;
+    
+    MacroInvocationTrap transformer;
+
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        this.source = source;
+
+        transformer = new MacroInvocationTrap(source.getSource(), source);
+
+        for(ASTNode node : nodes) {
+            if(node instanceof ClassNode) {
+                visitClass((ClassNode) node);
+            } else if(node instanceof ModuleNode) {
+                ModuleNode moduleNode = (ModuleNode) node;
+                for (ClassNode classNode : moduleNode.getClasses()) {
+                    visitClass(classNode);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        transformer.visitMethodCallExpression(call);
+        super.visitMethodCallExpression(call);
+    }
+
+    @Override
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
b/subprojects/groovy-macro/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
new file mode 100644
index 0000000..09d8397
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation
@@ -0,0 +1,2 @@
+#global transformation for macro support
+org.codehaus.groovy.macro.transform.MacroTransformation
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/0b5f0b03/subprojects/groovy-macro/src/test/groovy/groovy/SimpleMacroTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/test/groovy/groovy/SimpleMacroTest.groovy b/subprojects/groovy-macro/src/test/groovy/groovy/SimpleMacroTest.groovy
new file mode 100644
index 0000000..df46802
--- /dev/null
+++ b/subprojects/groovy-macro/src/test/groovy/groovy/SimpleMacroTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed 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 groovy
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.VariableScope
+import org.codehaus.groovy.ast.builder.AstAssert
+import org.codehaus.groovy.ast.expr.*
+import org.codehaus.groovy.ast.stmt.*
+
+import static org.codehaus.groovy.ast.expr.VariableExpression.*;
+
+/**
+ *
+ * @author Sergei Egorov <bsideup@gmail.com>
+ */
+@CompileStatic
+class SimpleMacroTest extends GroovyShellTestCase {
+    public void testMethod() {
+
+        def someVariable = new VariableExpression("someVariable");
+
+        ReturnStatement result = macro {
+            return new NonExistingClass($v{someVariable});
+        }
+        
+        def expected = new ReturnStatement(new ConstructorCallExpression(ClassHelper.make("NonExistingClass"),
new ArgumentListExpression(someVariable)));
+
+        assertSyntaxTree(expected, result);
+    }
+    
+    public void testAsIs() {
+        def expected = new BlockStatement([
+                new ExpressionStatement(new MethodCallExpression(THIS_EXPRESSION, "println",
new ArgumentListExpression(new ConstantExpression("foo"))))
+        ] as List<Statement>, new VariableScope());
+
+        BlockStatement result = macro(true) {
+            println "foo"
+        }
+
+        assertSyntaxTree(expected, result);
+    }
+
+    public void testInception() {
+
+        ConstructorCallExpression result = macro {
+            new NonExistingClass($v{macro {someVariable}});
+        }
+
+        def expected = new ConstructorCallExpression(ClassHelper.make("NonExistingClass"),
new ArgumentListExpression(new VariableExpression("someVariable")));
+
+        assertSyntaxTree(expected, result);
+    }
+
+    public void testBlock() {
+
+        def result = macro {
+            println "foo"
+            println "bar"
+        }
+
+        def expected = new BlockStatement(
+                [
+                        new ExpressionStatement(new MethodCallExpression(THIS_EXPRESSION,
"println", new ArgumentListExpression(new ConstantExpression("foo")))),
+                        new ExpressionStatement(new MethodCallExpression(THIS_EXPRESSION,
"println", new ArgumentListExpression(new ConstantExpression("bar")))),
+                ] as List<Statement>,
+                new VariableScope()
+        )
+
+        assertSyntaxTree(expected, result);
+    }
+    
+    protected void assertSyntaxTree(Object expected, Object result) {
+        AstAssert.assertSyntaxTree([expected], [result])
+    }
+}


Mime
View raw message