basic parser and parser tests

This commit is contained in:
User 2017-05-12 18:26:29 +02:00
parent e5ab8b2db7
commit c4f5c77fc4
7 changed files with 342 additions and 0 deletions

View File

@ -0,0 +1,126 @@
package pp.s1184725.boppi;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.tuple.Pair;
public class BasicParserHelper {
public static Pair<BasicParser,List<LogRecord>> getParserWithLog(Path file, Logger logger) throws IOException {
return Pair.of(getParser(CharStreams.fromFileName(file.toString()), logger), makeListLog(logger));
}
public static Pair<BasicParser,List<LogRecord>> getParserWithLog(String code, Logger logger) {
return Pair.of(getParser(CharStreams.fromString(code), logger), makeListLog(logger));
}
public static BasicParser getParser(CharStream stream, Logger logger) {
BasicLexer lexer = new BasicLexer(stream);
lexer.removeErrorListeners();
lexer.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
logger.severe(""+l+":"+c+" "+msg);
}
});
BasicParser parser = new BasicParser(new CommonTokenStream(lexer));
parser.removeErrorListeners();
parser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> r, Object oSym, int l, int c, String msg, RecognitionException e) {
logger.severe(""+l+":"+c+" "+msg);
}
});
return parser;
}
public static List<LogRecord> makeListLog(Logger logger) {
List<LogRecord> log = new ArrayList<LogRecord>();
Handler listLogger = new Handler() {
@Override
public void close() throws SecurityException {}
@Override
public void flush() {}
@Override
public void publish(LogRecord record) {
log.add(record);
}
};
for (Handler handler : logger.getHandlers())
logger.removeHandler(handler);
logger.setUseParentHandlers(false);
logger.addHandler(listLogger);
return log;
}
public static String getAnnotatedDOT(ParseTree tree, Annotations annotater) {
StringBuilder sb = new StringBuilder();
sb.append("digraph {\n");
new ParseTreeWalker().walk(new ParseTreeListener() {
private String escape(String str) {
return str.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
}
@Override
public void visitTerminal(TerminalNode node) {
sb.append("\tn"+node.hashCode()+"[label=<"+escape(node.getText())+">;shape=rect]\n");
}
@Override
public void visitErrorNode(ErrorNode node) {
sb.append("\tn"+node.hashCode()+"[label=<"+escape(node.getText())+">;style=filled;fillcolor=red]\n");
}
@Override
public void exitEveryRule(ParserRuleContext ctx) {
float hue = (ctx.getClass().hashCode() % 65521)/65521.0f;
sb.append("\tn"+ctx.hashCode()+"[label=<");
sb.append("rule: "+escape(ctx.getClass().getSimpleName())+"<br/>");
if (annotater.registers.get(ctx) != null)
sb.append("reg: <b>"+escape(annotater.registers.get(ctx).getName())+"</b><br/>");
if (annotater.variables.get(ctx) != null)
sb.append("var: "+escape(annotater.variables.get(ctx).toString())+"<br/>");
if (annotater.types.get(ctx) != null)
sb.append("type: "+escape(annotater.types.get(ctx).name())+"<br/>");
sb.append(">;style=filled;fillcolor=\""+hue+"+0.1+1\"]\n");
if (ctx.children != null)
for (ParseTree child : ctx.children)
sb.append("\tn"+ctx.hashCode()+" -> n"+child.hashCode()+"\n");
}
@Override
public void enterEveryRule(ParserRuleContext ctx) {}
}, tree);
sb.append("}\n");
return sb.toString();
}
}

View File

