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:51 UTC

[31/37] incubator-groovy git commit: Add ability to relax binary expression matching thanks to constraints on token type

Add ability to relax binary expression matching thanks to constraints on token type


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

Branch: refs/heads/master
Commit: 3271e6dcce6a02377848640ace5de048e91c6b21
Parents: bcd68a7
Author: Cedric Champeau <ce...@gmail.com>
Authored: Wed Nov 12 08:29:12 2014 +0100
Committer: Sergei Egorov <bs...@gmail.com>
Committed: Mon Sep 28 14:33:12 2015 +0300

----------------------------------------------------------------------
 .../groovy/macro/matcher/ASTMatcher.groovy      | 33 +++++++---
 .../macro/matcher/MatchingConstraints.groovy    | 33 +++-------
 .../macro/matcher/internal/AnyTokenMatch.groovy | 32 ++++++++++
 .../matcher/internal/ConstraintPredicate.groovy | 24 ++++++++
 .../internal/MatchingConstraintsBuilder.groovy  | 64 ++++++++++++++++++++
 .../groovy/macro/matcher/ASTMatcherTest.groovy  | 54 +++++++++++++++++
 6 files changed, 207 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/3271e6dc/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
index 727f39e..ab8b459 100644
--- a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/ASTMatcher.groovy
@@ -27,6 +27,7 @@ import org.codehaus.groovy.ast.stmt.Statement
 import org.codehaus.groovy.ast.stmt.WhileStatement
 import org.codehaus.groovy.classgen.BytecodeExpression
 import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.macro.matcher.internal.MatchingConstraintsBuilder
 
 @CompileStatic
 class ASTMatcher extends ContextualClassCodeVisitor {
@@ -98,18 +99,28 @@ class ASTMatcher extends ContextualClassCodeVisitor {
         }
     }
 
-    private String findPlaceholder(Object exp) {
+    public <T> T ifConstraint(T defaultValue, @DelegatesTo(MatchingConstraints) Closure<T> code) {
         def constraints = (List<MatchingConstraints>) treeContext.getUserdata(MatchingConstraints, true)
         if (constraints) {
-            def placeholders = constraints[0].placeholders
+            def clone = (Closure<T>) code.clone()
+            clone.resolveStrategy = Closure.DELEGATE_FIRST
+            clone.delegate = constraints[0]
+            clone()
+        } else {
+            defaultValue
+        }
+    }
+
+    private String findPlaceholder(Object exp) {
+        ifConstraint(null) {
             if ((exp instanceof VariableExpression && placeholders.contains(exp.name))) {
                 return exp.name
-            }
-            if ((exp instanceof ConstantExpression && placeholders.contains(exp.value))) {
+            } else if ((exp instanceof ConstantExpression && placeholders.contains(exp.value))) {
                 return exp.value
+            } else {
+                null
             }
         }
-        null
     }
 
     private void doWithNode(Object patternNode, Object foundNode, Closure cl) {
@@ -525,7 +536,13 @@ class ASTMatcher extends ContextualClassCodeVisitor {
                 rightExpression.visit(this)
             }
             if (bin.operation.type != expression.operation.type) {
-                failIfNot(false)
+                failIfNot(ifConstraint(false) {
+                    if (tokenPredicate) {
+                        tokenPredicate.apply(bin.operation)
+                    } else {
+                        false
+                    }
+                })
             }
         }
     }
