Code style in util and parser

This commit is contained in:
Arne Keller 2021-03-08 11:40:05 +01:00
parent 8d6782793e
commit 1b642e84f9
5 changed files with 88 additions and 103 deletions

View File

@ -118,52 +118,56 @@ public class LambdaLexer {
advance(); advance();
return new Result<>(t); return new Result<>(t);
default: default:
// only allow ascii characters in variable names return parseAtomToken(c);
if (Character.isLetter(c) && (int) c < 128) { }
int startPos = pos; }
// identifier
StringBuilder sb = new StringBuilder(); private Result<Token, ParseError> parseAtomToken(char c) {
do { // only allow ascii characters in variable names
sb.append(term.charAt(pos)); if (Character.isLetter(c) && (int) c < 128) {
advance(); int startPos = pos;
} while (pos < term.length() && Character.isLetterOrDigit(term.charAt(pos)) // identifier or keyword
&& (int) term.charAt(pos) < 128); StringBuilder sb = new StringBuilder();
if (pos < term.length() && (int) term.charAt(pos) >= 128) { do {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER sb.append(term.charAt(pos));
.withCharacter(term.charAt(pos), pos)); advance();
} } while (pos < term.length() && Character.isLetterOrDigit(term.charAt(pos))
String s = sb.toString(); && (int) term.charAt(pos) < 128);
TokenType type; if (pos < term.length() && (int) term.charAt(pos) >= 128) {
switch (s) { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER
case "let": .withCharacter(term.charAt(pos), pos));
type = TokenType.LET; }
break; String s = sb.toString();
case "in": TokenType type;
type = TokenType.IN; switch (s) {
break; case "let":
case "true": type = TokenType.LET;
type = TokenType.TRUE; break;
break; case "in":
case "false": type = TokenType.IN;
type = TokenType.FALSE; break;
break; case "true":
default: type = TokenType.TRUE;
type = TokenType.VARIABLE; break;
break; case "false":
} type = TokenType.FALSE;
return new Result<>(new Token(type, sb.toString(), startPos)); break;
} else if (Character.isDigit(c)) { default:
int startPos = pos; type = TokenType.VARIABLE;
// number literal break;
StringBuilder sb = new StringBuilder(); }
do { return new Result<>(new Token(type, sb.toString(), startPos));
sb.append(term.charAt(pos)); } else if (Character.isDigit(c)) {
advance(); int startPos = pos;
} while (pos < term.length() && Character.isDigit(term.charAt(pos))); // number literal
return new Result<>(new Token(TokenType.NUMBER, sb.toString(), startPos)); StringBuilder sb = new StringBuilder();
} else { do {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter(c, pos)); sb.append(term.charAt(pos));
} advance();
} while (pos < term.length() && Character.isDigit(term.charAt(pos)));
return new Result<>(new Token(TokenType.NUMBER, sb.toString(), startPos));
} else {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withCharacter(c, pos));
} }
} }
} }

View File

