Correctly scope variables in type assumptions

This commit is contained in:
Arne Keller 2021-07-09 12:46:04 +02:00
parent 394b5a3d23
commit 1b70e374ac
3 changed files with 58 additions and 6 deletions

View File

@ -213,7 +213,7 @@ public class TypeAssumptionParser {
} }
} }
// attempt to parse as type // attempt to parse as type
ParserResult<Type> result = new ParseTypeState1().handle(t); ParserResult<Type> result = new ParseTypeState1(alreadyParsed.size()).handle(t);
if (result.isError()) { if (result.isError()) {
return result.castError(); return result.castError();
} }
@ -254,6 +254,15 @@ public class TypeAssumptionParser {
private int parenthesisInitial = 0; private int parenthesisInitial = 0;
private int openParens = 0; private int openParens = 0;
/**
* Attached to any parsed type variables.
*/
private final int typeVariableUniqueIndex;
ParseTypeState1(int typeVariableUniqueIndex) {
this.typeVariableUniqueIndex = typeVariableUniqueIndex;
}
@Override @Override
public ParserResult<Type> handle(Token t) { public ParserResult<Type> handle(Token t) {
switch (t.getType()) { switch (t.getType()) {
@ -284,7 +293,7 @@ public class TypeAssumptionParser {
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); .withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
} }
// parse function type // parse function type
state = Optional.of(new ParseTypeStateExpectArrow().handle(t).getState()); state = Optional.of(new ParseTypeStateExpectArrow(typeVariableUniqueIndex).handle(t).getState());
return new ParserResult<>(this); return new ParserResult<>(this);
case LEFT_PARENTHESIS: case LEFT_PARENTHESIS:
openParens += 1; openParens += 1;
@ -294,7 +303,7 @@ public class TypeAssumptionParser {
if (stateParenthesis.isPresent()) { if (stateParenthesis.isPresent()) {
return handleInnerParenthesis(t); return handleInnerParenthesis(t);
} }
stateParenthesis = Optional.of(new ParseTypeState1()); stateParenthesis = Optional.of(new ParseTypeState1(typeVariableUniqueIndex));
parenthesisInitial = openParens - 1; parenthesisInitial = openParens - 1;
return new ParserResult<>(this); return new ParserResult<>(this);
case RIGHT_PARENTHESIS: case RIGHT_PARENTHESIS:
@ -372,11 +381,13 @@ public class TypeAssumptionParser {
} }
} }
private static Type parseLiteral(String text) { private Type parseLiteral(String text) {
Matcher typeVariableMatcher = TYPE_VARIABLE_PATTERN.matcher(text); Matcher typeVariableMatcher = TYPE_VARIABLE_PATTERN.matcher(text);
if (typeVariableMatcher.matches()) { if (typeVariableMatcher.matches()) {
int typeVariableIndex = Integer.parseInt(typeVariableMatcher.group(1)); int typeVariableIndex = Integer.parseInt(typeVariableMatcher.group(1));
return new TypeVariable(TypeVariableKind.USER_INPUT, typeVariableIndex); TypeVariable var = new TypeVariable(TypeVariableKind.USER_INPUT, typeVariableIndex);
var.setUniqueIndex(typeVariableUniqueIndex);
return var;
} else { } else {
return new NamedType(text); return new NamedType(text);
} }
@ -386,13 +397,22 @@ public class TypeAssumptionParser {
private static class ParseTypeStateExpectArrow implements ParserState<Type> { private static class ParseTypeStateExpectArrow implements ParserState<Type> {
private Optional<ParserState<Type>> state = Optional.empty(); private Optional<ParserState<Type>> state = Optional.empty();
/**
* Attached to any parsed type variables.
*/
private final int typeVariableUniqueIndex;
ParseTypeStateExpectArrow(int typeVariableUniqueIndex) {
this.typeVariableUniqueIndex = typeVariableUniqueIndex;
}
@Override @Override
public ParserResult<Type> handle(Token t) { public ParserResult<Type> handle(Token t) {
switch (t.getType()) { switch (t.getType()) {
case ARROW: case ARROW:
if (state.isEmpty()) { if (state.isEmpty()) {
// try parsing remainder as type // try parsing remainder as type
state = Optional.of(new ParseTypeState1()); state = Optional.of(new ParseTypeState1(typeVariableUniqueIndex));
return new ParserResult<>(this); return new ParserResult<>(this);
} }
default: default:
@ -446,6 +466,7 @@ public class TypeAssumptionParser {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)); .withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
} }
variable.setUniqueIndex(alreadyParsed.size());
variables.add(variable); variables.add(variable);
expectCommaOrDot = true; expectCommaOrDot = true;
return new ParserResult<>(this); return new ParserResult<>(this);

View File

@ -13,6 +13,11 @@ public class TypeVariable extends Type implements Comparable<TypeVariable> {
private final TypeVariableKind kind; private final TypeVariableKind kind;
private final int index; private final int index;
/**
* The same type variable can be universally quantified multiple times, but this index will differ.
*/
private int uniqueIndex;
/** /**
* Initializes a new TypeVariable with the given index. * Initializes a new TypeVariable with the given index.
* *
@ -42,6 +47,14 @@ public class TypeVariable extends Type implements Comparable<TypeVariable> {
return index; return index;
} }
public void setUniqueIndex(int uniqueIndex) {
this.uniqueIndex = uniqueIndex;
}
public int getUniqueIndex() {
return uniqueIndex;
}
/** /**
* Checks whether some type occurs in this type. * Checks whether some type occurs in this type.
* *

View File

@ -239,6 +239,24 @@ class TypeAssumptionParserTest {
assertEquals(10, error.getCause().get().getPos()); assertEquals(10, error.getCause().get().getPos());
} }
@Test
void correctlyScopesQuantifiedTypeVariables() {
TypeAssumptionParser parser = new TypeAssumptionParser();
Result<Map<VarTerm, TypeAbstraction>, ParseError> type =
parser.parse("id: ∀ t1 . t1 -> t1, fun: ∀t1,t2.t1->t2 ");
assertTrue(type.isOk());
Map<VarTerm, TypeAbstraction> res = type.unwrap();
TypeAbstraction id = res.get(new VarTerm("id"));
TypeVariable[] idVariables = id.getQuantifiedVariables().toArray(new TypeVariable[0]);
assertEquals(0, idVariables[0].getUniqueIndex());
TypeAbstraction fun = res.get(new VarTerm("fun"));
TypeVariable[] funVariables = fun.getQuantifiedVariables().toArray(new TypeVariable[0]);
assertEquals(1, funVariables[0].getUniqueIndex());
assertEquals(1, funVariables[1].getUniqueIndex());
assertEquals(1, ((TypeVariable) ((FunctionType) fun.getInnerType()).getParameter()).getUniqueIndex());
assertEquals(1, ((TypeVariable) ((FunctionType) fun.getInnerType()).getOutput()).getUniqueIndex());
}
@Test @Test
void errors() { void errors() {
Map<String, ParseError> tests = new HashMap<>(); Map<String, ParseError> tests = new HashMap<>();