Skip to content

Commit

Permalink
Add error logging to parser
Browse files Browse the repository at this point in the history
It's a bit shaky
  • Loading branch information
Botffy committed Dec 4, 2016
1 parent 741dd2c commit b3a0a90
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 18 deletions.
45 changes: 45 additions & 0 deletions src/main/java/ppke/itk/xplang/common/CompilerMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ppke.itk.xplang.common;

/**
* A compiler error we would like to display to the programmer.
*/
public final class CompilerMessage {
public enum Severity {
WARNING,
ERROR;
}

public static CompilerMessage error(String message, Location location) {
return new CompilerMessage(Severity.ERROR, message, location);
}

public static CompilerMessage warning(String message, Location location) {
return new CompilerMessage(Severity.WARNING, message, location);
}

private final Severity severity;
private final String message;
private final Location location;

private CompilerMessage(Severity severity, String message, Location location) {
this.severity = severity;
this.message = message;
this.location = location;
}

public Severity getSeverity() {
return severity;
}

public String getMessage() {
return message;
}

public Location getLocation() {
return location;
}

@Override public String toString() {
return String.format("%s %s", location, message);
}
}
21 changes: 21 additions & 0 deletions src/main/java/ppke/itk/xplang/common/ErrorLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ppke.itk.xplang.common;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ErrorLog {
private final List<CompilerMessage> messages = new ArrayList<>();

public void add(CompilerMessage error) {
messages.add(error);
}

public List<CompilerMessage> getErrorMessages() {
return Collections.unmodifiableList(messages);
}

public boolean isEmpty() {
return messages.isEmpty();
}
}
12 changes: 12 additions & 0 deletions src/main/java/ppke/itk/xplang/common/Location.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package ppke.itk.xplang.common;

import java.util.Objects;

/**
* Simple value class to hold location information about a piece of text in a file.
*/
Expand All @@ -15,4 +17,14 @@ public Location(int line, int column) {
@Override public String toString() {
return String.format("%s:%s", line, column);
}

@Override public boolean equals(Object object) {
if(!(object instanceof Location)) return false;
Location that = (Location) object;
return this.line == that.line && this.column == that.column;
}

@Override public int hashCode() {
return Objects.hash(line, column);
}
}
9 changes: 9 additions & 0 deletions src/main/java/ppke/itk/xplang/parser/ParseError.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ppke.itk.xplang.parser;

import ppke.itk.xplang.common.CompilerMessage;
import ppke.itk.xplang.common.Location;