@ -34,7 +34,7 @@ public class LambdaParser {
private static final Set<TokenType> ATOM_START_TOKENS private static final Set<TokenType> ATOM_START_TOKENS
= EnumSet.of(TokenType.VARIABLE, TokenType.NUMBER, TokenType.TRUE, = EnumSet.of(TokenType.VARIABLE, TokenType.NUMBER, TokenType.TRUE,
TokenType.FALSE, TokenType.LEFT_PARENTHESIS); TokenType.FALSE, TokenType.LEFT_PARENTHESIS, TokenType.LAMBDA, TokenType.LET);
/** /**
* Constructs a parser with the specified String * Constructs a parser with the specified String
@ -85,10 +85,10 @@ public class LambdaParser {
return t; return t;
} }
Optional<ParseError> next = expect(TokenType.EOF); Optional<ParseError> next = expect(TokenType.EOF);
if (next.isPresent()) { if (next.isEmpty()) {
return new Result<>(null, next.get()); return t;
} }
return t; return new Result<>(null, next.get());
} }
/** /**
@ -103,26 +103,10 @@ public class LambdaParser {
return new Result<>(null, error.get()); return new Result<>(null, error.get());
} }
} }
switch (token.getType()) { if (token.getType() == TokenType.EOF) {
case LAMBDA: return new Result<>(null, ParseError.TOO_FEW_TOKENS);
Result<AbsTerm, ParseError> abs = parseAbstraction();
if (abs.isError()) {
return new Result<>(abs);
}
return new Result<>(abs.unwrap());
case LET:
Result<LetTerm, ParseError> let = parseLet();
if (let.isError()) {
return new Result<>(let);
}
return new Result<>(let.unwrap());
case VARIABLE:
return new Result<>(parseApplication());
case EOF:
return new Result<>(null, ParseError.TOO_FEW_TOKENS);
default:
return parseApplication();
} }
return parseApplication();
} }
private Result<AbsTerm, ParseError> parseAbstraction() { private Result<AbsTerm, ParseError> parseAbstraction() {
@ -148,20 +132,12 @@ 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() {
Result<LambdaTerm, ParseError> left = parseAtom(); Result<LambdaTerm, ParseError> left = parsePart();
if (left.isError()) { if (left.isError()) {
return left; return left;
} }
while (ATOM_START_TOKENS.contains(token.getType()) while (ATOM_START_TOKENS.contains(token.getType())) {
|| token.getType() == TokenType.LAMBDA || token.getType() == TokenType.LET) { Result<LambdaTerm, ParseError> atom = parsePart();
Result<LambdaTerm, ParseError> atom;
if (token.getType() == TokenType.LAMBDA) {
atom = new Result<>(parseAbstraction());
} else if (token.getType() == TokenType.LET) {
atom = new Result<>(parseLet());
} else {
atom = parseAtom();
}
if (atom.isError()) { if (atom.isError()) {
return atom; return atom;
} }
@ -199,15 +175,28 @@ public class LambdaParser {
} }
/** /**
* Parses an atom (variable or number) or a parenthesised expression. * Parses a part of an expression (variable, constants, abstraction, let).
* *
* @return the term * @return the term
*/ */
private Result<LambdaTerm, ParseError> parseAtom() { private Result<LambdaTerm, ParseError> parsePart() {
switch (token.getType()) { switch (token.getType()) {
case VARIABLE: case VARIABLE:
Result<VarTerm, ParseError> var = parseVar(); Result<VarTerm, ParseError> var = parseVar();
return new Result<>(var.unwrap()); // variable token can always be parsed return new Result<>(var.unwrap()); // variable token can always be parsed
case LAMBDA:
return new Result<>(parseAbstraction());
case LET:
return new Result<>(parseLet());
case TRUE:
case FALSE:
String boolText = token.getText();
boolean b = Boolean.parseBoolean(boolText);
Optional<ParseError> error = nextToken();
if (error.isEmpty()) {
return new Result<>(new BooleanTerm(b));
}
return new Result<>(null, error.get());
case NUMBER: case NUMBER:
String number = token.getText(); String number = token.getText();
int n; int n;
@ -216,20 +205,11 @@ public class LambdaParser {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken(token)); return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken(token));
} }
Optional<ParseError> error = nextToken();
if (error.isPresent()) {
return new Result<>(null, error.get());
}
return new Result<>(new IntegerTerm(n));
case TRUE:
case FALSE:
String boolText = token.getText();
boolean b = Boolean.parseBoolean(boolText);
error = nextToken(); error = nextToken();
if (error.isPresent()) { if (error.isEmpty()) {
return new Result<>(null, error.get()); return new Result<>(new IntegerTerm(n));
} }
return new Result<>(new BooleanTerm(b)); return new Result<>(null, error.get());
default: default:
error = expect(TokenType.LEFT_PARENTHESIS); error = expect(TokenType.LEFT_PARENTHESIS);
if (error.isPresent()) { if (error.isPresent()) {
@ -237,19 +217,19 @@ public class LambdaParser {
} }
Result<LambdaTerm, ParseError> term = parseTerm(false); Result<LambdaTerm, ParseError> term = parseTerm(false);
error = expect(TokenType.RIGHT_PARENTHESIS); error = expect(TokenType.RIGHT_PARENTHESIS);
if (error.isPresent()) { if (error.isEmpty()) {
return new Result<>(null, error.get()); return term;
} }
return term; return new Result<>(null, error.get());
} }
} }
private Result<VarTerm, ParseError> parseVar() { private Result<VarTerm, ParseError> parseVar() {
String s = token.getText(); String s = token.getText();
Optional<ParseError> next = expect(TokenType.VARIABLE); Optional<ParseError> next = expect(TokenType.VARIABLE);
if (next.isPresent()) { if (next.isEmpty()) {
return new Result<>(null, next.get()); return new Result<>(new VarTerm(s));
} }
return new Result<>(new VarTerm(s)); return new Result<>(null, next.get());
} }
} }

View File

@ -24,7 +24,7 @@ public class Token {
RIGHT_PARENTHESIS, // ) RIGHT_PARENTHESIS, // )
DOT, // . DOT, // .
EQUALS, // = EQUALS, // =
ARROW, // -> TODO: document ARROW, // ->
EOF // pseudo token if end of file is reached EOF // pseudo token if end of file is reached
} }

View File

@ -27,6 +27,7 @@ public class Result<T, E> {
* *
* @param other the result * @param other the result
*/ */
@SuppressWarnings("unchecked")
public Result(Result<?, ?> other) { public Result(Result<?, ?> other) {
this.value = (T) other.value; this.value = (T) other.value;
this.error = (E) other.error; this.error = (E) other.error;
@ -42,7 +43,7 @@ public class Result<T, E> {
*/ */
public Result(T value, E error) { public Result(T value, E error) {
if ((value != null) == (error != null)) { if ((value != null) == (error != null)) {
throw new IllegalArgumentException("value xor error must be null in constructor!"); throw new IllegalArgumentException("value or error must be null in constructor!");
} }
this.value = value; this.value = value;
this.error = error; this.error = error;

View File

@ -31,7 +31,7 @@ public class LambdaTermGenerator extends Generator<String> {
} }
private LambdaTerm generateReal(SourceOfRandomness random) { private LambdaTerm generateReal(SourceOfRandomness random) {
if (random.nextInt(1, 7) < 3) { if (random.nextInt(1, 10) < 3) {
LambdaTerm one = generateReal(random); LambdaTerm one = generateReal(random);
LambdaTerm two = generateReal(random); LambdaTerm two = generateReal(random);
if (random.nextInt(1, 10) < 8) { if (random.nextInt(1, 10) < 8) {