Skip to content

Commit

Permalink
Merge pull request #210 from usethesource/pattern-match-fingerprint
Browse files Browse the repository at this point in the history
added IValue.getPatternMatchFingerprint() that simulates rascal-core:TopLevelType.getFingerprint(v) exactly for the value kinds we have in vallang
  • Loading branch information
jurgenvinju authored Oct 17, 2023
2 parents cdd2feb + c883dbd commit 0a90ceb
Show file tree
Hide file tree
Showing 18 changed files with 289 additions and 5 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>io.usethesource</groupId>
<artifactId>vallang</artifactId>
<version>0.15.2-SNAPSHOT</version>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<scm>
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/usethesource/vallang/IBool.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IBool extends IValue {
@Override
default int getMatchFingerprint() {
if (getValue()) {
return 3569038; /* "true".hashCode() */
}
else {
return 97196323; /* "false".hashCode() */
}
}

boolean getValue();
String getStringRepresentation();
IBool and(IBool other);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/usethesource/vallang/IConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
*/
public interface IConstructor extends INode {

@Override
default int getMatchFingerprint() {
return getName().hashCode() + 131 * arity();
}

/**
* @return the specific ConstructorType of this constructor
*/
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/usethesource/vallang/IExternalValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
* Note that NORMAL USE OF THE PDB DOES NOT REQUIRE IMPLEMENTING THIS INTERFACE
*/
public interface IExternalValue extends IValue {
/**
* External values must re-think their pattern match fingerprint,
* instead of returning `IValue.hashCode()` automatically.
*/
@Override
int getMatchFingerprint();

/**
* @return an ExternalType
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/usethesource/vallang/IInteger.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IInteger extends INumber {
@Override
default int getMatchFingerprint() {
if (signum() == 0) {
return 104431; /* "int".hashCode() */
}
else {
return hashCode();
}
}

/**
* @return this + other;
*/
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/usethesource/vallang/IList.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IList extends ICollection<IList> {

@Override
default int getMatchFingerprint() {
return 3322014; // "list".hashCode()
}

/**
* @return the number of elements in the list
*/
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/usethesource/vallang/IMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@

public interface IMap extends ICollection<IMap> {

@Override
default int getMatchFingerprint() {
return 107868; // "map".hashCode()
}

/**
* Adds a new entry to the map, mapping the key to value. If the
* key existed before, the old value will be lost.
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/usethesource/vallang/INode.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
* it recursively.
*/
public interface INode extends IValue, Iterable<IValue> {

@Override
default int getMatchFingerprint() {
int hash = getName().hashCode();

return hash == 0 ? 13547528 /* "node".hashCode() << 2*/ + arity() : hash + 131 * arity();
}

/**
* Get a child
* @param i the zero based index of the child
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/usethesource/vallang/IReal.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IReal extends INumber {
@Override
default int getMatchFingerprint() {
int hash = hashCode();
return hash == 0 ? 3496350 /* real.hashCode() */ : hash;
}

/**
* @return this + other;
*/
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/usethesource/vallang/ISet.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

public interface ISet extends ICollection<ISet> {

@Override
default int getMatchFingerprint() {
return 113762; // "set".hashCode()
}

/**
* Add an element to the set.
* @param element
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/io/usethesource/vallang/IString.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IString extends IValue, Iterable<Integer> {

@Override
default int getMatchFingerprint() {
if (length() == 0) {
return 114225; /* "str".hashCode() */
}
else {
return hashCode();
}
}

/**
* @return the Java string that this string represents
*/
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/usethesource/vallang/ITuple.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface ITuple extends Iterable<IValue>, IValue {
@Override
default int getMatchFingerprint() {
return 442900256 /* "tuple".hashCode() << 2 */ + arity();
}

/**
* Retrieve the given field at the given index.
*
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/io/usethesource/vallang/IValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,35 @@ public interface IValue {
* @return the {@link Type} of a value
*/
public Type getType();

/**
* This method is used exclusively by code generated by the Rascal compiler,
* or by the Rascal interpreter. The returned integer codes are opaque, although stable.
* If you need to know what kind of value you have, use the IValueVisitor or the ITypeVisitor
* interfaces and the `accept` methods on IValue and Type.
*
* @return an integer code that:
* * accurate reflects the identity of the top-level structure of this value
* * such that if pattern.match(this) ===> pattern.getPatternMatchFingerprint() == this.getPatternMatchFingerprint()
* * distinguishes maximally between different kinds of values
* * never makes the same or similar value have a different fingerprint
*/
default int getMatchFingerprint() {
return hashCode();
}

/**
* This method is used exclusively by code generated by the Rascal compiler,
*
* @return an integer code that:
* * is guaranteed to be different from `getMatchFingerPrint`
* * is guaranteed to be constant
* * is guaranteed to be the same for every IValue
*/
static int getDefaultMatchFingerprint() {
return 0;
}

/**
* Execute the {@link IValueVisitor} on the current node
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import io.usethesource.vallang.ISetWriter;
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.exceptions.IllegalOperationException;
import io.usethesource.vallang.type.Type;
import io.usethesource.vallang.util.AbstractTypeBag;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ else if (isRationalType(other)) {
}
}

@Override
public int hashCode(){
int h = value ^ 0x85ebca6b;
// based on the final Avalanching phase of MurmurHash2
Expand Down
20 changes: 19 additions & 1 deletion src/test/java/io/usethesource/vallang/ValueProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,26 @@ private RandomTypesConfig configureRandomTypes(TypeConfig typeConfig, int depth)
* @return an instance assignable to `cl`
*/
private IValue generateValue(IValueFactory vf, TypeStore ts, Class<? extends IValue> cl, ExpectedType expected, int depth, int width) {
Type expectedType = expected != null ? readType(ts, expected) : types.getOrDefault(cl, (x, n) -> tf.valueType()).apply(ts, expected);
Type expectedType = tf.voidType();


// this should terminate through random selection.
// only tuple types with nested void arguments can reduce to void.
int i = 0;
while (expectedType.isBottom() && i++ < 1000) {
if (expected != null) {
expectedType = readType(ts, expected);
break;
}
else {
expectedType = types
.getOrDefault(cl, (x, n) -> tf.valueType())
.apply(ts, expected);
}
}

assert !expectedType.isBottom() : cl + " generated void type?";

if (previous != null && rnd.nextInt(4) == 0 && previous.getType().isSubtypeOf(expectedType)) {
return rnd.nextBoolean() ? previous : reinstantiate(vf, ts, previous);
}
Expand Down
Loading

0 comments on commit 0a90ceb

Please sign in to comment.