@ -0,0 +1,100 @@
package pp.s1184725.boppi.test;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.logging.*;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import pp.s1184725.boppi.*;
public class ParserTest {
static final Path directory = Paths.get("src/pp/s1184725/boppi/test/parsing/");
private List<LogRecord> log;
static private List<LogRecord> parseAndGetLog(String code) {
Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger());
pair.getLeft().program();
return pair.getRight();
}
static private List<LogRecord> parseAndGetLog(Path code) throws IOException {
Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger());
pair.getLeft().program();
return pair.getRight();
}
@Test
public void correctExpressionTest() throws Exception {
log = parseAndGetLog(directory.resolve("simpleExpression.boppi"));
assertThat(log, empty());
}
@Test
public void wrongExpressionTest() {
log = parseAndGetLog("");
assertThat(log, not(empty()));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("~");
assertThat(log, not(empty()));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("0A");
assertThat(log, hasSize(1));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("do");
assertThat(log, hasSize(1));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("true true");
assertThat(log, hasSize(1));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
}
@Test
public void correctVariableTest() throws Exception {
log = parseAndGetLog(directory.resolve("simpleVariable.boppi"));
assertThat(log, empty());
}
@Test
public void wrongVariableTest() {
log = parseAndGetLog("var");
assertThat(log, hasSize(1));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("var bool 5");
assertThat(log, hasSize(1));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("var 'c' varname");
assertThat(log, not(empty()));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
log = parseAndGetLog("var bool; true true;");
assertThat(log, hasSize(2));
assertThat(log.get(0).getLevel(), is(Level.SEVERE));
}
@Test
public void correctScopeTest() throws Exception {
log = parseAndGetLog(directory.resolve("simpleScope.boppi"));
assertThat(log, empty());
}
@Test
public void correctConditionalTest() throws Exception {
log = parseAndGetLog(directory.resolve("if.boppi"));
assertThat(log, empty());
log = parseAndGetLog(directory.resolve("while.boppi"));
assertThat(log, empty());
}
}

View File

@ -0,0 +1,17 @@
// simple if
if true then true fi;
if true then 'q' else 42 fi;
// if with return type
var int myInt;
myInt := if true then 42 else 1337 fi;
// scope test
if var int ifWide; ifWide := 3; true then {
var int trueWide;
trueWide := ifWide;
} else {
var int falseWide;
falseWide := 3;
ifWide;
} fi;

View File

@ -0,0 +1,28 @@
// comment
/*
block comment //
*/
// integers and arithmetic
0;
(1);
2+3;
4-5;
6*7;
8/9;
--+-++4;
1+2*3/-4-(5*6-7)-72/8/9;
// characters
'c';
''';
'\';
// booleans and logical expressions
true;
!false;
!!(!!true);
4 > 5;
3 == 2;
1 < 2 == false;
!(true <> false) == ('c' == 'd');

View File

@ -0,0 +1,35 @@
// simple scoped blocks
{ 5 };
{ true; false };
{ { 'T' } };
4 == {'a'; { 4 } };
// simple scoped variables
var int myInt;
var int otherInt;
otherInt := 4;
myInt := {
otherInt+3
};
myInt := {
var char myChar;
myChar := 'A';
4
};
// redeclared variables
{
var int myInt;
myInt := 8;
otherInt := myInt+1;
};
var bool myBool;
myBool := {
var myInt myInt;
var myInt otherInt;
otherInt := {
var int myInt;
99 + (myInt := 100);
};
otherInt > 3;
}

View File

@ -0,0 +1,22 @@
// simple declaration
var bool myBool;
var char myChar;
var int myInt;
var bool _underscore;
var bool number1;
var bool CaPiTaL;
// simple assignment
myBool := true;
myChar := 'c';
myInt := 5;
// advanced assignment
var int otherInt;
myInt := otherInt := 3 + 3;
myBool := myInt == otherInt;
myBool := myChar <> 'A' && (myInt := 2) == 2;
// relative declaration
var myChar otherChar;
otherChar := 'D';

View File

@ -0,0 +1,14 @@
// simple while
while false do 5 od;
// scope test
var int goal;
var int sum;
goal := 42;
sum := 0;
while var int i; i := 0; i < 42 do
i := i+1;
var int i2;
i2 := i*i;
sum := sum + i2 + goal;
od;