1use std::sync::Arc;
2
3use sim_kernel::{
4 Cx, Demand, Diagnostic, Error, Expr, MacroExpander, MacroId, Phase, PreparedArgs, Result,
5 Symbol, Value,
6};
7use sim_shape::{AnyShape, CaptureShape, ExactExprShape, ListShape, Shape};
8
9use crate::{
10 functions::{FunctionCase, FunctionObject},
11 macros::{
12 LispMacro, MacroCx, MacroExpansionLimits, MacroObject, macro_value_with_parser_trust,
13 },
14};
15
16pub fn register_macro(cx: &mut Cx, mac: Arc<dyn LispMacro>) -> Result<MacroId> {
18 register_macro_with_parser_trust(cx, mac, true)
19}
20
21pub fn register_macro_with_parser_trust(
23 cx: &mut Cx,
24 mac: Arc<dyn LispMacro>,
25 parser_trusted: bool,
26) -> Result<MacroId> {
27 let symbol = mac.symbol();
28 cx.registry_mut()
29 .register_macro_value(symbol, macro_value_with_parser_trust(mac, parser_trusted))
30}
31
32pub struct RegistryMacroExpander {
35 limits: MacroExpansionLimits,
36}
37
38impl RegistryMacroExpander {
39 pub fn new() -> Self {
41 Self {
42 limits: MacroExpansionLimits::default(),
43 }
44 }
45
46 pub fn with_limits(limits: MacroExpansionLimits) -> Self {
48 Self { limits }
49 }
50}
51
52impl Default for RegistryMacroExpander {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl MacroExpander for RegistryMacroExpander {
59 fn expand_expr(&self, cx: &mut Cx, phase: Phase, expr: Expr) -> Result<Expr> {
60 let mut macro_cx = MacroCx::with_limits(cx, phase, self.limits);
61 expand_expr(&mut macro_cx, expr)
62 }
63}
64
65pub fn expand_expr(cx: &mut MacroCx<'_>, expr: Expr) -> Result<Expr> {
67 expand_expr_with_depth(cx, expr, 0)
68}
69
70fn expand_expr_with_depth(cx: &mut MacroCx<'_>, expr: Expr, depth: usize) -> Result<Expr> {
71 cx.charge(1)?;
72 if depth > cx.max_depth() {
73 return Err(cx.budget_error(format!(
74 "macro expansion exceeded depth limit of {}",
75 cx.max_depth()
76 )));
77 }
78
79 match expr {
80 Expr::List(items) => expand_list(cx, items, depth),
81 Expr::Call { operator, args } => expand_call(cx, *operator, args, depth),
82 Expr::Vector(items) => Ok(Expr::Vector(expand_many(cx, items, depth)?)),
83 Expr::Map(entries) => Ok(Expr::Map(
84 entries
85 .into_iter()
86 .map(|(key, value)| {
87 Ok((
88 expand_expr_with_depth(cx, key, depth)?,
89 expand_expr_with_depth(cx, value, depth)?,
90 ))
91 })
92 .collect::<Result<Vec<_>>>()?,
93 )),
94 Expr::Set(items) => Ok(Expr::Set(expand_many(cx, items, depth)?)),
95 Expr::Infix {
96 operator,
97 left,
98 right,
99 } => Ok(Expr::Infix {
100 operator,
101 left: Box::new(expand_expr_with_depth(cx, *left, depth)?),
102 right: Box::new(expand_expr_with_depth(cx, *right, depth)?),
103 }),
104 Expr::Prefix { operator, arg } => Ok(Expr::Prefix {
105 operator,
106 arg: Box::new(expand_expr_with_depth(cx, *arg, depth)?),
107 }),
108 Expr::Postfix { operator, arg } => Ok(Expr::Postfix {
109 operator,
110 arg: Box::new(expand_expr_with_depth(cx, *arg, depth)?),
111 }),
112 Expr::Block(items) => Ok(Expr::Block(expand_many(cx, items, depth)?)),
113 Expr::Annotated { expr, annotations } => Ok(Expr::Annotated {
114 expr: Box::new(expand_expr_with_depth(cx, *expr, depth)?),
115 annotations: annotations
116 .into_iter()
117 .map(|(symbol, value)| Ok((symbol, expand_expr_with_depth(cx, value, depth)?)))
118 .collect::<Result<Vec<_>>>()?,
119 }),
120 Expr::Extension { tag, payload } => Ok(Expr::Extension {
121 tag,
122 payload: Box::new(expand_expr_with_depth(cx, *payload, depth)?),
123 }),
124 Expr::Quote { .. }
125 | Expr::Nil
126 | Expr::Bool(_)
127 | Expr::Number(_)
128 | Expr::Symbol(_)
129 | Expr::Local(_)
130 | Expr::String(_)
131 | Expr::Bytes(_) => Ok(expr),
132 }
133}
134
135fn expand_list(cx: &mut MacroCx<'_>, items: Vec<Expr>, depth: usize) -> Result<Expr> {
136 let Some(Expr::Symbol(symbol)) = items.first() else {
137 return Ok(Expr::List(expand_many(cx, items, depth)?));
138 };
139 let Some(value) = cx.cx.registry().macro_by_symbol(symbol).cloned() else {
140 return Ok(Expr::List(expand_many(cx, items, depth)?));
141 };
142 expand_macro_form(cx, value, Expr::List(items), depth)
143}
144
145fn expand_call(
146 cx: &mut MacroCx<'_>,
147 operator: Expr,
148 args: Vec<Expr>,
149 depth: usize,
150) -> Result<Expr> {
151 if let Expr::Symbol(symbol) = &operator
152 && let Some(value) = cx.cx.registry().macro_by_symbol(symbol).cloned()
153 {
154 let input = Expr::List(std::iter::once(operator).chain(args).collect::<Vec<_>>());
155 return expand_macro_form(cx, value, input, depth);
156 }
157
158 Ok(Expr::Call {
159 operator: Box::new(expand_expr_with_depth(cx, operator, depth)?),
160 args: expand_many(cx, args, depth)?,
161 })
162}
163
164fn expand_macro_form(
165 cx: &mut MacroCx<'_>,
166 value: Value,
167 input: Expr,
168 depth: usize,
169) -> Result<Expr> {
170 let symbol = macro_head_symbol(&input);
171 if !cx.cx.eval_policy().allow_macro_expansion(cx.phase()) {
172 let name = symbol
173 .as_ref()
174 .map(Symbol::to_string)
175 .unwrap_or_else(|| "<unknown>".to_owned());
176 return Err(Error::Eval(format!(
177 "macro expansion for {name} is not allowed during {:?} by {} eval policy",
178 cx.phase(),
179 cx.cx.eval_policy_name()
180 )));
181 }
182 cx.cx.require(¯o_phase_capability(cx.phase()))?;
183
184 let mac = ResolvedMacro::from_value(&value).ok_or(Error::TypeMismatch {
185 expected: "macro object",
186 found: "non-macro object",
187 })?;
188 let shape = mac.syntax_shape();
189 if shape.is_effectful() && !mac.parser_trusted() {
190 return Err(Error::Eval(format!(
191 "macro {} uses an effectful syntax shape in an untrusted parse position",
192 mac.symbol()
193 )));
194 }
195 let macro_symbol = mac.symbol();
196 let (matched, expansion_input) = match shape.check_expr(cx.cx, &input)? {
197 matched if matched.accepted => (matched, input),
198 rejected => {
199 let Some(alias) = symbol.as_ref() else {
200 return Err(wrong_macro_shape(
201 cx,
202 ¯o_symbol,
203 shape.as_ref(),
204 rejected,
205 ));
206 };
207 if alias == ¯o_symbol {
208 return Err(wrong_macro_shape(
209 cx,
210 ¯o_symbol,
211 shape.as_ref(),
212 rejected,
213 ));
214 }
215 let normalized = retarget_macro_head(input.clone(), macro_symbol.clone());
216 match shape.check_expr(cx.cx, &normalized)? {
217 matched if matched.accepted => (matched, normalized),
218 _ => {
219 return Err(wrong_macro_shape(
220 cx,
221 ¯o_symbol,
222 shape.as_ref(),
223 rejected,
224 ));
225 }
226 }
227 }
228 };
229 if !matched.accepted {
230 return Err(wrong_macro_shape(
231 cx,
232 ¯o_symbol,
233 shape.as_ref(),
234 matched,
235 ));
236 }
237
238 let symbol = symbol.unwrap_or(macro_symbol);
239 cx.stack.push(symbol);
240 let expanded = mac.expand(cx, expansion_input, matched.captures);
241 let expanded = match expanded {
242 Ok(expanded) => expanded,
243 Err(error) => {
244 cx.stack.pop();
245 return Err(error);
246 }
247 };
248 let result = expand_expr_with_depth(cx, expanded, depth + 1);
249 cx.stack.pop();
250 result
251}
252
253enum ResolvedMacro<'a> {
254 Sdk(&'a MacroObject),
255 #[cfg(all(feature = "dynamic-native", not(target_arch = "wasm32")))]
256 Native(&'a sim_run_loaders::NativeAbiMacro),
257}
258
259impl<'a> ResolvedMacro<'a> {
260 fn from_value(value: &'a Value) -> Option<Self> {
261 if let Some(mac) = value.object().downcast_ref::<MacroObject>() {
262 return Some(Self::Sdk(mac));
263 }
264 #[cfg(all(feature = "dynamic-native", not(target_arch = "wasm32")))]
265 if let Some(mac) = value
266 .object()
267 .downcast_ref::<sim_run_loaders::NativeAbiMacro>()
268 {
269 return Some(Self::Native(mac));
270 }
271 None
272 }
273
274 fn symbol(&self) -> Symbol {
275 match self {
276 Self::Sdk(mac) => mac.macro_ref().symbol(),
277 #[cfg(all(feature = "dynamic-native", not(target_arch = "wasm32")))]
278 Self::Native(mac) => mac.symbol(),
279 }
280 }
281
282 fn syntax_shape(&self) -> Arc<dyn Shape> {
283 match self {
284 Self::Sdk(mac) => mac.syntax_shape(),
285 #[cfg(all(feature = "dynamic-native", not(target_arch = "wasm32")))]
286 Self::Native(mac) => mac.syntax_shape(),
287 }
288 }
289
290 fn parser_trusted(&self) -> bool {
291 match self {
292 Self::Sdk(mac) => mac.parser_trusted(),
293 #[cfg(all(feature = "dynamic-native", not(target_arch = "wasm32")))]
294 Self::Native(mac) => mac.parser_trusted(),
295 }
296 }
297
298 fn expand(
299 &self,
300 cx: &mut MacroCx<'_>,
301 input: Expr,
302 captures: sim_shape::Bindings,
303 ) -> Result<Expr> {
304 match self {
305 Self::Sdk(mac) => mac.macro_ref().expand(cx, input, captures),
306 #[cfg(all(feature = "dynamic-native", not(target_arch = "wasm32")))]
307 Self::Native(mac) => {
308 let _ = captures;
309 mac.expand(input)
310 }
311 }
312 }
313}
314
315fn macro_head_symbol(input: &Expr) -> Option<Symbol> {
316 match input {
317 Expr::List(items) => items.first().and_then(|item| match item {
318 Expr::Symbol(symbol) => Some(symbol.clone()),
319 _ => None,
320 }),
321 _ => None,
322 }
323}
324
325fn retarget_macro_head(input: Expr, target: Symbol) -> Expr {
326 match input {
327 Expr::List(mut items) if !items.is_empty() => {
328 items[0] = Expr::Symbol(target);
329 Expr::List(items)
330 }
331 other => other,
332 }
333}
334
335fn wrong_macro_shape(
336 cx: &mut MacroCx<'_>,
337 macro_symbol: &Symbol,
338 shape: &dyn Shape,
339 matched: sim_shape::ShapeMatch,
340) -> Error {
341 let mut diagnostics = vec![Diagnostic::error(format!(
342 "macro {} rejected syntax during {:?}",
343 macro_symbol,
344 cx.phase(),
345 ))];
346 if let Ok(doc) = shape.describe(cx.cx) {
347 diagnostics.push(Diagnostic::error(format!("syntax shape: {}", doc.name)));
348 diagnostics.extend(
349 doc.details
350 .into_iter()
351 .map(|detail| Diagnostic::error(format!("syntax detail: {detail}"))),
352 );
353 }
354 diagnostics.extend(matched.diagnostics);
355 Error::WrongShape {
356 expected: shape.id().unwrap_or(sim_kernel::ShapeId(0)),
357 diagnostics,
358 }
359}
360
361fn macro_phase_capability(phase: Phase) -> sim_kernel::CapabilityName {
362 match phase {
363 Phase::Read => sim_kernel::macro_expand_read_capability(),
364 Phase::Expand => sim_kernel::macro_expand_capability(),
365 Phase::Compile => sim_kernel::macro_expand_compile_capability(),
366 Phase::Eval => sim_kernel::macro_expand_eval_capability(),
367 }
368}
369
370fn expand_many(cx: &mut MacroCx<'_>, items: Vec<Expr>, depth: usize) -> Result<Vec<Expr>> {
371 items
372 .into_iter()
373 .map(|item| expand_expr_with_depth(cx, item, depth))
374 .collect()
375}
376
377pub fn macroexpand_function(
379 case_id: sim_kernel::CaseId,
380 function_id: sim_kernel::FunctionId,
381 symbol: Symbol,
382) -> FunctionObject {
383 FunctionObject::new(
384 function_id,
385 symbol.clone(),
386 vec![FunctionCase {
387 id: case_id,
388 name: Symbol::qualified(symbol.to_string(), "expr"),
389 args: Arc::new(ListShape::new(vec![Arc::new(CaptureShape::new(
390 Symbol::new("expr"),
391 Arc::new(AnyShape),
392 ))])),
393 result: Some(Arc::new(AnyShape)),
394 demand: vec![Demand::Value],
395 priority: 10,
396 implementation: macroexpand_impl,
397 }],
398 )
399}
400
401fn macroexpand_impl(
402 cx: &mut Cx,
403 prepared: &PreparedArgs,
404 _bindings: sim_shape::Bindings,
405) -> Result<Value> {
406 let expr = prepared
407 .get(0)
408 .ok_or_else(|| Error::Eval("macroexpand expects one expression".to_owned()))?
409 .object()
410 .as_expr(cx)?;
411 let expanded = cx.expand_macros(Phase::Expand, expr)?;
412 cx.factory().expr(expanded)
413}
414
415pub fn literal_head_shape(symbol: Symbol) -> Arc<dyn Shape> {
417 Arc::new(ExactExprShape::new(Expr::Symbol(symbol)))
418}
419
420pub fn list_macro_shape(head: Symbol, tail: Vec<Arc<dyn Shape>>) -> Arc<dyn Shape> {
422 let items = std::iter::once(literal_head_shape(head))
423 .chain(tail)
424 .collect::<Vec<_>>();
425 Arc::new(ListShape::new(items))
426}
427
428pub fn list_macro_shape_with_rest(
430 head: Symbol,
431 fixed_tail: Vec<Arc<dyn Shape>>,
432 rest: Arc<dyn Shape>,
433) -> Arc<dyn Shape> {
434 let items = std::iter::once(literal_head_shape(head))
435 .chain(fixed_tail)
436 .collect::<Vec<_>>();
437 Arc::new(ListShape::with_rest(items, rest))
438}
439
440pub fn positional_macro_shape(
443 head: Symbol,
444 fixed: &[Symbol],
445 rest: Option<&Symbol>,
446) -> Arc<dyn Shape> {
447 let fixed_tail = fixed
448 .iter()
449 .cloned()
450 .map(|name| Arc::new(CaptureShape::new(name, Arc::new(AnyShape))) as Arc<dyn Shape>)
451 .collect::<Vec<_>>();
452 match rest {
453 Some(rest) => list_macro_shape_with_rest(
454 head,
455 fixed_tail,
456 Arc::new(CaptureShape::new(rest.clone(), Arc::new(AnyShape))),
457 ),
458 None => list_macro_shape(head, fixed_tail),
459 }
460}