basic parser and parser tests
This commit is contained in:
parent
e5ab8b2db7
commit
c4f5c77fc4
|
@ -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("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
@ -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');
|
|
@ -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;
|
||||||
|
}
|
|
@ -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';
|
|
@ -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;
|
Loading…
Reference in New Issue