/**
Expand All @@ -18,4 +19,12 @@ abstract public class ParseError extends Exception {
super(message);
this.location = location;
}

public Location getLocation() {
return location;
}

public CompilerMessage toErrorMessage() {
return CompilerMessage.error(this.getMessage(), this.getLocation());
}
}
40 changes: 32 additions & 8 deletions src/main/java/ppke/itk/xplang/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,52 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ppke.itk.xplang.ast.Root;
import ppke.itk.xplang.common.CompilerMessage;
import ppke.itk.xplang.common.ErrorLog;

import java.io.Reader;

public class Parser {
private final static Logger log = LoggerFactory.getLogger("Root.Parser");

private final ErrorLog errorLog;
private Context context;
private Lexer lexer;
private Token act;

public Parser() {
this(new Context());
this(new Context(), new ErrorLog());
}

public Parser(ErrorLog errorLog) {
this(new Context(), errorLog);
}

public Parser(Context context) {
this(context, new ErrorLog());
}

public Parser(Context context, ErrorLog errorLog) {
this.context = context;
this.errorLog = errorLog;
}

/**
* Parse the given source text, using the given grammar.
* @param source The reader pointing at the source text to be parsed.
* @param grammar The grammar used during parsing.
* @return The root node of the generated {@link ppke.itk.xplang.ast AST}
* @throws ParseError if there were errors during the parsing.
* @return The root node of the generated {@link ppke.itk.xplang.ast AST}, or null if the parsing failed.
*/
public Root parse(Reader source, Grammar grammar) throws ParseError {
grammar.setup(context);
this.lexer = new Lexer(source, context.getSymbols());
advance();
return grammar.S(this);
public Root parse(Reader source, Grammar grammar) {
try {
grammar.setup(context);
this.lexer = new Lexer(source, context.getSymbols());
advance();
return grammar.S(this);
} catch(ParseError e) {
recordError(e.toErrorMessage());
return null;
}
}

/**
Expand Down Expand Up @@ -99,4 +115,12 @@ public Token accept(Symbol symbol, String message) throws LexerError, SyntaxErro
public Token accept(String symbolName, String message) throws LexerError, SyntaxError {
return accept(context.lookup(symbolName), message);
}

public void recordError(CompilerMessage errorMessage) {
errorLog.add(errorMessage);
}

public ErrorLog getErrorLog() {
return errorLog;
}
}
28 changes: 19 additions & 9 deletions src/main/java/ppke/itk/xplang/ui/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.slf4j.LoggerFactory;
import ppke.itk.xplang.ast.ASTPrinter;
import ppke.itk.xplang.ast.Root;
import ppke.itk.xplang.common.CompilerMessage;
import ppke.itk.xplang.common.ErrorLog;
import ppke.itk.xplang.interpreter.Interpreter;
import ppke.itk.xplang.lang.PlangGrammar;
import ppke.itk.xplang.parser.Grammar;
Expand Down Expand Up @@ -33,18 +35,26 @@ void run() {

Reader source = new StringReader("PROGRAM testing <_< >_> >_> \n >_> >_> program_vége");

ErrorLog errorLog = new ErrorLog();
Grammar grammar = new PlangGrammar();
Parser parser = new Parser();
try {
Root root = parser.parse(source, grammar);
Parser parser = new Parser(errorLog);

ASTPrinter printer = new ASTPrinter();
printer.visit(root);
Root root = parser.parse(source, grammar);
if(!errorLog.isEmpty()) {
printErrors(errorLog);
return;
}

ASTPrinter printer = new ASTPrinter();
printer.visit(root);

Interpreter interpreter = new Interpreter();
interpreter.visit(root);
}

Interpreter interpreter = new Interpreter();
interpreter.visit(root);
} catch(Exception e) {
System.out.println(e.getMessage());
private void printErrors(ErrorLog errorLog) {
for(CompilerMessage message : errorLog.getErrorMessages()) {
System.out.println(message);
}
}
}
64 changes: 64 additions & 0 deletions src/test/java/ppke/itk/xplang/parser/ParserErrorLoggingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package ppke.itk.xplang.parser;

import org.junit.Test;
import ppke.itk.xplang.ast.Root;
import ppke.itk.xplang.common.CompilerMessage;
import ppke.itk.xplang.common.Location;

import java.io.Reader;
import java.io.StringReader;
import java.util.List;
import java.util.regex.Pattern;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class ParserErrorLoggingTest {
private final static Symbol SYMBOL_FSR = new Symbol("FUNNY_STARE_RIGHT", Pattern.compile(">_>"));
private final static Symbol SYMBOL_FSL = new Symbol("FUNNY_STARE_LEFT", Pattern.compile("<_<"));
private final static Symbol SYMBOL_WS = new Symbol("WS", Pattern.compile("\\s+"), Symbol.Precedence.DEFAULT, false);
private final static Grammar grammar = new Grammar() {
@Override protected void setup(Context ctx) {
ctx.register(SYMBOL_FSR);
ctx.register(SYMBOL_FSL);
ctx.register(SYMBOL_WS);
}
@Override protected Root S(Parser parser) throws ParseError {
int iteration = 0;
while(!parser.actual().symbol().equals(Symbol.EOF)) {
parser.accept(iteration %2 == 0? SYMBOL_FSL : SYMBOL_FSR, "Expected %s, got %s");
iteration++;
}
return null;
}
};

@Test
public void parserShouldLogLexerError() {
Reader source = new StringReader("<_< öö >_>");
Parser parser = new Parser();
parser.parse(source, grammar);

List<CompilerMessage> messages = parser.getErrorLog().getErrorMessages();
assertFalse("Error log should not be empty after a lexing error.", messages.isEmpty());
assertEquals("Error log should give correct information about the error location.",
new Location(1,4),
messages.get(0).getLocation()
);
}

@Test
public void parserShouldLogSyntaxError() {
Reader source = new StringReader("<_< >_> >_> >_>");
Parser parser = new Parser();
parser.parse(source, grammar);

List<CompilerMessage> messages = parser.getErrorLog().getErrorMessages();
System.out.println(messages);
assertFalse("Error log should not be empty after a syntax error.", parser.getErrorLog().isEmpty());
assertEquals("Error log should give correct information about the error location.",
new Location(1,8),
messages.get(0).getLocation()
);
}
}
2 changes: 1 addition & 1 deletion src/test/java/ppke/itk/xplang/parser/ParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void acceptShouldAcceptAndAdvance() throws ParseError {
assertEquals("Accepting should advance the head",
ctx.lookup("FUNNY_STARE_RIGHT"), parser.actual().symbol()
);
} catch(Exception e) {
} catch(ParseError e) {
fail("Accepting should not throw exceptions if accepting the correct symbol");
}
}
Expand Down

0 comments on commit b3a0a90

Please sign in to comment.