1#[cfg(all(feature = "codec-lisp", feature = "shape"))]
2use std::sync::Arc;
3
4#[cfg(all(feature = "codec-lisp", feature = "shape"))]
5use sim_kernel::{Cx, Expr, Factory, Object, ObjectCompat, Shape, Symbol, Value};
6#[cfg(any(feature = "codec-binary", feature = "codec-lisp"))]
7use sim_kernel::{Lib, Result};
8#[cfg(all(feature = "codec-lisp", feature = "shape"))]
9use sim_shape::{AnyShape, CaptureShape, ListShape};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum ReexportKind {
14 Class,
16 Function,
18 Macro,
20 Shape,
22 Codec,
24 NumberDomain,
26 Value,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct ReexportSpec {
34 pub(crate) kind: ReexportKind,
35 pub(crate) export: sim_kernel::Symbol,
36 pub(crate) target: sim_kernel::Symbol,
37}
38
39impl ReexportSpec {
40 pub fn new(kind: ReexportKind, export: sim_kernel::Symbol, target: sim_kernel::Symbol) -> Self {
43 Self {
44 kind,
45 export,
46 target,
47 }
48 }
49
50 pub fn kind(&self) -> ReexportKind {
52 self.kind
53 }
54
55 pub fn export(&self) -> &sim_kernel::Symbol {
57 &self.export
58 }
59
60 pub fn target(&self) -> &sim_kernel::Symbol {
62 &self.target
63 }
64}
65
66#[cfg(any(
67 feature = "codec-binary",
68 all(feature = "codec-lisp", not(feature = "shape"))
69))]
70pub(crate) struct ReexportLib {
71 manifest: sim_kernel::LibManifest,
72 exports: Vec<ReexportSpec>,
73}
74
75#[cfg(any(
76 feature = "codec-binary",
77 all(feature = "codec-lisp", not(feature = "shape"))
78))]
79impl ReexportLib {
80 pub(crate) fn new(manifest: sim_kernel::LibManifest, exports: Vec<ReexportSpec>) -> Self {
81 Self { manifest, exports }
82 }
83}
84
85#[cfg(all(feature = "codec-lisp", feature = "shape"))]
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub(crate) struct SourceMacroSpec {
88 pub(crate) symbol: sim_kernel::Symbol,
89 pub(crate) fixed_params: Vec<sim_kernel::Symbol>,
90 pub(crate) rest_param: Option<sim_kernel::Symbol>,
91 pub(crate) template: sim_kernel::Expr,
92}
93
94#[cfg(all(feature = "codec-lisp", feature = "shape"))]
96#[derive(Clone)]
97pub struct SourceTemplateMacro {
98 symbol: Symbol,
99 syntax_shape: Arc<dyn Shape>,
100 template: Expr,
101 parser_trusted: bool,
102}
103
104#[cfg(all(feature = "codec-lisp", feature = "shape"))]
105impl SourceTemplateMacro {
106 pub fn new(symbol: Symbol, syntax_shape: Arc<dyn Shape>, template: Expr) -> Self {
109 Self {
110 symbol,
111 syntax_shape,
112 template,
113 parser_trusted: false,
114 }
115 }
116
117 pub fn symbol(&self) -> Symbol {
119 self.symbol.clone()
120 }
121
122 pub fn syntax_shape(&self) -> Arc<dyn Shape> {
124 self.syntax_shape.clone()
125 }
126
127 pub fn parser_trusted(&self) -> bool {
130 self.parser_trusted
131 }
132
133 pub fn expand(&self, _input: Expr, captures: sim_shape::Bindings) -> Result<Expr> {
135 crate::lisp_source::template::instantiate_macro_template(&self.template, &captures)
136 }
137}
138
139#[cfg(all(feature = "codec-lisp", feature = "shape"))]
140impl Object for SourceTemplateMacro {
141 fn display(&self, _cx: &mut Cx) -> Result<String> {
142 Ok(format!("#<source-macro {}>", self.symbol))
143 }
144
145 fn as_any(&self) -> &dyn std::any::Any {
146 self
147 }
148}
149
150#[cfg(all(feature = "codec-lisp", feature = "shape"))]
151impl ObjectCompat for SourceTemplateMacro {
152 fn class(&self, cx: &mut Cx) -> Result<sim_kernel::ClassRef> {
153 let symbol = Symbol::qualified("core", "Macro");
154 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
155 return Ok(value.clone());
156 }
157 cx.factory()
158 .class_stub(sim_kernel::CORE_MACRO_CLASS_ID, symbol)
159 }
160
161 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
162 Ok(Expr::Symbol(self.symbol.clone()))
163 }
164
165 fn as_table(&self, cx: &mut Cx) -> Result<sim_kernel::TableRef> {
166 let shape = self.syntax_shape();
167 let doc = shape.describe(cx)?;
168 let mut entries = vec![
169 (
170 Symbol::new("symbol"),
171 cx.factory().string(self.symbol.to_string())?,
172 ),
173 (Symbol::new("syntax-shape"), cx.factory().string(doc.name)?),
174 (
175 Symbol::new("parser-trusted"),
176 cx.factory().bool(self.parser_trusted)?,
177 ),
178 ];
179 for (index, detail) in doc.details.into_iter().enumerate() {
180 entries.push((
181 Symbol::qualified("syntax-detail", index.to_string()),
182 cx.factory().string(detail)?,
183 ));
184 }
185 cx.factory().table(entries)
186 }
187}
188
189#[cfg(all(feature = "codec-lisp", feature = "shape"))]
190pub(crate) struct SourceLib {
191 manifest: sim_kernel::LibManifest,
192 exports: Vec<ReexportSpec>,
193 macros: Vec<SourceMacroSpec>,
194}
195
196#[cfg(all(feature = "codec-lisp", feature = "shape"))]
197impl SourceLib {
198 pub(crate) fn new(
199 manifest: sim_kernel::LibManifest,
200 exports: Vec<ReexportSpec>,
201 macros: Vec<SourceMacroSpec>,
202 ) -> Self {
203 Self {
204 manifest,
205 exports,
206 macros,
207 }
208 }
209}
210
211#[cfg(any(feature = "codec-binary", feature = "codec-lisp"))]
212fn link_reexport(linker: &mut sim_kernel::Linker<'_>, export: &ReexportSpec) -> Result<()> {
213 match export.kind {
214 ReexportKind::Class => {
215 let value = linker
216 .registry()
217 .class_by_symbol(&export.target)
218 .cloned()
219 .ok_or(sim_kernel::Error::UnknownClass {
220 class: export.target.clone(),
221 })?;
222 linker.class_value(export.export.clone(), value)?;
223 }
224 ReexportKind::Function => {
225 let value = linker
226 .registry()
227 .function_by_symbol(&export.target)
228 .cloned()
229 .ok_or(sim_kernel::Error::UnknownFunction {
230 function: export.target.clone(),
231 })?;
232 linker.function_value(export.export.clone(), value)?;
233 }
234 ReexportKind::Macro => {
235 let value = linker
236 .registry()
237 .macro_by_symbol(&export.target)
238 .cloned()
239 .ok_or(sim_kernel::Error::UnknownSymbol {
240 symbol: export.target.clone(),
241 })?;
242 linker.macro_value(export.export.clone(), value)?;
243 }
244 ReexportKind::Shape => {
245 let value = linker
246 .registry()
247 .shape_by_symbol(&export.target)
248 .cloned()
249 .ok_or(sim_kernel::Error::UnknownSymbol {
250 symbol: export.target.clone(),
251 })?;
252 linker.shape_value(export.export.clone(), value)?;
253 }
254 ReexportKind::Codec => {
255 let value = linker
256 .registry()
257 .codec_by_symbol(&export.target)
258 .cloned()
259 .ok_or(sim_kernel::Error::UnknownSymbol {
260 symbol: export.target.clone(),
261 })?;
262 linker.codec_value(export.export.clone(), value)?;
263 }
264 ReexportKind::NumberDomain => {
265 let value = linker
266 .registry()
267 .number_domain_by_symbol(&export.target)
268 .cloned()
269 .ok_or(sim_kernel::Error::UnknownSymbol {
270 symbol: export.target.clone(),
271 })?;
272 linker.number_domain_value(export.export.clone(), value)?;
273 }
274 ReexportKind::Value => {
275 let value = linker
276 .registry()
277 .value_by_symbol(&export.target)
278 .cloned()
279 .ok_or(sim_kernel::Error::UnknownSymbol {
280 symbol: export.target.clone(),
281 })?;
282 linker.value(export.export.clone(), value)?;
283 }
284 }
285 Ok(())
286}
287
288#[cfg(all(feature = "codec-lisp", feature = "shape"))]
289impl Lib for SourceLib {
290 fn manifest(&self) -> sim_kernel::LibManifest {
291 self.manifest.clone()
292 }
293
294 fn load(
295 &self,
296 _cx: &mut sim_kernel::LoadCx,
297 linker: &mut sim_kernel::Linker<'_>,
298 ) -> Result<()> {
299 for export in &self.exports {
300 if matches!(export.kind, ReexportKind::Macro)
301 && export.export == export.target
302 && self.macros.iter().any(|mac| mac.symbol == export.export)
303 {
304 continue;
305 }
306 link_reexport(linker, export)?;
307 }
308
309 for mac in &self.macros {
310 let syntax_shape = positional_macro_shape(
311 mac.symbol.clone(),
312 &mac.fixed_params,
313 mac.rest_param.as_ref(),
314 );
315 let value = macro_value(Arc::new(SourceTemplateMacro::new(
316 mac.symbol.clone(),
317 syntax_shape,
318 mac.template.clone(),
319 )));
320 linker.macro_value(mac.symbol.clone(), value)?;
321 }
322
323 Ok(())
324 }
325}
326
327#[cfg(all(feature = "codec-lisp", feature = "shape"))]
328fn macro_value(mac: Arc<SourceTemplateMacro>) -> Value {
329 sim_kernel::DefaultFactory
330 .opaque(mac)
331 .expect("source macro object should always be boxable")
332}
333
334#[cfg(all(feature = "codec-lisp", feature = "shape"))]
335fn positional_macro_shape(head: Symbol, fixed: &[Symbol], rest: Option<&Symbol>) -> Arc<dyn Shape> {
336 let fixed_tail = fixed
337 .iter()
338 .cloned()
339 .map(|name| Arc::new(CaptureShape::new(name, Arc::new(AnyShape))) as Arc<dyn Shape>)
340 .collect::<Vec<_>>();
341 let items = std::iter::once(literal_head_shape(head))
342 .chain(fixed_tail)
343 .collect::<Vec<_>>();
344 match rest {
345 Some(rest) => Arc::new(ListShape::with_rest(
346 items,
347 Arc::new(CaptureShape::new(rest.clone(), Arc::new(AnyShape))),
348 )),
349 None => Arc::new(ListShape::new(items)),
350 }
351}
352
353#[cfg(all(feature = "codec-lisp", feature = "shape"))]
354fn literal_head_shape(symbol: Symbol) -> Arc<dyn Shape> {
355 Arc::new(sim_shape::ExactExprShape::new(Expr::Symbol(symbol)))
356}
357
358#[cfg(any(
359 feature = "codec-binary",
360 all(feature = "codec-lisp", not(feature = "shape"))
361))]
362impl Lib for ReexportLib {
363 fn manifest(&self) -> sim_kernel::LibManifest {
364 self.manifest.clone()
365 }
366
367 fn load(
368 &self,
369 _cx: &mut sim_kernel::LoadCx,
370 linker: &mut sim_kernel::Linker<'_>,
371 ) -> Result<()> {
372 for export in &self.exports {
373 link_reexport(linker, export)?;
374 }
375 Ok(())
376 }
377}