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:33 UTC
[13/37] incubator-groovy git commit: First prototype of an AST
pattern matcher. * implemetation is *far from complete* * no unit test *
implemented both match/find operations * moved the context aware visitor to
the macro project
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContext.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContext.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContext.java
new file mode 100644
index 0000000..6020592
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContext.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2003-2014 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.matcher;
+
+import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
+import groovy.lang.MapWithDefault;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class TreeContext {
+ private static enum TreeContextKey {
+ expression_replacement
+ }
+ final TreeContext parent;
+ final ASTNode node;
+ final List<TreeContext> siblings = new LinkedList<TreeContext>();
+ final List<TreeContextAction> onPopHandlers = new LinkedList<TreeContextAction>();
+ final Map<Object, List<?>> userdata = MapWithDefault.newInstance(
+ new HashMap<Object, List<?>>(),
+ new Closure(this) {
+ public Object doCall(Object key) {
+ return new LinkedList<Object>();
+ }
+ }
+ );
+
+ TreeContext(final TreeContext parent, final ASTNode node) {
+ this.parent = parent;
+ this.node = node;
+ if (parent!=null) {
+ parent.siblings.add(this);
+ }
+ }
+
+ public Map<?, ?> getUserdata() {
+ return userdata;
+ }
+
+ public TreeContext getParent() { return parent; }
+ public ASTNode getNode() { return node; }
+
+ public TreeContext fork(ASTNode node) {
+ return new TreeContext(this, node);
+ }
+
+ public boolean matches(ASTNodePredicate predicate) {
+ return predicate.matches(node);
+ }
+
+ public boolean matches(@DelegatesTo(value=ASTNode.class, strategy=Closure.DELEGATE_FIRST) Closure<Boolean> predicate) {
+ return MatcherUtils.cloneWithDelegate(predicate, node).call();
+ }
+
+ public List<TreeContext> getSiblings() {
+ return Collections.unmodifiableList(siblings);
+ }
+
+ public List<TreeContextAction> getOnPopHandlers() {
+ return Collections.unmodifiableList(onPopHandlers);
+ }
+
+ public void afterVisit(TreeContextAction action) {
+ onPopHandlers.add(action);
+ }
+
+ public void afterVisit(@DelegatesTo(value=TreeContext.class, strategy=Closure.DELEGATE_FIRST) Closure<?> action) {
+ Closure<?> clone = MatcherUtils.cloneWithDelegate(action, this);
+ afterVisit(DefaultGroovyMethods.asType(clone, TreeContextAction.class));
+ }
+
+ public void setReplacement(Expression replacement) {
+ userdata.put(TreeContextKey.expression_replacement, Collections.singletonList(replacement));
+ }
+
+ public Expression getReplacement() {
+ List<?> list = userdata.get(TreeContextKey.expression_replacement);
+ if (list.size()==1) {
+ return (Expression) list.get(0);
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("TreeContext{");
+ sb.append("node=").append(node!=null?node.getClass().getSimpleName():"undefined");
+ TreeContext p = parent;
+ if (p!=null) {
+ sb.append(", path=");
+ }
+ while (p!=null) {
+ sb.append(p.node!=null?p.node.getClass().getSimpleName():"undefined");
+ sb.append("<-");
+ p = p.parent;
+ }
+ //sb.append(", siblings=").append(siblings);
+ sb.append('}');
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContextAction.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContextAction.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContextAction.java
new file mode 100644
index 0000000..2c198d3
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/TreeContextAction.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003-2014 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.matcher;
+
+public interface TreeContextAction {
+ void call(TreeContext context);
+}
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
new file mode 100644
index 0000000..ae82081
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
@@ -0,0 +1,109 @@
+/*
+ * 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 <bs...@gmail.com>
+ */
+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) {
+ 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);
+
+ 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/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
new file mode 100644
index 0000000..5c41c04
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+import org.codehaus.groovy.control.CompilePhase;
+
+/**
+ *
+ * @author Sergei Egorov <bs...@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;
+ }
+
+ 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/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
new file mode 100644
index 0000000..e895867
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/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 <bs...@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/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
new file mode 100644
index 0000000..127fb4b
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
@@ -0,0 +1,178 @@
+/*
+ * 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.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.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.macro.runtime.MacroBuilder;
+import org.codehaus.groovy.macro.runtime.MacroSubstitutionKey;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
+
+/**
+ *
+ * @author Sergei Egorov <bs...@gmail.com>
+ */
+public class MacroInvocationTrap extends MethodInvocationTrap {
+
+ public MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
+ super(source, sourceUnit);
+ }
+
+ @Override
+ protected boolean handleTargetMethodCallExpression(MethodCallExpression macroCall) {
+ final ClosureExpression closureExpression = getClosureArgument(macroCall);
+
+ if(closureExpression == null) {
+ return true;
+ }
+
+ 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);
+
+ BlockStatement closureBlock = (BlockStatement) closureExpression.getCode();
+
+ Boolean asIs = false;
+
+ TupleExpression macroArguments = getMacroArguments(macroCall);
+
+ if(macroArguments == null) {
+ return true;
+ }
+
+ 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();
+ }
+ }
+
+ macroArgumentsExpressions.remove(macroArgumentsExpressions.size() - 1);
+
+ macroArgumentsExpressions.add(new ConstantExpression(source));
+ macroArgumentsExpressions.add(mapExpression);
+ macroArgumentsExpressions.add(new ClassExpression(ClassHelper.makeWithoutCaching(MacroBuilder.getMacroValue(closureBlock, asIs).getClass(), false)));
+
+ 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) {
+ 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;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/248e0fd9/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
new file mode 100644
index 0000000..65284b5
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
@@ -0,0 +1,39 @@
+/*
+ * 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.*;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+/**
+ *
+ * @author Sergei Egorov <bs...@gmail.com>
+ */
+
+@GroovyASTTransformation(phase = CompilePhase.CONVERSION)
+public class MacroTransformation extends MethodCallTransformation {
+
+ public static final String DOLLAR_VALUE = "$v";
+ public static final String MACRO_METHOD = "macro";
+
+ @Override
+ protected GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) {
+ return new MacroInvocationTrap(sourceUnit.getSource(), sourceUnit);
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/248e0fd9/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
deleted file mode 100644
index ae82081..0000000
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroBuilder.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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 <bs...@gmail.com>
- */
-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) {
- 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);
-
- 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/248e0fd9/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
deleted file mode 100644
index 5c41c04..0000000
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroGroovyMethods.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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;
-import org.codehaus.groovy.control.CompilePhase;
-
-/**
- *
- * @author Sergei Egorov <bs...@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;
- }
-
- 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/248e0fd9/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
deleted file mode 100644
index e895867..0000000
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/runtime/MacroSubstitutionKey.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 <bs...@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/248e0fd9/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
deleted file mode 100644
index 127fb4b..0000000
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.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.control.SourceUnit;
-import org.codehaus.groovy.control.io.ReaderSource;
-import org.codehaus.groovy.macro.runtime.MacroBuilder;
-import org.codehaus.groovy.macro.runtime.MacroSubstitutionKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
-
-/**
- *
- * @author Sergei Egorov <bs...@gmail.com>
- */
-public class MacroInvocationTrap extends MethodInvocationTrap {
-
- public MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
- super(source, sourceUnit);
- }
-
- @Override
- protected boolean handleTargetMethodCallExpression(MethodCallExpression macroCall) {
- final ClosureExpression closureExpression = getClosureArgument(macroCall);
-
- if(closureExpression == null) {
- return true;
- }
-
- 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);
-
- BlockStatement closureBlock = (BlockStatement) closureExpression.getCode();
-
- Boolean asIs = false;
-
- TupleExpression macroArguments = getMacroArguments(macroCall);
-
- if(macroArguments == null) {
- return true;
- }
-
- 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();
- }
- }
-
- macroArgumentsExpressions.remove(macroArgumentsExpressions.size() - 1);
-
- macroArgumentsExpressions.add(new ConstantExpression(source));
- macroArgumentsExpressions.add(mapExpression);
- macroArgumentsExpressions.add(new ClassExpression(ClassHelper.makeWithoutCaching(MacroBuilder.getMacroValue(closureBlock, asIs).getClass(), false)));
-
- 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) {
- 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;
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/248e0fd9/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
deleted file mode 100644
index 65284b5..0000000
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.*;
-import org.codehaus.groovy.control.CompilePhase;
-import org.codehaus.groovy.control.SourceUnit;
-import org.codehaus.groovy.transform.GroovyASTTransformation;
-
-/**
- *
- * @author Sergei Egorov <bs...@gmail.com>
- */
-
-@GroovyASTTransformation(phase = CompilePhase.CONVERSION)
-public class MacroTransformation extends MethodCallTransformation {
-
- public static final String DOLLAR_VALUE = "$v";
- public static final String MACRO_METHOD = "macro";
-
- @Override
- protected GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) {
- return new MacroInvocationTrap(sourceUnit.getSource(), sourceUnit);
- }
-}
-