diff --git a/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java b/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java index 1e7d721..4adaa24 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java +++ b/src/main/java/edu/kit/typicalc/model/parser/LambdaLexer.java @@ -118,52 +118,56 @@ public class LambdaLexer { advance(); return new Result<>(t); default: - // only allow ascii characters in variable names - if (Character.isLetter(c) && (int) c < 128) { - int startPos = pos; - // identifier - StringBuilder sb = new StringBuilder(); - do { - sb.append(term.charAt(pos)); - advance(); - } 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)); - } - String s = sb.toString(); - TokenType type; - switch (s) { - case "let": - type = TokenType.LET; - break; - case "in": - type = TokenType.IN; - break; - case "true": - type = TokenType.TRUE; - break; - case "false": - type = TokenType.FALSE; - break; - default: - type = TokenType.VARIABLE; - break; - } - return new Result<>(new Token(type, sb.toString(), startPos)); - } else if (Character.isDigit(c)) { - int startPos = pos; - // number literal - StringBuilder sb = new StringBuilder(); - do { - 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)); - } + return parseAtomToken(c); + } + } + + private Result parseAtomToken(char c) { + // only allow ascii characters in variable names + if (Character.isLetter(c) && (int) c < 128) { + int startPos = pos; + // identifier or keyword + StringBuilder sb = new StringBuilder(); + do { + sb.append(term.charAt(pos)); + advance(); + } 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)); + } + String s = sb.toString(); + TokenType type; + switch (s) { + case "let": + type = TokenType.LET; + break; + case "in": + type = TokenType.IN; + break; + case "true": + type = TokenType.TRUE; + break; + case "false": + type = TokenType.FALSE; + break; + default: + type = TokenType.VARIABLE; + break; + } + return new Result<>(new Token(type, sb.toString(), startPos)); + } else if (Character.isDigit(c)) { + int startPos = pos; + // number literal + StringBuilder sb = new StringBuilder(); + do { + 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)); } } } \ No newline at end of file diff --git a/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java b/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java index 9bbe3f7..755c41b 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java +++ b/src/main/java/edu/kit/typicalc/model/parser/LambdaParser.java @@ -34,7 +34,7 @@ public class LambdaParser { private static final Set ATOM_START_TOKENS = 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 @@ -85,10 +85,10 @@ public class LambdaParser { return t; } Optional next = expect(TokenType.EOF); - if (next.isPresent()) { - return new Result<>(null, next.get()); + if (next.isEmpty()) { + return t; } - return t; + return new Result<>(null, next.get()); } /** @@ -103,26 +103,10 @@ public class LambdaParser { return new Result<>(null, error.get()); } } - switch (token.getType()) { - case LAMBDA: - Result abs = parseAbstraction(); - if (abs.isError()) { - return new Result<>(abs); - } - return new Result<>(abs.unwrap()); - case LET: - Result 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(); + if (token.getType() == TokenType.EOF) { + return new Result<>(null, ParseError.TOO_FEW_TOKENS); } + return parseApplication(); } private Result parseAbstraction() { @@ -148,20 +132,12 @@ public class LambdaParser { * @return the term, or an error */ private Result parseApplication() { - Result left = parseAtom(); + Result left = parsePart(); if (left.isError()) { return left; } - while (ATOM_START_TOKENS.contains(token.getType()) - || token.getType() == TokenType.LAMBDA || token.getType() == TokenType.LET) { - Result atom; - if (token.getType() == TokenType.LAMBDA) { - atom = new Result<>(parseAbstraction()); - } else if (token.getType() == TokenType.LET) { - atom = new Result<>(parseLet()); - } else { - atom = parseAtom(); - } + while (ATOM_START_TOKENS.contains(token.getType())) { + Result atom = parsePart(); if (atom.isError()) { 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 */ - private Result parseAtom() { + private Result parsePart() { switch (token.getType()) { case VARIABLE: Result var = parseVar(); 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 error = nextToken(); + if (error.isEmpty()) { + return new Result<>(new BooleanTerm(b)); + } + return new Result<>(null, error.get()); case NUMBER: String number = token.getText(); int n; @@ -216,20 +205,11 @@ public class LambdaParser { } catch (NumberFormatException e) { return new Result<>(null, ParseError.UNEXPECTED_CHARACTER.withToken(token)); } - Optional 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(); - if (error.isPresent()) { - return new Result<>(null, error.get()); + if (error.isEmpty()) { + return new Result<>(new IntegerTerm(n)); } - return new Result<>(new BooleanTerm(b)); + return new Result<>(null, error.get()); default: error = expect(TokenType.LEFT_PARENTHESIS); if (error.isPresent()) { @@ -237,19 +217,19 @@ public class LambdaParser { } Result term = parseTerm(false); error = expect(TokenType.RIGHT_PARENTHESIS); - if (error.isPresent()) { - return new Result<>(null, error.get()); + if (error.isEmpty()) { + return term; } - return term; + return new Result<>(null, error.get()); } } private Result parseVar() { String s = token.getText(); Optional next = expect(TokenType.VARIABLE); - if (next.isPresent()) { - return new Result<>(null, next.get()); + if (next.isEmpty()) { + return new Result<>(new VarTerm(s)); } - return new Result<>(new VarTerm(s)); + return new Result<>(null, next.get()); } } diff --git a/src/main/java/edu/kit/typicalc/model/parser/Token.java b/src/main/java/edu/kit/typicalc/model/parser/Token.java index f9778c7..549964d 100644 --- a/src/main/java/edu/kit/typicalc/model/parser/Token.java +++ b/src/main/java/edu/kit/typicalc/model/parser/Token.java @@ -24,7 +24,7 @@ public class Token { RIGHT_PARENTHESIS, // ) DOT, // . EQUALS, // = - ARROW, // -> TODO: document + ARROW, // -> EOF // pseudo token if end of file is reached } diff --git a/src/main/java/edu/kit/typicalc/util/Result.java b/src/main/java/edu/kit/typicalc/util/Result.java index 5b8e254..fdf1945 100644 --- a/src/main/java/edu/kit/typicalc/util/Result.java +++ b/src/main/java/edu/kit/typicalc/util/Result.java @@ -27,6 +27,7 @@ public class Result { * * @param other the result */ + @SuppressWarnings("unchecked") public Result(Result other) { this.value = (T) other.value; this.error = (E) other.error; @@ -42,7 +43,7 @@ public class Result { */ public Result(T value, E error) { 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.error = error; diff --git a/src/test/java/edu/kit/typicalc/model/parser/LambdaTermGenerator.java b/src/test/java/edu/kit/typicalc/model/parser/LambdaTermGenerator.java index d1747be..06e0b2b 100644 --- a/src/test/java/edu/kit/typicalc/model/parser/LambdaTermGenerator.java +++ b/src/test/java/edu/kit/typicalc/model/parser/LambdaTermGenerator.java @@ -31,7 +31,7 @@ public class LambdaTermGenerator extends Generator { } private LambdaTerm generateReal(SourceOfRandomness random) { - if (random.nextInt(1, 7) < 3) { + if (random.nextInt(1, 10) < 3) { LambdaTerm one = generateReal(random); LambdaTerm two = generateReal(random); if (random.nextInt(1, 10) < 8) {