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])