Skip to main content

sim_run_loaders/
reexport.rs

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/// Kind of registry item a lib-pack or source-lib re-export links.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum ReexportKind {
14    /// Class re-export.
15    Class,
16    /// Function re-export.
17    Function,
18    /// Macro re-export.
19    Macro,
20    /// Shape re-export.
21    Shape,
22    /// Codec re-export.
23    Codec,
24    /// Number-domain re-export.
25    NumberDomain,
26    /// Runtime value re-export.
27    Value,
28}
29
30/// One re-export entry mapping an exported symbol to a target already present
31/// in the registry, tagged by the kind of item it links.
32#[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    /// Creates a re-export specification from its kind, exported symbol, and
41    /// target symbol.
42    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    /// Returns the kind of registry item this spec links.
51    pub fn kind(&self) -> ReexportKind {
52        self.kind
53    }
54
55    /// Returns the symbol exposed by the loaded lib.
56    pub fn export(&self) -> &sim_kernel::Symbol {
57        &self.export
58    }
59
60    /// Returns the registry symbol linked to the exported symbol.
61    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/// Macro object emitted by a Lisp source lib for a source-authored `defmacro`.
95#[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    /// Creates a source template macro whose parser output is treated as
107    /// untrusted.
108    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    /// Returns the macro symbol.
118    pub fn symbol(&self) -> Symbol {
119        self.symbol.clone()
120    }
121
122    /// Returns the syntax shape used to match macro calls.
123    pub fn syntax_shape(&self) -> Arc<dyn Shape> {
124        self.syntax_shape.clone()
125    }
126
127    /// Returns whether the parse output is trusted for effectful syntax
128    /// shapes.
129    pub fn parser_trusted(&self) -> bool {
130        self.parser_trusted
131    }
132
133    /// Expands a matched macro form using the captured template bindings.
134    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}