From dbe831d5d3fa7523925eee9b86862fa79ffbcb50 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Tue, 17 Oct 2023 10:09:04 +0200 Subject: [PATCH 01/19] bumped vallang to 1.0.0-RC2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2b9bffe76f..89d3e3815cf 100644 --- a/pom.xml +++ b/pom.xml @@ -364,7 +364,7 @@ io.usethesource vallang - 0.15.1 + 1.0.0-RC2 org.ow2.asm From 19ea311aa8aabebb3fdf5ab51a1f3d4ca20ff37f Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Tue, 17 Oct 2023 14:51:04 +0200 Subject: [PATCH 02/19] import --- pom.xml | 2 +- src/org/rascalmpl/interpreter/result/AbstractFunction.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 89d3e3815cf..87bdb22f229 100644 --- a/pom.xml +++ b/pom.xml @@ -361,7 +361,7 @@ junit 4.13.1 - + io.usethesource vallang 1.0.0-RC2 diff --git a/src/org/rascalmpl/interpreter/result/AbstractFunction.java b/src/org/rascalmpl/interpreter/result/AbstractFunction.java index a0a29e38fc2..326a1a19725 100644 --- a/src/org/rascalmpl/interpreter/result/AbstractFunction.java +++ b/src/org/rascalmpl/interpreter/result/AbstractFunction.java @@ -39,8 +39,8 @@ import org.rascalmpl.uri.URIUtil; import org.rascalmpl.values.RascalValueFactory; import org.rascalmpl.values.functions.IFunction; - -import io.usethesource.vallang.IConstructor; + +import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IExternalValue; import io.usethesource.vallang.IListWriter; import io.usethesource.vallang.ISourceLocation; From 7a1044e2ed1bbf34c7e825e9fcacd3e20b04bfe9 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Tue, 31 Oct 2023 18:39:53 +0100 Subject: [PATCH 03/19] implemented fingerprints for rascal specific values --- src/org/rascalmpl/library/vis/Charts.rsc | 4 +- src/org/rascalmpl/tasks/Transaction.java | 5 ++ .../rascalmpl/values/RascalValueFactory.java | 46 ++++++------------- .../rascalmpl/values/functions/IFunction.java | 5 ++ .../rascalmpl/values/parsetrees/ITree.java | 21 ++++++++- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/org/rascalmpl/library/vis/Charts.rsc b/src/org/rascalmpl/library/vis/Charts.rsc index 0a49bc12493..c01f0b17659 100644 --- a/src/org/rascalmpl/library/vis/Charts.rsc +++ b/src/org/rascalmpl/library/vis/Charts.rsc @@ -344,6 +344,7 @@ data ChartType data ChartOptions = chartOptions( bool responsive=true, + bool animations=false, ChartPlugins plugins = chartPlugins() ); @@ -395,13 +396,14 @@ A chart has a typical default layout that we can reuse for all kinds of chart ty provides the template and immediately instantiates the client and the server to start displaying the chart in a browser. } -Response(Request) chartServer(ChartData theData, ChartType \type=\bar(), str title="Chart", ChartAutoColorMode colorMode=\data(), bool legend=false) +Response(Request) chartServer(ChartData theData, ChartType \type=\bar(), str title="Chart", ChartAutoColorMode colorMode=\data(), bool legend=false, bool animations=false) = chartServer( chart( \type=\type, \data=theData, options=chartOptions( responsive=true, + animations=animations, plugins=chartPlugins( legend=chartLegend( position=top(), diff --git a/src/org/rascalmpl/tasks/Transaction.java b/src/org/rascalmpl/tasks/Transaction.java index f5fd61968e6..a5973405a33 100644 --- a/src/org/rascalmpl/tasks/Transaction.java +++ b/src/org/rascalmpl/tasks/Transaction.java @@ -417,6 +417,11 @@ public synchronized void expire(Object key) { map.remove(k); removed.add(k); } + + @Override + public int getMatchFingerprint() { + return hashCode(); + } } class Key { diff --git a/src/org/rascalmpl/values/RascalValueFactory.java b/src/org/rascalmpl/values/RascalValueFactory.java index abab1594b4e..9f6d4efed79 100644 --- a/src/org/rascalmpl/values/RascalValueFactory.java +++ b/src/org/rascalmpl/values/RascalValueFactory.java @@ -30,7 +30,6 @@ import io.usethesource.capsule.util.collection.AbstractSpecialisedImmutableMap; import io.usethesource.vallang.IConstructor; -import io.usethesource.vallang.IExternalValue; import io.usethesource.vallang.IInteger; import io.usethesource.vallang.IList; import io.usethesource.vallang.IListWriter; @@ -385,8 +384,16 @@ else if (constructor == Type_Reified || constructor.getAbstractDataType() == ADT @Override public IConstructor reifiedType(IConstructor symbol, IMap definitions) { + // This is where the "reified type contract" is implemented. + // A few inocuous lines of code that have a lot riding on them. + + // Contract: The dynamic type of a `type(symbol, definitionsMap)` constructor instance + // is `type[what the symbol value represents]`. + // So here the symbol value is "unlifted" to the {@see Type} representation (by `symbolToType`). + // Therefore you can count on that, for example, `type(int(), ())` has type `type[int]`. java.util.Map bindings = Collections.singletonMap(RascalValueFactory.TypeParam, tr.symbolToType(symbol, definitions)); + return super.constructor(RascalValueFactory.Type_Reified.instantiate(bindings), symbol, definitions); } @@ -505,9 +512,9 @@ public ITree amb(ISet alternatives) { * and {@link AbstractArgumentList} abstract classes. */ - static class CharInt implements ITree, IExternalValue { + static class CharInt implements ITree { final int ch; - + @Override public boolean isChar() { return true; @@ -526,11 +533,6 @@ public ITree accept(TreeVisitor v) throws E { public CharInt(int ch) { this.ch = ch; } - - @Override - public IConstructor encodeAsConstructor() { - return this; - } @Override public IValue get(int i) throws IndexOutOfBoundsException { @@ -675,7 +677,7 @@ protected ITree wrap(ITree content, io.usethesource.capsule.Map.Immutable ITree accept(TreeVisitor v) throws E { return (ITree) v.visitTreeChar(this); } - - @Override - public IConstructor encodeAsConstructor() { - return this; - } @Override public IValue get(int i) throws IndexOutOfBoundsException { @@ -850,7 +847,7 @@ protected ITree wrap(ITree content, io.usethesource.capsule.Map.Immutable ITree accept(TreeVisitor v) throws E { return (ITree) v.visitTreeCycle(this); } - @Override - public IConstructor encodeAsConstructor() { - return this; - } - @Override public String getName() { return Tree_Cycle.getName(); @@ -1034,7 +1026,7 @@ public IValue get(int i) throws IndexOutOfBoundsException { } } - private static class Amb implements ITree, IExternalValue { + private static class Amb implements ITree { protected final ISet alternatives; public Amb(ISet alts) { @@ -1051,11 +1043,6 @@ public ITree accept(TreeVisitor v) throws E { return (ITree) v.visitTreeAmb(this); } - @Override - public IConstructor encodeAsConstructor() { - return this; - } - @Override public String getName() { return Tree_Amb.getName(); @@ -1410,7 +1397,7 @@ public IInteger getCharacter() { } } - private static abstract class AbstractAppl implements ITree, IExternalValue { + private static abstract class AbstractAppl implements ITree { protected final IConstructor production; protected final boolean isMatchIgnorable; protected Type type = null; @@ -1439,11 +1426,6 @@ public boolean isAppl() { return true; } - @Override - public IConstructor encodeAsConstructor() { - return this; - } - @Override public String getName() { return Tree_Appl.getName(); diff --git a/src/org/rascalmpl/values/functions/IFunction.java b/src/org/rascalmpl/values/functions/IFunction.java index 9ff1ad04d67..b3b449f5587 100644 --- a/src/org/rascalmpl/values/functions/IFunction.java +++ b/src/org/rascalmpl/values/functions/IFunction.java @@ -22,6 +22,11 @@ public interface IFunction extends IExternalValue { + @Override + default int getMatchFingerprint() { + return 3154628 /* "func".hashCode() */ + 89 * getType().hashCode(); + } + /** * Invokes the receiver function. * diff --git a/src/org/rascalmpl/values/parsetrees/ITree.java b/src/org/rascalmpl/values/parsetrees/ITree.java index 8917e3d8b3b..2b6b3de6b12 100644 --- a/src/org/rascalmpl/values/parsetrees/ITree.java +++ b/src/org/rascalmpl/values/parsetrees/ITree.java @@ -3,14 +3,31 @@ import org.rascalmpl.values.parsetrees.visitors.TreeVisitor; import io.usethesource.vallang.IConstructor; +import io.usethesource.vallang.IExternalValue; import io.usethesource.vallang.IInteger; import io.usethesource.vallang.IList; import io.usethesource.vallang.INode; import io.usethesource.vallang.ISet; import io.usethesource.vallang.IValue; +import io.usethesource.vallang.visitors.IValueVisitor; -public interface ITree extends IConstructor { +public interface ITree extends IConstructor, IExternalValue { + @Override + default IConstructor encodeAsConstructor() { + return this; + } + + @Override + default T accept(IValueVisitor v) throws E { + return v.visitExternal(this); + } + + @Override + default int getMatchFingerprint() { + return 3568542 /* "tree".hashCode() */ + 41 * getProduction().hashCode(); + } + default boolean isAppl() { return false; } @@ -55,4 +72,4 @@ default INode setChildren(IValue[] childArray) { return result; } -} \ No newline at end of file +} From a0b78fa34513165e464652a790dc0ac0de9b79e6 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Wed, 1 Nov 2023 17:27:49 +0100 Subject: [PATCH 04/19] revived some tests that had not been in use for years --- pom.xml | 1 + test/org/rascalmpl/TypeReificationTest.java | 24 ++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index 1e6b66a7932..9fc19714997 100644 --- a/pom.xml +++ b/pom.xml @@ -219,6 +219,7 @@ **/org/rascalmpl/test/AllSuiteParallel.java **/org/rascalmpl/test/value/AllTests.java + **/org/rascalmpl/*Test.java diff --git a/test/org/rascalmpl/TypeReificationTest.java b/test/org/rascalmpl/TypeReificationTest.java index 4ac07edbfd6..78c0e83dafc 100644 --- a/test/org/rascalmpl/TypeReificationTest.java +++ b/test/org/rascalmpl/TypeReificationTest.java @@ -36,7 +36,7 @@ public void testJustRandomTypesWithoutExceptions() { Random rnd = new Random(); List collector = new LinkedList<>(); - int tries = 50000; + int tries = 500; for (int i = 0; i < tries; i++) { collector.add(tf.randomType(store, rnd, 5)); @@ -53,11 +53,7 @@ public void testEmptyTupleBidirectionality() { public void testEmptyTupleReturnFunBidirectionality() { TypeFactory tf = TypeFactory.getInstance(); - testOne(tf.functionType(tf.tupleEmpty(), tf.voidType(), tf.voidType()), new TypeStore()); - testOne(tf.functionType(tf.tupleEmpty(), tf.voidType(), null), new TypeStore()); - testOne(tf.functionType(tf.tupleEmpty(), tf.voidType(), tf.tupleEmpty()), new TypeStore()); - testOne(tf.functionType(tf.tupleEmpty(), tf.tupleEmpty(), tf.voidType()), new TypeStore()); - testOne(tf.functionType(tf.tupleEmpty(), tf.tupleEmpty(), null), new TypeStore()); + testOne(tf.functionType(tf.tupleEmpty(), tf.tupleEmpty(), tf.tupleEmpty()), new TypeStore()); } @@ -91,8 +87,8 @@ public void testFuncTypeKeywordParameter() { public void testFuncTypeParametersOrder() { TypeFactory tf = TypeFactory.getInstance(); - testOne(tf.functionType(tf.voidType(), tf.tupleType(new Type[] {tf.integerType(), tf.realType()}, new String[] {"a", "b"}), null), new TypeStore()); - testOne(tf.functionType(tf.voidType(), tf.tupleType(tf.integerType(), tf.realType()), null), new TypeStore()); + testOne(tf.functionType(tf.voidType(), tf.tupleType(new Type[] {tf.integerType(), tf.realType()}, new String[] {"a", "b"}), tf.tupleEmpty()), new TypeStore()); + testOne(tf.functionType(tf.voidType(), tf.tupleType(tf.integerType(), tf.realType()), tf.tupleEmpty()), new TypeStore()); } public void testFuncTypeReificationBidirectionality() { @@ -100,10 +96,14 @@ public void testFuncTypeReificationBidirectionality() { TypeStore store = new TypeStore(); for (int i = 0; i < 50; i++) { - Type type = tf.randomType(store); - while (!type.isFunction()) { - type = tf.randomType(store); + Type returnType = tf.randomType(store); + Type arg = tf.randomType(store); + + if (arg.isBottom()) { + continue; } + + Type type = tf.functionType(returnType, tf.tupleType(arg), tf.tupleEmpty()); testOne(type, store); } @@ -113,7 +113,7 @@ public void testTypeReificationBidirectionality() { TypeFactory tf = TypeFactory.getInstance(); TypeStore store = new TypeStore(); - for (int i = 0; i < 10_000; i++) { + for (int i = 0; i < 100; i++) { testOne(tf.randomType(store), store); } } From 56787fd40892e7a51d3eaec01056f646afd19cbb Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Wed, 1 Nov 2023 17:38:56 +0100 Subject: [PATCH 05/19] forgot to add --- test/org/rascalmpl/MatchFingerprintTest.java | 77 ++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/org/rascalmpl/MatchFingerprintTest.java diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java new file mode 100644 index 00000000000..70be9bc1d06 --- /dev/null +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2016, Jurgen J. Vinju, Centrum Wiskunde & Informatica (CWI) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.rascalmpl; + +import java.io.IOException; +import java.io.StringReader; + +import org.rascalmpl.interpreter.Evaluator; +import org.rascalmpl.interpreter.env.GlobalEnvironment; +import org.rascalmpl.interpreter.env.ModuleEnvironment; +import org.rascalmpl.types.RascalTypeFactory; + +import io.usethesource.vallang.IConstructor; +import io.usethesource.vallang.exceptions.FactTypeUseException; +import io.usethesource.vallang.io.StandardTextReader; +import io.usethesource.vallang.type.Type; +import io.usethesource.vallang.type.TypeFactory; +import org.rascalmpl.values.IRascalValueFactory; +import org.rascalmpl.values.RascalFunctionValueFactory; +import org.rascalmpl.values.functions.IFunction; +import org.rascalmpl.values.parsetrees.ITree; + +import junit.framework.TestCase; + +/** + * These tests check the hard contract on the integer values for `int IValue.getMatchFingerprint()`. + * Do not change these tests unless you are absolutely sure you know what you are doing. For one + * thing, changing fingerprints implies a pretty hairy bootstrap dependency hoop you have to jump through. + * Typically you would have to add a mode to the run-time to make it switch between the next version + * of the fingerprints for new code, and the old version of the fingerprints for the current run-time + * that runs the compiler. + */ +public class MatchFingerprintTest extends TestCase { + private final GlobalEnvironment heap = new GlobalEnvironment(); + private final ModuleEnvironment root = new ModuleEnvironment("root", heap); + 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()); + + IFunction func = VF.function(intint, (args, kwargs) -> { + return VF.integer(0); + }); + + // these magic numbers are sacred + assertEquals(func.getMatchFingerprint(), "func".hashCode() + 89 * intint.hashCode()); + } + + + 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()); + } + catch (FactTypeUseException | IOException e) { + fail(e.getMessage()); + } + } + + +} From afe38a0166416dd924955d9536fcd570ac2e5e28 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 2 Nov 2023 08:57:53 +0100 Subject: [PATCH 06/19] added getConcreteMatchFingerprint to ITree --- .../rascalmpl/values/RascalValueFactory.java | 54 ++++++++++++++++++- .../rascalmpl/values/parsetrees/ITree.java | 17 +++++- .../values/parsetrees/TreeAdapter.java | 1 + test/org/rascalmpl/MatchFingerprintTest.java | 50 +++++++++++++++-- 4 files changed, 114 insertions(+), 8 deletions(-) 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()); + } + } } From 879bff99d1bacfd76dad860b02f942af87b327b2 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 2 Nov 2023 09:15:35 +0100 Subject: [PATCH 07/19] fixed a bug that let the test fail --- src/org/rascalmpl/values/RascalValueFactory.java | 8 ++------ test/org/rascalmpl/MatchFingerprintTest.java | 16 ++++++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/org/rascalmpl/values/RascalValueFactory.java b/src/org/rascalmpl/values/RascalValueFactory.java index 69911110028..65a76ca1f6d 100644 --- a/src/org/rascalmpl/values/RascalValueFactory.java +++ b/src/org/rascalmpl/values/RascalValueFactory.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; import org.rascalmpl.parser.gtd.util.ArrayList; +import org.rascalmpl.types.NonTerminalType; import org.rascalmpl.types.RascalTypeFactory; import org.rascalmpl.types.TypeReifier; import org.rascalmpl.values.parsetrees.ITree; @@ -1050,18 +1051,13 @@ public Amb(ISet alts) { @Override public int getConcreteMatchFingerprint() { - return 96694 /* "amb".hashCode() */ + 43 * alternatives.getElementType().hashCode(); + return 96694 /* "amb".hashCode() */ + 43 * ((NonTerminalType) alternatives.getElementType()).getSymbol().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 { diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java index 72674cde85a..c0b3001c43b 100644 --- a/test/org/rascalmpl/MatchFingerprintTest.java +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -67,7 +67,7 @@ public void testTreeApplFingerPrintStability() { IConstructor prod = (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString)); ITree tree = VF.appl(prod, VF.list()); - assertEquals(tree.getMatchFingerprint(), "prod".hashCode() + 131 * prod.arity()); + assertEquals(tree.getMatchFingerprint(), "appl".hashCode() + 131 * 2); assertEquals(tree.getConcreteMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); } catch (FactTypeUseException | IOException e) { @@ -76,15 +76,19 @@ public void testTreeApplFingerPrintStability() { } public void testTreeAmbFingerPrintStability() { - String prodString = "prod(sort(\"E\"),[],{})"; + String prodString1 = "prod(sort(\"E\"),[],{})"; + String prodString2 = "prod(sort(\"E\"),[empty()],{})"; 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)); + IConstructor prod1= (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString1)); + ITree tree1 = VF.appl(prod1, VF.list()); + IConstructor prod2= (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString2)); + ITree tree2 = VF.appl(prod2, VF.list()); + + ITree amb = VF.amb(VF.set(tree1, tree2)); assertEquals(amb.getMatchFingerprint(), "amb".hashCode() + 131); - assertEquals(tree.getConcreteMatchFingerprint(), "amb".hashCode() + 43 * TreeAdapter.getType(amb).hashCode()); + assertEquals(amb.getConcreteMatchFingerprint(), "amb".hashCode() + 43 * TreeAdapter.getType(amb).hashCode()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); From 500536d163e0f0c73192d923d3e7ff27001bd3d4 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 2 Nov 2023 09:16:08 +0100 Subject: [PATCH 08/19] added an assert --- src/org/rascalmpl/values/RascalValueFactory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/rascalmpl/values/RascalValueFactory.java b/src/org/rascalmpl/values/RascalValueFactory.java index 65a76ca1f6d..ad971e1dede 100644 --- a/src/org/rascalmpl/values/RascalValueFactory.java +++ b/src/org/rascalmpl/values/RascalValueFactory.java @@ -1046,6 +1046,7 @@ private static class Amb implements ITree { protected final ISet alternatives; public Amb(ISet alts) { + assert alts.size() > 0; this.alternatives = alts; } From 5ff2a9365108779e98d3f0634713a36432aeb99c Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 2 Nov 2023 12:18:21 +0100 Subject: [PATCH 09/19] removed spurious change in Chart.rsc --- src/org/rascalmpl/library/vis/Charts.rsc | 2 +- test/org/rascalmpl/MatchFingerprintTest.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/org/rascalmpl/library/vis/Charts.rsc b/src/org/rascalmpl/library/vis/Charts.rsc index c01f0b17659..9c2e5cfc2e0 100644 --- a/src/org/rascalmpl/library/vis/Charts.rsc +++ b/src/org/rascalmpl/library/vis/Charts.rsc @@ -344,7 +344,7 @@ data ChartType data ChartOptions = chartOptions( bool responsive=true, - bool animations=false, + bool animations=true, ChartPlugins plugins = chartPlugins() ); diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java index c0b3001c43b..16781a03f93 100644 --- a/test/org/rascalmpl/MatchFingerprintTest.java +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -18,8 +18,6 @@ import org.rascalmpl.interpreter.Evaluator; import org.rascalmpl.interpreter.env.GlobalEnvironment; import org.rascalmpl.interpreter.env.ModuleEnvironment; -import org.rascalmpl.types.RascalTypeFactory; - import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IInteger; import io.usethesource.vallang.exceptions.FactTypeUseException; From 63489116e8bd8f53d76b1a0aa87fc94c14e7526f Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 2 Nov 2023 12:34:31 +0100 Subject: [PATCH 10/19] added tests to cover also the trees with keyword parameters --- test/org/rascalmpl/MatchFingerprintTest.java | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java index 16781a03f93..abe33556702 100644 --- a/test/org/rascalmpl/MatchFingerprintTest.java +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -20,6 +20,7 @@ import org.rascalmpl.interpreter.env.ModuleEnvironment; import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IInteger; +import io.usethesource.vallang.ISourceLocation; import io.usethesource.vallang.exceptions.FactTypeUseException; import io.usethesource.vallang.io.StandardTextReader; import io.usethesource.vallang.type.Type; @@ -60,6 +61,7 @@ public void testFunctionFingerPrintStability() { public void testTreeApplFingerPrintStability() { String prodString = "prod(sort(\"E\"),[],{})"; + ISourceLocation loc = VF.sourceLocation("BLABLA"); try { IConstructor prod = (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString)); @@ -67,6 +69,12 @@ public void testTreeApplFingerPrintStability() { assertEquals(tree.getMatchFingerprint(), "appl".hashCode() + 131 * 2); assertEquals(tree.getConcreteMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); + + // and now WITH a keyword parameter + tree = (ITree) tree.asWithKeywordParameters().setParameter("src", loc); + + assertEquals(tree.getMatchFingerprint(), "appl".hashCode() + 131 * 2); + assertEquals(tree.getConcreteMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); @@ -76,6 +84,7 @@ public void testTreeApplFingerPrintStability() { public void testTreeAmbFingerPrintStability() { String prodString1 = "prod(sort(\"E\"),[],{})"; String prodString2 = "prod(sort(\"E\"),[empty()],{})"; + ISourceLocation loc = VF.sourceLocation("BLABLA"); try { IConstructor prod1= (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString1)); @@ -87,6 +96,12 @@ public void testTreeAmbFingerPrintStability() { assertEquals(amb.getMatchFingerprint(), "amb".hashCode() + 131); assertEquals(amb.getConcreteMatchFingerprint(), "amb".hashCode() + 43 * TreeAdapter.getType(amb).hashCode()); + + // and now WITH a keyword parameter + amb = (ITree) amb.asWithKeywordParameters().setParameter("src", loc); + + assertEquals(amb.getMatchFingerprint(), "amb".hashCode() + 131); + assertEquals(amb.getConcreteMatchFingerprint(), "amb".hashCode() + 43 * TreeAdapter.getType(amb).hashCode()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); @@ -94,11 +109,19 @@ public void testTreeAmbFingerPrintStability() { } public void testTreeCharFingerPrintStability() { + ISourceLocation loc = VF.sourceLocation("BLABLA"); + 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()); + + // and now WITH a keyword parameter + theChar = (ITree) theChar.asWithKeywordParameters().setParameter("src", loc); + + assertEquals(theChar.getMatchFingerprint(), "char".hashCode() + 131); + assertEquals(theChar.getConcreteMatchFingerprint(), "char".hashCode() + ((IInteger) theChar.get(0)).intValue()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); @@ -106,11 +129,19 @@ public void testTreeCharFingerPrintStability() { } public void testTreeCycleFingerPrintStability() { + ISourceLocation loc = VF.sourceLocation("BLABLA"); + 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()); + + // and now WITH a keyword parameter + theCycle = (ITree) theCycle.asWithKeywordParameters().setParameter("src", loc); + + assertEquals(theCycle.getMatchFingerprint(), "cycle".hashCode() + 2 * 131); + assertEquals(theCycle.getConcreteMatchFingerprint(), "cycle".hashCode() + 13 * theCycle.get(0).hashCode()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); From ef6ecdeb75611faed6c54546434c57d06a9952b2 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Thu, 2 Nov 2023 13:39:45 +0100 Subject: [PATCH 11/19] added some tests --- test/org/rascalmpl/MatchFingerprintTest.java | 23 +++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java index abe33556702..93283cd5b1e 100644 --- a/test/org/rascalmpl/MatchFingerprintTest.java +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -12,6 +12,8 @@ */ package org.rascalmpl; +import static org.junit.Assert.assertNotEquals; + import java.io.IOException; import java.io.StringReader; @@ -81,6 +83,25 @@ public void testTreeApplFingerPrintStability() { } } + public void testTreeFingerprintDifferentiation() { + String prodString1 = "prod(sort(\"E\"),[],{})"; + String prodString2 = "prod(sort(\"E\"),[empty()],{})"; + + try { + IConstructor prod1= (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString1)); + ITree tree1 = VF.appl(prod1, VF.list()); + IConstructor prod2= (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString2)); + ITree tree2 = VF.appl(prod2, VF.list()); + + // these two assert explain the different between `getMatchFingerprint` and `getConcreteMatchFingerprint` + assertEquals(tree1.getMatchFingerprint(), tree2.getMatchFingerprint()); + assertNotEquals(tree1.getConcreteMatchFingerprint(), tree2.getMatchFingerprint()); + } + catch (FactTypeUseException | IOException e) { + fail(e.getMessage()); + } + } + public void testTreeAmbFingerPrintStability() { String prodString1 = "prod(sort(\"E\"),[],{})"; String prodString2 = "prod(sort(\"E\"),[empty()],{})"; @@ -91,7 +112,7 @@ public void testTreeAmbFingerPrintStability() { ITree tree1 = VF.appl(prod1, VF.list()); IConstructor prod2= (IConstructor) new StandardTextReader().read(VF, RascalFunctionValueFactory.getStore(), RascalFunctionValueFactory.Production, new StringReader(prodString2)); ITree tree2 = VF.appl(prod2, VF.list()); - + ITree amb = VF.amb(VF.set(tree1, tree2)); assertEquals(amb.getMatchFingerprint(), "amb".hashCode() + 131); From 21b489a28c4f4abf62a65a12250a0127bf7ee89e Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 10:40:17 +0100 Subject: [PATCH 12/19] added Rascal implementation of the fingerprinting for use in the compiler including a bunch of tests --- .../lang/rascal/matching/Fingerprint.rsc | 123 ++++++++++++++++++ .../rascal/matching/internal/Fingerprint.java | 27 ++++ .../rascalmpl/values/RascalValueFactory.java | 2 +- 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc create mode 100644 src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc new file mode 100644 index 00000000000..ab2cef832fa --- /dev/null +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -0,0 +1,123 @@ +@license{ + Copyright (c) 2023 CWI + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html +} +@contributor{Jurgen Vinju - Jurgen.Vinju@cwi.nl} +@synopsis{Core functions for implementing fast pattern matching in the Rascal compiler.} +@description{ +These functions tie together the run-time features of IValue and ITree for computing fast +fingerprints, with compile-time information for generating switch cases that uses these fingerprints. + +There are several explicit contracts implemented here: + * a fingerprint is (almost) never `0`. + * the fingerprint functions in this module implement exactly the fingerprinting of the run-time that the generated code will be linked against. + This contract is tested with internal tests in this module. + * `value matches pattern ==> fingerprint(pattern) == fingerprint(value)` such that a fingerprint is always an over-approximation of matching. It may + never be the case that a value should match a pattern and the fingerprint contradicts this. + This contract is tested by the pattern matching tests for the interpreter and the compiler. + * fingerprints distinguish the identity of the outermost value construct as much as possible. I.e. production rules and constructors are + mapped to different codes as much as possible, without breaking the fingerprinting contract. + This contract is not automatically tested. Performance regressions may be caused by accidental fingerprinting collisions. + * there is also an equals contract: `value1 equals value2 ==> fingerprint(value1) == fingerprint(value2)`, which is a collorary from the pattern + matching contract if you consider that patterns may also be equality tests. + +As you can read the computation of fingerprints reuses a lot of internal hashcodes. Mainly these boil down to the hash codes of: +* Java internal strings +* Java integers +* Vallang implementations of nested constructors for Symbol and Production. + +And so when one of these hashCode implementations changes, the code below may _not_ break and _not_ fail any test +and still break the backward compatibility of all previously generated code. The tests in the vallang project try to +detect such an event by replicating the hashcode computations literally in some of the regression tests. +} +module lang::rascal::matching::Fingerprint + +extend ParseTree; +import Node; +import List; + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Compute the match fingerprint for any constant value. Only used for testing purposes.} +@description{ +To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, +the fingerprinting computation is replicated in this module. However, the computation should be the +same as this internalFingerprint function as long as nothing changes between compiler and run-time versions +in the computations for fingerprinting. +} +private java int internalFingerprint(value x); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Compute the concrete match fingerprint for any parse `Tree`. Only used for testing purposes.} +@description{ +To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, +the fingerprinting computation is replicated in this module. However, the computation should be the +same as this internalFingerprint function as long as nothing changes between compiler and run-time versions +in the computations for fingerprinting. +} +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +private java int internalConcreteFingerprint(Tree x); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Get the Object.hashCode() of the Java implementation of a Rascal value.} +@description{ +This hash code is sometimes a part of computing a fingerprint. Do not make this function +public. Rascal values are hashed already and exactly these hashes are used internally by the +set, relation and map data-structures. There is no need to write Rascal programs that "hash +on the hash", and it would leak implementation details that are very hard to encapsulate again. +} +private java int internalHashcode(value x); + +@synopsis{Computes a unique fingerprint for each kind of tree based on the identity of the top-level tree node.} +@description{ +Concrete fingerprint implements the pattern matching contract: +`value matches pattern ==> fingerprint(pattern) == fingerprint(value)` + +For normal parse trees the fingerprint function makes sure that there are different integers if the +top-level production is different. This makes it possible to quickly switch on the outermost production rule +while pattern matching. + +To complete the function for the other kinds of trees, even though less important for efficiency, we also +implement a sensible encoding that follows the contract and tries to differentiate as much as possible between different values. +} +int concreteFingerprint(appl(Production p, list[Tree] _)) = concreteFingerprint(p); +int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), _})) = internalHashcode("appl") + 43 * internalHashcode(t) + when label(_, Symbol t) := s || Symbol t := s; +int concreteFingerprint(char(int ch)) = internalHashcode("char") + internalHashcode(ch); +int concreteFingerprint(cycle(Symbol s, int _)) = internalHashcode("cycle") + 13 * internalHashcode(s); + +@synopsis{Compute a fingerprint for a match pattern with this outermost production rule} +int concreteFingerprint(Production p) = 3568542 + 41 * internalHashcode(p); + +@synopsis{These two implementations are intentional clones.} +test bool concreteFingerprintAlignment(Tree x) = concreteFingerprint(x) == internalConcreteFingerprint(x); + +@synopsis{Computes a unique fingerprint for each kind of value based on the identity of the top-level kind.} +@description{ +Fingerprint implements the pattern matching contract: +`value matches pattern ==> fingerprint(pattern) == fingerprint(value)` + +Work is done to avoid generating the 0 fingerprint for simple values like empty strings and 0 integers, etc. +} +int fingerprint(str r) = internalHashcode(r) == 0 ? internalHashcode("str") : internalHashcode(r); +int fingerprint(int r) = internalHashcode(r) == 0 ? internalHashcode("int") : internalHashcode(r); +int fingerprint(real r) = internalHashcode(r) == 0 ? internalHashcode("real") : internalHashcode(r); +int fingerprint(rat r) = internalHashcode(r) == 0 ? internalHashcode("rat") : internalHashcode(r); +int fingerprint(value t) = tupleFingerprint(size(fields)) when \tuple(list[Symbol] fields) := typeOf(t); +default int fingerprint(value n) = internalHashcode(n); + +int fingerprint(node n) = fingerprint(getName(n), arity(n)); + +int fingerprint("" , int arity) = internalHashcode("node") + 131 * arity; +default int fingerprint(str name, int arity) = internalHashcode(name) + 131 * arity; + +int fingerprint(list[value] l) = internalHashcode("list"); +int fingerprint(set[value] l) = internalHashcode("set"); +int fingerprint(map[value,value] l) = internalHashcode("map"); + +int tupleFingerprint(int arity) = internalHashcode("tuple") + arity; + +@synopsis{These two implementations are intentional clones.} +test bool fingerprintAlignment(value x) = fingerprint(x) == internalFingerprint(x); \ No newline at end of file diff --git a/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java b/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java new file mode 100644 index 00000000000..d405e0c00a3 --- /dev/null +++ b/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java @@ -0,0 +1,27 @@ +package org.rascalmpl.library.lang.rascal.matching.internal; + +import org.rascalmpl.values.parsetrees.ITree; + +import io.usethesource.vallang.IInteger; +import io.usethesource.vallang.IValue; +import io.usethesource.vallang.IValueFactory; + +public class Fingerprint { + private final IValueFactory vf; + + public Fingerprint(IValueFactory vf) { + this.vf = vf; + } + + public IInteger internalFingerprint(IValue v) { + return vf.integer(v.getMatchFingerprint()); + } + + public IInteger internalConcreteFingerprint(ITree v) { + return vf.integer(v.getConcreteMatchFingerprint()); + } + + public IInteger internalHashCode(IValue v) { + return vf.integer(v.hashCode()); + } +} diff --git a/src/org/rascalmpl/values/RascalValueFactory.java b/src/org/rascalmpl/values/RascalValueFactory.java index ad971e1dede..214343d23e7 100644 --- a/src/org/rascalmpl/values/RascalValueFactory.java +++ b/src/org/rascalmpl/values/RascalValueFactory.java @@ -1447,7 +1447,7 @@ private static abstract class AbstractAppl implements ITree { @Override public int getConcreteMatchFingerprint() { - return 3568542 /* "tree".hashCode() */ + 41 * production.hashCode(); + return 3000939 /* "appl".hashCode() */ + 41 * production.hashCode(); } @Override From 6e6299e096f459ca30af4658ad66303dead6d001 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 10:44:39 +0100 Subject: [PATCH 13/19] streamlining --- .../lang/rascal/matching/Fingerprint.rsc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index ab2cef832fa..e7d0ec9c891 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -108,16 +108,21 @@ int fingerprint(rat r) = internalHashcode(r) == 0 ? internalHashcode(" int fingerprint(value t) = tupleFingerprint(size(fields)) when \tuple(list[Symbol] fields) := typeOf(t); default int fingerprint(value n) = internalHashcode(n); -int fingerprint(node n) = fingerprint(getName(n), arity(n)); +int fingerprint(node n) = nodeFingerprint(getName(n), arity(n)); -int fingerprint("" , int arity) = internalHashcode("node") + 131 * arity; -default int fingerprint(str name, int arity) = internalHashcode(name) + 131 * arity; +int fingerprint(list[value] l) = listFingerprint(); +int fingerprint(set[value] l) = setFingerprint(); +int fingerprint(map[value,value] l) = mapFingerprint(); -int fingerprint(list[value] l) = internalHashcode("list"); -int fingerprint(set[value] l) = internalHashcode("set"); -int fingerprint(map[value,value] l) = internalHashcode("map"); + +int nodeFingerprint("" , int arity) = internalHashcode("node") + 131 * arity; +default int nodeFingerprint(str name, int arity) = internalHashcode(name) + 131 * arity; int tupleFingerprint(int arity) = internalHashcode("tuple") + arity; +int listFingerprint() = internalHashcode("list"); +int setFingerprint() = internalHashcode("set"); +int mapFingerprint() = internalHashcode("map"); +int constructorFingerprint(str name, int arity) = nodeFingerprint(name, arity); @synopsis{These two implementations are intentional clones.} test bool fingerprintAlignment(value x) = fingerprint(x) == internalFingerprint(x); \ No newline at end of file From 66d94f2dc027ea4a13b122cee4741132c1a0cc02 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 10:54:27 +0100 Subject: [PATCH 14/19] bugfixes --- .../rascalmpl/library/lang/rascal/matching/Fingerprint.rsc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index e7d0ec9c891..4f7afe99d87 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -83,13 +83,13 @@ To complete the function for the other kinds of trees, even though less importan implement a sensible encoding that follows the contract and tries to differentiate as much as possible between different values. } int concreteFingerprint(appl(Production p, list[Tree] _)) = concreteFingerprint(p); -int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), _})) = internalHashcode("appl") + 43 * internalHashcode(t) +int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), _})) = internalHashcode("amb") + 43 * internalHashcode(t) when label(_, Symbol t) := s || Symbol t := s; int concreteFingerprint(char(int ch)) = internalHashcode("char") + internalHashcode(ch); int concreteFingerprint(cycle(Symbol s, int _)) = internalHashcode("cycle") + 13 * internalHashcode(s); @synopsis{Compute a fingerprint for a match pattern with this outermost production rule} -int concreteFingerprint(Production p) = 3568542 + 41 * internalHashcode(p); +int concreteFingerprint(Production p) = internalHashcode("appl") + 41 * internalHashcode(p); @synopsis{These two implementations are intentional clones.} test bool concreteFingerprintAlignment(Tree x) = concreteFingerprint(x) == internalConcreteFingerprint(x); From 8452b21328574111e4472f200537d1f2a7872ed5 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 11:06:33 +0100 Subject: [PATCH 15/19] bumped vallang to 1.0.0-RC3 for changes in the fingerprinting --- pom.xml | 2 +- .../rascalmpl/library/lang/rascal/matching/Fingerprint.rsc | 6 +++++- test/org/rascalmpl/MatchFingerprintTest.java | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 9fc19714997..feba2b5d1c5 100644 --- a/pom.xml +++ b/pom.xml @@ -365,7 +365,7 @@ io.usethesource vallang - 1.0.0-RC2 + 1.0.0-RC3 org.ow2.asm diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index 4f7afe99d87..002f9e78063 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -14,7 +14,11 @@ fingerprints, with compile-time information for generating switch cases that use There are several explicit contracts implemented here: * a fingerprint is (almost) never `0`. * the fingerprint functions in this module implement exactly the fingerprinting of the run-time that the generated code will be linked against. - This contract is tested with internal tests in this module. + This contract is tested with internal tests in this module: fingerprintAlignment and concreteFingerprintAlignment. + If these tests fail, it is possible that during a bootstrap cycle of 3 steps, + the contract is temporarily not satisfied in the first and second steps. To break the impasse, the code below allows us to generate fingerprints for + the _next_ run-time version, while the current run-time still runs the _previous_ version of the compiler. We have to disable the `concreteFingerprintAlignment` + and `fingerprintAlignment` tests temporarily during the first and second run. * `value matches pattern ==> fingerprint(pattern) == fingerprint(value)` such that a fingerprint is always an over-approximation of matching. It may never be the case that a value should match a pattern and the fingerprint contradicts this. This contract is tested by the pattern matching tests for the interpreter and the compiler. diff --git a/test/org/rascalmpl/MatchFingerprintTest.java b/test/org/rascalmpl/MatchFingerprintTest.java index 93283cd5b1e..41614b94b05 100644 --- a/test/org/rascalmpl/MatchFingerprintTest.java +++ b/test/org/rascalmpl/MatchFingerprintTest.java @@ -70,13 +70,13 @@ public void testTreeApplFingerPrintStability() { ITree tree = VF.appl(prod, VF.list()); assertEquals(tree.getMatchFingerprint(), "appl".hashCode() + 131 * 2); - assertEquals(tree.getConcreteMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); + assertEquals(tree.getConcreteMatchFingerprint(), "appl".hashCode() + 41 * prod.hashCode()); // and now WITH a keyword parameter tree = (ITree) tree.asWithKeywordParameters().setParameter("src", loc); assertEquals(tree.getMatchFingerprint(), "appl".hashCode() + 131 * 2); - assertEquals(tree.getConcreteMatchFingerprint(), "tree".hashCode() + 41 * prod.hashCode()); + assertEquals(tree.getConcreteMatchFingerprint(), "appl".hashCode() + 41 * prod.hashCode()); } catch (FactTypeUseException | IOException e) { fail(e.getMessage()); From 5bb00ae983bf04cef19d61e4df45e994225af2da Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 11:33:26 +0100 Subject: [PATCH 16/19] put private stuff down in the module --- .../lang/rascal/matching/Fingerprint.rsc | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index 002f9e78063..03717e71020 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -43,36 +43,7 @@ extend ParseTree; import Node; import List; -@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} -@synopsis{Compute the match fingerprint for any constant value. Only used for testing purposes.} -@description{ -To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, -the fingerprinting computation is replicated in this module. However, the computation should be the -same as this internalFingerprint function as long as nothing changes between compiler and run-time versions -in the computations for fingerprinting. -} -private java int internalFingerprint(value x); - -@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} -@synopsis{Compute the concrete match fingerprint for any parse `Tree`. Only used for testing purposes.} -@description{ -To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, -the fingerprinting computation is replicated in this module. However, the computation should be the -same as this internalFingerprint function as long as nothing changes between compiler and run-time versions -in the computations for fingerprinting. -} -@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} -private java int internalConcreteFingerprint(Tree x); -@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} -@synopsis{Get the Object.hashCode() of the Java implementation of a Rascal value.} -@description{ -This hash code is sometimes a part of computing a fingerprint. Do not make this function -public. Rascal values are hashed already and exactly these hashes are used internally by the -set, relation and map data-structures. There is no need to write Rascal programs that "hash -on the hash", and it would leak implementation details that are very hard to encapsulate again. -} -private java int internalHashcode(value x); @synopsis{Computes a unique fingerprint for each kind of tree based on the identity of the top-level tree node.} @description{ @@ -95,9 +66,6 @@ int concreteFingerprint(cycle(Symbol s, int _)) = in @synopsis{Compute a fingerprint for a match pattern with this outermost production rule} int concreteFingerprint(Production p) = internalHashcode("appl") + 41 * internalHashcode(p); -@synopsis{These two implementations are intentional clones.} -test bool concreteFingerprintAlignment(Tree x) = concreteFingerprint(x) == internalConcreteFingerprint(x); - @synopsis{Computes a unique fingerprint for each kind of value based on the identity of the top-level kind.} @description{ Fingerprint implements the pattern matching contract: @@ -128,5 +96,40 @@ int setFingerprint() = internalHashcode("set"); int mapFingerprint() = internalHashcode("map"); int constructorFingerprint(str name, int arity) = nodeFingerprint(name, arity); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Compute the match fingerprint for any constant value. Only used for testing purposes.} +@description{ +To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, +the fingerprinting computation is replicated in this module. However, the computation should be the +same as this internalFingerprint function as long as nothing changes between compiler and run-time versions +in the computations for fingerprinting. +} +private java int internalFingerprint(value x); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Compute the concrete match fingerprint for any parse `Tree`. Only used for testing purposes.} +@description{ +To decouple the Rascal compilers code generator from the bootstrapped run-time it is running in itself, +the fingerprinting computation is replicated in this module. However, the computation should be the +same as this internalFingerprint function as long as nothing changes between compiler and run-time versions +in the computations for fingerprinting. +} +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +private java int internalConcreteFingerprint(Tree x); + +@javaClass{org.rascalmpl.library.lang.rascal.matching.internal.Fingerprint} +@synopsis{Get the Object.hashCode() of the Java implementation of a Rascal value.} +@description{ +This hash code is sometimes a part of computing a fingerprint. Do not make this function +public. Rascal values are hashed already and exactly these hashes are used internally by the +set, relation and map data-structures. There is no need to write Rascal programs that "hash +on the hash", and it would leak implementation details that are very hard to encapsulate again. +} +private java int internalHashcode(value x); + +@synopsis{These two implementations are intentional clones.} +test bool fingerprintAlignment(value x) = fingerprint(x) == internalFingerprint(x); + @synopsis{These two implementations are intentional clones.} -test bool fingerprintAlignment(value x) = fingerprint(x) == internalFingerprint(x); \ No newline at end of file +test bool concreteFingerprintAlignment(Tree x) = concreteFingerprint(x) == internalConcreteFingerprint(x); From a3f6378beaab6c09eb07e3f733b210e4264ab441 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 14:35:38 +0100 Subject: [PATCH 17/19] final fixes --- .../lang/rascal/matching/Fingerprint.rsc | 34 +++++++++---------- .../rascal/matching/internal/Fingerprint.java | 5 +-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index 03717e71020..6364ae4af71 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -43,8 +43,6 @@ extend ParseTree; import Node; import List; - - @synopsis{Computes a unique fingerprint for each kind of tree based on the identity of the top-level tree node.} @description{ Concrete fingerprint implements the pattern matching contract: @@ -58,13 +56,13 @@ To complete the function for the other kinds of trees, even though less importan implement a sensible encoding that follows the contract and tries to differentiate as much as possible between different values. } int concreteFingerprint(appl(Production p, list[Tree] _)) = concreteFingerprint(p); -int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), _})) = internalHashcode("amb") + 43 * internalHashcode(t) +int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), _})) = internalHashCode("amb") + 43 * internalHashCode(t) when label(_, Symbol t) := s || Symbol t := s; -int concreteFingerprint(char(int ch)) = internalHashcode("char") + internalHashcode(ch); -int concreteFingerprint(cycle(Symbol s, int _)) = internalHashcode("cycle") + 13 * internalHashcode(s); +int concreteFingerprint(char(int ch)) = internalHashCode("char") + internalHashCode(ch); +int concreteFingerprint(cycle(Symbol s, int _)) = internalHashCode("cycle") + 13 * internalHashCode(s); @synopsis{Compute a fingerprint for a match pattern with this outermost production rule} -int concreteFingerprint(Production p) = internalHashcode("appl") + 41 * internalHashcode(p); +int concreteFingerprint(Production p) = internalHashCode("appl") + 41 * internalHashCode(p); @synopsis{Computes a unique fingerprint for each kind of value based on the identity of the top-level kind.} @description{ @@ -73,12 +71,12 @@ Fingerprint implements the pattern matching contract: Work is done to avoid generating the 0 fingerprint for simple values like empty strings and 0 integers, etc. } -int fingerprint(str r) = internalHashcode(r) == 0 ? internalHashcode("str") : internalHashcode(r); -int fingerprint(int r) = internalHashcode(r) == 0 ? internalHashcode("int") : internalHashcode(r); -int fingerprint(real r) = internalHashcode(r) == 0 ? internalHashcode("real") : internalHashcode(r); -int fingerprint(rat r) = internalHashcode(r) == 0 ? internalHashcode("rat") : internalHashcode(r); +int fingerprint(str r) = internalHashCode(r) == 0 ? internalHashCode("str") : internalHashCode(r); +int fingerprint(int r) = internalHashCode(r) == 0 ? internalHashCode("int") : internalHashCode(r); +int fingerprint(real r) = internalHashCode(r) == 0 ? internalHashCode("real") : internalHashCode(r); +int fingerprint(rat r) = internalHashCode(r) == 0 ? internalHashCode("rat") : internalHashCode(r); int fingerprint(value t) = tupleFingerprint(size(fields)) when \tuple(list[Symbol] fields) := typeOf(t); -default int fingerprint(value n) = internalHashcode(n); +default int fingerprint(value n) = internalHashCode(n); int fingerprint(node n) = nodeFingerprint(getName(n), arity(n)); @@ -87,13 +85,13 @@ int fingerprint(set[value] l) = setFingerprint(); int fingerprint(map[value,value] l) = mapFingerprint(); -int nodeFingerprint("" , int arity) = internalHashcode("node") + 131 * arity; -default int nodeFingerprint(str name, int arity) = internalHashcode(name) + 131 * arity; +int nodeFingerprint("" , int arity) = internalHashCode("node") + 131 * arity; +default int nodeFingerprint(str name, int arity) = internalHashCode(name) + 131 * arity; -int tupleFingerprint(int arity) = internalHashcode("tuple") + arity; -int listFingerprint() = internalHashcode("list"); -int setFingerprint() = internalHashcode("set"); -int mapFingerprint() = internalHashcode("map"); +int tupleFingerprint(int arity) = internalHashCode("tuple") + arity; +int listFingerprint() = internalHashCode("list"); +int setFingerprint() = internalHashCode("set"); +int mapFingerprint() = internalHashCode("map"); int constructorFingerprint(str name, int arity) = nodeFingerprint(name, arity); @@ -126,7 +124,7 @@ public. Rascal values are hashed already and exactly these hashes are used inter set, relation and map data-structures. There is no need to write Rascal programs that "hash on the hash", and it would leak implementation details that are very hard to encapsulate again. } -private java int internalHashcode(value x); +private java int internalHashCode(value x); @synopsis{These two implementations are intentional clones.} test bool fingerprintAlignment(value x) = fingerprint(x) == internalFingerprint(x); diff --git a/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java b/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java index d405e0c00a3..1ad933f4e3f 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java +++ b/src/org/rascalmpl/library/lang/rascal/matching/internal/Fingerprint.java @@ -2,6 +2,7 @@ import org.rascalmpl.values.parsetrees.ITree; +import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IInteger; import io.usethesource.vallang.IValue; import io.usethesource.vallang.IValueFactory; @@ -17,8 +18,8 @@ public IInteger internalFingerprint(IValue v) { return vf.integer(v.getMatchFingerprint()); } - public IInteger internalConcreteFingerprint(ITree v) { - return vf.integer(v.getConcreteMatchFingerprint()); + public IInteger internalConcreteFingerprint(IConstructor v) { + return vf.integer(((ITree) v).getConcreteMatchFingerprint()); } public IInteger internalHashCode(IValue v) { From 37a36c635cca4d608584574192eb333f5b197272 Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 14:42:29 +0100 Subject: [PATCH 18/19] added missing bools --- .../library/lang/rascal/matching/Fingerprint.rsc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index 6364ae4af71..ce4f6d688c9 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -71,10 +71,10 @@ Fingerprint implements the pattern matching contract: Work is done to avoid generating the 0 fingerprint for simple values like empty strings and 0 integers, etc. } -int fingerprint(str r) = internalHashCode(r) == 0 ? internalHashCode("str") : internalHashCode(r); -int fingerprint(int r) = internalHashCode(r) == 0 ? internalHashCode("int") : internalHashCode(r); -int fingerprint(real r) = internalHashCode(r) == 0 ? internalHashCode("real") : internalHashCode(r); -int fingerprint(rat r) = internalHashCode(r) == 0 ? internalHashCode("rat") : internalHashCode(r); +int fingerprint(str r) = hash == 0 ? internalHashCode("str") : hash when int hash := internalHashCode(r); +int fingerprint(int r) = hash == 0 ? internalHashCode("int") : hash when int hash := internalHashCode(r); +int fingerprint(real r) = hash == 0 ? internalHashCode("real") : hash when int hash := internalHashCode(r); +int fingerprint(rat r) = hash == 0 ? internalHashCode("rat") : hash when int hash := internalHashCode(r); int fingerprint(value t) = tupleFingerprint(size(fields)) when \tuple(list[Symbol] fields) := typeOf(t); default int fingerprint(value n) = internalHashCode(n); @@ -84,6 +84,8 @@ int fingerprint(list[value] l) = listFingerprint(); int fingerprint(set[value] l) = setFingerprint(); int fingerprint(map[value,value] l) = mapFingerprint(); +int fingerprint(true) = internalHashCode("true"); +int fingerprint(false) = internalHashCode("true"); int nodeFingerprint("" , int arity) = internalHashCode("node") + 131 * arity; default int nodeFingerprint(str name, int arity) = internalHashCode(name) + 131 * arity; From 268e9c0eebf67350b76e8b1ccec8912a33dffbec Mon Sep 17 00:00:00 2001 From: "Jurgen J. Vinju" Date: Fri, 3 Nov 2023 14:55:38 +0100 Subject: [PATCH 19/19] fixes --- .../rascalmpl/library/lang/rascal/matching/Fingerprint.rsc | 1 + src/org/rascalmpl/values/RascalValueFactory.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc index ce4f6d688c9..fa06484a54a 100644 --- a/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc +++ b/src/org/rascalmpl/library/lang/rascal/matching/Fingerprint.rsc @@ -58,6 +58,7 @@ implement a sensible encoding that follows the contract and tries to differentia int concreteFingerprint(appl(Production p, list[Tree] _)) = concreteFingerprint(p); int concreteFingerprint(amb({appl(prod(Symbol s, _, _), list[Tree] _), _})) = internalHashCode("amb") + 43 * internalHashCode(t) when label(_, Symbol t) := s || Symbol t := s; +int concreteFingerprint(amb({})) = internalHashCode("amb"); int concreteFingerprint(char(int ch)) = internalHashCode("char") + internalHashCode(ch); int concreteFingerprint(cycle(Symbol s, int _)) = internalHashCode("cycle") + 13 * internalHashCode(s); diff --git a/src/org/rascalmpl/values/RascalValueFactory.java b/src/org/rascalmpl/values/RascalValueFactory.java index 214343d23e7..8ec9fc76a0a 100644 --- a/src/org/rascalmpl/values/RascalValueFactory.java +++ b/src/org/rascalmpl/values/RascalValueFactory.java @@ -1052,6 +1052,10 @@ public Amb(ISet alts) { @Override public int getConcreteMatchFingerprint() { + if (alternatives.isEmpty()) { + return 96694; + } + return 96694 /* "amb".hashCode() */ + 43 * ((NonTerminalType) alternatives.getElementType()).getSymbol().hashCode(); }