nomiscript/compiler/special/
mod.rs1mod binding;
2mod compile_eval;
3mod control;
4pub(in crate::compiler::special) mod iteration;
5mod labels;
6mod lambda;
7mod macros;
8mod structure;
9
10use tracing::debug;
11
12use crate::ast::Expr;
13use crate::error::{Error, Result};
14use crate::runtime::SymbolTable;
15
16use crate::ast::WasmType;
17
18use super::context::CompileContext;
19use super::emit::FunctionEmitter;
20
21pub(in crate::compiler) use control::{
22 form_diverges_for_test, is_runtime_test, is_truthy, reject_non_boolean_runtime_test,
23};
24pub(in crate::compiler) use lambda::monomorph::lookup_or_emit_monomorph;
25pub(in crate::compiler) use lambda::{
26 is_capture_free, try_emit_lambda_for_host_iter, try_emit_lambda_for_value,
27};
28pub(in crate::compiler) use structure::rhs_has_runtime_store;
29pub(in crate::compiler) use structure::set_place as setf_set_place;
30
31pub(super) type EvalFn = fn(&mut SymbolTable, &[Expr]) -> Result<Expr>;
32pub(super) type CompileFn =
33 fn(&mut CompileContext, &mut FunctionEmitter, &mut SymbolTable, &[Expr]) -> Result<()>;
34pub(super) type StackFn =
35 fn(&mut CompileContext, &mut FunctionEmitter, &mut SymbolTable, &[Expr]) -> Result<WasmType>;
36
37pub(super) struct SpecialFormSpec {
44 pub name: &'static str,
45 pub eval: EvalFn,
46 pub compile: CompileFn,
47 pub stack: Option<StackFn>,
48 pub effect: Option<CompileFn>,
49}
50
51const DOMAINS: &[&[SpecialFormSpec]] = &[
52 control::FORMS,
53 binding::FORMS,
54 lambda::FORMS,
55 macros::FORMS,
56 compile_eval::FORMS,
57 iteration::FORMS,
58 labels::FORMS,
59 structure::FORMS,
60];
61
62fn lookup(name: &str) -> Option<&'static SpecialFormSpec> {
63 DOMAINS
64 .iter()
65 .flat_map(|d| d.iter())
66 .find(|s| s.name == name)
67}
68
69pub(in crate::compiler) fn is_special_form(name: &str) -> bool {
74 lookup(name).is_some()
75}
76
77pub fn call(symbols: &mut SymbolTable, name: &str, args: &[Expr]) -> Result<Expr> {
78 debug!(special_form = %name, args = args.len(), "calling special form");
79 match lookup(name) {
80 Some(spec) => (spec.eval)(symbols, args),
81 None => Err(Error::Compile(format!(
82 "special form '{name}' not yet implemented"
83 ))),
84 }
85}
86
87pub(super) fn compile(
88 ctx: &mut CompileContext,
89 emit: &mut FunctionEmitter,
90 symbols: &mut SymbolTable,
91 name: &str,
92 args: &[Expr],
93) -> Result<()> {
94 debug!(special_form = %name, args = args.len(), "compiling special form");
95 match lookup(name) {
96 Some(spec) => (spec.compile)(ctx, emit, symbols, args),
97 None => Err(Error::Compile(format!(
98 "special form '{name}' not yet implemented"
99 ))),
100 }
101}
102
103pub(super) fn compile_for_effect(
104 ctx: &mut CompileContext,
105 emit: &mut FunctionEmitter,
106 symbols: &mut SymbolTable,
107 name: &str,
108 args: &[Expr],
109) -> Result<()> {
110 match lookup(name) {
111 Some(spec) => (spec.effect.unwrap_or(spec.compile))(ctx, emit, symbols, args),
112 None => Err(Error::Compile(format!(
113 "special form '{name}' not yet implemented"
114 ))),
115 }
116}
117
118pub(super) fn compile_for_stack(
119 ctx: &mut CompileContext,
120 emit: &mut FunctionEmitter,
121 symbols: &mut SymbolTable,
122 name: &str,
123 args: &[Expr],
124) -> Result<WasmType> {
125 match lookup(name) {
126 Some(spec) => match spec.stack {
127 Some(f) => f(ctx, emit, symbols, args),
128 None => Err(Error::Compile(format!(
129 "special form '{name}' cannot produce stack value"
130 ))),
131 },
132 None => Err(Error::Compile(format!(
133 "special form '{name}' not yet implemented"
134 ))),
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use std::collections::HashSet;
142
143 #[test]
144 fn registry_names_unique() {
145 let mut seen = HashSet::new();
146 for spec in DOMAINS.iter().flat_map(|d| d.iter()) {
147 assert!(
148 seen.insert(spec.name),
149 "duplicate special-form registration: {}",
150 spec.name
151 );
152 }
153 }
154
155 #[test]
156 fn registry_lookup_covers_every_entry() {
157 for spec in DOMAINS.iter().flat_map(|d| d.iter()) {
158 assert!(
159 lookup(spec.name).is_some(),
160 "lookup({}) returned None",
161 spec.name
162 );
163 }
164 }
165}