Add 'again' macro for recursive expansion

The 'again' macro is a new addition to the macro system, allowing for
recursive macro expansion. This commit introduces the necessary AST
nodes, macro expansion logic, and an integration test to ensure its
functionality. The `soa_series.myc` example has also been updated to
demonstrate its usage.
This commit is contained in:
Michael Schimmel
2026-03-09 11:22:32 +01:00
parent fa23a4c125
commit 11fc1e0e48
4 changed files with 44 additions and 1 deletions
+3
View File
@@ -0,0 +1,3 @@
(do
(repeat n 10 (print n)
)
+3 -1
View File
@@ -1,4 +1,4 @@
;; Output: 26.7
;; Output: 228
;; Benchmark: 1.4us
;; Benchmark-Repeat: 1406
(do
@@ -8,6 +8,8 @@
(push my_ticks {:price 11.2 :volume 200 :msg "B"})
(push my_ticks {:price 15.5 :volume 300 :msg "C"})
(repeat n 100 (push my_ticks {:price (+ n 15.5) :volume (ceil (/ n 300)) :msg "??"})
)
(def prices (.price my_ticks))
+22
View File
@@ -275,6 +275,17 @@ impl<E: MacroEvaluator> MacroExpander<E> {
})
}
UntypedKind::Again { args } => {
let expanded_args = self.expand_recursive(*args)?;
Ok(Node {
identity: node.identity,
kind: UntypedKind::Again {
args: Box::new(expanded_args),
},
ty: (),
})
}
_ => Ok(node),
}
}
@@ -536,6 +547,17 @@ impl<E: MacroEvaluator> MacroExpander<E> {
})
}
UntypedKind::Again { args } => {
let expanded_args = self.expand_template(*args, state)?;
Ok(Node {
identity: node.identity,
kind: UntypedKind::Again {
args: Box::new(expanded_args),
},
ty: (),
})
}
_ => Ok(node),
}
}
+16
View File
@@ -813,4 +813,20 @@ mod tests {
let res = env.run_script(&source_fixed);
assert!(res.is_ok(), "Inlining slot clash triggered: {:?}", res.err());
}
#[test]
fn test_repro_placeholder_in_template_failure() {
let env = Environment::new();
let source = r#"
(do
(macro repeat [var limit body]
`((fn [~var __limit]
(if (< ~var __limit)
(do ~body (again (+ ~var 1) __limit))))
0 ~limit))
(repeat n 10 42))
"#;
let res = env.run_script(source);
assert!(res.is_ok(), "Expansion failed to strip placeholders: {:?}", res.err());
}
}