You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2015/10/07 21:26:23 UTC

[03/37] incubator-groovy git commit: new groovy-macro subproject with code from MacroGroovy (initial contribution)

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/20560359
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/20560359
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/20560359

Branch: refs/heads/master
Commit: 205603597f13606c356c18940546f6dc3f87a7e0
Parents: 6d277a0
Author: Sergey Egorov <bs...@gmail.com>
Authored: Thu Jul 3 00:46:13 2014 +0300
Committer: Sergei Egorov <bs...@gmail.com>
Committed: Mon Sep 28 14:32:04 2015 +0300

----------------------------------------------------------------------
 .../groovy/macro/runtime/MacroBuilder.java      |  16 +-
 .../macro/runtime/MacroGroovyMethods.java       |   9 -
 .../macro/transform/MacroInvocationTrap.java    | 175 ++++++++++++-------
 .../macro/transform/MacroTransformation.java    |  41 ++++-
 .../test/groovy/groovy/SimpleMacroTest.groovy   |  45 +----
 5 files changed, 155 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/20560359/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
index ae82081..2c66f3e 100644
--- 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
@@ -39,26 +39,14 @@ import java.util.Map;
 public enum MacroBuilder {
     INSTANCE;
 
-    public <T> T macro(String source, final Map<MacroSubstitutionKey, Closure<Expression>> context, Class<T> resultClass) {
-        return macro(false, source, context, resultClass);
-    }
-
-    public <T> T macro(boolean asIs, String source, final Map<MacroSubstitutionKey, Closure<Expression>> context, Class<T> resultClass) {
-        return macro(CompilePhase.CONVERSION, asIs, source, context, resultClass);
-    }
-
-    public <T> T macro(CompilePhase compilePhase, String source, final Map<MacroSubstitutionKey, Closure<Expression>> context, Class<T> resultClass) {
-        return macro(compilePhase, false, source, context, resultClass);
-    }
-
     @SuppressWarnings("unchecked")
-    public <T> T macro(CompilePhase compilePhase, boolean asIs, String source, final Map<MacroSubstitutionKey, Closure<Expression>> context, Class<T> resultClass) {
+    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, true, labelledSource);
+        List<ASTNode> nodes = (new AstBuilder()).buildFromString(CompilePhase.CONVERSION, true, labelledSource);
 
         for(ASTNode node : nodes) {
             if (node instanceof BlockStatement) {

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/20560359/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
index 5c41c04..578a11f 100644
--- 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
@@ -17,7 +17,6 @@ package org.codehaus.groovy.macro.runtime;
 
 import groovy.lang.Closure;
 import groovy.lang.DelegatesTo;
-import org.codehaus.groovy.control.CompilePhase;
 
 /**
  *
@@ -39,12 +38,4 @@ public class MacroGroovyMethods {
     public static <T> T macro(Object self, boolean asIs, @DelegatesTo(MacroValuePlaceholder.class) Closure cl) {
         return null;
     }
-
-    public static <T> T macro(Object self, CompilePhase compilePhase, @DelegatesTo(MacroValuePlaceholder.class) Closure cl) {
-        return null;
-    }
-
-    public static <T> T macro(Object self, CompilePhase compilePhase, boolean asIs, @DelegatesTo(MacroValuePlaceholder.class) Closure cl) {
-        return null;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/20560359/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
index 127fb4b..7ac10df 100644
--- 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
@@ -15,15 +15,18 @@
  */
 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.MethodInvocationTrap;
 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;
@@ -34,107 +37,122 @@ import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
  *
  * @author Sergei Egorov <bs...@gmail.com>
  */
-public class MacroInvocationTrap extends MethodInvocationTrap {
+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;
+    }
 
-    public MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
-        super(source, 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)
+        );
     }
 
-    @Override
-    protected boolean handleTargetMethodCallExpression(MethodCallExpression macroCall) {
-        final ClosureExpression closureExpression = getClosureArgument(macroCall);
+    /**
+     * 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 true;
+            return;
         }
 
         if(closureExpression.getParameters() != null && closureExpression.getParameters().length > 0) {
             addError("Macro closure arguments are not allowed", closureExpression);
-            return true;
         }
-
+        
         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(closureExpression);
+        
+        String source = convertClosureToSource(this.source, closureExpression);
 
         BlockStatement closureBlock = (BlockStatement) closureExpression.getCode();
-
+        
         Boolean asIs = false;
-
+        
         TupleExpression macroArguments = getMacroArguments(macroCall);
-
+        
         if(macroArguments == null) {
-            return true;
+            return;
         }
 
         List<Expression> macroArgumentsExpressions = macroArguments.getExpressions();
-
-        if(macroArgumentsExpressions.size() == 2 || macroArgumentsExpressions.size() == 3) {
-            Expression asIsArgumentExpression = macroArgumentsExpressions.get(macroArgumentsExpressions.size() - 2);
-            if((asIsArgumentExpression instanceof ConstantExpression)) {
-                ConstantExpression asIsConstantExpression = (ConstantExpression) asIsArgumentExpression;
-
-                if(!(asIsConstantExpression.getValue() instanceof Boolean)) {
-                    addError("AsIs argument value should be boolean", asIsConstantExpression);
-                    return true;
-                }
-
-                asIs = (Boolean) asIsConstantExpression.getValue();
+        
+        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;
             }
-        }
 
-        macroArgumentsExpressions.remove(macroArgumentsExpressions.size() - 1);
+            asIs = (Boolean) asIsConstantExpression.getValue();
+        }
 
-        macroArgumentsExpressions.add(new ConstantExpression(source));
-        macroArgumentsExpressions.add(mapExpression);
-        macroArgumentsExpressions.add(new ClassExpression(ClassHelper.makeWithoutCaching(MacroBuilder.getMacroValue(closureBlock, asIs).getClass(), false)));
+        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);
-        
-        return true;
-    }
-
-    @Override
-    protected boolean isBuildInvocation(MethodCallExpression call) {
-        return isBuildInvocation(call, MacroTransformation.MACRO_METHOD);
-    }
-
-    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;
     }
     
     protected TupleExpression getMacroArguments(MethodCallExpression call) {
@@ -175,4 +193,41 @@ public class MacroInvocationTrap extends MethodInvocationTrap {
 
         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/20560359/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
index 65284b5..cdf0f07 100644
--- 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
@@ -15,25 +15,56 @@
  */
 package org.codehaus.groovy.macro.transform;
 
-import org.codehaus.groovy.ast.*;
+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 <bs...@gmail.com>
  */
-
 @GroovyASTTransformation(phase = CompilePhase.CONVERSION)
-public class MacroTransformation extends MethodCallTransformation {
+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 GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) {
-        return new MacroInvocationTrap(sourceUnit.getSource(), sourceUnit);
+    protected SourceUnit getSourceUnit() {
+        return source;
     }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/20560359/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
index 9a29fc3..df46802 100644
--- a/subprojects/groovy-macro/src/test/groovy/groovy/SimpleMacroTest.groovy
+++ b/subprojects/groovy-macro/src/test/groovy/groovy/SimpleMacroTest.groovy
@@ -21,7 +21,6 @@ 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 org.codehaus.groovy.control.CompilePhase
 
 import static org.codehaus.groovy.ast.expr.VariableExpression.*;
 
@@ -30,10 +29,7 @@ import static org.codehaus.groovy.ast.expr.VariableExpression.*;
  * @author Sergei Egorov <bs...@gmail.com>
  */
 @CompileStatic
-class SimpleMacroTest extends GroovyTestCase {
-    
-    static final String TO_LOWER_CASE_METHOD_NAME = macro { "".toLowerCase() }.getMethodAsString()
-    
+class SimpleMacroTest extends GroovyShellTestCase {
     public void testMethod() {
 
         def someVariable = new VariableExpression("someVariable");
@@ -46,7 +42,7 @@ class SimpleMacroTest extends GroovyTestCase {
 
         assertSyntaxTree(expected, result);
     }
-
+    
     public void testAsIs() {
         def expected = new BlockStatement([
                 new ExpressionStatement(new MethodCallExpression(THIS_EXPRESSION, "println", new ArgumentListExpression(new ConstantExpression("foo"))))
@@ -69,12 +65,6 @@ class SimpleMacroTest extends GroovyTestCase {
 
         assertSyntaxTree(expected, result);
     }
-    
-    public void testMethodName() {
-        // Very useful when you don't want to hardcode method or variable names
-        assertEquals("toLowerCase", TO_LOWER_CASE_METHOD_NAME)
-        assertEquals("valueOf", macro { String.valueOf() }.getMethodAsString())
-    }
 
     public void testBlock() {
 
@@ -93,37 +83,6 @@ class SimpleMacroTest extends GroovyTestCase {
 
         assertSyntaxTree(expected, result);
     }
-
-    public void testCompilePhase() {
-
-        def result = macro(CompilePhase.FINALIZATION) {
-            println "foo"
-            println "bar"
-        }
-
-        def expected = new BlockStatement(
-                [
-                        new ExpressionStatement(new MethodCallExpression(THIS_EXPRESSION, "println", new ArgumentListExpression(new ConstantExpression("foo")))),
-                        // In FINALIZATION phase last println will be return statement
-                        new ReturnStatement(new MethodCallExpression(THIS_EXPRESSION, "println", new ArgumentListExpression(new ConstantExpression("bar")))),
-                ] as List<Statement>,
-                new VariableScope()
-        )
-
-        assertSyntaxTree(expected, result);
-    }
-
-    public void testAsIsWithCompilePhase() {
-        def expected = new BlockStatement([
-                new ReturnStatement(new MethodCallExpression(THIS_EXPRESSION, "println", new ArgumentListExpression(new ConstantExpression("foo"))))
-        ] as List<Statement>, new VariableScope());
-
-        def result = macro(CompilePhase.FINALIZATION, true) {
-            println "foo"
-        }
-
-        assertSyntaxTree(expected, result);
-    }
     
     protected void assertSyntaxTree(Object expected, Object result) {
         AstAssert.assertSyntaxTree([expected], [result])