diff --git a/src/pp/s1184725/boppi/test/BoppiTests.java b/src/pp/s1184725/boppi/test/BoppiTests.java index 119f0b2..dc3552a 100644 --- a/src/pp/s1184725/boppi/test/BoppiTests.java +++ b/src/pp/s1184725/boppi/test/BoppiTests.java @@ -3,11 +3,14 @@ package pp.s1184725.boppi.test; import java.util.List; import java.util.logging.*; -import org.antlr.v4.runtime.CharStream; +import java.io.*; + +import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.ParseTree; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import org.apache.commons.lang3.StringUtils; import pp.iloc.Simulator; import pp.iloc.eval.Machine; @@ -18,7 +21,7 @@ import pp.s1184725.boppi.*; * 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) @@ -53,7 +56,7 @@ public class BoppiTests { /** * Runs a test with warnings enabled. - * + * * @param test * the test to run */ @@ -78,99 +81,146 @@ public class BoppiTests { /** * 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)); + parse(codeFromString(code)); } /** * Parses code from a file, logging to {@link #log}. - * + * * @param file * the file name */ public static void parseFile(String file) { - parse(ToolChain.getCharStream(BoppiTests.class, 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(); + parse(codeFromFile(file)); } /** * 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)); + check(parse(codeFromString(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(BoppiTests.class, 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); + check(parse(codeFromFile(file))); } /** * 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"); + ParseTree ast = parse(codeFromString(code)); + run(generate(ast, check(ast)), input); } /** * 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(BoppiTests.class, file), String.join("\n", input) + "\n"); + ParseTree ast = parse(codeFromFile(file)); + run(generate(ast, check(ast)), input); } - private static void compileAndRun(CharStream stream, String input) { + + + private static CharStream codeFromString(String code) { + printCode(new StringReader(code)); + return ToolChain.getCharStream(code); + } + + private static CharStream codeFromFile(String file) { + printCode(new InputStreamReader(BoppiTests.class.getResourceAsStream(file))); + return ToolChain.getCharStream(BoppiTests.class, file); + } + + private static ParseTree parse(CharStream stream) { 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); + printHeader("uncaught parse errors"); + ParseTree tree = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); + printHeader("parse log"); + printLog(log); - if (Simulator.DEBUG) - System.out.println(program.prettyPrint()); + return tree; + } - out = ToolChain.execute(program, logger, input).split("\n"); + private static Annotations check(ParseTree tree) { + Logger logger = Logger.getAnonymousLogger(); + List tempLog = ToolChain.makeListLog(logger); + logger.setLevel(logLevel); + + printHeader("uncaught checker errors"); + Annotations an = ToolChain.getAnnotations(tree, logger); + printHeader("checker log"); + printLog(tempLog); + + log.addAll(tempLog); + return an; + } + + private static Program generate(ParseTree ast, Annotations an) { + Logger logger = Logger.getAnonymousLogger(); + List tempLog = ToolChain.makeListLog(logger); + logger.setLevel(logLevel); + + printHeader("uncaught generation errors"); + Program program = ToolChain.getILOC(ast, logger, an); + printHeader("generation log"); + printLog(tempLog); + + log.addAll(tempLog); + return program; + } + + private static void run(Program program, String[] in) { + Logger logger = Logger.getAnonymousLogger(); + List tempLog = ToolChain.makeListLog(logger); + logger.setLevel(logLevel); + + printHeader("iloc code"); + String iloc = program.prettyPrint(); + System.out.println("..."); + System.out.println(iloc.substring(iloc.indexOf("end of stdlib"))); + printHeader("execution errors"); + String input = String.join("\n", in) + "\n"; + String output = ToolChain.execute(program, logger, input); + printHeader("execution log"); + printLog(tempLog); + printHeader("input"); + System.out.print(input); + printHeader("output"); + System.out.println(output); + + log.addAll(tempLog); + out = output.split("\n"); vm = ToolChain.machine; } @@ -178,7 +228,7 @@ public class BoppiTests { * Compiles and runs a program in debug mode. This writes the ILOC code, the * simulation, program output and all logged messages to the standard * output. - * + * * @param code * the code to run * @param input @@ -192,7 +242,7 @@ public class BoppiTests { * Compiles and runs a program in debug mode. This writes the ILOC code, the * simulation, program output and all logged messages to the standard * output. - * + * * @param file * the file name * @param input @@ -205,7 +255,7 @@ public class BoppiTests { /** * 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 @@ -240,4 +290,31 @@ public class BoppiTests { Simulator.DEBUG = false; } + + + private static void printHeader(String header) { + printHeader(header, '-'); + } + + private static void printHeader(String header, char separator) { + System.out.println(StringUtils.center(" "+header.toUpperCase()+" ", 150, separator)); + } + + private static void printCode(Reader r) { + System.out.println("\n\n\n"+StringUtils.repeat("=", 150)); + + BufferedReader bufReader = new BufferedReader(r); + printHeader("boppi code"); + try { + for (String line = bufReader.readLine(); line != null; line = bufReader.readLine()) + System.out.println("> " + line); + } + catch (IOException e) {} + } + + private static void printLog(List logs) { + logs.forEach((entry) -> System.out.println(entry.getMessage())); + } + + } diff --git a/util/object-oriented-poc.boppi b/util/object-oriented-poc.boppi new file mode 100644 index 0000000..b07c2e2 --- /dev/null +++ b/util/object-oriented-poc.boppi @@ -0,0 +1,85 @@ +/** + * Proof of concept for Object-Oriented Programming in Boppi. + * Note that this is a very basic notion of OO that only includes private + * members. It does not support type checking, inheritance or public members. + * It requires a tremendous amount of boilerplate to work without proper + * tuples, parametrization or OO class syntax. + */ + +//Declaring dummy functions for type aliases because Boppi disallows +//the use of Void directly in a type. +function intToVoid(int a) 0; +function int voidToInt() 0; +function class() 0; + + + +//Class declaration whose sole purpose is to make the code look better. +//`Rectangle` type checks with every other `()->Void` type. +var class Rectangle; + + + +//Helpers for making methods available outside their closure (the function +//references should be passed via a return value if tuples were supported) +//Assigning them immediately to suppress warnings. +var voidToInt _getWidth; _getWidth := voidToInt; +var intToVoid _setWidth; _setWidth := intToVoid; +var voidToInt _getHeight; _getHeight := voidToInt; +var intToVoid _setHeight; _setHeight := intToVoid; +var voidToInt _getArea; _getArea := voidToInt; + +//Wrappers to make a `getWidth()` call less of an eyesore and allow you +//to nest these calls without overriding the target closure in between +function int getWidth (Rectangle rect) { rect();_getWidth() }; +function setWidth (Rectangle rect, int width) { rect();_setWidth(width) }; +function int getHeight(Rectangle rect) { rect();_getHeight() }; +function setHeight(Rectangle rect, int height) { rect();_setHeight(height) }; +function int getArea (Rectangle rect) { rect();_getArea() }; + + + +//A class definition looks decent apart from the bind function to expose +//the class methods. You can even add constructor arguments to your liking. +function Rectangle newRectangle() { + var int width; + var int height; + + function int getWidth() + width; + + function setWidth(int newWidth) { + width := newWidth; + }; + + function int getHeight() + height; + + function setHeight(int newHeight) { + height := newHeight; + }; + + function int getArea() + width*height; + + + function bind() { + _getWidth := getWidth; + _setWidth := setWidth; + _getHeight := getHeight; + _setHeight := setHeight; + _getArea := getArea; + }; + bind +}; + + + +//Time to use the class +var Rectangle a; a := newRectangle(); +var Rectangle b; b := newRectangle(); + +setWidth(a, 4); +setWidth(b, getWidth(a)+2); +setHeight(b, 3); +print(getArea(b));