refactor BoppiTests class, add object-oriented PoC
This commit is contained in:
parent
ec8617354e
commit
1558ddac71
|
@ -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<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;
|
||||
}
|
||||
|
||||
|
@ -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<LogRecord> logs) {
|
||||
logs.forEach((entry) -> System.out.println(entry.getMessage()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
Loading…
Reference in New Issue