refactor BoppiTests class, add object-oriented PoC

This commit is contained in:
User 2019-02-07 02:16:09 +01:00
parent ec8617354e
commit 1558ddac71
2 changed files with 203 additions and 41 deletions

View File

@ -3,11 +3,14 @@ package pp.s1184725.boppi.test;
import java.util.List; import java.util.List;
import java.util.logging.*; 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.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 org.apache.commons.lang3.StringUtils;
import pp.iloc.Simulator; import pp.iloc.Simulator;
import pp.iloc.eval.Machine; import pp.iloc.eval.Machine;
@ -18,7 +21,7 @@ import pp.s1184725.boppi.*;
* Test suite for all Boppi language features. This suite contains helper * Test suite for all Boppi language features. This suite contains helper
* functions and attributes to assist test cases. Each test case should consider * functions and attributes to assist test cases. Each test case should consider
* a single language feature across all compiler stages. * a single language feature across all compiler stages.
* *
* @author Frank Wibbelink * @author Frank Wibbelink
*/ */
@RunWith(Suite.class) @RunWith(Suite.class)
@ -53,7 +56,7 @@ public class BoppiTests {
/** /**
* Runs a test with warnings enabled. * Runs a test with warnings enabled.
* *
* @param test * @param test
* the test to run * the test to run
*/ */
@ -78,99 +81,146 @@ public class BoppiTests {
/** /**
* Parses a string of code, logging to {@link #log}. * Parses a string of code, logging to {@link #log}.
* *
* @param code * @param code
* the code to parse * the code to parse
*/ */
public static void parseString(String code) { public static void parseString(String code) {
parse(ToolChain.getCharStream(code)); parse(codeFromString(code));
} }
/** /**
* Parses code from a file, logging to {@link #log}. * Parses code from a file, logging to {@link #log}.
* *
* @param file * @param file
* the file name * the file name
*/ */
public static void parseFile(String file) { public static void parseFile(String file) {
parse(ToolChain.getCharStream(BoppiTests.class, file)); parse(codeFromFile(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}. * Parses and type checks a string of code, logging to {@link #log}.
* *
* @param code * @param code
* the code to type check * the code to type check
*/ */
public static void checkString(String code) { 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}. * Parses and type checks code from a file, logging to {@link #log}.
* *
* @param file * @param file
* the file name * the file name
*/ */
public static void checkFile(String file) { public static void checkFile(String file) {
check(ToolChain.getCharStream(BoppiTests.class, file)); check(parse(codeFromFile(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 * Compiles and runs a program from a string, logging to {@link #log} and
* writing lines of output to {@link #out}. * writing lines of output to {@link #out}.
* *
* @param code * @param code
* the code to type check * the code to type check
* @param input * @param input
* lines of input for the program * lines of input for the program
*/ */
public static void compileAndRunString(String code, String... input) { 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 * Compiles and runs a program from a file, logging to {@link #log} and
* writing lines of output to {@link #out}. * writing lines of output to {@link #out}.
* *
* @param file * @param file
* the file name * the file name
* @param input * @param input
* lines of input for the program * lines of input for the program
*/ */
public static void compileAndRunFile(String file, String... input) { 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(); Logger logger = Logger.getAnonymousLogger();
log = ToolChain.makeListLog(logger); log = ToolChain.makeListLog(logger);
logger.setLevel(logLevel); logger.setLevel(logLevel);
ParseTree ast = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program(); printHeader("uncaught parse errors");
Annotations annotations = ToolChain.getAnnotations(ast, logger); ParseTree tree = ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
Program program = ToolChain.getILOC(ast, logger, annotations); printHeader("parse log");
printLog(log);
if (Simulator.DEBUG) return tree;
System.out.println(program.prettyPrint()); }
out = ToolChain.execute(program, logger, input).split("\n"); private static Annotations check(ParseTree tree) {
Logger logger = Logger.getAnonymousLogger();
List<LogRecord> 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<LogRecord> 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<LogRecord> 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; vm = ToolChain.machine;
} }
@ -178,7 +228,7 @@ public class BoppiTests {
* Compiles and runs a program in debug mode. This writes the ILOC code, the * Compiles and runs a program in debug mode. This writes the ILOC code, the
* simulation, program output and all logged messages to the standard * simulation, program output and all logged messages to the standard
* output. * output.
* *
* @param code * @param code
* the code to run * the code to run
* @param input * @param input
@ -192,7 +242,7 @@ public class BoppiTests {
* Compiles and runs a program in debug mode. This writes the ILOC code, the * Compiles and runs a program in debug mode. This writes the ILOC code, the
* simulation, program output and all logged messages to the standard * simulation, program output and all logged messages to the standard
* output. * output.
* *
* @param file * @param file
* the file name * the file name
* @param input * @param input
@ -205,7 +255,7 @@ public class BoppiTests {
/** /**
* Compiles and runs a program in debug mode, printing the generated * Compiles and runs a program in debug mode, printing the generated
* program, a simulation of the program and all log messages. * program, a simulation of the program and all log messages.
* *
* @param stream * @param stream
* a character stream with the program code * a character stream with the program code
* @param input * @param input
@ -240,4 +290,31 @@ public class BoppiTests {
Simulator.DEBUG = false; 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<LogRecord> logs) {
logs.forEach((entry) -> System.out.println(entry.getMessage()));
}
} }

View File

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