Refactor ParseError into object instead of enum

See #12
This commit is contained in:
Arne Keller 2021-07-19 10:56:46 +02:00
parent 7310eec398
commit 0285305f13
8 changed files with 211 additions and 109 deletions

View File

@ -28,6 +28,7 @@ public class ModelImpl implements Model {
@Override
public Result<TypeInfererInterface, ParseError> getTypeInferer(String lambdaTerm,
String typeAssumptions) {
System.out.println(lambdaTerm);
// Parse Lambda Term
LambdaParser parser = new LambdaParser(lambdaTerm);
Result<LambdaTerm, ParseError> result = parser.parse();

View File

@ -16,6 +16,7 @@ public class LambdaLexer {
* the given term as a String
*/
private final String term;
private final ParseError.ErrorType errorType;
/**
* current position in the term
*/
@ -27,8 +28,9 @@ public class LambdaLexer {
*
* @param term the term to lex
*/
public LambdaLexer(String term) {
public LambdaLexer(String term, ParseError.ErrorType errorType) {
this.term = term;
this.errorType = errorType;
Deque<Token> tokens = new ArrayDeque<>();
while (true) {
Result<Token, ParseError> token = parseNextToken();
@ -89,13 +91,12 @@ public class LambdaLexer {
advance();
return new Result<>(t);
} else {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER
.withCharacter(term.charAt(pos + 1), pos + 1, term, ParseError.ErrorType.TERM_ERROR));
return new Result<>(null, ParseError.unexpectedCharacter2(
term.charAt(pos + 1), pos + 1, term, errorType));
}
} else {
return new Result<>(null,
ParseError.UNEXPECTED_CHARACTER
.withCharacter(' ', term.length(), term, ParseError.ErrorType.TERM_ERROR));
ParseError.unexpectedCharacter2(' ', term.length(), term, errorType));
}
// bunch of single-character tokens
case '.':
@ -149,8 +150,7 @@ public class LambdaLexer {
} while (pos < term.length() && Character.isLetterOrDigit(term.charAt(pos))
&& (int) term.charAt(pos) < 128);
if (pos < term.length() && (int) term.charAt(pos) >= 128) {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER
.withCharacter(term.charAt(pos), pos, term, ParseError.ErrorType.TERM_ERROR));
return new Result<>(null, ParseError.unexpectedCharacter2(term.charAt(pos), pos, term, errorType));
}
String s = sb.toString();
TokenType type;
@ -182,8 +182,8 @@ public class LambdaLexer {
} while (pos < term.length() && Character.isDigit(term.charAt(pos)));
return new Result<>(new Token(TokenType.NUMBER, sb.toString(), term, startPos));
} else {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter(c, pos, term,
ParseError.ErrorType.TERM_ERROR));
return new Result<>(null, ParseError.unexpectedCharacter2(c, pos, term,
errorType));
}
}

View File

