diff --git a/src/org/rascalmpl/values/RascalValueFactory.java b/src/org/rascalmpl/values/RascalValueFactory.java index 9f6d4efed79..69911110028 100644 --- a/src/org/rascalmpl/values/RascalValueFactory.java +++ b/src/org/rascalmpl/values/RascalValueFactory.java @@ -519,6 +519,11 @@ static class CharInt implements ITree { public boolean isChar() { return true; } + + @Override + public int getConcreteMatchFingerprint() { + return 3052374 /* "char".hashCode() */ + ch; + } @Override public INode setChildren(IValue[] childArray) { @@ -683,6 +688,11 @@ private static class CharByte implements ITree { public CharByte(byte ch) { this.ch = ch; } + + @Override + public int getConcreteMatchFingerprint() { + return 3052374 /* "char".hashCode() */ + ch; + } @Override public boolean isChar() { @@ -855,6 +865,11 @@ public Cycle(IConstructor symbol, int cycleLength) { this.symbol = symbol; this.cycleLength = cycleLength; } + + @Override + public int getConcreteMatchFingerprint() { + return 95131878 /* "cycle".hashCode() */ + 13 * symbol.hashCode(); + } @Override public boolean isCycle() { @@ -1033,11 +1048,21 @@ public Amb(ISet alts) { this.alternatives = alts; } + @Override + public int getConcreteMatchFingerprint() { + return 96694 /* "amb".hashCode() */ + 43 * alternatives.getElementType().hashCode(); + } + @Override public boolean isAmb() { return true; } + @Override + public int getMatchFingerprint() { + return 96694 /* Tree_Amb.getName().hashCode() */ + 131; // should be equal what IConstructor does for the amb constructor! + } + @Override public ITree accept(TreeVisitor v) throws E { return (ITree) v.visitTreeAmb(this); @@ -1101,8 +1126,8 @@ public boolean hasNext() { public IValue next() { count++; switch(count) { - case 1: return getAlternatives(); - default: return null; + case 1: return getAlternatives(); + default: return null; } } }; @@ -1217,6 +1242,11 @@ public ApplWithKeywordParametersFacade(IConstructor content, io.usethesource.cap super(content, parameters); } + @Override + public int getConcreteMatchFingerprint() { + return ((ITree) content).getConcreteMatchFingerprint(); + } + @Override public ITree accept(TreeVisitor v) throws E { return v.visitTreeAppl(this); @@ -1268,6 +1298,11 @@ public AmbWithKeywordParametersFacade(IConstructor content, io.usethesource.caps super(content, parameters); } + @Override + public int getConcreteMatchFingerprint() { + return ((ITree) content).getConcreteMatchFingerprint(); + } + @Override public ITree accept(TreeVisitor v) throws E { return v.visitTreeAmb(this); @@ -1314,6 +1349,11 @@ public CycleWithKeywordParametersFacade(IConstructor content, io.usethesource.ca super(content, parameters); } + @Override + public int getConcreteMatchFingerprint() { + return ((ITree) content).getConcreteMatchFingerprint(); + } + @Override public ITree accept(TreeVisitor v) throws E { return v.visitTreeCycle(this); @@ -1356,6 +1396,11 @@ public CharWithKeywordParametersFacade(IConstructor content, io.usethesource.cap super(content, parameters); } + @Override + public int getConcreteMatchFingerprint() { + return ((ITree) content).getConcreteMatchFingerprint(); + } + @Override public ITree accept(TreeVisitor v) throws E { return v.visitTreeChar(this); @@ -1403,6 +1448,11 @@ private static abstract class AbstractAppl implements ITree { protected Type type = null; + @Override + public int getConcreteMatchFingerprint() { + return 3568542 /* "tree".hashCode() */ + 41 * production.hashCode(); + } + @Override public ITree accept(TreeVisitor v) throws E { return v.visitTreeAppl(this); diff --git a/src/org/rascalmpl/values/parsetrees/ITree.java b/src/org/rascalmpl/values/parsetrees/ITree.java index 2b6b3de6b12..e534be637af 100644 --- a/src/org/rascalmpl/values/parsetrees/ITree.java +++ b/src/org/rascalmpl/values/parsetrees/ITree.java @@ -25,9 +25,24 @@ default T accept(IValueVisitor v) throws E { @Override default int getMatchFingerprint() { - return 3568542 /* "tree".hashCode() */ + 41 * getProduction().hashCode(); + // ITrees must simulate their constructor prints in case + // we pattern match on the abstract Tree data-type + return IConstructor.super.getMatchFingerprint(); } + /** + * Concrete patterns need another layer of fingerprinting on top + * of `getMatchFingerprint`. The reason is that _the same IValue_ + * can be matched against an abstract pattern of the Tree data-type, + * and against concrete patterns. + * + * Like before, the match-fingerprint contract is: + * if pattern.match(tree) ==> pattern.fingerprint() == match.fingerprint(); + * + * @return a unique code for each outermost ITree node + */ + int getConcreteMatchFingerprint(); + default boolean isAppl() { return false; } diff --git a/src/org/rascalmpl/values/parsetrees/TreeAdapter.java b/src/org/rascalmpl/values/parsetrees/TreeAdapter.java index 68cb24672da..4ab329a2a92 100644 --- a/src/org/rascalmpl/values/parsetrees/TreeAdapter.java +++ b/src/org/rascalmpl/values/parsetrees/TreeAdapter.java @@ -288,6 +288,7 @@ else if (isChar(tree)) { return SymbolAdapter.charClass(TreeAdapter.getCharacter(tree)); } else if (isAmb(tree)) { + // ambiguities are never empty return getType((ITree) getAlternatives(tree).iterator().next()); } throw new ImplementationError("ITree does not have a type"); diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java index 70be9bc1d06..72674cde85a 100644 --- a/test/org/rascalmpl/MatchFingerprintTest.java +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -21,6 +21,7 @@ import org.rascalmpl.types.RascalTypeFactory; import io.usethesource.vallang.IConstructor; +import io.usethesource.vallang.IInteger; import io.usethesource.vallang.exceptions.FactTypeUseException; import io.usethesource.vallang.io.StandardTextReader; import io.usethesource.vallang.type.Type; @@ -29,6 +30,7 @@ import org.rascalmpl.values.RascalFunctionValueFactory; import org.rascalmpl.values.functions.IFunction; import org.rascalmpl.values.parsetrees.ITree; +import org.rascalmpl.values.parsetrees.TreeAdapter; import junit.framework.TestCase; @@ -46,7 +48,6 @@ public class MatchFingerprintTest extends TestCase { private final Evaluator eval = new Evaluator(IRascalValueFactory.getInstance(), System.in, System.err, System.out, root, heap); private final RascalFunctionValueFactory VF = eval.getFunctionValueFactory(); private final TypeFactory TF = TypeFactory.getInstance(); - private final RascalTypeFactory RTF = RascalTypeFactory.getInstance(); public void testFunctionFingerPrintStability() { Type intint = TF.functionType(TF.integerType(), TF.tupleType(TF.integerType()), TF.tupleEmpty()); @@ -59,19 +60,58 @@ public void testFunctionFingerPrintStability() { assertEquals(func.getMatchFingerprint(), "func".hashCode() + 89 * intint.hashCode()); } + public void testTreeApplFingerPrintStability() { + String prodString = "prod(sort(\"E\"),[],{})"; - public void testTreeFingerPrintStability() { - String prodString="prod(sort(\"E\"),[],{})"; try { IConstructor prod = (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString)); ITree tree = VF.appl(prod, VF.list()); - assertEquals(tree.getMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); + assertEquals(tree.getMatchFingerprint(), "prod".hashCode() + 131 * prod.arity()); + assertEquals(tree.getConcreteMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); } } - + public void testTreeAmbFingerPrintStability() { + String prodString = "prod(sort(\"E\"),[],{})"; + + try { + IConstructor prod = (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString)); + ITree tree = VF.appl(prod, VF.list()); + ITree amb = VF.amb(VF.set(tree)); + + assertEquals(amb.getMatchFingerprint(), "amb".hashCode() + 131); + assertEquals(tree.getConcreteMatchFingerprint(), "amb".hashCode() + 43 * TreeAdapter.getType(amb).hashCode()); + } + catch (FactTypeUseException | IOException e) { + fail(e.getMessage()); + } + } + + public void testTreeCharFingerPrintStability() { + try { + ITree theChar = (ITree) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Tree, new StringReader("char(32)")); + + assertEquals(theChar.getMatchFingerprint(), "char".hashCode() + 131); + assertEquals(theChar.getConcreteMatchFingerprint(), "char".hashCode() + ((IInteger) theChar.get(0)).intValue()); + } + catch (FactTypeUseException | IOException e) { + fail(e.getMessage()); + } + } + + public void testTreeCycleFingerPrintStability() { + try { + ITree theCycle = (ITree) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Tree, new StringReader("cycle(sort(\"A\"), 3)")); + + assertEquals(theCycle.getMatchFingerprint(), "cycle".hashCode() + 2 * 131); + assertEquals(theCycle.getConcreteMatchFingerprint(), "cycle".hashCode() + 13 * theCycle.get(0).hashCode()); + } + catch (FactTypeUseException | IOException e) { + fail(e.getMessage()); + } + } }