summaryrefslogtreecommitdiff
path: root/transpile-tests
diff options
context:
space:
mode:
authorRobert Obkircher <robert.obkircher@gmail.com>2024-11-07 19:41:09 +0100
committerCliff Click <cliffc@acm.org>2024-11-16 15:05:18 -0800
commit2ccaa33540a4b44bac5847226501a14b9e8396b8 (patch)
treef2d4f6bb9cfdc4a0300e7be8b81449b12e25cbf0 /transpile-tests
parent86290d37fb49fb7839beefc405c43dfe664375b8 (diff)
downloadSimple-2ccaa33540a4b44bac5847226501a14b9e8396b8.tar.gz
Simple-2ccaa33540a4b44bac5847226501a14b9e8396b8.zip
transpile-tests: improve parsing of evaluator assertions and update RustPrinter
Diffstat (limited to 'transpile-tests')
-rw-r--r--transpile-tests/README.md7
-rw-r--r--transpile-tests/src/main/java/com/seaofnodes/simple/transpile/AstVisitor.java31
-rw-r--r--transpile-tests/src/main/java/com/seaofnodes/simple/transpile/TestMethod.java5
-rw-r--r--transpile-tests/src/main/java/com/seaofnodes/simple/transpile/printers/RustPrinter.java52
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();
}
}