diff --git a/doc/report.tex b/doc/report.tex index ee636b5..5675ec3 100644 --- a/doc/report.tex +++ b/doc/report.tex @@ -2,7 +2,7 @@ \usepackage[margin=1in]{geometry} \usepackage{graphicx} \usepackage{pdfpages} -\usepackage[dutch]{babel} +\usepackage[english]{babel} \usepackage{amsmath} \usepackage{amsfonts} \usepackage[scientific-notation=true,round-precision=5,round-mode=figures]{siunitx} diff --git a/src/pp/s1184725/boppi/Annotations.java b/src/pp/s1184725/boppi/Annotations.java index 5d610cc..aeda0de 100644 --- a/src/pp/s1184725/boppi/Annotations.java +++ b/src/pp/s1184725/boppi/Annotations.java @@ -5,10 +5,12 @@ import java.util.Stack; import org.antlr.v4.runtime.tree.ParseTreeProperty; import pp.iloc.model.Reg; +import pp.s1184725.boppi.type.*; /** * This class holds properties of all AST nodes during compilation. - * + * + * @author Frank Wibbelink */ public class Annotations { /** diff --git a/src/pp/s1184725/boppi/BoppiChecker.java b/src/pp/s1184725/boppi/BoppiChecker.java index a0b5aed..e40ef34 100644 --- a/src/pp/s1184725/boppi/BoppiChecker.java +++ b/src/pp/s1184725/boppi/BoppiChecker.java @@ -7,14 +7,15 @@ import java.util.stream.Collectors; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; -import pp.s1184725.boppi.antlr.BoppiBaseVisitor; -import pp.s1184725.boppi.antlr.BoppiLexer; +import pp.s1184725.boppi.antlr.*; import pp.s1184725.boppi.antlr.BoppiParser.*; +import pp.s1184725.boppi.type.*; /** * This class performs type checking and variable assignment on a bare parse * tree. - * + * + * @author Frank Wibbelink */ public class BoppiChecker extends BoppiBaseVisitor { private Annotations an; @@ -305,7 +306,7 @@ public class BoppiChecker extends BoppiBaseVisitor { @Override public Type visitTypeTuple(TypeTupleContext ctx) { - return new TupleType(ctx.children.stream().map(this::visit).collect(Collectors.toList())); + return new TupleType(ctx.type().stream().map(this::visit).collect(Collectors.toList())); } @Override diff --git a/src/pp/s1184725/boppi/BoppiGenerator.java b/src/pp/s1184725/boppi/BoppiGenerator.java index c95b937..eb4d61a 100644 --- a/src/pp/s1184725/boppi/BoppiGenerator.java +++ b/src/pp/s1184725/boppi/BoppiGenerator.java @@ -10,12 +10,14 @@ import org.apache.commons.lang3.StringUtils; import pp.iloc.eval.Machine; import pp.iloc.model.*; -import pp.s1184725.boppi.antlr.BoppiBaseVisitor; -import pp.s1184725.boppi.antlr.BoppiLexer; +import pp.s1184725.boppi.antlr.*; import pp.s1184725.boppi.antlr.BoppiParser.*; +import pp.s1184725.boppi.type.*; /** * The generator stage of the Boppi toolchain. + * + * @author Frank Wibbelink */ public class BoppiGenerator extends BoppiBaseVisitor { private static final int ARBASESIZE = 16; diff --git a/src/pp/s1184725/boppi/CachingSymbolTable.java b/src/pp/s1184725/boppi/CachingSymbolTable.java index f3c71c7..bf4af66 100644 --- a/src/pp/s1184725/boppi/CachingSymbolTable.java +++ b/src/pp/s1184725/boppi/CachingSymbolTable.java @@ -3,6 +3,8 @@ package pp.s1184725.boppi; import java.util.*; import java.util.function.Supplier; +import pp.s1184725.boppi.type.*; + /** * This class maps string identifiers to {@link Variable} instances. It takes * care of lexical scope, memory offsets and variable lifetime. This @@ -12,6 +14,8 @@ import java.util.function.Supplier; * * @param * the typing class + * + * @author Frank Wibbelink */ public class CachingSymbolTable { protected Stack>> symbolMapStack; diff --git a/src/pp/s1184725/boppi/DebugCachingSymbolTable.java b/src/pp/s1184725/boppi/DebugCachingSymbolTable.java index 704d793..868fed9 100644 --- a/src/pp/s1184725/boppi/DebugCachingSymbolTable.java +++ b/src/pp/s1184725/boppi/DebugCachingSymbolTable.java @@ -2,12 +2,16 @@ package pp.s1184725.boppi; import java.util.EmptyStackException; +import pp.s1184725.boppi.type.*; + /** * Same functionality as {@link CachingSymbolTable} except actions on the table * are logged to the console. * * @param * the typing class + * + * @author Frank Wibbelink */ public class DebugCachingSymbolTable extends CachingSymbolTable { diff --git a/src/pp/s1184725/boppi/ToolChain.java b/src/pp/s1184725/boppi/ToolChain.java index 341ab8d..a0dd6e6 100644 --- a/src/pp/s1184725/boppi/ToolChain.java +++ b/src/pp/s1184725/boppi/ToolChain.java @@ -12,12 +12,12 @@ import org.apache.commons.lang3.tuple.Pair; import pp.iloc.Simulator; import pp.iloc.model.Program; -import pp.s1184725.boppi.antlr.BoppiLexer; -import pp.s1184725.boppi.antlr.BoppiParser; +import pp.s1184725.boppi.antlr.*; /** * This class provides methods for all steps in the Boppi tool chain. - * + * + * @author Frank Wibbelink */ public class ToolChain { diff --git a/src/pp/s1184725/boppi/Variable.java b/src/pp/s1184725/boppi/Variable.java index 5d91ade..ebe13d2 100644 --- a/src/pp/s1184725/boppi/Variable.java +++ b/src/pp/s1184725/boppi/Variable.java @@ -6,6 +6,8 @@ package pp.s1184725.boppi; * * @param * the typing class + * + * @author Frank Wibbelink */ public class Variable { private final T type; diff --git a/src/pp/s1184725/boppi/test/AllTests.java b/src/pp/s1184725/boppi/test/AllTests.java index 7847288..2cdaa2d 100644 --- a/src/pp/s1184725/boppi/test/AllTests.java +++ b/src/pp/s1184725/boppi/test/AllTests.java @@ -1,29 +1,55 @@ package pp.s1184725.boppi.test; import java.nio.file.*; -import java.util.logging.Level; +import java.util.List; +import java.util.logging.*; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.tree.ParseTree; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import pp.iloc.Simulator; +import pp.iloc.model.Program; +import pp.s1184725.boppi.*; + /** - * Test suite for all Boppi compiler stages and language features + * Test suite for all Boppi language features. This suite contains helper + * functions and attributes to assist test cases. Each test case should consider + * a single language feature across all compiler stages. + * + * @author Frank Wibbelink */ @RunWith(Suite.class) -@SuiteClasses({ CheckerTest.class, GeneratorTest.class, ParserTest.class }) +@SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class }) public class AllTests { /** - * The location of test programs + * The path for test programs */ - public static final Path directory = Paths + public static final Path TEST_PROGRAM_LOCATION = Paths .get("src/" + AllTests.class.getPackage().getName().replaceAll("\\.", "/") + "/programs/"); + /** + * The default logging level + */ + public static final Level DEFAULT_LOG_LEVEL = Level.SEVERE; + + /** + * Log records of last test + */ + public static List log; + + /** + * Output of last program split by line breaks + */ + public static String[] out; + /** * The level of error reporting to use for the tests. Can be changed to * include finer tests. */ - public static Level logLevel = Level.SEVERE; + private static Level logLevel = DEFAULT_LOG_LEVEL; /** * Runs a test with warnings enabled. @@ -34,6 +60,132 @@ public class AllTests { public static void withWarnings(Runnable test) { logLevel = Level.WARNING; test.run(); - logLevel = Level.SEVERE; + logLevel = DEFAULT_LOG_LEVEL; } + + /** + * Parses a string of code, logging to {@link #log}. + * + * @param code + * the code to parse + */ + public static void parseString(String code) { + parse(ToolChain.getCharStream(code)); + } + + /** + * Parses code from a file, logging to {@link #log}. + * + * @param file + * the file name + */ + public static void parseFile(String file) { + parse(ToolChain.getCharStream(TEST_PROGRAM_LOCATION.resolve(file))); + } + + private static void parse(CharStream stream) { + Logger logger = Logger.getAnonymousLogger(); + log = ToolChain.makeListLog(logger); + logger.setLevel(logLevel); + + ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + } + + /** + * Parses and type checks a string of code, logging to {@link #log}. + * + * @param code + * the code to type check + */ + public static void checkString(String code) { + check(ToolChain.getCharStream(code)); + } + + /** + * Parses and type checks code from a file, logging to {@link #log}. + * + * @param file + * the file name + */ + public static void checkFile(String file) { + check(ToolChain.getCharStream(TEST_PROGRAM_LOCATION.resolve(file))); + } + + private static void check(CharStream stream) { + Logger logger = Logger.getAnonymousLogger(); + log = ToolChain.makeListLog(logger); + logger.setLevel(logLevel); + + ToolChain.getAnnotations(ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(), logger); + } + + /** + * Compiles and runs a program from a string, logging to {@link #log} and + * writing lines of output to {@link #out}. + * + * @param code + * the code to type check + * @param input + * lines of input for the program + */ + public static void compileAndRunString(String code, String... input) { + compileAndRun(ToolChain.getCharStream(code), String.join("\n", input) + "\n"); + } + + /** + * Compiles and runs a program from a file, logging to {@link #log} and + * writing lines of output to {@link #out}. + * + * @param file + * the file name + * @param input + * lines of input for the program + */ + public static void compileAndRunFile(String file, String... input) { + compileAndRun(ToolChain.getCharStream(TEST_PROGRAM_LOCATION.resolve(file)), String.join("\n", input) + "\n"); + } + + private static void compileAndRun(CharStream stream, String input) { + Logger logger = Logger.getAnonymousLogger(); + log = ToolChain.makeListLog(logger); + logger.setLevel(logLevel); + + ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + Annotations annotations = ToolChain.getAnnotations(ast, logger); + Program program = ToolChain.getILOC(ast, logger, annotations); + + if (Simulator.DEBUG) + System.out.println(program.prettyPrint()); + + out = ToolChain.execute(program, logger, input).split("\n"); + } + + /** + * Compiles and runs a program in debug mode, printing the generated + * program, a simulation of the program and all log messages. + * + * @param stream + * a character stream with the program code + * @param input + * lines of input for the program + */ + public static void debugRun(CharStream stream, String... input) { + Simulator.DEBUG = true; + String in = String.join("\n", input) + "\n"; + Logger logger = Logger.getAnonymousLogger(); + log = ToolChain.makeListLog(logger); + logger.setLevel(Level.FINEST); + + ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + Annotations annotations = ToolChain.getAnnotations(ast, logger); + Program program = ToolChain.getILOC(ast, logger, annotations); + + System.out.println(program.prettyPrint()); + + out = ToolChain.execute(program, logger, in).split("\n"); + + log.forEach((entry) -> System.out.println(entry.getMessage())); + Simulator.DEBUG = false; + } + } diff --git a/src/pp/s1184725/boppi/test/CheckerTest.java b/src/pp/s1184725/boppi/test/CheckerTest.java deleted file mode 100644 index 111d948..0000000 --- a/src/pp/s1184725/boppi/test/CheckerTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package pp.s1184725.boppi.test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -import java.util.List; -import java.util.logging.*; - -import org.antlr.v4.runtime.CharStream; -import org.junit.Test; - -import pp.s1184725.boppi.ToolChain; - -/** - * Test cases for the semantic checking stage for all language features. - */ -public class CheckerTest { - private List log; - - private void checkString(String code) { - check(ToolChain.getCharStream(code)); - } - - private void checkFile(String file) { - check(ToolChain.getCharStream(AllTests.directory.resolve(file))); - } - - private void check(CharStream stream) { - Logger logger = Logger.getAnonymousLogger(); - log = ToolChain.makeListLog(logger); - logger.setLevel(AllTests.logLevel); - - ToolChain.getAnnotations(ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(), logger); - } - - /** - * Correct basic expressions - */ - @Test - public void correctExpressionTest() { - checkFile("simpleExpression.boppi"); - assertThat(log, is(empty())); - } - - /** - * Basic expressions with type errors - */ - @Test - public void wrongExpressionTest() { - checkString("+true"); - assertThat(log, hasSize(1)); - - checkString("5 || true"); - assertThat(log, hasSize(1)); - - checkString("6 + 'c'"); - assertThat(log, hasSize(1)); - - checkString("4 + print(5, 6)"); - assertThat(log, hasSize(1)); - - checkString("print(print(3, 5))"); - assertThat(log, hasSize(1)); - } - - /** - * Correct variable use - */ - @Test - public void correctVariableTest() { - checkFile("simpleVariable.boppi"); - assertThat(log, is(empty())); - } - - /** - * Variable use with type errors - */ - @Test - public void wrongVariableTest() { - checkString("var bool name; name := 5"); - assertThat(log, hasSize(1)); - - checkString("undefinedName"); - assertThat(log, hasSize(1)); - - checkString("var undefinedType name; 1"); - assertThat(log, hasSize(1)); - - checkString("var bool endsWithDeclaration;"); - assertThat(log, hasSize(1)); - - checkString("var bool var1; var var1 var2; var2 := 'c' "); - assertThat(log, hasSize(1)); - } - - /** - * Correct variable scoping - */ - @Test - public void correctScopeTest() { - checkFile("simpleScope.boppi"); - assertThat(log, is(empty())); - } - - /** - * Variable redeclaring and out-of-scope errors - */ - @Test - public void wrongScopeTest() { - checkString("var bool var1; var bool var1; 1"); - assertThat(log, hasSize(1)); - - checkString("var bool var1; var char var1; 1"); - assertThat(log, hasSize(1)); - - checkString("{ var int var1; var1 := 4}; var int var2; var1"); - assertThat(log, hasSize(1)); - } - - /** - * Correct if-else and loop use - */ - @Test - public void correctConditionalTest() { - checkFile("if.boppi"); - assertThat(log, is(empty())); - - checkFile("while.boppi"); - assertThat(log, is(empty())); - } - - /** - * Correct function declaration and calls - */ - @Test - public void correctFunctionTest() { - checkString("function int id(int a) a; 1"); - assertThat(log, is(empty())); - - checkString("function int id(int a) a; id(1); 1"); - assertThat(log, is(empty())); - - checkString("var int a; function int const() a; 1"); - assertThat(log, is(empty())); - - checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); - assertThat(log, is(empty())); - - checkString("function char const(char a, char b) a; print(const('A', 'T'))"); - assertThat(log, is(empty())); - - checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); - assertThat(log, is(empty())); - } - - /** - * Function use with type errors - */ - @Test - public void wrongFunctionTest() { - checkString("function int add(int a, char b) a+b; print(add(4, 'T')+1)"); - assertThat(log, hasSize(1)); - - checkString("function char add(int a, int b) a+b; print(add(4, 5))"); - assertThat(log, hasSize(1)); - - checkString("function int add(int a, int b) a+b; print(add(4, 'T')+1)"); - assertThat(log, hasSize(1)); - } -} diff --git a/src/pp/s1184725/boppi/test/ConditionalTest.java b/src/pp/s1184725/boppi/test/ConditionalTest.java new file mode 100644 index 0000000..0d75cb6 --- /dev/null +++ b/src/pp/s1184725/boppi/test/ConditionalTest.java @@ -0,0 +1,68 @@ +package pp.s1184725.boppi.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import org.junit.Test; + +/** + * Tests for conditionals, i.e. if, if-else branching and while loops. + * + * @author Frank Wibbelink + */ +public class ConditionalTest { + + /** + * Correct if-else and loop use + */ + @Test + public void correctConditionalParsing() { + AllTests.parseFile("if.boppi"); + assertThat(AllTests.log, empty()); + + AllTests.parseFile("while.boppi"); + assertThat(AllTests.log, empty()); + } + + /** + * Correct if-else and loop use + */ + @Test + public void correctConditionalChecking() { + AllTests.checkFile("if.boppi"); + assertThat(AllTests.log, is(empty())); + + AllTests.checkFile("while.boppi"); + assertThat(AllTests.log, is(empty())); + } + + /** + * Correct evaluation of basic programs + */ + @Test + public void basicPrograms() { + AllTests.compileAndRunString("print(5*3)"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("15"))); + + AllTests.compileAndRunString("print({var int x; x := 8; print(x)})"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("8", "8"))); + + AllTests.compileAndRunString("print('T', 'e', 's', 't', '!')"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("T", "e", "s", "t", "!"))); + + AllTests.compileAndRunString("var int x; var int y; x := 3*(y := 4); print(x,y)"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("12", "4"))); + + AllTests.compileAndRunFile("basicProgram1.boppi", "1", "T"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("T", "T"))); + + AllTests.compileAndRunFile("fibonacciIterative.boppi", "6"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("13"))); + } + +} diff --git a/src/pp/s1184725/boppi/test/ExpressionTest.java b/src/pp/s1184725/boppi/test/ExpressionTest.java new file mode 100644 index 0000000..c57ff5c --- /dev/null +++ b/src/pp/s1184725/boppi/test/ExpressionTest.java @@ -0,0 +1,106 @@ +package pp.s1184725.boppi.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +import java.util.logging.Level; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +/** + * Test cases for the basic expression language. + * + * @author Frank Wibbelink + */ +public class ExpressionTest { + + /** + * Correct basic expressions + */ + @Test + public void correctExpressionParsing() { + AllTests.parseFile("simpleExpression.boppi"); + assertThat(AllTests.log, empty()); + } + + /** + * Basic expressions with syntax errors + */ + @Test + public void wrongExpressionParsing() { + AllTests.parseString(""); + assertThat(AllTests.log, not(empty())); + + AllTests.parseString("~"); + assertThat(AllTests.log, not(empty())); + + AllTests.parseString("0A"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.parseString("do"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.parseString("true true"); + assertThat(AllTests.log, hasSize(1)); + } + + /** + * Correct basic expressions + */ + @Test + public void correctExpressionChecking() { + AllTests.checkFile("simpleExpression.boppi"); + assertThat(AllTests.log, is(empty())); + } + + /** + * Basic expressions with type errors + */ + @Test + public void wrongExpressionChecking() { + AllTests.checkString("+true"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("5 || true"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("6 + 'c'"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("4 + print(5, 6)"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("print(print(3, 5))"); + assertThat(AllTests.log, hasSize(1)); + } + + /** + * Correct basic expressions + */ + @Test + public void correctExpressionGeneration() { + AllTests.compileAndRunFile("simpleExpression.boppi"); + assertThat(AllTests.log, is(empty())); + + int n = 12; + String expression = StringUtils.repeat("1+(", n) + "1" + StringUtils.repeat(")", n); + + AllTests.withWarnings(() -> { + AllTests.compileAndRunString("print(" + expression + ")", "" + n); + assertThat(AllTests.log, hasSize(1)); + assertThat(AllTests.log.get(0).getLevel(), is(Level.WARNING)); + }); + } + + /** + * Basic expression with run-time error + */ + @Test + public void wrongExpressionGeneration() { + AllTests.compileAndRunString("1/0"); + assertThat(AllTests.log, hasSize(1)); + assertThat(AllTests.log.get(0).getMessage(), containsString("zero")); + } + +} diff --git a/src/pp/s1184725/boppi/test/GeneratorTest.java b/src/pp/s1184725/boppi/test/GeneratorTest.java deleted file mode 100644 index f2ec6c5..0000000 --- a/src/pp/s1184725/boppi/test/GeneratorTest.java +++ /dev/null @@ -1,178 +0,0 @@ -package pp.s1184725.boppi.test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; - -import java.util.List; -import java.util.logging.*; - -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.tree.ParseTree; -import org.apache.commons.lang3.StringUtils; -import org.junit.Test; - -import pp.iloc.Simulator; -import pp.iloc.model.Program; -import pp.s1184725.boppi.*; - -/** - * Test cases for the code generating stage for all language features. - */ -public class GeneratorTest { - private List log; - private String[] out; - - private void compileAndRunString(String code, String... input) { - compileAndRun(ToolChain.getCharStream(code), String.join("\n", input) + "\n"); - } - - private void compileAndRunFile(String file, String... input) { - compileAndRun(ToolChain.getCharStream(AllTests.directory.resolve(file)), String.join("\n", input) + "\n"); - } - - private void compileAndRun(CharStream stream, String input) { - Logger logger = Logger.getAnonymousLogger(); - log = ToolChain.makeListLog(logger); - logger.setLevel(AllTests.logLevel); - - ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); - Annotations annotations = ToolChain.getAnnotations(ast, logger); - Program program = ToolChain.getILOC(ast, logger, annotations); - - if (Simulator.DEBUG) - System.out.println(program.prettyPrint()); - - out = ToolChain.execute(program, logger, input).split("\n"); - } - - @SuppressWarnings("unused") - private void debugRun(CharStream stream, String... input) { - Simulator.DEBUG = true; - String in = String.join("\n", input) + "\n"; - Logger logger = Logger.getAnonymousLogger(); - log = ToolChain.makeListLog(logger); - logger.setLevel(Level.FINEST); - - ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); - Annotations annotations = ToolChain.getAnnotations(ast, logger); - Program program = ToolChain.getILOC(ast, logger, annotations); - - System.out.println(program.prettyPrint()); - - out = ToolChain.execute(program, logger, in).split("\n"); - - log.forEach((entry) -> System.out.println(entry.getMessage())); - Simulator.DEBUG = false; - } - - /** - * Correct basic expressions - */ - @Test - public void correctExpressionTest() { - compileAndRunFile("simpleExpression.boppi"); - assertThat(log, is(empty())); - - int n = 12; - String expression = StringUtils.repeat("1+(", n) + "1" + StringUtils.repeat(")", n); - AllTests.withWarnings(() -> { - compileAndRunString("print(" + expression + ")", "" + n); - assertThat(log, hasSize(1)); - assertThat(log.get(0).getLevel(), is(Level.WARNING)); - }); - } - - /** - * Basic expression with run-time error - */ - @Test - public void wrongExpressionTest() { - compileAndRunString("1/0"); - assertThat(log, hasSize(1)); - assertThat(log.get(0).getMessage(), containsString("zero")); - } - - /** - * Correct variable use - */ - @Test - public void correctVariableTest() { - compileAndRunFile("simpleVariable.boppi"); - assertThat(log, is(empty())); - } - - /** - * Correct evaluation of basic programs - */ - @Test - public void basicPrograms() { - compileAndRunString("print(5*3)"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("15"))); - - compileAndRunString("print({var int x; x := 8; print(x)})"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("8", "8"))); - - compileAndRunString("print('T', 'e', 's', 't', '!')"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("T", "e", "s", "t", "!"))); - - compileAndRunString("var int x; var int y; x := 3*(y := 4); print(x,y)"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("12", "4"))); - - compileAndRunFile("basicProgram1.boppi", "1", "T"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("T", "T"))); - - compileAndRunFile("fibonacciIterative.boppi", "6"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("13"))); - } - - /** - * Correct evaluation of basic functions - */ - @Test - public void correctSimpleFunctionTest() { - compileAndRunString("function int id(int a) a; 1"); - assertThat(log, is(empty())); - - compileAndRunString("function int id(int a) a; id(1); 1"); - assertThat(log, is(empty())); - - compileAndRunString("var int a; function int const(int b) a; 1"); - assertThat(log, is(empty())); - - compileAndRunString("var int a; a := 1; function int const(int b) a; print(const(4))"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("1"))); - - compileAndRunString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("10"))); - - compileAndRunString("function char const(char a, char b) a; print(const('A', 'T'))"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("A"))); - } - - /** - * Correct evaluation of recursive functions, nested functions and function - * passing - */ - @Test - public void correctComplexFunctionTest() { - compileAndRunString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); - assertThat(log, is(empty())); - - compileAndRunFile("recursiveFactorial.boppi", "5"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("120"))); - - compileAndRunFile("simpleFunctionPassing.boppi"); - assertThat(log, is(empty())); - assertThat(out, is(arrayContaining("40", "104", "1", "2", "8"))); - } -} diff --git a/src/pp/s1184725/boppi/test/ParserTest.java b/src/pp/s1184725/boppi/test/ParserTest.java deleted file mode 100644 index 228de40..0000000 --- a/src/pp/s1184725/boppi/test/ParserTest.java +++ /dev/null @@ -1,150 +0,0 @@ -package pp.s1184725.boppi.test; - -import java.util.List; -import java.util.logging.*; - -import org.antlr.v4.runtime.CharStream; -import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import pp.s1184725.boppi.*; - -/** - * Test cases for the parsing stage for all language features. - */ -public class ParserTest { - private List log; - - private void parseString(String code) { - parse(ToolChain.getCharStream(code)); - } - - private void parseFile(String file) { - parse(ToolChain.getCharStream(AllTests.directory.resolve(file))); - } - - private void parse(CharStream stream) { - Logger logger = Logger.getAnonymousLogger(); - log = ToolChain.makeListLog(logger); - logger.setLevel(AllTests.logLevel); - - ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); - } - - /** - * Correct basic expressions - */ - @Test - public void correctExpressionTest() { - parseFile("simpleExpression.boppi"); - assertThat(log, empty()); - } - - /** - * Basic expressions with syntax errors - */ - @Test - public void wrongExpressionTest() { - parseString(""); - assertThat(log, not(empty())); - - parseString("~"); - assertThat(log, not(empty())); - - parseString("0A"); - assertThat(log, hasSize(1)); - - parseString("do"); - assertThat(log, hasSize(1)); - - parseString("true true"); - assertThat(log, hasSize(1)); - } - - /** - * Correct variable use - */ - @Test - public void correctVariableTest() { - parseFile("simpleVariable.boppi"); - assertThat(log, empty()); - } - - /** - * Variable use with syntax errors - */ - @Test - public void wrongVariableTest() { - parseString("var"); - assertThat(log, hasSize(1)); - - parseString("var bool 5"); - assertThat(log, hasSize(1)); - - parseString("var 'c' varname"); - assertThat(log, not(empty())); - - parseString("var bool; true true;"); - assertThat(log, hasSize(2)); - } - - /** - * Correct variable scoping - */ - @Test - public void correctScopeTest() { - parseFile("simpleScope.boppi"); - assertThat(log, empty()); - } - - /** - * Correct if-else and loop use - */ - @Test - public void correctConditionalTest() { - parseFile("if.boppi"); - assertThat(log, empty()); - - parseFile("while.boppi"); - assertThat(log, empty()); - } - - /** - * Correct function declaration and calls - */ - @Test - public void correctFunctionTest() { - parseString("function int id(int a) a; 1"); - assertThat(log, is(empty())); - - parseString("function int id(int a) a; id(1)"); - assertThat(log, is(empty())); - - parseString("var int a; a := 1; function int const() a; const()"); - assertThat(log, is(empty())); - - parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); - assertThat(log, is(empty())); - - parseString("function char const(char a, char b) a; print(const('A', 'T'))"); - assertThat(log, is(empty())); - - parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); - assertThat(log, is(empty())); - } - - /** - * Function use with syntax errors - */ - @Test - public void wrongFunctionTest() { - parseString("function doNothing(int a, char b) a+b; print(add(4, 'T')+1)"); - assertThat(log, hasSize(1)); - - parseString("function char add(int a, int b);"); - assertThat(log, hasSize(1)); - - parseString("function int add(int a, int b) a+b; add(4,)"); - assertThat(log, hasSize(1)); - } -} diff --git a/src/pp/s1184725/boppi/test/SimpleFunctionTest.java b/src/pp/s1184725/boppi/test/SimpleFunctionTest.java new file mode 100644 index 0000000..9297e4f --- /dev/null +++ b/src/pp/s1184725/boppi/test/SimpleFunctionTest.java @@ -0,0 +1,137 @@ +package pp.s1184725.boppi.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import org.junit.Test; + +/** + * Tests for function use and simple function passing (i.e. no closures required). + * + * @author Frank Wibbelink + */ +public class SimpleFunctionTest { + + /** + * Correct function declaration and calls + */ + @Test + public void correctFunctionParsing() { + AllTests.parseString("function int id(int a) a; 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.parseString("function int id(int a) a; id(1)"); + assertThat(AllTests.log, is(empty())); + + AllTests.parseString("var int a; a := 1; function int const() a; const()"); + assertThat(AllTests.log, is(empty())); + + AllTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); + assertThat(AllTests.log, is(empty())); + + AllTests.parseString("function char const(char a, char b) a; print(const('A', 'T'))"); + assertThat(AllTests.log, is(empty())); + + AllTests.parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); + assertThat(AllTests.log, is(empty())); + } + + /** + * Function use with syntax errors + */ + @Test + public void wrongFunctionParsing() { + AllTests.parseString("function doNothing(int a, char b) a+b; print(add(4, 'T')+1)"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.parseString("function char add(int a, int b);"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.parseString("function int add(int a, int b) a+b; add(4,)"); + assertThat(AllTests.log, hasSize(1)); + } + + /** + * Correct function declaration and calls + */ + @Test + public void correctFunctionChecking() { + AllTests.checkString("function int id(int a) a; 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.checkString("function int id(int a) a; id(1); 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.checkString("var int a; function int const() a; 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); + assertThat(AllTests.log, is(empty())); + + AllTests.checkString("function char const(char a, char b) a; print(const('A', 'T'))"); + assertThat(AllTests.log, is(empty())); + + AllTests.checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); + assertThat(AllTests.log, is(empty())); + } + + /** + * Function use with type errors + */ + @Test + public void wrongFunctionChecking() { + AllTests.checkString("function int add(int a, char b) a+b; print(add(4, 'T')+1)"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("function char add(int a, int b) a+b; print(add(4, 5))"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("function int add(int a, int b) a+b; print(add(4, 'T')+1)"); + assertThat(AllTests.log, hasSize(1)); + } + + /** + * Correct evaluation of basic functions + */ + @Test + public void correctSimpleFunctionGeneration() { + AllTests.compileAndRunString("function int id(int a) a; 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.compileAndRunString("function int id(int a) a; id(1); 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.compileAndRunString("var int a; function int const(int b) a; 1"); + assertThat(AllTests.log, is(empty())); + + AllTests.compileAndRunString("var int a; a := 1; function int const(int b) a; print(const(4))"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("1"))); + + AllTests.compileAndRunString("function int add(int a, int b) a+b; print(add(4, 5)+1)"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("10"))); + + AllTests.compileAndRunString("function char const(char a, char b) a; print(const('A', 'T'))"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("A"))); + } + + /** + * Correct evaluation of recursive functions, nested functions and function + * passing + */ + @Test + public void correctComplexFunctionGeneration() { + AllTests.compileAndRunString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}"); + assertThat(AllTests.log, is(empty())); + + AllTests.compileAndRunFile("recursiveFactorial.boppi", "5"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("120"))); + + AllTests.compileAndRunFile("simpleFunctionPassing.boppi"); + assertThat(AllTests.log, is(empty())); + assertThat(AllTests.out, is(arrayContaining("40", "104", "1", "2", "8"))); + } + +} diff --git a/src/pp/s1184725/boppi/test/SimpleVariableTest.java b/src/pp/s1184725/boppi/test/SimpleVariableTest.java new file mode 100644 index 0000000..1d92a5c --- /dev/null +++ b/src/pp/s1184725/boppi/test/SimpleVariableTest.java @@ -0,0 +1,113 @@ +package pp.s1184725.boppi.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import org.junit.Test; + +/** + * Tests for simple (i.e. no functions involved) variable use. + * + * @author Frank Wibbelink + */ +public class SimpleVariableTest { + + /** + * Correct variable use + */ + @Test + public void correctVariableParsing() { + AllTests.parseFile("simpleVariable.boppi"); + assertThat(AllTests.log, empty()); + } + + /** + * Variable use with syntax errors + */ + @Test + public void wrongVariableParsing() { + AllTests.parseString("var"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.parseString("var bool 5"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.parseString("var 'c' varname"); + assertThat(AllTests.log, not(empty())); + + AllTests.parseString("var bool; true true;"); + assertThat(AllTests.log, hasSize(2)); + } + + /** + * Correct variable scoping + */ + @Test + public void correctScopeParsing() { + AllTests.parseFile("simpleScope.boppi"); + assertThat(AllTests.log, empty()); + } + + /** + * Correct variable use + */ + @Test + public void correctVariableChecking() { + AllTests.checkFile("simpleVariable.boppi"); + assertThat(AllTests.log, is(empty())); + } + + /** + * Variable use with type errors + */ + @Test + public void wrongVariableChecking() { + AllTests.checkString("var bool name; name := 5"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("undefinedName"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("var undefinedType name; 1"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("var bool endsWithDeclaration;"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("var bool var1; var var1 var2; var2 := 'c' "); + assertThat(AllTests.log, hasSize(1)); + } + + /** + * Correct variable scoping + */ + @Test + public void correctScopeChecking() { + AllTests.checkFile("simpleScope.boppi"); + assertThat(AllTests.log, is(empty())); + } + + /** + * Variable redeclaring and out-of-scope errors + */ + @Test + public void wrongScopeChecking() { + AllTests.checkString("var bool var1; var bool var1; 1"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("var bool var1; var char var1; 1"); + assertThat(AllTests.log, hasSize(1)); + + AllTests.checkString("{ var int var1; var1 := 4}; var int var2; var1"); + assertThat(AllTests.log, hasSize(1)); + } + + /** + * Correct variable use + */ + @Test + public void correctVariableGeneration() { + AllTests.compileAndRunFile("simpleVariable.boppi"); + assertThat(AllTests.log, is(empty())); + } + +} diff --git a/src/pp/s1184725/boppi/test/programs/simpleFunctionPassing.boppi b/src/pp/s1184725/boppi/test/programs/simpleFunctionPassing.boppi index 28d9c5d..b0e06af 100644 --- a/src/pp/s1184725/boppi/test/programs/simpleFunctionPassing.boppi +++ b/src/pp/s1184725/boppi/test/programs/simpleFunctionPassing.boppi @@ -1,7 +1,6 @@ -var int undefined; -function int intDiadic(int a, int b) undefined; +var (int,int)->int intBinOp; //unused variable for type aliasing -function intDiadic getMultiply() { +function intBinOp getMultiply() { function int multiply(int a, int b) a*b; multiply @@ -9,7 +8,7 @@ function intDiadic getMultiply() { function int biasedMultiply(int a, int b) a*b+4; -var intDiadic myMultiply; +var intBinOp myMultiply; myMultiply := getMultiply(); print(myMultiply(5, 8)); diff --git a/src/pp/s1184725/boppi/FunctionType.java b/src/pp/s1184725/boppi/type/FunctionType.java similarity index 93% rename from src/pp/s1184725/boppi/FunctionType.java rename to src/pp/s1184725/boppi/type/FunctionType.java index 4ad4f17..a20cee6 100644 --- a/src/pp/s1184725/boppi/FunctionType.java +++ b/src/pp/s1184725/boppi/type/FunctionType.java @@ -1,9 +1,11 @@ -package pp.s1184725.boppi; +package pp.s1184725.boppi.type; import pp.iloc.eval.Machine; /** - * The (->) type. Takes exactly two types as arguments. + * The (->) type. Takes exactly two types as arguments. + * + * @author Frank Wibbelink */ public class FunctionType implements Type { private Type argument, result; diff --git a/src/pp/s1184725/boppi/SimpleType.java b/src/pp/s1184725/boppi/type/SimpleType.java similarity index 95% rename from src/pp/s1184725/boppi/SimpleType.java rename to src/pp/s1184725/boppi/type/SimpleType.java index 4ec653e..4bbaf73 100644 --- a/src/pp/s1184725/boppi/SimpleType.java +++ b/src/pp/s1184725/boppi/type/SimpleType.java @@ -1,4 +1,4 @@ -package pp.s1184725.boppi; +package pp.s1184725.boppi.type; import static pp.s1184725.boppi.antlr.BoppiLexer.*; @@ -13,6 +13,8 @@ import pp.s1184725.boppi.antlr.BoppiLexer; *
  • boolean (sized {@link Machine#INT_SIZE})
  • *
  • void (empty/unit type)
  • * + * + * @author Frank Wibbelink */ public enum SimpleType implements Type { diff --git a/src/pp/s1184725/boppi/TupleType.java b/src/pp/s1184725/boppi/type/TupleType.java similarity index 84% rename from src/pp/s1184725/boppi/TupleType.java rename to src/pp/s1184725/boppi/type/TupleType.java index 1c631c4..4684e3a 100644 --- a/src/pp/s1184725/boppi/TupleType.java +++ b/src/pp/s1184725/boppi/type/TupleType.java @@ -1,11 +1,13 @@ -package pp.s1184725.boppi; +package pp.s1184725.boppi.type; import java.util.*; import java.util.stream.Collectors; /** * The n-tuple type. Takes exactly {@link #size()} types. The element indices in - * the tuple are 0-based. + * the tuple are zero-based. + * + * @author Frank Wibbelink */ public class TupleType extends AbstractList implements Type { /** @@ -19,6 +21,7 @@ public class TupleType extends AbstractList implements Type { * Creates an n-tuple of the given types. * * @param parameterTypes + * a list of types for this tuple */ public TupleType(List parameterTypes) { parameters = new ArrayList<>(parameterTypes); @@ -59,7 +62,8 @@ public class TupleType extends AbstractList implements Type { @Override public String toString() { - return parameters.stream().map(Type::toString).collect(Collectors.joining(",", "(", ")")); + return parameters.stream().map((type) -> type == null ? "TYPE = NULL" : type.toString()) + .collect(Collectors.joining(",", "(", ")")); } @Override diff --git a/src/pp/s1184725/boppi/Type.java b/src/pp/s1184725/boppi/type/Type.java similarity index 77% rename from src/pp/s1184725/boppi/Type.java rename to src/pp/s1184725/boppi/type/Type.java index f6f5ea0..31d4f45 100644 --- a/src/pp/s1184725/boppi/Type.java +++ b/src/pp/s1184725/boppi/type/Type.java @@ -1,7 +1,9 @@ -package pp.s1184725.boppi; +package pp.s1184725.boppi.type; /** * Generic type interface. All types must have at least these properties. + * + * @author Frank Wibbelink */ public interface Type { diff --git a/javadoc.xml b/util/javadoc.xml similarity index 55% rename from javadoc.xml rename to util/javadoc.xml index c59d080..d5cc8bd 100644 --- a/javadoc.xml +++ b/util/javadoc.xml @@ -1,7 +1,7 @@ - +