Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/macros.vx
Original file line number Diff line number Diff line change
@@ -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")))
36 changes: 36 additions & 0 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
}
114 changes: 114 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()"));
}
}
Loading