From 4d096156d65dd268ac5fb2428106255757585e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thales=20de=20Fran=C3=A7a?= Date: Sat, 21 Mar 2026 18:47:56 -0300 Subject: [PATCH] feat: add macro integration tests and example file Add examples/macros.vx demonstrating defmacro with unless and when macros. Add end-to-end compilation tests in lib.rs verifying macros compile through the full pipeline to Go. Add interpreter-based integration tests for defmacro expansion. --- examples/macros.vx | 11 +++++ src/interpreter.rs | 36 ++++++++++++++ src/lib.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 examples/macros.vx diff --git a/examples/macros.vx b/examples/macros.vx new file mode 100644 index 0000000..0745c6a --- /dev/null +++ b/examples/macros.vx @@ -0,0 +1,11 @@ +(defmacro unless [test body] + (syntax-list (quote if) test (quote nil) body)) + +(defmacro when [test body] + (syntax-list (quote if) test body (quote nil))) + +(defn main [] + (unless (> 1 10) + (println "1 is not greater than 10")) + (when (< 1 10) + (println "1 is less than 10"))) diff --git a/src/interpreter.rs b/src/interpreter.rs index f22860b..6d67e9b 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1135,4 +1135,40 @@ mod tests { panic!("expected SList"); } } + + #[test] + fn defmacro_unless_via_interpreter() { + let result = eval_source( + r#"(defmacro unless [test body] + (syntax-list (quote if) test (quote nil) body)) + +(unless true nil)"#, + ) + .unwrap(); + assert!(matches!(result, Value::Unit)); + } + + #[test] + fn defmacro_when_via_interpreter() { + let result = eval_source( + r#"(defmacro when [test body] + (syntax-list (quote if) test body (quote nil))) + +(when true nil)"#, + ) + .unwrap(); + assert!(matches!(result, Value::Unit)); + } + + #[test] + fn defmacro_double_via_interpreter() { + let result = eval_source( + r#"(defmacro double [x] + (syntax-list (quote +) x x)) + +(double 21)"#, + ) + .unwrap(); + assert!(matches!(result, Value::Int(42))); + } } diff --git a/src/lib.rs b/src/lib.rs index 66800e3..ac2689e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -419,4 +419,118 @@ mod tests { let types = extract_exported_types(&hir_module, &["add".into()]); assert!(types.is_empty()); } + + fn compile_source(source: &str) -> CompileResult { + compile(source, "test.vx") + } + + #[test] + fn defmacro_unless_compiles() { + let result = compile_source( + r#"(defmacro unless [test body] + (syntax-list (quote if) test (quote nil) body)) + +(defn main [] + (unless (> 1 10) + (println "1 is not greater than 10")))"#, + ); + assert!( + result.diagnostics.is_empty(), + "diagnostics: {:?}", + result.diagnostics + ); + assert!(result.go_source.contains("func main()")); + assert!(result.go_source.contains("fmt.Println")); + } + + #[test] + fn defmacro_when_compiles() { + let result = compile_source( + r#"(defmacro when [test body] + (syntax-list (quote if) test body (quote nil))) + +(defn main [] + (when (< 1 10) + (println "1 is less than 10")))"#, + ); + assert!( + result.diagnostics.is_empty(), + "diagnostics: {:?}", + result.diagnostics + ); + assert!(result.go_source.contains("func main()")); + } + + #[test] + fn defmacro_stripped_from_go_output() { + let result = compile_source( + r#"(defmacro noop [] + (quote nil)) + +(defn main [] + (println "hello"))"#, + ); + assert!( + result.diagnostics.is_empty(), + "diagnostics: {:?}", + result.diagnostics + ); + assert!(!result.go_source.contains("defmacro")); + assert!(!result.go_source.contains("noop")); + } + + #[test] + fn defmacro_with_hygiene_compiles() { + let result = compile_source( + r#"(defmacro with-temp [body] + (syntax-list (quote let) + (syntax-list (quote tmp) (quote 0)) + body)) + +(defn main [] + (let [tmp 42] + (println (str (with-temp tmp)))))"#, + ); + assert!( + result.diagnostics.is_empty(), + "diagnostics: {:?}", + result.diagnostics + ); + assert!(result.go_source.contains("func main()")); + } + + #[test] + fn defmacro_multiple_macros_compile() { + let result = compile_source( + r#"(defmacro unless [test body] + (syntax-list (quote if) test (quote nil) body)) + +(defmacro when [test body] + (syntax-list (quote if) test body (quote nil))) + +(defn main [] + (unless (> 1 10) + (println "not greater")) + (when (< 1 10) + (println "less than")))"#, + ); + assert!( + result.diagnostics.is_empty(), + "diagnostics: {:?}", + result.diagnostics + ); + assert!(result.go_source.contains("func main()")); + } + + #[test] + fn defmacro_example_file_compiles() { + let source = std::fs::read_to_string("examples/macros.vx").expect("macros.vx should exist"); + let result = compile(&source, "examples/macros.vx"); + assert!( + result.diagnostics.is_empty(), + "diagnostics: {:?}", + result.diagnostics + ); + assert!(result.go_source.contains("func main()")); + } }