package plc.project; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.io.PrintWriter; import java.io.StringWriter; import java.util.function.Function; import java.util.function.Consumer; import java.util.stream.Stream; public class EndToEndGeneratorTests { @ParameterizedTest(name = "{0}") @MethodSource void testSource(String test, String input, String expected) { test(input, expected, new Scope(null), Parser::parseSource); } private static Stream testSource() { return Stream.of( Arguments.of("Hello, World!", // DEF main(): Integer DO // print("Hello, World!"); // RETURN 0; // END "DEF main(): Integer DO\n print(\"Hello, World!\");\n RETURN 0;\nEND", String.join(System.lineSeparator(), "public class Main {", "", " public static void main(String[] args) {", " System.exit(new Main().main());", " }", "", " int main() {", " System.out.println(\"Hello, World!\");", " return 0;", " }", "", "}" ) ) ); } @ParameterizedTest(name = "{0}") @MethodSource void testDeclarationStatement(String test, String input, String expected) { test(input, expected, new Scope(null), Parser::parseStatement); } private static Stream testDeclarationStatement() { return Stream.of( Arguments.of("Declaration", // LET name: Integer; "LET name: Integer;", "int name;" ), Arguments.of("Initialization", // LET name = 1.0; "LET name = 1.0;", "double name = 1.0;" ) ); } @ParameterizedTest(name = "{0}") @MethodSource void testIfStatement(String test, String input, String expected) { test(input, expected, new Scope(null), Parser::parseStatement); } private static Stream testIfStatement() { return Stream.of( Arguments.of("If", // IF TRUE DO // print(1); // END "IF TRUE DO\n print(1);\nEND", String.join(System.lineSeparator(), "if (true) {", " System.out.println(1);", "}" ) ), Arguments.of("Else", // IF FALSE DO // print(1); // ELSE // print(0); // END "IF FALSE DO\n print(1);\nELSE\n print(0);\nEND", String.join(System.lineSeparator(), "if (false) {", " System.out.println(1);", "} else {", " System.out.println(0);", "}" ) ) ); } @ParameterizedTest(name = "{0}") @MethodSource void testForStatement(String test, String input, String expected) { test(input, expected, init(new Scope(null), scope -> { scope.defineVariable("num", "num", Environment.Type.INTEGER, false, Environment.NIL); }), Parser::parseStatement); } private static Stream testForStatement() { return Stream.of( Arguments.of("For", // FOR (num = 0; num < 5; num = num + 1) // print(num); // END "FOR (num = 0; num < 5; num = num + 1)\n print(num);\nEND", String.join(System.lineSeparator(), "for ( num = 0; num < 5; num = num + 1 ) {", " System.out.println(num);", "}" ) ) ); } @ParameterizedTest(name = "{0}") @MethodSource void testBinaryExpression(String test, String input, String expected) { test(input, expected, new Scope(null), Parser::parseExpression); } private static Stream testBinaryExpression() { return Stream.of( Arguments.of("And", // TRUE && FALSE "TRUE && FALSE", "true && false" ), Arguments.of("Concatenation", // "Ben" + 10 "\"Ben\" + 10", "\"Ben\" + 10" ) ); } @ParameterizedTest(name = "{0}") @MethodSource void testFunctionExpression(String test, String input, String expected) { test(input, expected, new Scope(null), Parser::parseExpression); } private static Stream testFunctionExpression() { return Stream.of( Arguments.of("Print", // print("Hello, World!") "print(\"Hello, World!\")", "System.out.println(\"Hello, World!\")" ) ); } /** * Helper function for tests, using a StringWriter as the output stream. */ private static void test(String input, String expected, Scope scope, Function function) { StringWriter writer = new StringWriter(); Lexer lexer = new Lexer(input); Parser parser = new Parser(lexer.lex()); Ast ast = function.apply(parser); Analyzer analyzer = new Analyzer(scope); analyzer.visit(ast); new Generator(new PrintWriter(writer)).visit(ast); Assertions.assertEquals(expected, writer.toString()); } /** * Runs a callback on the given value, used for inline initialization. */ private static T init(T value, Consumer initializer) { initializer.accept(value); return value; } }