diff --git a/src/com/annimon/ownlang/Main.java b/src/com/annimon/ownlang/Main.java index 913687e..390341d 100644 --- a/src/com/annimon/ownlang/Main.java +++ b/src/com/annimon/ownlang/Main.java @@ -70,10 +70,15 @@ public final class Main { } } - final Statement program = new Parser(tokens).parse(); + final Parser parser = new Parser(tokens); + final Statement program = parser.parse(); if (showAst) { System.out.println(program.toString()); } + if (parser.getParseErrors().hasErrors()) { + System.out.println(parser.getParseErrors()); + return; + } program.accept(new FunctionAdder()); // program.accept(new VariablePrinter()); program.accept(new AssignValidator()); diff --git a/src/com/annimon/ownlang/parser/ParseError.java b/src/com/annimon/ownlang/parser/ParseError.java new file mode 100644 index 0000000..7506552 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ParseError.java @@ -0,0 +1,25 @@ +package com.annimon.ownlang.parser; + +public final class ParseError { + + private final int line; + private final Exception exception; + + public ParseError(int line, Exception exception) { + this.line = line; + this.exception = exception; + } + + public int getLine() { + return line; + } + + public Exception getException() { + return exception; + } + + @Override + public String toString() { + return "ParseError on line " + line + ": " + exception.getMessage(); + } +} diff --git a/src/com/annimon/ownlang/parser/ParseErrors.java b/src/com/annimon/ownlang/parser/ParseErrors.java new file mode 100644 index 0000000..345bb2d --- /dev/null +++ b/src/com/annimon/ownlang/parser/ParseErrors.java @@ -0,0 +1,40 @@ +package com.annimon.ownlang.parser; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public final class ParseErrors implements Iterable { + + private final List errors; + + public ParseErrors() { + errors = new ArrayList<>(); + } + + public void clear() { + errors.clear(); + } + + public void add(Exception ex, int line) { + errors.add(new ParseError(line, ex)); + } + + public boolean hasErrors() { + return !errors.isEmpty(); + } + + @Override + public Iterator iterator() { + return errors.iterator(); + } + + @Override + public String toString() { + final StringBuilder result = new StringBuilder(); + for (ParseError error : errors) { + result.append(error).append(System.lineSeparator()); + } + return result.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index ae8cdbe..47d2245 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -38,22 +38,55 @@ public final class Parser { private final List tokens; private final int size; + private final ParseErrors parseErrors; private int pos; public Parser(List tokens) { this.tokens = tokens; size = tokens.size(); + parseErrors = new ParseErrors(); + } + + public ParseErrors getParseErrors() { + return parseErrors; } public Statement parse() { + parseErrors.clear(); final BlockStatement result = new BlockStatement(); while (!match(TokenType.EOF)) { - result.add(statement()); + try { + result.add(statement()); + } catch (Exception ex) { + parseErrors.add(ex, getErrorLine()); + recover(); + } } return result; } + private int getErrorLine() { + if (size == 0) return 0; + if (pos >= size) return tokens.get(size - 1).getRow(); + return tokens.get(pos).getRow(); + } + + private void recover() { + int preRecoverPosition = pos; + for (int i = preRecoverPosition; i < size; i++) { + pos = i; + try { + statement(); + // successfully parsed, + pos = i; // restore position + return; + } catch (Exception ex) { + // fail + } + } + } + private Statement block() { final BlockStatement block = new BlockStatement(); consume(TokenType.LBRACE);