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