Include token in ParseError + more error handling

This commit is contained in:
Arne Keller 2021-01-29 09:39:58 +01:00
parent 07a7218192
commit 4e5488e1eb
4 changed files with 96 additions and 32 deletions

15
pom.xml
View File

@ -8,7 +8,7 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@ -116,6 +116,19 @@
<version>5.7.0</version> <version>5.7.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- need these for IntelliJ -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.vaadin.componentfactory</groupId> <groupId>com.vaadin.componentfactory</groupId>
<artifactId>tooltip</artifactId> <artifactId>tooltip</artifactId>

View File

@ -37,7 +37,6 @@ public class LambdaParser {
*/ */
public LambdaParser(String term) { public LambdaParser(String term) {
this.lexer = new LambdaLexer(term); this.lexer = new LambdaLexer(term);
nextToken();
} }
/** /**
@ -58,10 +57,13 @@ public class LambdaParser {
* Returns false otherwise. * Returns false otherwise.
* @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 boolean expect(TokenType type) { private Optional<ParseError> expect(TokenType type) {
TokenType current = token.getType(); TokenType current = token.getType();
nextToken(); // TODO: Fehlerbehandlung Optional<ParseError> error = nextToken();
return current == type; if (current != type) {
return Optional.of(ParseError.UNEXPECTED_TOKEN.withToken(token));
}
return error;
} }
/** /**
@ -69,9 +71,13 @@ public class LambdaParser {
* @return the term given by the String * @return the term given by the String
*/ */
public Result<LambdaTerm, ParseError> parse() { public Result<LambdaTerm, ParseError> parse() {
Result<LambdaTerm, ParseError> t = parseTerm(); Result<LambdaTerm, ParseError> t = parseTerm(true);
if (!expect(TokenType.EOF)) { if (t.isError()) {
return new Result<>(null, ParseError.TOO_MANY_TOKENS); return t;
}
Optional<ParseError> next = expect(TokenType.EOF);
if (next.isPresent()) {
return new Result<>(null, next.get());
} }
return t; return t;
} }
@ -80,7 +86,13 @@ public class LambdaParser {
* Parses a term. * Parses a term.
* @return the term, or an error * @return the term, or an error
*/ */
private Result<LambdaTerm, ParseError> parseTerm() { private Result<LambdaTerm, ParseError> parseTerm(boolean next) {
if (next) {
Optional<ParseError> error = nextToken();
if (error.isPresent()) {
return new Result<>(null, error.get());
}
}
switch (token.getType()) { switch (token.getType()) {
case LAMBDA: case LAMBDA:
Result<AbsTerm, ParseError> abs = parseAbstraction(); Result<AbsTerm, ParseError> abs = parseAbstraction();
@ -98,10 +110,11 @@ public class LambdaParser {
private Result<AbsTerm, ParseError> parseAbstraction() { private Result<AbsTerm, ParseError> parseAbstraction() {
nextToken(); nextToken();
Result<VarTerm, ParseError> var = parseVar(); Result<VarTerm, ParseError> var = parseVar();
if (!expect(TokenType.DOT)) { Optional<ParseError> next = expect(TokenType.DOT);
return new Result<>(null, ParseError.UNEXPECTED_TOKEN); if (next.isPresent()) {
return new Result<>(null, next.get());
} }
Result<LambdaTerm, ParseError> body = parseTerm(); Result<LambdaTerm, ParseError> body = parseTerm(false);
// TODO: Fehlerbehandlung // TODO: Fehlerbehandlung
return new Result<>(new AbsTerm(var.unwrap(), body.unwrap())); return new Result<>(new AbsTerm(var.unwrap(), body.unwrap()));
} }
@ -111,22 +124,37 @@ public class LambdaParser {
* @return the term, or an error * @return the term, or an error
*/ */
private Result<LambdaTerm, ParseError> parseApplication() { private Result<LambdaTerm, ParseError> parseApplication() {
LambdaTerm left = parseAtom().unwrap(); // TODO: Fehlerbehandlung Result<LambdaTerm, ParseError> left = parseAtom();
while (ATOM_START_TOKENS.contains(token.getType())) { if (left.isError()) {
LambdaTerm atom = parseAtom().unwrap(); // TODO: Fehlerbehandlung return left;
left = new AppTerm(left, atom);
} }
return new Result<>(left); while (ATOM_START_TOKENS.contains(token.getType())) {
Result<LambdaTerm, ParseError> atom = parseAtom();
if (atom.isError()) {
return atom;
}
left = new Result<>(new AppTerm(left.unwrap(), atom.unwrap()));
}
return left;
} }
private Result<LetTerm, ParseError> parseLet() { private Result<LetTerm, ParseError> parseLet() {
// TODO: Fehlerbehandlung // TODO: Fehlerbehandlung
expect(TokenType.LET); Optional<ParseError> error = expect(TokenType.LET);
if (error.isPresent()) {
return new Result<>(null, error.get());
}
VarTerm var = parseVar().unwrap(); VarTerm var = parseVar().unwrap();
expect(TokenType.EQ); error = expect(TokenType.EQ);
LambdaTerm def = parseTerm().unwrap(); if (error.isPresent()) {
expect(TokenType.IN); return new Result<>(null, error.get());
LambdaTerm body = parseTerm().unwrap(); }
LambdaTerm def = parseTerm(false).unwrap();
error = expect(TokenType.IN);
if (error.isPresent()) {
return new Result<>(null, error.get());
}
LambdaTerm body = parseTerm(false).unwrap();
return new Result<>(new LetTerm(var, def, body)); return new Result<>(new LetTerm(var, def, body));
} }
@ -145,7 +173,7 @@ public class LambdaParser {
try { try {
n = Integer.parseInt(number); n = Integer.parseInt(number);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER); return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken(token));
} }
nextToken(); nextToken();
return new Result<>(new IntegerTerm(n)); return new Result<>(new IntegerTerm(n));
@ -157,7 +185,7 @@ public class LambdaParser {
return new Result<>(new BooleanTerm(b)); return new Result<>(new BooleanTerm(b));
default: default:
expect(TokenType.LP); expect(TokenType.LP);
Result<LambdaTerm, ParseError> term = parseTerm(); Result<LambdaTerm, ParseError> term = parseTerm(false);
expect(TokenType.RP); expect(TokenType.RP);
return term; return term;
} }
@ -165,8 +193,9 @@ public class LambdaParser {
private Result<VarTerm, ParseError> parseVar() { private Result<VarTerm, ParseError> parseVar() {
String s = token.getText(); String s = token.getText();
if (!expect(TokenType.VARIABLE)) { Optional<ParseError> next = expect(TokenType.VARIABLE);
return new Result<>(null, ParseError.UNEXPECTED_TOKEN); if (next.isPresent()) {
return new Result<>(null, next.get());
} }
return new Result<>(new VarTerm(s)); return new Result<>(new VarTerm(s));
} }

View File

@ -7,11 +7,6 @@ public enum ParseError {
*/ */
UNEXPECTED_TOKEN, UNEXPECTED_TOKEN,
/**
* some tokens were remaining after parsing a full lambda term
*/
TOO_MANY_TOKENS,
/** /**
* some tokens were required, but not provided * some tokens were required, but not provided
*/ */
@ -20,5 +15,23 @@ public enum ParseError {
/** /**
* the string contained a character not allowed in that context * the string contained a character not allowed in that context
*/ */
UNEXPECTED_CHARACTER UNEXPECTED_CHARACTER;
private Token cause;
public ParseError withToken(Token cause) {
this.cause = cause;
return this;
}
/**
* @return the token associated with this error, or null if none
*/
public Token getCause() { // TODO: document
return cause;
}
ParseError() {
}
} }

View File

@ -85,5 +85,14 @@ class LambdaParserTest {
void miscellaneousTerms() { void miscellaneousTerms() {
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)");
assertEquals(ParseError.UNEXPECTED_TOKEN, parser.parse().unwrapError());
System.out.println("parsing ??");
parser = new LambdaParser("??");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
parser = new LambdaParser("123333333333333");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
parser = new LambdaParser("x 123333333333333");
assertEquals(ParseError.UNEXPECTED_CHARACTER, parser.parse().unwrapError());
} }
} }