@@ -971,8 +988,8 @@ class ASTMatcher extends ContextualClassCodeVisitor {
      */
     public static ASTNode withConstraints(
             final ASTNode pattern,
-            final @DelegatesTo(value=MatchingConstraints.Builder, strategy=Closure.DELEGATE_ONLY) Closure constraintsSpec) {
-        def builder = new MatchingConstraints.Builder()
+            final @DelegatesTo(value=MatchingConstraintsBuilder, strategy=Closure.DELEGATE_ONLY) Closure constraintsSpec) {
+        def builder = new MatchingConstraintsBuilder()
         def constraints = builder.build(constraintsSpec)
         pattern.putNodeMetaData(MatchingConstraints, constraints)
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/3271e6dc/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/MatchingConstraints.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/MatchingConstraints.groovy b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/MatchingConstraints.groovy
index 82306c0..ed25d35 100644
--- a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/MatchingConstraints.groovy
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/MatchingConstraints.groovy
@@ -17,7 +17,10 @@
 package org.codehaus.groovy.macro.matcher
 
 import groovy.transform.CompileStatic
-import groovy.transform.PackageScope
+import groovy.transform.Immutable
+import org.codehaus.groovy.macro.matcher.internal.AnyTokenMatch
+import org.codehaus.groovy.macro.matcher.internal.ConstraintPredicate
+import org.codehaus.groovy.syntax.Token
 
 /**
  * Represents constraints in AST pattern matching.
@@ -26,31 +29,11 @@ import groovy.transform.PackageScope
  * @since 2.4.0
  */
 @CompileStatic
+@Immutable(knownImmutables = ['tokenPredicate'])
 class MatchingConstraints {
-    final Set<String> placeholders = new LinkedHashSet<>()
+    public final static ConstraintPredicate<Token> ANY_TOKEN = AnyTokenMatch.INSTANCE
 
-    @PackageScope static class Builder {
-        private final MatchingConstraints constraints = new MatchingConstraints()
+    final Set<String> placeholders
+    final ConstraintPredicate<Token> tokenPredicate
 
-        MatchingConstraints build(@DelegatesTo(value=Builder, strategy=Closure.DELEGATE_ONLY) Closure spec) {
-            def clone = (Closure) spec.clone()
-            clone.delegate = this
-            clone.resolveStrategy = Closure.DELEGATE_ONLY
-            clone()
-
-            constraints
-        }
-
-        def getProperty(String name) {
-            if ('constraints'==name) {
-                return constraints
-            }
-            name
-        }
-
-        Builder placeholder(String... names) {
-            names.each { String it -> constraints.placeholders.add(it) }
-            this
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/3271e6dc/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/AnyTokenMatch.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/AnyTokenMatch.groovy b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/AnyTokenMatch.groovy
new file mode 100644
index 0000000..afc91c1
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/AnyTokenMatch.groovy
@@ -0,0 +1,32 @@
+/*
+ * 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.internal
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.syntax.Token
+
+@CompileStatic
+class AnyTokenMatch implements ConstraintPredicate<Token> {
+    public static final AnyTokenMatch INSTANCE = new AnyTokenMatch()
+
+    private AnyTokenMatch() {}
+
+    @Override
+    boolean apply(final Token a) {
+        true
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/3271e6dc/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/ConstraintPredicate.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/ConstraintPredicate.groovy b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/ConstraintPredicate.groovy
new file mode 100644
index 0000000..5c4fdc8
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/ConstraintPredicate.groovy
@@ -0,0 +1,24 @@
+/*
+ * 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.internal
+
+import groovy.transform.CompileStatic
+
+@CompileStatic
+interface ConstraintPredicate<T> {
+    boolean apply(T a)
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/3271e6dc/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/MatchingConstraintsBuilder.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/MatchingConstraintsBuilder.groovy b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/MatchingConstraintsBuilder.groovy
new file mode 100644
index 0000000..070013b
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/matcher/internal/MatchingConstraintsBuilder.groovy
@@ -0,0 +1,64 @@
+/*
+ * 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.internal
+
+import org.codehaus.groovy.macro.matcher.MatchingConstraints
+import org.codehaus.groovy.syntax.Token
+
+class MatchingConstraintsBuilder {
+    Set<String> placeholders = new LinkedHashSet<>()
+    ConstraintPredicate<Token> tokenPredicate
+
+
+    MatchingConstraints build(@DelegatesTo(value=MatchingConstraintsBuilder, strategy=Closure.DELEGATE_ONLY) Closure spec) {
+        def clone = (Closure) spec.clone()
+        clone.delegate = this
+        clone.resolveStrategy = Closure.DELEGATE_ONLY
+        clone()
+
+        new MatchingConstraints(
+                placeholders: Collections.unmodifiableSet(placeholders),
+                tokenPredicate: tokenPredicate)
+    }
+
+    def propertyMissing(String name) {
+        name
+    }
+
+    MatchingConstraintsBuilder placeholder(String... names) {
+        names.each { String it -> placeholders.add(it) }
+        this
+    }
+
+    MatchingConstraintsBuilder anyToken() {
+        tokenPredicate = MatchingConstraints.ANY_TOKEN
+        this
+    }
+
+    MatchingConstraintsBuilder token(@DelegatesTo(value=Token, strategy = Closure.DELEGATE_FIRST) Closure<Boolean> predicate) {
+        def clone = (Closure<Boolean>) predicate.clone()
+        clone.resolveStrategy = Closure.DELEGATE_FIRST
+        tokenPredicate = new ConstraintPredicate<Token>() {
+            @Override
+            boolean apply(final Token a) {
+                clone.delegate = a
+                clone.call(a)
+            }
+        }
+        this
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/3271e6dc/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy b/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
index 9802a2a..c8ff1fc 100644
--- a/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
+++ b/subprojects/groovy-macro/src/test/groovy/org/codehaus/groovy/macro/matcher/ASTMatcherTest.groovy
@@ -18,6 +18,7 @@ package org.codehaus.groovy.macro.matcher
 
 import org.codehaus.groovy.ast.ClassNode
 import org.codehaus.groovy.ast.expr.ArrayExpression
+import org.codehaus.groovy.ast.expr.BinaryExpression
 import org.codehaus.groovy.ast.expr.BitwiseNegationExpression
 import org.codehaus.groovy.ast.expr.CastExpression
 import org.codehaus.groovy.ast.expr.ClassExpression
@@ -31,6 +32,7 @@ import org.codehaus.groovy.ast.stmt.IfStatement
 import org.codehaus.groovy.ast.stmt.WhileStatement
 import org.codehaus.groovy.control.CompilePhase
 import org.codehaus.groovy.macro.transform.MacroClass
+import org.codehaus.groovy.syntax.Types
 
 class ASTMatcherTest extends GroovyTestCase {
     void testMatchesSimpleVar() {
@@ -654,4 +656,56 @@ class ASTMatcherTest extends GroovyTestCase {
             assert ast2.matches(pattern)
         }
     }
+
+    void testMacroCombination() {
+        use(ASTMatcher) {
+            def lhs = macro { a }
+            def rhs = macro { b }
+            def ast = macro { $v { lhs } + $v { rhs } }
+            assert ast instanceof BinaryExpression
+            assert ast.leftExpression.is(lhs)
+            assert ast.rightExpression.is(rhs)
+            def pattern = macro { a + b }
+            assert ast.matches(pattern)
+        }
+    }
+
+    void testRelaxedBinaryExpression() {
+        use(ASTMatcher) {
+            def ast1 = macro { a + b }
+            def ast2 = macro { a - b }
+            def ast3 = macro { a * b }
+            def ast4 = macro { a + c }
+            def pattern = macro {
+                a + b
+            }.withConstraints {
+                anyToken()
+            }
+            assert ast1.matches(pattern)
+            assert ast2.matches(pattern)
+            assert ast3.matches(pattern)
+            assert !ast4.matches(pattern)
+        }
+    }
+
+    void testRelaxedBinaryExpressionWithConstrainedToken() {
+        use(ASTMatcher) {
+            def ast1 = macro { a + b }
+            def ast2 = macro { a - b }
+            def ast3 = macro { a * b }
+            def ast4 = macro { a + c }
+            def pattern = macro {
+                a + b
+            }.withConstraints {
+                token {
+                    type in [Types.PLUS, Types.MINUS]
+                }
+            }
+            assert ast1.matches(pattern)
+            assert ast2.matches(pattern)
+            assert !ast3.matches(pattern)
+            assert !ast4.matches(pattern)
+        }
+    }
+
 }