@ -42,7 +42,7 @@ public class LambdaParser {
* @param term String to parse
*/
public LambdaParser(String term) {
this.lexer = new LambdaLexer(term);
this.lexer = new LambdaLexer(term, ParseError.ErrorType.TERM_ERROR);
}
/**
@ -69,7 +69,7 @@ public class LambdaParser {
TokenType current = token.getType();
Optional<ParseError> error = nextToken();
if (current != type) {
return Optional.of(ParseError.UNEXPECTED_TOKEN.withToken(lastToken,
return Optional.of(ParseError.unexpectedToken(lastToken,
ParseError.ErrorType.TERM_ERROR).expectedType(type));
}
return error;
@ -91,8 +91,7 @@ public class LambdaParser {
return t;
}
return new Result<>(null,
ParseError.UNEXPECTED_TOKEN
.withToken(last, ParseError.ErrorType.TERM_ERROR)
ParseError.unexpectedToken(last, ParseError.ErrorType.TERM_ERROR)
.expectedTypes(ATOM_START_TOKENS));
}
@ -109,8 +108,7 @@ public class LambdaParser {
}
}
if (token.getType() == TokenType.EOF) {
return new Result<>(null, ParseError.UNEXPECTED_TOKEN
.withToken(token, ParseError.ErrorType.TERM_ERROR)
return new Result<>(null, ParseError.unexpectedToken(token, ParseError.ErrorType.TERM_ERROR)
.expectedInput(ExpectedInput.TERM));
}
return parseApplication();
@ -210,7 +208,7 @@ public class LambdaParser {
try {
n = Integer.parseInt(number);
} catch (NumberFormatException e) {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken(
return new Result<>(null, ParseError.unexpectedCharacter(
token, ParseError.ErrorType.TERM_ERROR));
}
error = nextToken();

View File

@ -1,8 +1,6 @@
package edu.kit.typicalc.model.parser;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.*;
/**
* Errors that can occur when parsing a lambda term or type assumption.
@ -10,17 +8,44 @@ import java.util.Optional;
* @see LambdaLexer
* @see LambdaParser
*/
public enum ParseError {
public final class ParseError {
/**
* the lambda term didn't meet the specified syntax
*/
UNEXPECTED_TOKEN,
private ParseError(ErrorCause unexpectedToken) {
this.causeEnum = unexpectedToken;
}
/**
* the string contained a character not allowed in that context
*/
UNEXPECTED_CHARACTER;
public enum ErrorCause {
/**
* the lambda term didn't meet the specified syntax
*/
UNEXPECTED_TOKEN,
/**
* the string contained a character not allowed in that context
*/
UNEXPECTED_CHARACTER
}
private final ErrorCause causeEnum;
public ErrorCause getCauseEnum() {
return causeEnum;
}
public static ParseError unexpectedToken(Token cause, ErrorType source) {
var self = new ParseError(ErrorCause.UNEXPECTED_TOKEN);
return self.withToken(cause, source);
}
public static ParseError unexpectedCharacter(Token cause, ErrorType source) {
var self = new ParseError(ErrorCause.UNEXPECTED_CHARACTER);
return self.withToken(cause, source);
}
public static ParseError unexpectedCharacter2(char cause, int position, String term, ErrorType errorType) {
var self = new ParseError(ErrorCause.UNEXPECTED_CHARACTER);
return self.withCharacter(cause, position, term, errorType);
}
public enum ErrorType {
/**
@ -64,18 +89,32 @@ public enum ParseError {
* @return this object
*/
public ParseError expectedType(Token.TokenType needed) {
this.needed = Optional.of(List.of(needed));
this.needed = Optional.of(new ArrayList<>(List.of(needed)));
return this;
}
/**
* Attach expected token types to this error.
* Set expected token types of this error.
*
* @param needed the possible token types
* @return this object
*/
public ParseError expectedTypes(Collection<Token.TokenType> needed) {
this.needed = Optional.of(needed);
this.needed = Optional.of(new ArrayList<>(needed));
return this;
}
/**
* Add an expected token type to this error.
*
* @param needed the possible token type
* @return this object
*/
public ParseError attachExpectedType(Token.TokenType needed) {
if (this.needed.isEmpty()) {
this.needed = Optional.of(new ArrayList<>());
}
this.needed.get().add(needed);
return this;
}
@ -109,9 +148,9 @@ public enum ParseError {
/**
* Attach a character and position to this error.
*
* @param cause the character
* @param cause the character
* @param position it's position
* @param term the term that is parsed
* @param term the term that is parsed
* @return this object
*/
public ParseError withCharacter(char cause, int position, String term, ErrorType errorType) {
@ -175,7 +214,36 @@ public enum ParseError {
this.errorType = Optional.of(errorType);
}
ParseError() {
@Override
public String toString() {
return "ParseError{"
+ "cause=" + cause
+ ", needed=" + needed
+ ", expected=" + expected
+ ", term='" + term + '\''
+ ", wrongChar=" + wrongChar
+ ", correctChar=" + correctChar
+ ", position=" + position
+ ", errorType=" + errorType
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ParseError that = (ParseError) o;
return wrongChar == that.wrongChar && correctChar == that.correctChar && position == that.position
&& causeEnum == that.causeEnum && cause.equals(that.cause) && needed.equals(that.needed)
&& expected.equals(that.expected) && term.equals(that.term) && errorType.equals(that.errorType);
}
@Override
public int hashCode() {
return Objects.hash(causeEnum, cause, needed, expected, term, wrongChar, correctChar, position, errorType);
}
}

View File

@ -25,7 +25,8 @@ public class TypeAssumptionParser {
*/
public Result<Map<VarTerm, TypeAbstraction>, ParseError> parse(String assumptions) {
ParserState<Map<VarTerm, TypeAbstraction>> state = new InitialState(new LinkedHashMap<>());
LambdaLexer lexer = new LambdaLexer(cleanAssumptionText(assumptions));
LambdaLexer lexer = new LambdaLexer(
cleanAssumptionText(assumptions), ParseError.ErrorType.TYPE_ASSUMPTION_ERROR);
Optional<Token> extraToken = Optional.empty();
while (true) {
Token token1;
@ -151,8 +152,8 @@ public class TypeAssumptionParser {
case EOF:
return new ParserResult<>(alreadyParsed);
default:
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
}
@ -171,8 +172,7 @@ public class TypeAssumptionParser {
if (t.getType() == TokenType.COLON) {
return new ParserResult<>(new ExpectingTypeDef(alreadyParsed, var));
} else {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
}
@ -201,8 +201,8 @@ public class TypeAssumptionParser {
if (typeVariables.isEmpty()) {
return new ParserResult<>(new ExpectingTypeVariables(alreadyParsed, var));
} else {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
if (state.isPresent()) {
@ -245,8 +245,8 @@ public class TypeAssumptionParser {
case COMMA:
return new ParserResult<>(new InitialState(alreadyParsed));
default:
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
}
@ -281,8 +281,8 @@ public class TypeAssumptionParser {
return handleInnerParenthesis(t);
}
if (parsedType.isPresent()) {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
Type type = parseLiteral(t.getText());
// try parsing function type (see below)
@ -296,8 +296,9 @@ public class TypeAssumptionParser {
return handleInnerParenthesis(t);
}
if (parsedType.isEmpty()) {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedInput(ExpectedInput.TYPE));
}
// parse function type
state = Optional.of(new ParseTypeStateExpectArrow(typeVariableUniqueIndex).handle(t).getState());
@ -315,6 +316,10 @@ public class TypeAssumptionParser {
return new ParserResult<>(this);
case RIGHT_PARENTHESIS:
openParens -= 1;
if (openParens < parenthesisInitial) { // too many closed parenthesis
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
if (state.isPresent()) {
return handleInner(t);
}
@ -336,28 +341,42 @@ public class TypeAssumptionParser {
if (parsedType.isPresent()) {
return new ParserResult<>(this); // parenthesized part may be start of function
}
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
case COMMA:
case EOF:
if (state.isPresent()) {
return handleInner(t).attachToken(t);
}
if (stateParenthesis.isPresent() && openParens == parenthesisInitial) {
return handleInnerParenthesis(t).attachToken(t);
if (stateParenthesis.isPresent()) {
if (openParens != parenthesisInitial) { // parenthesis mismatch
// feed dummy token to inner parser to get expected tokens at this point
var it = handleInnerParenthesis(
new Token(TokenType.EQUALS, "", "", 0))
.attachToken(t);
return it.modifyError(err -> err
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.attachExpectedType(TokenType.RIGHT_PARENTHESIS));
} else {
return handleInnerParenthesis(t).attachToken(t);
}
}
if (parsedType.isPresent() && openParens == parenthesisInitial) {
return new ParserResult<>(parsedType.get()).attachToken(t);
}
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
return new ParserResult<>(ParseError.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedInput(ExpectedInput.TYPE));
default:
if (state.isPresent()) {
return handleInner(t);
}
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
if (parsedType.isPresent()) {
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedType(TokenType.ARROW));
}
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
@ -434,8 +453,7 @@ public class TypeAssumptionParser {
return new ParserResult<>(this);
}
} else {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
}
@ -456,20 +474,20 @@ public class TypeAssumptionParser {
switch (t.getType()) {
case VARIABLE:
if (expectCommaOrDot) {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedTypes(List.of(TokenType.COMMA, Token.TokenType.DOT)));
}
String input = t.getText();
if (!TYPE_VARIABLE_PATTERN.matcher(input).matches()) {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
int i = Integer.parseInt(input.substring(1));
TypeVariable variable = new TypeVariable(TypeVariableKind.USER_INPUT, i);
if (variables.contains(variable)) {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
variable.setUniqueIndex(alreadyParsed.size());
variables.add(variable);
@ -480,8 +498,8 @@ public class TypeAssumptionParser {
expectCommaOrDot = false;
return new ParserResult<>(this);
} else {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedType(TokenType.VARIABLE));
}
case DOT:
@ -490,18 +508,18 @@ public class TypeAssumptionParser {
// parse actual type
return new ParserResult<>(new ExpectingTypeDef(alreadyParsed, variables, var));
} else {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedType(TokenType.VARIABLE));
}
default:
if (expectCommaOrDot) {
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedTypes(List.of(TokenType.COMMA, TokenType.DOT)));
}
return new ParserResult<>(ParseError.UNEXPECTED_TOKEN
.withToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
return new ParserResult<>(ParseError
.unexpectedToken(t, ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
}
}
}

View File

@ -32,6 +32,7 @@ public class ErrorView extends VerticalLayout implements LocaleChangeObserver {
private final Paragraph hint;
public ErrorView(ParseError error) {
System.out.println(error);
this.error = error;
VerticalLayout container = new VerticalLayout();
container.setId(ERROR_CONTENT_ID);
@ -72,7 +73,7 @@ public class ErrorView extends VerticalLayout implements LocaleChangeObserver {
} else if (errorType.get() == ParseError.ErrorType.TYPE_ASSUMPTION_ERROR) {
descriptionForError = "error.typeAssumptionForError";
}
switch (error) {
switch (error.getCauseEnum()) {
case UNEXPECTED_TOKEN:
Optional<Token> cause = error.getCause();
if (cause.isPresent()) {

View File

@ -6,8 +6,7 @@ import edu.kit.typicalc.util.Result;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
class LambdaParserTest {
private static final VarTerm X = new VarTerm("x");
@ -97,62 +96,62 @@ class LambdaParserTest {
void errorHandling() {
LambdaParser parser = new LambdaParser("");
ParseError error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(TokenType.EOF, error.getCause().get().getType());
parser = new LambdaParser("λx.");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.EOF, "", "λx.", 3), error.getCause().get());
assertEquals(ExpectedInput.TERM, error.getExpectedInput().get());
parser = new LambdaParser("x)");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.RIGHT_PARENTHESIS, ")", "x)", 1), error.getCause().get());
parser = new LambdaParser("??");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
assertEquals(ParseError.ErrorCause.UNEXPECTED_CHARACTER, parser.parse().unwrapError().getCauseEnum());
parser = new LambdaParser("");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
assertEquals(ParseError.ErrorCause.UNEXPECTED_CHARACTER, parser.parse().unwrapError().getCauseEnum());
parser = new LambdaParser("ä");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
assertEquals(ParseError.ErrorCause.UNEXPECTED_CHARACTER, parser.parse().unwrapError().getCauseEnum());
parser = new LambdaParser("123333333333333");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
assertEquals(ParseError.ErrorCause.UNEXPECTED_CHARACTER, parser.parse().unwrapError().getCauseEnum());
parser = new LambdaParser("x 123333333333333");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_CHARACTER, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_CHARACTER, error.getCauseEnum());
assertEquals(new Token(TokenType.NUMBER, "123333333333333", "x 123333333333333", 2),
error.getCause().get());
parser = new LambdaParser("λ)");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.RIGHT_PARENTHESIS, ")", "λ)", 1), error.getCause().get());
parser = new LambdaParser("λx=");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.EQUALS, "=", "λx=", 2), error.getCause().get());
parser = new LambdaParser("λx..");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.DOT, ".", "λx..", 3), error.getCause().get());
assertEquals(ExpectedInput.TERM, error.getExpectedInput().get());
parser = new LambdaParser("let ) =");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.RIGHT_PARENTHESIS, ")", "let ) =", 4), error.getCause().get());
parser = new LambdaParser("let x .");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.DOT, ".", "let x .", 6), error.getCause().get());
parser = new LambdaParser("let x = )");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.RIGHT_PARENTHESIS, ")", "let x = )",8), error.getCause().get());
parser = new LambdaParser("let x = y )");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.RIGHT_PARENTHESIS, ")", "let x = y )", 10), error.getCause().get());
parser = new LambdaParser("let x = y in )");
error = parser.parse().unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(new Token(TokenType.RIGHT_PARENTHESIS, ")", "let x = y in )", 13), error.getCause().get());
}
@ -237,13 +236,24 @@ class LambdaParserTest {
assertEquals(ExpectedInput.TERM, err.getExpectedInput().get());
}
@Test
void changingErrors() {
// parse error objects should behave normally
ParseError err1 = getParseError("λx.λ");
ParseError err2 = getParseError("λx.");
ParseError err3 = getParseError("λx.λ");
assertEquals(err1, err3);
assertNotEquals(err2, err3);
}
@Test
void equality() {
EqualsVerifier.forClass(Token.class).usingGetClass().verify();
EqualsVerifier.simple().forClass(ParseError.class).usingGetClass().verify();
}
static ParseError getParseError(String term) {
LambdaParser parser = new LambdaParser(term);
var parser = new LambdaParser(term);
return parser.parse().unwrapError();
}
}

View File

@ -219,7 +219,7 @@ class TypeAssumptionParserTest {
Result<Map<VarTerm, TypeAbstraction>, ParseError> type = parser.parse("id: ∀ t1");
assertTrue(type.isError());
ParseError error = type.unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(Token.TokenType.EOF, error.getCause().get().getType());
Collection<Token.TokenType> expected = error.getExpected().get();
assertEquals(2, expected.size());
@ -233,7 +233,7 @@ class TypeAssumptionParserTest {
Result<Map<VarTerm, TypeAbstraction>, ParseError> type = parser.parse("id: ∀ t1, t1 : t1 -> t1");
assertTrue(type.isError());
ParseError error = type.unwrapError();
assertEquals(ParseError.UNEXPECTED_TOKEN, error);
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, error.getCauseEnum());
assertEquals(Token.TokenType.VARIABLE, error.getCause().get().getType());
assertEquals(10, error.getCause().get().getPos());
}
@ -258,25 +258,31 @@ class TypeAssumptionParserTest {
@Test
void errors() {
Map<String, ParseError> tests = new HashMap<>();
Map<String, ParseError> tests = new LinkedHashMap<>();
tests.put("",
ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.EOF, "", "", 0),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
tests.put("ö", ParseError.UNEXPECTED_CHARACTER);
ParseError.unexpectedToken(new Token(Token.TokenType.EOF, "", "type1:", 6),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedInput(ExpectedInput.TYPE)
.expectedType(Token.TokenType.UNIVERSAL_QUANTIFIER));
tests.put("ö", ParseError.unexpectedCharacter2('ö', 6, "type1:ö", ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
tests.put("(x",
ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.EOF, "", "(x", 2),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
ParseError.unexpectedToken(new Token(Token.TokenType.EOF, "", "type1:(x", 8),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedTypes(List.of(Token.TokenType.ARROW, Token.TokenType.RIGHT_PARENTHESIS)));
tests.put("-> x",
ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.ARROW, "->", "-> x", 0),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
ParseError.unexpectedToken(new Token(Token.TokenType.ARROW, "->", "type1:-> x", 6),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedInput(ExpectedInput.TYPE)
.expectedType(Token.TokenType.UNIVERSAL_QUANTIFIER)
);
tests.put("x 11",
ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.NUMBER, "11", "x 11", 2),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
tests.put("x )", ParseError.UNEXPECTED_TOKEN.withToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", "x )", 2),
ParseError.unexpectedToken(new Token(Token.TokenType.NUMBER, "11", "type1:x 11", 8),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR)
.expectedType(Token.TokenType.ARROW));
tests.put("x )", ParseError.unexpectedToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", "type1:x )", 8),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
tests.put("x -> (x) )", ParseError.unexpectedToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", "type1:x -> (x) )", 15),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
tests.put("x -> (x) )", ParseError.UNEXPECTED_TOKEN
.withToken(new Token(Token.TokenType.RIGHT_PARENTHESIS, ")", "x -> (x) )", 9),
ParseError.ErrorType.TYPE_ASSUMPTION_ERROR));
for (Map.Entry<String, ParseError> entry : tests.entrySet()) {
TypeAssumptionParser parser = new TypeAssumptionParser();
Result<Map<VarTerm, TypeAbstraction>, ParseError> type = parser.parse("type1:" + entry.getKey());
@ -289,12 +295,12 @@ class TypeAssumptionParserTest {
TypeAssumptionParser parser = new TypeAssumptionParser();
Result<Map<VarTerm, TypeAbstraction>, ParseError> type = parser.parse("föhn: int");
assertTrue(type.isError());
assertEquals(ParseError.UNEXPECTED_CHARACTER, type.unwrapError());
assertEquals(ParseError.ErrorCause.UNEXPECTED_CHARACTER, type.unwrapError().getCauseEnum());
assertEquals(1, type.unwrapError().getPosition());
parser = new TypeAssumptionParser();
type = parser.parse("1typ:int");
assertTrue(type.isError());
assertEquals(ParseError.UNEXPECTED_TOKEN, type.unwrapError());
assertEquals(ParseError.ErrorCause.UNEXPECTED_TOKEN, type.unwrapError().getCauseEnum());
assertEquals(0, type.unwrapError().getPosition());
}