mirror of
https://gitlab.kit.edu/uskyk/typicalc.git
synced 2024-11-12 12:13:10 +00:00
Unification tests, miscellaneous code style fixes
This commit is contained in:
parent
dee5e2b3f0
commit
b716b204b3
@ -2,6 +2,8 @@ package edu.kit.typicalc.model;
|
|||||||
|
|
||||||
import edu.kit.typicalc.model.type.Type;
|
import edu.kit.typicalc.model.type.Type;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constrains two types to be equal.
|
* Constrains two types to be equal.
|
||||||
*/
|
*/
|
||||||
@ -35,4 +37,21 @@ public class Constraint {
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Constraint that = (Constraint) o;
|
||||||
|
return (a.equals(that.a) && b.equals(that.b))
|
||||||
|
|| (a.equals(that.b) && b.equals(that.a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(a, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package edu.kit.typicalc.model;
|
|||||||
import edu.kit.typicalc.model.type.Type;
|
import edu.kit.typicalc.model.type.Type;
|
||||||
import edu.kit.typicalc.model.type.TypeVariable;
|
import edu.kit.typicalc.model.type.TypeVariable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A substitution specifies that some type should be replaced by a different type.
|
* A substitution specifies that some type should be replaced by a different type.
|
||||||
*/
|
*/
|
||||||
@ -36,4 +38,26 @@ public class Substitution {
|
|||||||
Type getType() {
|
Type getType() {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Substitution[" + a + " => " + b + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Substitution that = (Substitution) o;
|
||||||
|
return a.equals(that.a) && b.equals(that.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(a, b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
package edu.kit.typicalc.model;
|
package edu.kit.typicalc.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Errors that can occur during unification.
|
||||||
|
*
|
||||||
|
* @see Unification
|
||||||
|
* @see UnificationStep
|
||||||
|
*/
|
||||||
public enum UnificationError {
|
public enum UnificationError {
|
||||||
|
/**
|
||||||
|
* Unification would lead to an infinite type.
|
||||||
|
*/
|
||||||
INFINITE_TYPE,
|
INFINITE_TYPE,
|
||||||
|
/**
|
||||||
|
* Some {@link edu.kit.typicalc.model.type.NamedType} is not equal to another type.
|
||||||
|
*/
|
||||||
DIFFERENT_TYPES
|
DIFFERENT_TYPES
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ public class LambdaLexer {
|
|||||||
return new Result<>(t);
|
return new Result<>(t);
|
||||||
default:
|
default:
|
||||||
if (Character.isLetter(c)) {
|
if (Character.isLetter(c)) {
|
||||||
|
int startPos = pos;
|
||||||
// identifier
|
// identifier
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
do {
|
do {
|
||||||
@ -133,17 +134,17 @@ public class LambdaLexer {
|
|||||||
type = TokenType.VARIABLE;
|
type = TokenType.VARIABLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new Result<>(new Token(type, sb.toString(), pos));
|
return new Result<>(new Token(type, sb.toString(), startPos));
|
||||||
} else if (Character.isDigit(c)) {
|
} else if (Character.isDigit(c)) {
|
||||||
|
int startPos = pos;
|
||||||
// number literal
|
// number literal
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
do {
|
do {
|
||||||
sb.append(term.charAt(pos));
|
sb.append(term.charAt(pos));
|
||||||
advance();
|
advance();
|
||||||
} while (pos < term.length() && Character.isDigit(term.charAt(pos)));
|
} while (pos < term.length() && Character.isDigit(term.charAt(pos)));
|
||||||
return new Result<>(new Token(TokenType.NUMBER, sb.toString(), pos));
|
return new Result<>(new Token(TokenType.NUMBER, sb.toString(), startPos));
|
||||||
} else {
|
} else {
|
||||||
//throw new ParseException("Illegal character '" + term.charAt(pos) + "'");
|
|
||||||
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER);
|
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,10 +58,11 @@ public class LambdaParser {
|
|||||||
* @param type the token type to compare the current token type to
|
* @param type the token type to compare the current token type to
|
||||||
*/
|
*/
|
||||||
private Optional<ParseError> expect(TokenType type) {
|
private Optional<ParseError> expect(TokenType type) {
|
||||||
|
Token lastToken = token;
|
||||||
TokenType current = token.getType();
|
TokenType current = token.getType();
|
||||||
Optional<ParseError> error = nextToken();
|
Optional<ParseError> error = nextToken();
|
||||||
if (current != type) {
|
if (current != type) {
|
||||||
return Optional.of(ParseError.UNEXPECTED_TOKEN.withToken(token));
|
return Optional.of(ParseError.UNEXPECTED_TOKEN.withToken(lastToken));
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package edu.kit.typicalc.model.parser;
|
package edu.kit.typicalc.model.parser;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A token of the Prolog language.
|
* A token of the Prolog language.
|
||||||
*/
|
*/
|
||||||
@ -73,6 +75,23 @@ public class Token {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return type + "(\"" + text + "\")";
|
return type + "(\"" + text + "\")[" + pos + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Token token = (Token) o;
|
||||||
|
return pos == token.pos && type == token.type && Objects.equals(text, token.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(type, text, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package edu.kit.typicalc.model.type;
|
|||||||
|
|
||||||
import edu.kit.typicalc.model.TypeVariableFactory;
|
import edu.kit.typicalc.model.TypeVariableFactory;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -11,8 +12,8 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class TypeAbstraction {
|
public class TypeAbstraction {
|
||||||
|
|
||||||
private Type type;
|
private final Type type;
|
||||||
private List<TypeVariable> quantifiedVariables;
|
private final List<TypeVariable> quantifiedVariables;
|
||||||
/**
|
/**
|
||||||
* Initializes a new type abstraction with its type and the type variables bound by
|
* Initializes a new type abstraction with its type and the type variables bound by
|
||||||
* the for-all quantifier.
|
* the for-all quantifier.
|
||||||
@ -31,7 +32,7 @@ public class TypeAbstraction {
|
|||||||
*/
|
*/
|
||||||
public TypeAbstraction(Type type) {
|
public TypeAbstraction(Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.quantifiedVariables = null;
|
this.quantifiedVariables = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,7 +115,12 @@ public class TypeVariable extends Type {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Result<UnificationActions, UnificationError> constrainEqualToVariable(TypeVariable type) {
|
public Result<UnificationActions, UnificationError> constrainEqualToVariable(TypeVariable type) {
|
||||||
return UnificationUtil.variableVariable(this, type);
|
return UnificationUtil.variableVariable(type, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TypeVariable[" + kind + "_" + index + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -18,8 +18,6 @@ final class UnificationUtil {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check for infinite types
|
|
||||||
|
|
||||||
static Result<UnificationActions, UnificationError> functionFunction(FunctionType a, FunctionType b) {
|
static Result<UnificationActions, UnificationError> functionFunction(FunctionType a, FunctionType b) {
|
||||||
return new Result<>(new UnificationActions(List.of(
|
return new Result<>(new UnificationActions(List.of(
|
||||||
new Constraint(a.getParameter(), b.getParameter()),
|
new Constraint(a.getParameter(), b.getParameter()),
|
||||||
@ -31,6 +29,9 @@ final class UnificationUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Result<UnificationActions, UnificationError> functionVariable(FunctionType a, TypeVariable b) {
|
static Result<UnificationActions, UnificationError> functionVariable(FunctionType a, TypeVariable b) {
|
||||||
|
if (a.contains(b)) {
|
||||||
|
return new Result<>(null, UnificationError.INFINITE_TYPE);
|
||||||
|
}
|
||||||
return new Result<>(new UnificationActions(Collections.emptyList(),
|
return new Result<>(new UnificationActions(Collections.emptyList(),
|
||||||
Optional.of(new Substitution(b, a))));
|
Optional.of(new Substitution(b, a))));
|
||||||
}
|
}
|
||||||
@ -41,8 +42,12 @@ final class UnificationUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Result<UnificationActions, UnificationError> variableVariable(TypeVariable a, TypeVariable b) {
|
static Result<UnificationActions, UnificationError> variableVariable(TypeVariable a, TypeVariable b) {
|
||||||
return new Result<>(new UnificationActions(Collections.emptyList(),
|
if (!a.equals(b)) {
|
||||||
Optional.of(new Substitution(a, b))));
|
return new Result<>(new UnificationActions(Collections.emptyList(),
|
||||||
|
Optional.of(new Substitution(a, b))));
|
||||||
|
} else {
|
||||||
|
return new Result<>(new UnificationActions());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<UnificationActions, UnificationError> namedNamed(NamedType a, NamedType b) {
|
static Result<UnificationActions, UnificationError> namedNamed(NamedType a, NamedType b) {
|
||||||
|
@ -9,6 +9,7 @@ import edu.kit.typicalc.model.term.IntegerTerm;
|
|||||||
import edu.kit.typicalc.model.term.LambdaTerm;
|
import edu.kit.typicalc.model.term.LambdaTerm;
|
||||||
import edu.kit.typicalc.model.term.LetTerm;
|
import edu.kit.typicalc.model.term.LetTerm;
|
||||||
import edu.kit.typicalc.model.term.VarTerm;
|
import edu.kit.typicalc.model.term.VarTerm;
|
||||||
|
import edu.kit.typicalc.model.parser.Token.TokenType;
|
||||||
import edu.kit.typicalc.util.Result;
|
import edu.kit.typicalc.util.Result;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -85,12 +86,16 @@ class LambdaParserTest {
|
|||||||
LambdaParser parser = new LambdaParser("");
|
LambdaParser parser = new LambdaParser("");
|
||||||
assertEquals(ParseError.TOO_FEW_TOKENS, parser.parse().unwrapError());
|
assertEquals(ParseError.TOO_FEW_TOKENS, parser.parse().unwrapError());
|
||||||
parser = new LambdaParser("x)");
|
parser = new LambdaParser("x)");
|
||||||
assertEquals(ParseError.UNEXPECTED_TOKEN, parser.parse().unwrapError());
|
ParseError error = parser.parse().unwrapError();
|
||||||
|
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
|
||||||
|
assertEquals(new Token(TokenType.RP, ")", 1), error.getCause());
|
||||||
parser = new LambdaParser("??");
|
parser = new LambdaParser("??");
|
||||||
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
|
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
|
||||||
parser = new LambdaParser("123333333333333");
|
parser = new LambdaParser("123333333333333");
|
||||||
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
|
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
|
||||||
parser = new LambdaParser("x 123333333333333");
|
parser = new LambdaParser("x 123333333333333");
|
||||||
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
|
error = parser.parse().unwrapError();
|
||||||
|
assertEquals(ParseError.UNEXPECTED_CHARACTER, error);
|
||||||
|
assertEquals(new Token(TokenType.NUMBER, "123333333333333", 2), error.getCause());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
package edu.kit.typicalc.model.type;
|
||||||
|
|
||||||
|
import edu.kit.typicalc.model.Constraint;
|
||||||
|
import edu.kit.typicalc.model.Substitution;
|
||||||
|
import edu.kit.typicalc.model.UnificationError;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class UnificationTest {
|
||||||
|
@Test
|
||||||
|
void all() {
|
||||||
|
TypeVariable a1 = new TypeVariable(TypeVaribaleKind.TREE, 1);
|
||||||
|
TypeVariable a2 = new TypeVariable(TypeVaribaleKind.TREE, 2);
|
||||||
|
TypeVariable a3 = new TypeVariable(TypeVaribaleKind.TREE, 3);
|
||||||
|
FunctionType id = new FunctionType(a1, a1);
|
||||||
|
FunctionType fun = new FunctionType(a2, a3);
|
||||||
|
NamedType string = new NamedType("string");
|
||||||
|
NamedType object = new NamedType("object");
|
||||||
|
|
||||||
|
// Function constraints
|
||||||
|
UnificationError error = id.constrainEqualTo(a1).unwrapError();
|
||||||
|
assertEquals(UnificationError.INFINITE_TYPE, error);
|
||||||
|
|
||||||
|
UnificationActions actions = id.constrainEqualTo(a2).unwrap();
|
||||||
|
assertEquals(new Substitution(a2, id), actions.getSubstitution().get());
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
|
||||||
|
error = id.constrainEqualTo(string).unwrapError();
|
||||||
|
assertEquals(UnificationError.DIFFERENT_TYPES, error);
|
||||||
|
|
||||||
|
actions = id.constrainEqualTo(fun).unwrap();
|
||||||
|
assertTrue(actions.getSubstitution().isEmpty());
|
||||||
|
Collection<Constraint> constraints = actions.getConstraints();
|
||||||
|
assertEquals(2, constraints.size());
|
||||||
|
assertTrue(constraints.contains(new Constraint(a1, a2)));
|
||||||
|
assertTrue(constraints.contains(new Constraint(a1, a3)));
|
||||||
|
|
||||||
|
// Variable constraints
|
||||||
|
actions = a1.constrainEqualTo(a1).unwrap();
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
assertTrue(actions.getSubstitution().isEmpty());
|
||||||
|
|
||||||
|
actions = a1.constrainEqualTo(a2).unwrap();
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
assertEquals(new Substitution(a1, a2), actions.getSubstitution().get());
|
||||||
|
|
||||||
|
error = a1.constrainEqualTo(id).unwrapError();
|
||||||
|
assertEquals(UnificationError.INFINITE_TYPE, error);
|
||||||
|
|
||||||
|
actions = a2.constrainEqualTo(id).unwrap();
|
||||||
|
assertEquals(new Substitution(a2, id), actions.getSubstitution().get());
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
|
||||||
|
actions = a1.constrainEqualTo(string).unwrap();
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
assertEquals(new Substitution(a1, string), actions.getSubstitution().get());
|
||||||
|
|
||||||
|
// Named type constraints
|
||||||
|
actions = string.constrainEqualTo(string).unwrap();
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
assertTrue(actions.getSubstitution().isEmpty());
|
||||||
|
|
||||||
|
error = string.constrainEqualTo(object).unwrapError();
|
||||||
|
assertEquals(UnificationError.DIFFERENT_TYPES, error);
|
||||||
|
|
||||||
|
error = string.constrainEqualTo(id).unwrapError();
|
||||||
|
assertEquals(UnificationError.DIFFERENT_TYPES, error);
|
||||||
|
|
||||||
|
actions = string.constrainEqualTo(a1).unwrap();
|
||||||
|
assertTrue(actions.getConstraints().isEmpty());
|
||||||
|
assertEquals(new Substitution(a1, string), actions.getSubstitution().get());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user