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