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