[fine] test compilation, start removing print

This commit is contained in:
John Doty 2024-01-11 06:33:08 -08:00
parent d8db65af55
commit d8988cb2cf
8 changed files with 239 additions and 81 deletions

View file

@ -1,30 +1,32 @@
use fine::compiler::{compile, Function, Module};
use fine::parser::SyntaxTree;
use fine::semantics::{Semantics, Type};
use fine::tokens::Lines;
use pretty_assertions::assert_eq;
use std::fmt::Write as _;
fn rebase_concrete(source_path: &str, dump: &str) {
fn rebase_section(source_path: &str, section: &str, value: &str) {
let contents = std::fs::read_to_string(source_path)
.expect(&format!("unable to read input file {}", source_path));
let mut result = String::new();
let mut lines = contents.lines();
// Search for the "concrete:" section.
let mut found_concrete_section = false;
// Search for the section.
let mut found_section = false;
let marker = format!("// @{section}:");
while let Some(line) = lines.next() {
result.push_str(line);
result.push_str("\n");
if line == "// @concrete:" {
found_concrete_section = true;
if line == marker {
found_section = true;
break;
}
}
if !found_concrete_section {
if !found_section {
panic!(
"unable to locate the concrete section in {}. Is there a line that starts with '// concrete:'?",
source_path
"unable to locate the {section} section in {source_path}. Is there a line that starts with '// @{section}:'?"
);
}
@ -39,7 +41,7 @@ fn rebase_concrete(source_path: &str, dump: &str) {
// OK we're out of concrete syntax tree; copy in the
// new CST. (We do this inline so we don't lose
// `line`.)
for expected_line in dump.lines() {
for expected_line in value.lines() {
result.push_str("// | ");
result.push_str(expected_line);
result.push_str("\n");
@ -70,18 +72,24 @@ fn rebase_concrete(source_path: &str, dump: &str) {
std::fs::write(source_path, result).expect("unable to write the new file!");
}
fn assert_concrete(tree: &SyntaxTree, expected: &str, source_path: &str) {
let dump = tree.dump(false);
fn should_rebase() -> bool {
let rebase = std::env::var("FINE_TEST_REBASE")
.unwrap_or(String::new())
.to_lowercase();
match rebase.as_str() {
"1" | "true" | "yes" | "y" => {
if dump != expected {
rebase_concrete(source_path, &dump)
}
"1" | "true" | "yes" | "y" => true,
_ => false,
}
}
fn assert_concrete(tree: &SyntaxTree, expected: &str, source_path: &str) {
let dump = tree.dump(false);
if dump != expected {
if should_rebase() {
rebase_section(source_path, "concrete", &dump)
} else {
assert_eq!(expected, dump, "concrete syntax trees did not match (set FINE_TEST_REBASE=1 to auto-rebase if the diff is expected)")
}
_ => assert_eq!(expected, dump, "concrete syntax trees did not match (set FINE_TEST_REBASE=1 to auto-rebase if the diff is expected)"),
}
}
@ -181,4 +189,58 @@ fn assert_type_error_at(
);
}
fn dump_function(out: &mut String, function: &Function) -> std::fmt::Result {
writeln!(
out,
"function {} ({} args, {} locals):",
function.name(),
function.args(),
function.locals()
)?;
let strings = function.strings();
writeln!(out, " strings ({}):", strings.len())?;
for (i, s) in strings.iter().enumerate() {
writeln!(out, " {}: {}", i, s)?; // TODO: ESCAPE
}
let code = function.instructions();
writeln!(out, " code ({}):", code.len())?;
for (i, inst) in code.iter().enumerate() {
writeln!(out, " {}: {:?}", i, inst)?;
}
Ok(())
}
fn dump_module(out: &mut String, module: &Module) -> std::fmt::Result {
for function in module.functions() {
dump_function(out, function)?;
}
Ok(())
}
fn assert_compiles_to(tree: &SyntaxTree, lines: &Lines, expected: &str, source_path: &str) {
let semantics = Semantics::new(tree, lines);
let module = compile(&semantics);
let mut actual = String::new();
dump_module(&mut actual, &module).expect("no dumping?");
if expected != actual {
if should_rebase() {
rebase_section(source_path, "compiles-to", &actual)
} else {
semantic_assert_eq!(
&semantics,
None,
expected,
actual,
"did not compile as expected (set FINE_TEST_REBASE=1 to auto-rebase if the diff is expected)"
)
}
}
}
include!(concat!(env!("OUT_DIR"), "/generated_tests.rs"));

View file

@ -1,3 +1,8 @@
fun foo(x: f64) {
x + 7
}
// @type: 20 f64
// @concrete:
// | File
// | FunctionDecl
@ -22,9 +27,14 @@
// | Number:'"7"'
// | RightBrace:'"}"'
// |
fun foo(x: f64) {
x + 7
}
// @type: 613 f64
// @compiles-to:
// | function foo (1 args, 0 locals):
// | strings (0):
// | code (3):
// | 0: LoadArgument(0)
// | 1: PushFloat(7.0)
// | 2: FloatAdd
// | function << module >> (0 args, 0 locals):
// | strings (0):
// | code (0):
// |

View file

@ -1,3 +1,6 @@
1 * 2 + -3 * 4;
// @type: 6 f64
// @concrete:
// | File
// | ExpressionStatement
@ -18,7 +21,19 @@
// | LiteralExpression
// | Number:'"4"'
// | Semicolon:'";"'
//
1 * 2 + -3 * 4;
// @type: 532 f64
// |
// @compiles-to:
// | function << module >> (0 args, 0 locals):
// | strings (0):
// | code (10):
// | 0: PushFloat(1.0)
// | 1: PushFloat(2.0)
// | 2: FloatMultiply
// | 3: PushFloat(3.0)
// | 4: PushFloat(-1.0)
// | 5: FloatMultiply
// | 6: PushFloat(4.0)
// | 7: FloatMultiply
// | 8: FloatAdd
// | 9: Discard
// |

View file

@ -1,3 +1,8 @@
let x = 23;
let y = x * 2;
y;
// @type: 27 f64
// @concrete:
// | File
// | LetStatement
@ -18,17 +23,21 @@
// | LiteralExpression
// | Number:'"2"'
// | Semicolon:'";"'
// | PrintStatement
// | Print:'"print"'
// | LeftParen:'"("'
// | ExpressionStatement
// | Identifier
// | Identifier:'"y"'
// | RightParen:'")"'
// | Semicolon:'";"'
// |
let x = 23;
let y = x * 2;
print(y);
// @type: 667 f64
// @compiles-to:
// | function << module >> (0 args, 0 locals):
// | strings (0):
// | code (8):
// | 0: PushFloat(23.0)
// | 1: StoreModule(0)
// | 2: LoadModule(0)
// | 3: PushFloat(2.0)
// | 4: FloatMultiply
// | 5: StoreModule(1)
// | 6: LoadModule(1)
// | 7: Discard
// |