refactored types, split tests into features instead of stages, added author

This commit is contained in:
User 2017-07-21 13:22:23 +02:00
parent 610611e212
commit b617136564
22 changed files with 630 additions and 528 deletions

View File

@ -2,7 +2,7 @@
\usepackage[margin=1in]{geometry} \usepackage[margin=1in]{geometry}
\usepackage{graphicx} \usepackage{graphicx}
\usepackage{pdfpages} \usepackage{pdfpages}
\usepackage[dutch]{babel} \usepackage[english]{babel}
\usepackage{amsmath} \usepackage{amsmath}
\usepackage{amsfonts} \usepackage{amsfonts}
\usepackage[scientific-notation=true,round-precision=5,round-mode=figures]{siunitx} \usepackage[scientific-notation=true,round-precision=5,round-mode=figures]{siunitx}

View File

@ -5,10 +5,12 @@ import java.util.Stack;
import org.antlr.v4.runtime.tree.ParseTreeProperty; import org.antlr.v4.runtime.tree.ParseTreeProperty;
import pp.iloc.model.Reg; import pp.iloc.model.Reg;
import pp.s1184725.boppi.type.*;
/** /**
* This class holds properties of all AST nodes during compilation. * This class holds properties of all AST nodes during compilation.
* *
* @author Frank Wibbelink
*/ */
public class Annotations { public class Annotations {
/** /**

View File

@ -7,14 +7,15 @@ import java.util.stream.Collectors;
import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTree;
import pp.s1184725.boppi.antlr.BoppiBaseVisitor; import pp.s1184725.boppi.antlr.*;
import pp.s1184725.boppi.antlr.BoppiLexer;
import pp.s1184725.boppi.antlr.BoppiParser.*; import pp.s1184725.boppi.antlr.BoppiParser.*;
import pp.s1184725.boppi.type.*;
/** /**
* This class performs type checking and variable assignment on a bare parse * This class performs type checking and variable assignment on a bare parse
* tree. * tree.
* *
* @author Frank Wibbelink
*/ */
public class BoppiChecker extends BoppiBaseVisitor<Type> { public class BoppiChecker extends BoppiBaseVisitor<Type> {
private Annotations an; private Annotations an;
@ -305,7 +306,7 @@ public class BoppiChecker extends BoppiBaseVisitor<Type> {
@Override @Override
public Type visitTypeTuple(TypeTupleContext ctx) { 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 @Override

View File

@ -10,12 +10,14 @@ import org.apache.commons.lang3.StringUtils;
import pp.iloc.eval.Machine; import pp.iloc.eval.Machine;
import pp.iloc.model.*; import pp.iloc.model.*;
import pp.s1184725.boppi.antlr.BoppiBaseVisitor; import pp.s1184725.boppi.antlr.*;
import pp.s1184725.boppi.antlr.BoppiLexer;
import pp.s1184725.boppi.antlr.BoppiParser.*; import pp.s1184725.boppi.antlr.BoppiParser.*;
import pp.s1184725.boppi.type.*;
/** /**
* The generator stage of the Boppi toolchain. * The generator stage of the Boppi toolchain.
*
* @author Frank Wibbelink
*/ */
public class BoppiGenerator extends BoppiBaseVisitor<Void> { public class BoppiGenerator extends BoppiBaseVisitor<Void> {
private static final int ARBASESIZE = 16; private static final int ARBASESIZE = 16;

View File

@ -3,6 +3,8 @@ package pp.s1184725.boppi;
import java.util.*; import java.util.*;
import java.util.function.Supplier; import java.util.function.Supplier;
import pp.s1184725.boppi.type.*;
/** /**
* This class maps string identifiers to {@link Variable} instances. It takes * This class maps string identifiers to {@link Variable} instances. It takes
* care of lexical scope, memory offsets and variable lifetime. This * care of lexical scope, memory offsets and variable lifetime. This
@ -12,6 +14,8 @@ import java.util.function.Supplier;
* *
* @param <T> * @param <T>
* the typing class * the typing class
*
* @author Frank Wibbelink
*/ */
public class CachingSymbolTable<T extends Type> { public class CachingSymbolTable<T extends Type> {
protected Stack<Map<String, Variable<T>>> symbolMapStack; protected Stack<Map<String, Variable<T>>> symbolMapStack;

View File

@ -2,12 +2,16 @@ package pp.s1184725.boppi;
import java.util.EmptyStackException; import java.util.EmptyStackException;
import pp.s1184725.boppi.type.*;
/** /**
* Same functionality as {@link CachingSymbolTable} except actions on the table * Same functionality as {@link CachingSymbolTable} except actions on the table
* are logged to the console. * are logged to the console.
* *
* @param <T> * @param <T>
* the typing class * the typing class
*
* @author Frank Wibbelink
*/ */
public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<T> { public class DebugCachingSymbolTable<T extends Type> extends CachingSymbolTable<T> {

View File

@ -12,12 +12,12 @@ import org.apache.commons.lang3.tuple.Pair;
import pp.iloc.Simulator; import pp.iloc.Simulator;
import pp.iloc.model.Program; import pp.iloc.model.Program;
import pp.s1184725.boppi.antlr.BoppiLexer; import pp.s1184725.boppi.antlr.*;
import pp.s1184725.boppi.antlr.BoppiParser;
/** /**
* This class provides methods for all steps in the Boppi tool chain. * This class provides methods for all steps in the Boppi tool chain.
* *
* @author Frank Wibbelink
*/ */
public class ToolChain { public class ToolChain {

View File

@ -6,6 +6,8 @@ package pp.s1184725.boppi;
* *
* @param <T> * @param <T>
* the typing class * the typing class
*
* @author Frank Wibbelink
*/ */
public class Variable<T> { public class Variable<T> {
private final T type; private final T type;

View File

@ -1,29 +1,55 @@
package pp.s1184725.boppi.test; package pp.s1184725.boppi.test;
import java.nio.file.*; 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.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; 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) @RunWith(Suite.class)
@SuiteClasses({ CheckerTest.class, GeneratorTest.class, ParserTest.class }) @SuiteClasses({ ExpressionTest.class, SimpleVariableTest.class, ConditionalTest.class, SimpleFunctionTest.class })
public class AllTests { 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/"); .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<LogRecord> 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 * The level of error reporting to use for the tests. Can be changed to
* include finer tests. * include finer tests.
*/ */
public static Level logLevel = Level.SEVERE; private static Level logLevel = DEFAULT_LOG_LEVEL;
/** /**
* Runs a test with warnings enabled. * Runs a test with warnings enabled.
@ -34,6 +60,132 @@ public class AllTests {
public static void withWarnings(Runnable test) { public static void withWarnings(Runnable test) {
logLevel = Level.WARNING; logLevel = Level.WARNING;
test.run(); 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;
}
} }

View File

@ -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<LogRecord> 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));
}
}

View File

@ -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")));
}
}

View File

@ -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"));
}
}

View File

@ -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<LogRecord> 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")));
}
}

View File

@ -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<LogRecord> 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));
}
}

View File

@ -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")));
}
}

View File

@ -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()));
}
}

View File

@ -1,7 +1,6 @@
var int undefined; var (int,int)->int intBinOp; //unused variable for type aliasing
function int intDiadic(int a, int b) undefined;
function intDiadic getMultiply() { function intBinOp getMultiply() {
function int multiply(int a, int b) function int multiply(int a, int b)
a*b; a*b;
multiply multiply
@ -9,7 +8,7 @@ function intDiadic getMultiply() {
function int biasedMultiply(int a, int b) a*b+4; function int biasedMultiply(int a, int b) a*b+4;
var intDiadic myMultiply; var intBinOp myMultiply;
myMultiply := getMultiply(); myMultiply := getMultiply();
print(myMultiply(5, 8)); print(myMultiply(5, 8));

View File

@ -1,9 +1,11 @@
package pp.s1184725.boppi; package pp.s1184725.boppi.type;
import pp.iloc.eval.Machine; import pp.iloc.eval.Machine;
/** /**
* The (->) type. Takes exactly two types as arguments. * The (-&gt;) type. Takes exactly two types as arguments.
*
* @author Frank Wibbelink
*/ */
public class FunctionType implements Type { public class FunctionType implements Type {
private Type argument, result; private Type argument, result;

View File

@ -1,4 +1,4 @@
package pp.s1184725.boppi; package pp.s1184725.boppi.type;
import static pp.s1184725.boppi.antlr.BoppiLexer.*; import static pp.s1184725.boppi.antlr.BoppiLexer.*;
@ -13,6 +13,8 @@ import pp.s1184725.boppi.antlr.BoppiLexer;
* <li>boolean (sized {@link Machine#INT_SIZE})</li> * <li>boolean (sized {@link Machine#INT_SIZE})</li>
* <li>void (empty/unit type)</li> * <li>void (empty/unit type)</li>
* </ul> * </ul>
*
* @author Frank Wibbelink
*/ */
public enum SimpleType implements Type { public enum SimpleType implements Type {

View File

@ -1,11 +1,13 @@
package pp.s1184725.boppi; package pp.s1184725.boppi.type;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* The n-tuple type. Takes exactly {@link #size()} types. The element indices in * 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<Type> implements Type { public class TupleType extends AbstractList<Type> implements Type {
/** /**
@ -19,6 +21,7 @@ public class TupleType extends AbstractList<Type> implements Type {
* Creates an n-tuple of the given types. * Creates an n-tuple of the given types.
* *
* @param parameterTypes * @param parameterTypes
* a list of types for this tuple
*/ */
public TupleType(List<Type> parameterTypes) { public TupleType(List<Type> parameterTypes) {
parameters = new ArrayList<>(parameterTypes); parameters = new ArrayList<>(parameterTypes);
@ -59,7 +62,8 @@ public class TupleType extends AbstractList<Type> implements Type {
@Override @Override
public String toString() { 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 @Override

View File

@ -1,7 +1,9 @@
package pp.s1184725.boppi; package pp.s1184725.boppi.type;
/** /**
* Generic type interface. All types must have at least these properties. * Generic type interface. All types must have at least these properties.
*
* @author Frank Wibbelink
*/ */
public interface Type { public interface Type {

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="javadoc"> <project default="javadoc">
<target name="javadoc"> <target name="javadoc">
<javadoc access="public" author="true" classpath="lib/commons-lang3-3.5.jar;lib/antlr-4.7-complete.jar;lib/junit-4.12.jar;lib/hamcrest-all-1.3.jar" destdir="doc/javadoc" nodeprecated="false" nodeprecatedlist="false" noindex="false" nonavbar="false" notree="false" packagenames="pp.s1184725.boppi,pp.s1184725.boppi.test" source="1.8" sourcepath="src" splitindex="false" use="true" version="true"> <javadoc access="public" author="true" classpath="../lib/commons-lang3-3.5.jar;../lib/antlr-4.7-complete.jar;../lib/junit-4.12.jar;../lib/hamcrest-all-1.3.jar" destdir="../doc/javadoc" nodeprecated="false" nodeprecatedlist="false" noindex="false" nonavbar="false" notree="false" packagenames="pp.*" source="1.8" sourcepath="../src" splitindex="false" use="true" version="true">
<link href="http://www.antlr.org/api/Java/"/> <link href="http://www.antlr.org/api/Java/"/>
<link href="http://hamcrest.org/JavaHamcrest/javadoc/1.3/"/> <link href="http://hamcrest.org/JavaHamcrest/javadoc/1.3/"/>
<link href="https://docs.oracle.com/javase/8/docs/api/"/> <link href="https://docs.oracle.com/javase/8/docs/api/"/>