diff options
| author | Robert Obkircher <robert.obkircher@gmail.com> | 2024-11-07 19:41:09 +0100 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2024-11-16 15:05:18 -0800 |
| commit | 2ccaa33540a4b44bac5847226501a14b9e8396b8 (patch) | |
| tree | f2d4f6bb9cfdc4a0300e7be8b81449b12e25cbf0 | |
| parent | 86290d37fb49fb7839beefc405c43dfe664375b8 (diff) | |
| download | Simple-2ccaa33540a4b44bac5847226501a14b9e8396b8.tar.gz Simple-2ccaa33540a4b44bac5847226501a14b9e8396b8.zip | |
transpile-tests: improve parsing of evaluator assertions and update RustPrinter
4 files changed, 65 insertions, 30 deletions
diff --git a/transpile-tests/README.md b/transpile-tests/README.md index 0f13756a..aafd3161 100644 --- a/transpile-tests/README.md +++ b/transpile-tests/README.md @@ -2,7 +2,8 @@ This module may be helpful if you want to create an implementation of Simple in another language. It can parse the JUnit tests to automatically translate most of the boilerplate. -Some manual tweaks will still be required though, because only common patterns are detected. +Some manual tweaks will still be required though, because only common patterns are detected +(e.g. 01: testSimpleProgram, testZero; 04: testVarArg; 08: testRegress5; 09: testMeet, ...) ## Usage @@ -12,7 +13,3 @@ and implement a pretty printer for your favourite language. For an example you can refer to `printers.RustPrinter`, which produces tests similar to the ones used by [Simple-Rust](https://github.com/SeaOfNodes/Simple-Rust). - -Note that although all tests of all chapters are parsed, -you probably only want to transpile the latest test of each chapter. -For the rest you can just manually port the small changes between chapters. diff --git a/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/AstVisitor.java b/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/AstVisitor.java index 36b3d368..0d007d51 100644 --- a/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/AstVisitor.java +++ b/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/AstVisitor.java @@ -73,15 +73,34 @@ class AstVisitor extends TreeScanner<Void, Void> { } else { if (args.size() != 2) throw new RuntimeException("Unexpected number of arguments " + node); - if (args.get(1).toString().endsWith(".toString()") || args.get(1).toString().endsWith(".print()")) { - current.assertStopEquals = (String) literal(args.get(0)); - } else if (args.get(1).toString().contains("evaluate(")) { - Number result = (Number) literal(args.get(0)); - var evalArgs = ((MethodInvocationTree) args.get(1)).getArguments(); + if (args.get(1).toString().contains("evaluate(")) { + // visit evaluate(..) or evaluate(..).toString() + var eval = args.get(1).accept(new TreeScanner<MethodInvocationTree, Void>() { + @Override + public MethodInvocationTree reduce(MethodInvocationTree r1, MethodInvocationTree r2) { + return r1 != null ? r1 : r2; + } + + @Override + public MethodInvocationTree visitMethodInvocation(MethodInvocationTree n, Void unused1) { + if (n.getMethodSelect().toString().endsWith(".evaluate")) + return n; + return super.visitMethodInvocation(n, unused1); + } + }, null); + + Object result = literal(args.get(0)); + var evalArgs = eval.getArguments(); if (evalArgs.size() != 2 || !evalArgs.get(0).toString().equals("stop")) throw new RuntimeException("Unexpected eval arguments " + node); long parameter = Long.parseLong(literal(evalArgs.get(1)).toString()); current.evaluations.add(new TestMethod.Evaluation(result, parameter)); + } else if (args.get(1).toString().endsWith(".toString()") || args.get(1).toString().endsWith(".print()")) { + if (current.assertStopEquals == null) { + // Assume the first one prints the stop node. Ignore assertions from evaluator: + // assertEquals("int[] {\n # :int;\n [] :int;\n}",obj.struct().toString()); + current.assertStopEquals = (String) literal(args.get(0)); + } } } } @@ -113,7 +132,7 @@ class AstVisitor extends TreeScanner<Void, Void> { case "TypeInteger.BOT" -> new TestMethod.Arg.IntBot(); case "TypeInteger.TOP" -> new TestMethod.Arg.IntTop(); case String s -> { - var parts = s.split("\\(|\\)"); + var parts = s.split("[()]"); if (parts.length != 2 | !parts[0].equals("TypeInteger.constant")) throw new RuntimeException("Unexpected Argument " + s); yield new TestMethod.Arg.IntConstant(Long.parseLong(parts[1])); diff --git a/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/TestMethod.java b/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/TestMethod.java index 107b5f56..d0c6cfa8 100644 --- a/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/TestMethod.java +++ b/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/TestMethod.java @@ -31,6 +31,9 @@ public class TestMethod { } } - public record Evaluation(Number result, long parameter) { + /** + * result is a literal value + **/ + public record Evaluation(Object result, long parameter) { } } diff --git a/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/printers/RustPrinter.java b/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/printers/RustPrinter.java index 5397eb5e..115a075a 100644 --- a/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/printers/RustPrinter.java +++ b/transpile-tests/src/main/java/com/seaofnodes/simple/transpile/printers/RustPrinter.java @@ -57,12 +57,17 @@ public class RustPrinter { out.println(""" use crate::datastructures::arena::DroplessArena; use crate::sea_of_nodes::parser::Parser; - use crate::sea_of_nodes::types::Types; - """); + use crate::sea_of_nodes::types::Types;"""); - if (tests.stream().anyMatch(t -> t.parseErrorMessage != null)) { + if (tests.stream().anyMatch(t -> t.assertStopRetCtrlIsProj || t.assertStopRetCtrlIsCProj || t.assertStopRetCtrlIsRegion)) { + out.println("use crate::sea_of_nodes::nodes::Op;"); + } + if (tests.stream().anyMatch(t -> t.parseErrorMessage != null && !t.iterate)) { out.println("use crate::sea_of_nodes::tests::test_error;"); } + if (tests.stream().anyMatch(t -> t.parseErrorMessage != null && t.iterate)) { + out.println("use crate::sea_of_nodes::tests::test_error_iterate;"); + } if (tests.stream().anyMatch(t -> t.irPrinter && !t.irPrinterLLVM)) { out.println("use crate::sea_of_nodes::ir_printer::pretty_print;"); } @@ -70,7 +75,7 @@ public class RustPrinter { out.println("use crate::sea_of_nodes::ir_printer::pretty_print_llvm;"); } if (tests.stream().anyMatch(t -> !t.evaluations.isEmpty())) { - out.println("use crate::sea_of_nodes::graph_evaluator::evaluate;"); + out.println("use crate::sea_of_nodes::tests::evaluator::{evaluate, Object};"); } for (TestMethod test : tests) { @@ -83,14 +88,18 @@ public class RustPrinter { out.println("#[test]"); out.println("fn " + snakeCase(test.name) + "() {"); if (test.parseErrorMessage != null) { - out.println("test_error(" + string(test.parserInput) + ", " + string(test.parseErrorMessage) + ");"); + if (test.iterate) { + out.println("test_error_iterate(" + string(test.parserInput) + ", " + string(test.parseErrorMessage) + ");"); + } else { + out.println("test_error(" + string(test.parserInput) + ", " + string(test.parseErrorMessage) + ");"); + } } else if (test.parserInput != null) { out.println("let arena = DroplessArena::new();"); out.println("let types = Types::new(&arena);"); if (test.parserArg == null) { - out.println("let mut parser = Parser::new(" + string(test.parserInput, true) + ", &types);"); + out.println("let mut parser = Parser::new(" + string(test.parserInput) + ", &types);"); } else { - out.printf("let mut parser = Parser::new_with_arg(%s, &types, %s);\n", string(test.parserInput, true), switch (test.parserArg) { + out.printf("let mut parser = Parser::new_with_arg(%s, &types, %s);\n", string(test.parserInput), switch (test.parserArg) { case TestMethod.Arg.IntBot ignored -> "types.ty_int_bot"; case TestMethod.Arg.IntTop ignored -> "types.ty_int_top"; case TestMethod.Arg.IntConstant c -> "types.get_int(" + c.value() + ")"; @@ -105,6 +114,7 @@ public class RustPrinter { } if (test.iterate) { out.println("parser.iterate(stop);"); + out.println("parser.type_check(stop).unwrap();\n"); } if (test.showAfterIterate) { out.println("parser.show_graph();"); @@ -116,16 +126,26 @@ public class RustPrinter { out.println("assert_eq!(parser.print(stop), " + string(test.assertStopEquals) + ");"); } if (test.assertStopRetCtrlIsProj) { - out.println("assert!(matches!(parser.nodes.ret_ctrl(stop), Node::Proj(_)));"); + out.println("assert!(matches!(parser.nodes.ret_ctrl(stop), Op::Proj(_)));"); } if (test.assertStopRetCtrlIsCProj) { - out.println("assert!(matches!(parser.nodes.ret_ctrl(stop), Node::CProj(_)));"); + out.println("assert!(matches!(parser.nodes.ret_ctrl(stop), Op::CProj(_)));"); } if (test.assertStopRetCtrlIsRegion) { - out.println("assert!(matches!(parser.nodes.ret_ctrl(stop), Node::Region{..}));"); + out.println("assert!(matches!(parser.nodes.ret_ctrl(stop), Op::Region{..}));"); } for (var evaluation : test.evaluations) { - out.println("assert_eq!(evaluate(&parser.nodes, stop, Some(" + evaluation.parameter() + "), None), " + evaluation.result() + ");"); + var result = switch (evaluation.result()) { + case null -> "Object::Null"; + case Integer l -> "Object::Long(" + l + ")"; + case Long l -> "Object::Long(" + l + ")"; + case Float d -> "Object::Double(" + d + ")"; + case Double d -> "Object::Double(" + d + ")"; + case String s -> string(s); + default -> + throw new RuntimeException("unexpected evaluation result: " + evaluation.result().getClass()); + }; + out.println("assert_eq!(" + result + ", evaluate(&parser.nodes, stop, Some(" + evaluation.parameter() + "), None).1);"); } } else { out.println("todo!();"); @@ -134,22 +154,18 @@ public class RustPrinter { } private String string(String value) { - return string(value, false); - } - - private String string(String value, boolean multiline) { var escaped = value.replace("\\", "\\\\") .replace("\r", "\\r") .replace("\t", "\\t") .replace("\"", "\\\""); - if (multiline && escaped.contains("\n")) { + if (escaped.contains("\n")) { return "\"\\\n" + escaped + "\""; } else { - return "\"" + escaped.replace("\n", "\\n") + "\""; + return "\"" + escaped + "\""; } } private String snakeCase(String identifier) { - return identifier.replaceAll("[A-Z]+", "_$0").toLowerCase(); + return identifier.replaceAll("([A-Z]+)|([0-9]+)", "_$0").toLowerCase(); } } |
