mirror of
https://gitlab.kit.edu/uskyk/typicalc.git
synced 2024-11-08 18:30:42 +00:00
Improve type assumption error messages
includes codestyle fixes
This commit is contained in:
parent
adb10a01aa
commit
301af06c69
@ -7,5 +7,10 @@ public enum ExpectedInput {
|
||||
/**
|
||||
* Any kind of lambda term.
|
||||
*/
|
||||
TERM
|
||||
TERM,
|
||||
|
||||
/**
|
||||
* Any kind of type.
|
||||
*/
|
||||
TYPE
|
||||
}
|
||||
|
@ -31,12 +31,7 @@ public enum ParseError {
|
||||
/**
|
||||
* This error was created when parsing the type assumptions
|
||||
*/
|
||||
TYPE_ASSUMPTION_ERROR,
|
||||
|
||||
/**
|
||||
* initial error type
|
||||
*/
|
||||
INITIAL_ERROR
|
||||
TYPE_ASSUMPTION_ERROR
|
||||
}
|
||||
|
||||
private Optional<Token> cause = Optional.empty();
|
||||
@ -46,7 +41,7 @@ public enum ParseError {
|
||||
private char wrongChar = '\0';
|
||||
private char correctChar = '\0';
|
||||
private int position = -1;
|
||||
private ErrorType errorType = ErrorType.INITIAL_ERROR;
|
||||
private Optional<ErrorType> errorType = Optional.empty();
|
||||
|
||||
/**
|
||||
* Attach a token to this error.
|
||||
@ -58,7 +53,7 @@ public enum ParseError {
|
||||
this.cause = Optional.of(cause);
|
||||
this.term = cause.getSourceText();
|
||||
this.position = cause.getPos();
|
||||
this.errorType = errorType;
|
||||
this.errorType = Optional.of(errorType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -123,7 +118,7 @@ public enum ParseError {
|
||||
this.wrongChar = cause;
|
||||
this.position = position;
|
||||
this.term = term;
|
||||
this.errorType = errorType;
|
||||
this.errorType = Optional.of(errorType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -172,12 +167,12 @@ public enum ParseError {
|
||||
/**
|
||||
* @return the error type
|
||||
*/
|
||||
public ErrorType getErrorType() {
|
||||
public Optional<ErrorType> getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
protected void setErrorType(ErrorType errorType) {
|
||||
this.errorType = errorType;
|
||||
this.errorType = Optional.of(errorType);
|
||||
}
|
||||
|
||||
ParseError() {
|
||||
|
@ -6,6 +6,7 @@ import edu.kit.typicalc.model.type.*;
|
||||
import edu.kit.typicalc.util.Result;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -119,6 +120,13 @@ public class TypeAssumptionParser {
|
||||
return this;
|
||||
}
|
||||
|
||||
ParserResult<T> modifyError(UnaryOperator<ParseError> fun) {
|
||||
if (error.isPresent()) {
|
||||
error = Optional.of(fun.apply(error.get()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Optional<Token> extraToken() {
|
||||
return this.extraToken;
|
||||
}
|
||||
@ -129,7 +137,7 @@ public class TypeAssumptionParser {
|
||||
}
|
||||
|
||||
private static class InitialState implements ParserState<Map<VarTerm, TypeAbstraction>> {
|
||||
private Map<VarTerm, TypeAbstraction> alreadyParsed = new LinkedHashMap<>();
|
||||
private final Map<VarTerm, TypeAbstraction> alreadyParsed;
|
||||
|
||||
InitialState(Map<VarTerm, TypeAbstraction> alreadyParsed) {
|
||||
this.alreadyParsed = alreadyParsed;
|
||||
@ -150,8 +158,9 @@ public class TypeAssumptionParser {
|
||||
}
|
||||
|
||||
private static class ExpectingColon implements ParserState<Map<VarTerm, TypeAbstraction>> {
|
||||
private Map<VarTerm, TypeAbstraction> alreadyParsed;
|
||||
private final Map<VarTerm, TypeAbstraction> alreadyParsed;
|
||||
private final VarTerm var;
|
||||
|
||||
ExpectingColon(Map<VarTerm, TypeAbstraction> alreadyParsed, VarTerm var) {
|
||||
this.alreadyParsed = alreadyParsed;
|
||||
this.var = var;
|
||||
@ -159,10 +168,9 @@ public class TypeAssumptionParser {
|
||||
|
||||
@Override
|
||||
public ParserResult<Map<VarTerm, TypeAbstraction>> handle(Token t) {
|
||||
switch (t.getType()) {
|
||||
case COLON:
|
||||
if (t.getType() == TokenType.COLON) {
|
||||
return new ParserResult<>(new ExpectingTypeDef(alreadyParsed, var));
|
||||
default:
|
||||
} else {
|
||||
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
|
||||
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
|
||||
}
|
||||
@ -174,6 +182,7 @@ public class TypeAssumptionParser {
|
||||
private final Set<TypeVariable> typeVariables;
|
||||
private final VarTerm var;
|
||||
private Optional<ParserState<Type>> state = Optional.empty();
|
||||
|
||||
ExpectingTypeDef(Map<VarTerm, TypeAbstraction> alreadyParsed, VarTerm var) {
|
||||
this.alreadyParsed = alreadyParsed;
|
||||
this.typeVariables = new TreeSet<>();
|
||||
@ -188,15 +197,14 @@ public class TypeAssumptionParser {
|
||||
|
||||
@Override
|
||||
public ParserResult<Map<VarTerm, TypeAbstraction>> handle(Token t) {
|
||||
switch (t.getType()) {
|
||||
case UNIVERSAL_QUANTIFIER:
|
||||
if (t.getType() == TokenType.UNIVERSAL_QUANTIFIER) {
|
||||
if (typeVariables.isEmpty()) {
|
||||
return new ParserResult<>(new ExpectingTypeVariables(alreadyParsed, var));
|
||||
} else {
|
||||
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
|
||||
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
|
||||
}
|
||||
default:
|
||||
}
|
||||
if (state.isPresent()) {
|
||||
ParserState<Type> status = state.get();
|
||||
// already parsing type
|
||||
@ -215,13 +223,12 @@ public class TypeAssumptionParser {
|
||||
// attempt to parse as type
|
||||
ParserResult<Type> result = new ParseTypeState1(alreadyParsed.size()).handle(t);
|
||||
if (result.isError()) {
|
||||
return result.castError();
|
||||
return result.modifyError(error -> error.expectedType(TokenType.UNIVERSAL_QUANTIFIER)).castError();
|
||||
}
|
||||
state = Optional.of(result.getState());
|
||||
return new ParserResult<>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExpectingCommaBeforeNextType implements ParserState<Map<VarTerm, TypeAbstraction>> {
|
||||
private final Map<VarTerm, TypeAbstraction> alreadyParsed;
|
||||
@ -343,7 +350,8 @@ public class TypeAssumptionParser {
|
||||
return new ParserResult<>(parsedType.get()).attachToken(t);
|
||||
}
|
||||
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
|
||||
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
|
||||
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
|
||||
.expectedInput(ExpectedInput.TYPE));
|
||||
default:
|
||||
if (state.isPresent()) {
|
||||
return handleInner(t);
|
||||
@ -408,14 +416,11 @@ public class TypeAssumptionParser {
|
||||
|
||||
@Override
|
||||
public ParserResult<Type> handle(Token t) {
|
||||
switch (t.getType()) {
|
||||
case ARROW:
|
||||
if (state.isEmpty()) {
|
||||
if (t.getType() == TokenType.ARROW && state.isEmpty()) {
|
||||
// try parsing remainder as type
|
||||
state = Optional.of(new ParseTypeState1(typeVariableUniqueIndex));
|
||||
return new ParserResult<>(this);
|
||||
}
|
||||
default:
|
||||
if (state.isPresent()) {
|
||||
ParserState<Type> status = state.get();
|
||||
// already parsing type
|
||||
@ -434,13 +439,13 @@ public class TypeAssumptionParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExpectingTypeVariables implements ParserState<Map<VarTerm, TypeAbstraction>> {
|
||||
private final Map<VarTerm, TypeAbstraction> alreadyParsed;
|
||||
private final VarTerm var;
|
||||
private final Set<TypeVariable> variables = new TreeSet<>();
|
||||
private boolean expectCommaOrDot = false;
|
||||
|
||||
ExpectingTypeVariables(Map<VarTerm, TypeAbstraction> alreadyParsed, VarTerm var) {
|
||||
this.alreadyParsed = alreadyParsed;
|
||||
this.var = var;
|
||||
|
@ -47,6 +47,14 @@ public class ErrorView extends VerticalLayout implements LocaleChangeObserver {
|
||||
add(container, infoContent);
|
||||
}
|
||||
|
||||
private String errorMessageForToken(Token token) {
|
||||
if (token.getType() != Token.TokenType.EOF) {
|
||||
return getTranslation("error.wrongCharacter") + token.getText();
|
||||
} else {
|
||||
return getTranslation("error.tooFewTokensHelp");
|
||||
}
|
||||
}
|
||||
|
||||
private Component buildErrorMessage(ParseError error) {
|
||||
VerticalLayout additionalInformation = new VerticalLayout();
|
||||
additionalInformation.setId(ADDITIONAL_INFO_ID);
|
||||
@ -54,26 +62,21 @@ public class ErrorView extends VerticalLayout implements LocaleChangeObserver {
|
||||
Paragraph summary = new Paragraph(getTranslation("root." + error.toString()));
|
||||
summary.setId(ERROR_SUMMARY_ID);
|
||||
String term = error.getTerm();
|
||||
String descriptionForError;
|
||||
ParseError.ErrorType errorType = error.getErrorType();
|
||||
if (errorType == ParseError.ErrorType.TERM_ERROR) {
|
||||
String descriptionForError = null;
|
||||
Optional<ParseError.ErrorType> errorType = error.getErrorType();
|
||||
if (errorType.isEmpty()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (errorType.get() == ParseError.ErrorType.TERM_ERROR) {
|
||||
descriptionForError = "error.termForError";
|
||||
} else if (errorType == ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) {
|
||||
} else if (errorType.get() == ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) {
|
||||
descriptionForError = "error.typeAssumptionForError";
|
||||
} else {
|
||||
//should never happen
|
||||
descriptionForError = "error";
|
||||
}
|
||||
switch (error) {
|
||||
case UNEXPECTED_TOKEN:
|
||||
Optional<Token> cause = error.getCause();
|
||||
if (cause.isPresent()) {
|
||||
String errorText;
|
||||
if (cause.get().getType() != Token.TokenType.EOF) {
|
||||
errorText = getTranslation("error.wrongCharacter") + cause.get().getText();
|
||||
} else {
|
||||
errorText = getTranslation("error.tooFewTokensHelp");
|
||||
}
|
||||
String errorText = errorMessageForToken(cause.get());
|
||||
additionalInformation.add(new Span(new Pre(getTranslation(descriptionForError) + term
|
||||
+ "\n" + " ".repeat(Math.max(getTranslation(descriptionForError).length(),
|
||||
cause.get().getPos() + getTranslation(descriptionForError).length()))
|
||||
|
@ -128,6 +128,7 @@ tokentype.EQUALS==
|
||||
tokentype.ARROW=->
|
||||
tokentype.EOF=Ende der Eingabe
|
||||
expectedinput.TERM=beliebiger Term
|
||||
expectedinput.TYPE=beliebiger Typ
|
||||
root.or=oder
|
||||
root.slideExp=Typicalc ist eine Anwendung zur Visualisierung von Typinferenz. In der folgenden Slideshow wird ein \
|
||||
Beispielszenario mit den wichtigsten Funktionen der Website vorgeführt. Das relevante Bedienelement ist jeweils mit \
|
||||
|
@ -120,6 +120,7 @@ tokentype.EQUALS==
|
||||
tokentype.ARROW=->
|
||||
tokentype.EOF=End of input
|
||||
expectedinput.TERM=any lambda term
|
||||
expectedinput.TYPE=any type
|
||||
root.or=or
|
||||
root.slideExp=Typicalc is an application for the visualisation of type inference. The slideshow below the text \
|
||||
demonstrates the most important features of the website. In each slide the relevant operating element is surrounded \
|
||||
|
@ -6,7 +6,6 @@ import edu.kit.typicalc.util.Result;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static edu.kit.typicalc.model.type.NamedType.BOOLEAN;
|
||||
import static edu.kit.typicalc.model.type.NamedType.INT;
|
||||
@ -328,4 +327,23 @@ class TypeAssumptionParserTest {
|
||||
assertEquals(entry.getValue(), type.unwrap().get(new VarTerm("type1")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorCase1() {
|
||||
ParseError e = parse("id: a ->");
|
||||
assertEquals(ExpectedInput.TYPE, e.getExpectedInput().get());
|
||||
assertEquals(Token.TokenType.EOF, e.getCause().get().getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorCase2() {
|
||||
ParseError e = parse("id: ");
|
||||
assertEquals(ExpectedInput.TYPE, e.getExpectedInput().get());
|
||||
assertTrue(e.getExpected().get().contains(Token.TokenType.UNIVERSAL_QUANTIFIER));
|
||||
assertEquals(Token.TokenType.EOF, e.getCause().get().getType());
|
||||
}
|
||||
|
||||
static ParseError parse(String input) {
|
||||
return new TypeAssumptionParser().parse(input).unwrapError();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user