From c4f5c77fc40998d0ed2f78b147b3de9d98bbb651 Mon Sep 17 00:00:00 2001 From: User <> Date: Fri, 12 May 2017 18:26:29 +0200 Subject: [PATCH] basic parser and parser tests --- src/pp/s1184725/boppi/BasicParserHelper.java | 126 ++++++++++++++++++ src/pp/s1184725/boppi/test/ParserTest.java | 100 ++++++++++++++ src/pp/s1184725/boppi/test/parsing/if.boppi | 17 +++ .../boppi/test/parsing/simpleExpression.boppi | 28 ++++ .../boppi/test/parsing/simpleScope.boppi | 35 +++++ .../boppi/test/parsing/simpleVariable.boppi | 22 +++ .../s1184725/boppi/test/parsing/while.boppi | 14 ++ 7 files changed, 342 insertions(+) create mode 100644 src/pp/s1184725/boppi/BasicParserHelper.java create mode 100644 src/pp/s1184725/boppi/test/ParserTest.java create mode 100644 src/pp/s1184725/boppi/test/parsing/if.boppi create mode 100644 src/pp/s1184725/boppi/test/parsing/simpleExpression.boppi create mode 100644 src/pp/s1184725/boppi/test/parsing/simpleScope.boppi create mode 100644 src/pp/s1184725/boppi/test/parsing/simpleVariable.boppi create mode 100644 src/pp/s1184725/boppi/test/parsing/while.boppi diff --git a/src/pp/s1184725/boppi/BasicParserHelper.java b/src/pp/s1184725/boppi/BasicParserHelper.java new file mode 100644 index 0000000..18e39f7 --- /dev/null +++ b/src/pp/s1184725/boppi/BasicParserHelper.java @@ -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> getParserWithLog(Path file, Logger logger) throws IOException { + return Pair.of(getParser(CharStreams.fromFileName(file.toString()), logger), makeListLog(logger)); + } + + public static Pair> 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 makeListLog(Logger logger) { + List log = new ArrayList(); + 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())+"
"); + if (annotater.registers.get(ctx) != null) + sb.append("reg: "+escape(annotater.registers.get(ctx).getName())+"
"); + if (annotater.variables.get(ctx) != null) + sb.append("var: "+escape(annotater.variables.get(ctx).toString())+"
"); + if (annotater.types.get(ctx) != null) + sb.append("type: "+escape(annotater.types.get(ctx).name())+"
"); + 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(); + } + +} diff --git a/src/pp/s1184725/boppi/test/ParserTest.java b/src/pp/s1184725/boppi/test/ParserTest.java new file mode 100644 index 0000000..af63cbd --- /dev/null +++ b/src/pp/s1184725/boppi/test/ParserTest.java @@ -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 log; + + static private List parseAndGetLog(String code) { + Pair> pair = BasicParserHelper.getParserWithLog(code, Logger.getAnonymousLogger()); + pair.getLeft().program(); + return pair.getRight(); + } + + static private List parseAndGetLog(Path code) throws IOException { + Pair> 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()); + } + +} diff --git a/src/pp/s1184725/boppi/test/parsing/if.boppi b/src/pp/s1184725/boppi/test/parsing/if.boppi new file mode 100644 index 0000000..62a3037 --- /dev/null +++ b/src/pp/s1184725/boppi/test/parsing/if.boppi @@ -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; diff --git a/src/pp/s1184725/boppi/test/parsing/simpleExpression.boppi b/src/pp/s1184725/boppi/test/parsing/simpleExpression.boppi new file mode 100644 index 0000000..072170c --- /dev/null +++ b/src/pp/s1184725/boppi/test/parsing/simpleExpression.boppi @@ -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'); diff --git a/src/pp/s1184725/boppi/test/parsing/simpleScope.boppi b/src/pp/s1184725/boppi/test/parsing/simpleScope.boppi new file mode 100644 index 0000000..2ee25e7 --- /dev/null +++ b/src/pp/s1184725/boppi/test/parsing/simpleScope.boppi @@ -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; +} diff --git a/src/pp/s1184725/boppi/test/parsing/simpleVariable.boppi b/src/pp/s1184725/boppi/test/parsing/simpleVariable.boppi new file mode 100644 index 0000000..e85c4b8 --- /dev/null +++ b/src/pp/s1184725/boppi/test/parsing/simpleVariable.boppi @@ -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'; diff --git a/src/pp/s1184725/boppi/test/parsing/while.boppi b/src/pp/s1184725/boppi/test/parsing/while.boppi new file mode 100644 index 0000000..1698093 --- /dev/null +++ b/src/pp/s1184725/boppi/test/parsing/while.boppi @@ -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;