diff --git a/fine/build.rs b/fine/build.rs index 0b92b92b..f9f82d57 100644 --- a/fine/build.rs +++ b/fine/build.rs @@ -20,7 +20,7 @@ fn generate_test_for_file(path: PathBuf) -> String { }; let line = line.trim(); - if line == "@disabled" { + if line == "@ignore" { disabled = quote! { #[ignore] }; } else if line == "@concrete:" { let mut concrete = String::new(); @@ -78,6 +78,10 @@ fn generate_test_for_file(path: PathBuf) -> String { assertions.push(quote! { crate::assert_type_error_at(&_tree, &_lines, #pos, #expected, #display_path); }); + } else if line == "@no-errors" { + assertions.push(quote! { + crate::assert_no_errors(&_tree, &_lines); + }); } } diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 92eb81ef..dae8e40f 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -219,10 +219,15 @@ pub fn compile(semantics: &Semantics) -> Module { fn file(c: &mut Compiler, t: TreeRef) { let tree = &c.syntax[t]; compiler_assert_eq!(c, t, tree.kind, TreeKind::File, "must be compiling a file"); - for i in 0..tree.children.len() { - if let Some(t) = tree.nth_tree(i) { - compile_statement(c, t, false); + + let children: Vec<_> = tree.child_trees().collect(); + if children.len() == 0 { + c.push(Instruction::PushNothing); + } else { + for i in 0..children.len() - 1 { + compile_statement(c, children[i], false); } + compile_statement(c, *children.last().unwrap(), true); } } @@ -485,18 +490,23 @@ fn compile_if_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { } fn compile_expression_statement(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { - compile_expression(c, tree.nth_tree(0)?); - if tree - .nth_token(1) - .is_some_and(|t| t.kind == TokenKind::Semicolon) - { - c.push(Instruction::Discard); - if gen_value { - c.push(Instruction::PushNothing); + if let Some(expr) = tree.nth_tree(0) { + compile_expression(c, expr); + + if tree + .nth_token(1) + .is_some_and(|t| t.kind == TokenKind::Semicolon) + { + c.push(Instruction::Discard); + if gen_value { + c.push(Instruction::PushNothing); + } + } else if !gen_value { + c.push(Instruction::Discard); } - } else if !gen_value { - c.push(Instruction::Discard); - } + } else if gen_value { + c.push(Instruction::PushNothing); + }; OK } diff --git a/fine/src/parser.rs b/fine/src/parser.rs index 8bc8c0ce..c00dd7f3 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -641,7 +641,9 @@ fn statement_return(p: &mut CParser) { fn statement_expression(p: &mut CParser) { let m = p.start(); - expression(p); + if !p.at(TokenKind::RightBrace) && !p.at(TokenKind::Semicolon) { + expression(p); + } if !p.at(TokenKind::RightBrace) { p.expect( TokenKind::Semicolon, diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 3de5c052..6f3fcff2 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -929,6 +929,18 @@ impl<'a> Semantics<'a> { Some(*ret.clone()) } + Type::MagicPrintGarbage => { + if arg_types.len() > 1 { + self.report_error_tree(tree, "print takes a single argument"); + Some(Type::Error) + } else if arg_types.len() == 0 { + Some(Type::Nothing) + } else { + let mut arg_types = arg_types; + let (_, t) = arg_types.pop().unwrap(); + Some(t) + } + } _ => { self.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}")); Some(Type::Error) diff --git a/fine/tests/README.md b/fine/tests/README.md index e228e6cb..2b8a7c7c 100644 --- a/fine/tests/README.md +++ b/fine/tests/README.md @@ -37,7 +37,7 @@ e.g., a test might look like this: The various assertions are as follows: -- The `// @disabled` directive marks the test as ignored. +- The `// @ignore` directive marks the test as ignored. - The `// @concrete:` assertion says that the following lines (prefixed with `// | `, as above) describe the concrete syntax tree diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index cd0445c3..0e747b0e 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -1,6 +1,6 @@ use fine::compiler::{compile, Function, Module}; use fine::parser::SyntaxTree; -use fine::semantics::{Semantics, Type}; +use fine::semantics::{check, Error, Semantics, Type}; use fine::tokens::Lines; use pretty_assertions::assert_eq; use std::fmt::Write as _; @@ -243,4 +243,19 @@ fn assert_compiles_to(tree: &SyntaxTree, lines: &Lines, expected: &str, source_p } } +fn assert_no_errors(tree: &SyntaxTree, lines: &Lines) { + let semantics = Semantics::new(tree, lines); + check(&semantics); + + let expected_errors: Vec = Vec::new(); + let errors = semantics.snapshot_errors(); + semantic_assert_eq!( + &semantics, + None, + expected_errors, + errors, + "expected no errors" + ); +} + include!(concat!(env!("OUT_DIR"), "/generated_tests.rs")); diff --git a/fine/tests/expression/argument.fine b/fine/tests/expression/argument.fine index 614aba4d..e4d5bdd1 100644 --- a/fine/tests/expression/argument.fine +++ b/fine/tests/expression/argument.fine @@ -36,5 +36,6 @@ fun foo(x: f64) { // | 2: FloatAdd // | function << module >> (0 args, 0 locals): // | strings (0): -// | code (0): +// | code (1): +// | 0: PushNothing // | diff --git a/fine/tests/expression/arithmetic.fine b/fine/tests/expression/arithmetic.fine index 3323f33c..ba1912a8 100644 --- a/fine/tests/expression/arithmetic.fine +++ b/fine/tests/expression/arithmetic.fine @@ -25,7 +25,7 @@ // @compiles-to: // | function << module >> (0 args, 0 locals): // | strings (0): -// | code (10): +// | code (11): // | 0: PushFloat(1.0) // | 1: PushFloat(2.0) // | 2: FloatMultiply @@ -36,4 +36,5 @@ // | 7: FloatMultiply // | 8: FloatAdd // | 9: Discard +// | 10: PushNothing // | diff --git a/fine/tests/expression/empty_statement.fine b/fine/tests/expression/empty_statement.fine index 710bef3d..afa4fbc2 100644 --- a/fine/tests/expression/empty_statement.fine +++ b/fine/tests/expression/empty_statement.fine @@ -1,5 +1,14 @@ -// @disabled -// @concrete: -// | - ; + +// @no-errors +// @concrete: +// | File +// | ExpressionStatement +// | Semicolon:'";"' +// | +// @compiles-to: +// | function << module >> (0 args, 0 locals): +// | strings (0): +// | code (1): +// | 0: PushNothing +// | diff --git a/fine/tests/expression/variable.fine b/fine/tests/expression/variable.fine index ec2faf59..b7d07538 100644 --- a/fine/tests/expression/variable.fine +++ b/fine/tests/expression/variable.fine @@ -3,6 +3,7 @@ let y = x * 2; let z = print(y); z; +// @no-errors // @type: 41 f64 // @concrete: // | File @@ -46,7 +47,7 @@ z; // @compiles-to: // | function << module >> (0 args, 0 locals): // | strings (0): -// | code (12): +// | code (13): // | 0: PushFloat(23.0) // | 1: StoreModule(0) // | 2: LoadModule(0) @@ -59,4 +60,5 @@ z; // | 9: StoreModule(2) // | 10: LoadModule(2) // | 11: Discard +// | 12: PushNothing // |