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)
+ }
+ }
+
}