|
|
|
@@ -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<Program> 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> program() { |
|
|
|
List<Program> statements = new ArrayList<>(); |
|
|
|
boolean run = true; |
|
|
|
int start = position; |
|
|
|
while (run) { |
|
|
|
Optional<Program> 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<Program> moreStatements = new ArrayList<>(); |
|
|
|
while (test(";")) { |
|
|
|
consume(";"); |
|
|
|
Program statement = statement(); |
|
|
|
moreStatements.add(statement); |
|
|
|
} |
|
|
|
Optional<Program> 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<Program> statement() { |
|
|
|
Program statement() { |
|
|
|
int start = position; |
|
|
|
Optional<Program> 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<Program> loop() { |
|
|
|
if (token("while") && token("(")) { |
|
|
|
Optional<Expression> condition = expression(); |
|
|
|
return condition.flatMap(cond -> { |
|
|
|
if (token(")") && token("{")) { |
|
|
|
Optional<Program> program = program(); |
|
|
|
return program.filter(pgm -> token("}")).map(pgm -> new Loop(cond, pgm)); |
|
|
|
} |
|
|
|
return Optional.empty(); |
|
|
|
}); |
|
|
|
} |
|
|
|
return Optional.empty(); |
|
|
|
} |
|
|
|
|
|
|
|
Optional<Program> conditional() { |
|
|
|
if (token("if") && token("(")) { |
|
|
|
Optional<Expression> condition = expression(); |
|
|
|
return condition.flatMap(cond -> { |
|
|
|
if (token(")") && token("then") && token("{")) { |
|
|
|
Optional<Program> thenCase = program(); |
|
|
|
return thenCase.flatMap(thenC -> { |
|
|
|
if (token("}") && token("else") && token("{")) { |
|
|
|
Optional<Program> elseCase = program(); |
|
|
|
return elseCase.filter(elseC -> token("}")).map(elseC -> new Conditional(cond, thenC, elseC)); |
|
|
|
} |
|
|
|
return Optional.empty(); |
|
|
|
}); |
|
|
|
} |
|
|
|
return Optional.empty(); |
|
|
|
}); |
|
|
|
} |
|
|
|
return Optional.empty(); |
|
|
|
} |
|
|
|
|
|
|
|
Optional<Program> assignment() { |
|
|
|
Optional<Identifier> identifier = identifier(); |
|
|
|
return identifier.flatMap(id -> { |
|
|
|
if (token(":=")) { |
|
|
|
Optional<Expression> 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> expression() { |
|
|
|
List<OperatorWithExpression> atoms = new ArrayList<>(); |
|
|
|
Operator operator = Operator.PLUS; |
|
|
|
private boolean testOperator() { |
|
|
|
int start = position; |
|
|
|
while (operator != Operator.NONE) { |
|
|
|
Optional<Expression> 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> 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<OperatorWithExpression> 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<Expression> atom() { |
|
|
|
Expression atom() { |
|
|
|
int start = position; |
|
|
|
Optional<Expression> result; |
|
|
|
if (token("(")) { |
|
|
|
Optional<Expression> 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<Expression> 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 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; |
|
|
|
} |
|
|
|
} |