From 5a0820b9b5feee8663ad4e39ceebafecd079e4ff Mon Sep 17 00:00:00 2001 From: Malte Schmitz Date: Fri, 25 Nov 2016 00:18:39 +0100 Subject: [PATCH] Optional -> SyntaxException --- src/parser/Parser.java | 271 ++++++++++++++++++++++---------------------- test/parser/ParserTest.java | 52 +++------ 2 files changed, 146 insertions(+), 177 deletions(-) diff --git a/src/parser/Parser.java b/src/parser/Parser.java index 1972b7b..722a3f6 100644 --- a/src/parser/Parser.java +++ b/src/parser/Parser.java @@ -6,7 +6,6 @@ import program.*; import java.util.ArrayList; import java.util.List; -import java.util.Optional; public class Parser { @@ -18,8 +17,7 @@ public class Parser { } public Program parse() { - Optional programOpt = program(); - Program program = programOpt.orElseThrow(() -> new SyntaxException("Program", position)); + Program program = program(); whitespace(); if (position < input.length()) { throw new SyntaxException("End of input", position); @@ -27,92 +25,73 @@ public class Parser { return program; } - Optional program() { - List statements = new ArrayList<>(); - boolean run = true; - int start = position; - while (run) { - Optional statement = statement(); - statement.ifPresent(stmt -> statements.add(stmt)); - if (statement.isPresent()) { - start = position; - run = token(";"); - } else { - position = start; - run = false; - } + Program program() { + Program firstStatement = statement(); + List moreStatements = new ArrayList<>(); + while (test(";")) { + consume(";"); + Program statement = statement(); + moreStatements.add(statement); } - Optional program = Optional.empty(); - for (Program statement: statements) { - if (!program.isPresent()) { - program = Optional.of(statement); - } else { - program = program.map(pgm -> new Composition(pgm, statement)); - } + Program program = firstStatement; + for (Program statement: moreStatements) { + program = new Composition(program, statement); } return program; } - Optional statement() { + Program statement() { int start = position; - Optional result = assignment(); - if (!result.isPresent()) { + Program statement; + try { + statement = assignment(); + } catch (SyntaxException se) { position = start; - result = conditional(); - if (!result.isPresent()) { + try { + statement = conditional(); + } catch (SyntaxException se2) { position = start; - result = loop(); + statement = loop(); } } - return result; + return statement; } - Optional loop() { - if (token("while") && token("(")) { - Optional condition = expression(); - return condition.flatMap(cond -> { - if (token(")") && token("{")) { - Optional program = program(); - return program.filter(pgm -> token("}")).map(pgm -> new Loop(cond, pgm)); - } - return Optional.empty(); - }); - } - return Optional.empty(); - } - - Optional conditional() { - if (token("if") && token("(")) { - Optional condition = expression(); - return condition.flatMap(cond -> { - if (token(")") && token("then") && token("{")) { - Optional thenCase = program(); - return thenCase.flatMap(thenC -> { - if (token("}") && token("else") && token("{")) { - Optional elseCase = program(); - return elseCase.filter(elseC -> token("}")).map(elseC -> new Conditional(cond, thenC, elseC)); - } - return Optional.empty(); - }); - } - return Optional.empty(); - }); - } - return Optional.empty(); - } - - Optional assignment() { - Optional identifier = identifier(); - return identifier.flatMap(id -> { - if (token(":=")) { - Optional expression = expression(); - return expression.map(exp -> new Assignment(id, exp)); - } - return Optional.empty(); - }); + Program loop() { + consume("while"); + consume("("); + Expression condition = expression(); + consume(")"); + consume("{"); + Program program = program(); + consume("}"); + return new Loop(condition, program); + } + + Program conditional() { + consume("if"); + consume("("); + Expression condition = expression(); + consume(")"); + consume("then"); + consume("{"); + Program thenCase = program(); + consume("}"); + consume("else"); + consume("{"); + Program elseCase = program(); + consume("}"); + return new Conditional(condition, thenCase, elseCase); } - static class OperatorWithExpression { + Program assignment() { + Identifier identifier = identifier(); + consume(":="); + Expression expression = expression(); + return new Assignment(identifier, expression); + } + + private static class OperatorWithExpression { private final Operator operator; private final Expression expression; @@ -122,79 +101,82 @@ public class Parser { } } - enum Operator { - PLUS, MINUS, NONE; - - Expression toOperation(Expression leftHandSide, Expression rightHandSide) { - if (this == Operator.PLUS) { - return new Addition(leftHandSide, rightHandSide); - } else if (this == Operator.MINUS) { - return new Subtraction(leftHandSide, rightHandSide); - } else { - throw new RuntimeException("Operator invalid"); - } - } - } + private enum Operator { PLUS, MINUS } - Optional expression() { - List atoms = new ArrayList<>(); - Operator operator = Operator.PLUS; + private boolean testOperator() { int start = position; - while (operator != Operator.NONE) { - Optional atom = atom(); - Operator op = operator; - atom.ifPresent(at -> atoms.add(new OperatorWithExpression(op, at))); - if (atom.isPresent()) { - start = position; - operator = operator(); - } else { - operator = Operator.NONE; - position = start; - } - } - Optional expression = Optional.empty(); - for (OperatorWithExpression atom: atoms) { - if (!expression.isPresent()) { - expression = Optional.of(atom.expression); - } else { - expression = expression.map(expr -> atom.operator.toOperation(expr, atom.expression)); - } - } - return expression; + boolean result; + try { + operator(); + result = true; + } catch (SyntaxException se) { + result = false; + } + position = start; + return result; } - Operator operator() { - if (token("+")) { + private Operator operator() { + whitespace(); + char next = (char) 0; + if (position < input.length()) { + next = input.charAt(position); + position += 1; + } + if (next == '+') { return Operator.PLUS; - } else if (token("-")) { + } else if (next == '-') { return Operator.MINUS; } else { - return Operator.NONE; + throw new SyntaxException("Operator", position); + } + } + + Expression expression() { + Expression firstAtom = atom(); + List moreAtoms = new ArrayList<>(); + while(testOperator()) { + Operator operator = operator(); + Expression expression = atom(); + moreAtoms.add(new OperatorWithExpression(operator, expression)); + } + Expression expression = firstAtom; + for (OperatorWithExpression atom: moreAtoms) { + switch (atom.operator) { + case PLUS: + expression = new Addition(expression, atom.expression); + break; + case MINUS: + expression = new Subtraction(expression, atom.expression); + break; + } } + return expression; } - Optional atom() { + Expression atom() { int start = position; - Optional result; - if (token("(")) { - Optional expression = expression(); - result = expression.filter(exp -> token(")")); - } else { + Expression result; + try { + consume("("); + result = expression(); + consume(")"); + } catch (SyntaxException se) { position = start; - result = integer(); - if (!result.isPresent()) { - position = start; - result = identifier().map(id -> id); + try { + result = integer(); + } catch (SyntaxException se2) { + result = identifier(); } } return result; } - boolean isLowerLetter(char ch) { + private boolean isLowerLetter(char ch) { return ch >= 'a' && ch <= 'z'; } - Optional integer() { + Expression integer() { whitespace(); int start = position; boolean minus = position < input.length() && input.charAt(position) == '-'; @@ -207,37 +189,50 @@ public class Parser { digitsFound = true; } if (digitsFound) { - return Optional.of(new Int(Integer.parseInt(input.substring(start, position)))); + return new Int(Integer.parseInt(input.substring(start, position))); } else { - return Optional.empty(); + throw new SyntaxException("Integer", position); } } - Optional identifier() { + Identifier identifier() { whitespace(); int start = position; while (position < input.length() && isLowerLetter(input.charAt(position))) { position += 1; } if (position > start) { - Identifier identifier = new Identifier(input.substring(start, position)); - return Optional.of(identifier); + return new Identifier(input.substring(start, position)); + } else { + throw new SyntaxException("Identifier", position); } - return Optional.empty(); } - void whitespace() { + private void whitespace() { while(position < input.length() && Character.isWhitespace(input.charAt(position))) { position += 1; } } - boolean token(String token) { + private void consume(String token) { whitespace(); - boolean success = position + token.length() <= input.length() && input.substring(position, position + token.length()).equals(token); - if (success) { + if (position + token.length() <= input.length() && input.substring(position, position + token.length()).equals(token)) { position += token.length(); + } else { + throw new SyntaxException(token, position); } + } + + private boolean test(String token) { + int start = position; + boolean success; + try { + consume(token); + success = true; + } catch (SyntaxException se) { + success = false; + } + position = start; return success; } } diff --git a/test/parser/ParserTest.java b/test/parser/ParserTest.java index 5daec11..8daef57 100644 --- a/test/parser/ParserTest.java +++ b/test/parser/ParserTest.java @@ -1,7 +1,6 @@ package parser; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import org.junit.Test; import expression.*; @@ -35,43 +34,43 @@ public class ParserTest { @Test public void testProgram() { Parser parser = new Parser(programCode); - assertEquals(Optional.of(program), parser.program()); + assertEquals(program, parser.program()); } @Test public void testStatementAssignment() { Parser parser = new Parser(assignmentCode); - assertEquals(Optional.of(assignment), parser.statement()); + assertEquals(assignment, parser.statement()); } @Test public void testStatementConditional() { Parser parser = new Parser(conditionalCode); - assertEquals(Optional.of(conditional), parser.statement()); + assertEquals(conditional, parser.statement()); } @Test public void testStatementLoop() { Parser parser = new Parser(loopCode); - assertEquals(Optional.of(loop), parser.statement()); + assertEquals(loop, parser.statement()); } @Test public void testAssignment() { Parser parser = new Parser(assignmentCode); - assertEquals(Optional.of(assignment), parser.assignment()); + assertEquals(assignment, parser.assignment()); } @Test public void testConditional() { Parser parser = new Parser(conditionalCode); - assertEquals(Optional.of(conditional), parser.conditional()); + assertEquals(conditional, parser.conditional()); } @Test public void testLoop() { Parser parser = new Parser(loopCode); - assertEquals(Optional.of(loop), parser.loop()); + assertEquals(loop, parser.loop()); } final String expressionCode = "a+b - (c - 56) + -47"; @@ -80,61 +79,36 @@ public class ParserTest { @Test public void testExpression() { Parser parser = new Parser(expressionCode); - assertEquals(Optional.of(expression), parser.expression()); - } - - @Test - public void testOperatorMinus() { - Parser parser = new Parser("-"); - assertEquals(Operator.MINUS, parser.operator()); - } - - @Test - public void testOperatorPlus() { - Parser parser = new Parser("+"); - assertEquals(Operator.PLUS, parser.operator()); + assertEquals(expression, parser.expression()); } @Test public void testAtomExpression() { Parser parser = new Parser("(" + expressionCode + ")"); - assertEquals(Optional.of(expression), parser.atom()); + assertEquals(expression, parser.atom()); } @Test public void testAtomNumber() { Parser parser = new Parser("37658"); - assertEquals(Optional.of(new Int(37658)), parser.atom()); + assertEquals(new Int(37658), parser.atom()); } @Test public void testAtomIdentifier() { Parser parser = new Parser("egjfd"); - assertEquals(Optional.of(new Identifier("egjfd")), parser.atom()); + assertEquals(new Identifier("egjfd"), parser.atom()); } @Test public void testNumber() { Parser parser = new Parser("37658"); - assertEquals(Optional.of(new Int(37658)), parser.integer()); + assertEquals(new Int(37658), parser.integer()); } @Test public void testIdentifier() { Parser parser = new Parser("egjfd"); - assertEquals(Optional.of(new Identifier("egjfd")), parser.identifier()); - } - - @Test - public void testWhitespace() { - Parser parser = new Parser(" \n\t x"); - parser.whitespace(); - assertEquals('x', parser.input.charAt(parser.position)); - } - - @Test - public void testToken() { - Parser parser = new Parser("gehjfwdk"); - assertTrue(parser.token("gehjfwdk")); + assertEquals(new Identifier("egjfd"), parser.identifier()); } }