Skip to main content

sim/loaders/
source.rs

1mod compile;
2
3#[cfg(all(feature = "codec-lisp", feature = "codec-binary"))]
4use sim_kernel::Symbol;
5use sim_kernel::{Cx, Lib, LibLoader, LibSource, Result};
6
7#[cfg(not(feature = "shape"))]
8use super::reexport::ReexportLib;
9#[cfg(feature = "shape")]
10use super::reexport::SourceLib;
11
12/// Loader that compiles `.lisp` source files into libs using a Lisp codec.
13#[cfg(feature = "codec-lisp")]
14pub struct LispSourceLoader {
15    codec: sim_kernel::Symbol,
16}
17
18#[cfg(feature = "codec-lisp")]
19impl Default for LispSourceLoader {
20    fn default() -> Self {
21        Self::new(sim_kernel::Symbol::qualified("codec", "lisp"))
22    }
23}
24
25#[cfg(feature = "codec-lisp")]
26impl LispSourceLoader {
27    /// Creates a loader that decodes source with the codec named `codec`.
28    pub fn new(codec: sim_kernel::Symbol) -> Self {
29        Self { codec }
30    }
31}
32
33#[cfg(feature = "codec-lisp")]
34impl LibLoader for LispSourceLoader {
35    fn can_load(&self, source: &LibSource) -> bool {
36        sim_run_loaders::path_from_source(source).is_ok_and(|path| {
37            path.is_some_and(|path| path.extension().is_some_and(|ext| ext == "lisp"))
38        })
39    }
40
41    fn load(&self, cx: &mut Cx, source: LibSource) -> Result<Box<dyn Lib>> {
42        let Some(path) = sim_run_loaders::path_from_source(&source)? else {
43            return Err(sim_kernel::Error::HostError(
44                "lisp source loader received unsupported source".to_owned(),
45            ));
46        };
47        let text = std::fs::read_to_string(&path).map_err(|err| {
48            sim_kernel::Error::HostError(format!(
49                "failed to read lisp source {}: {err}",
50                path.display()
51            ))
52        })?;
53        let expr = sim_codec::decode_with_codec(
54            cx,
55            &self.codec,
56            sim_codec::Input::Text(text),
57            sim_kernel::ReadPolicy::default(),
58        )?;
59        compile_lisp_source_lib(path, expr)
60    }
61
62    fn inspect_manifest(
63        &self,
64        cx: &mut Cx,
65        source: &LibSource,
66    ) -> Result<Option<sim_kernel::LibManifest>> {
67        let Some(path) = sim_run_loaders::path_from_source(source)? else {
68            return Ok(None);
69        };
70        let text = std::fs::read_to_string(&path).map_err(|err| {
71            sim_kernel::Error::HostError(format!(
72                "failed to read lisp source {}: {err}",
73                path.display()
74            ))
75        })?;
76        let expr = sim_codec::decode_with_codec(
77            cx,
78            &self.codec,
79            sim_codec::Input::Text(text),
80            sim_kernel::ReadPolicy::default(),
81        )?;
82        Ok(Some(
83            compile::compile_lisp_source_parts(path, expr)?.manifest,
84        ))
85    }
86}
87
88#[cfg(all(feature = "codec-lisp", feature = "codec-binary"))]
89/// Compiles Lisp source text into an in-memory binary lib pack.
90pub fn compile_lisp_source_text_to_pack(
91    cx: &mut Cx,
92    codec: &sim_kernel::Symbol,
93    source_path: impl Into<std::path::PathBuf>,
94    text: impl Into<String>,
95) -> Result<super::binary_pack::BinaryLibPack> {
96    let path = source_path.into();
97    let expr = sim_codec::decode_with_codec(
98        cx,
99        codec,
100        sim_codec::Input::Text(text.into()),
101        sim_kernel::ReadPolicy::default(),
102    )?;
103    compile_lisp_source_pack(path, expr)
104}
105
106#[cfg(all(feature = "codec-lisp", feature = "codec-binary"))]
107/// Compiles Lisp source text and encodes it as binary lib pack bytes.
108pub fn encode_lisp_source_text_to_binary_pack(
109    cx: &mut Cx,
110    codec: &Symbol,
111    source_path: impl Into<std::path::PathBuf>,
112    text: impl Into<String>,
113) -> Result<Vec<u8>> {
114    let pack = compile_lisp_source_text_to_pack(cx, codec, source_path, text)?;
115    super::binary_pack::encode_binary_lib_pack(&pack)
116}
117
118#[cfg(all(feature = "codec-lisp", feature = "codec-binary"))]
119/// Reads a Lisp source file, compiles it, and writes a binary lib pack to disk.
120pub fn export_lisp_source_file_to_binary_pack(
121    cx: &mut Cx,
122    codec: &Symbol,
123    source_path: impl AsRef<std::path::Path>,
124    output_path: impl AsRef<std::path::Path>,
125) -> Result<()> {
126    let source_path = source_path.as_ref();
127    let output_path = output_path.as_ref();
128    let text = std::fs::read_to_string(source_path).map_err(|err| {
129        sim_kernel::Error::HostError(format!(
130            "failed to read lisp source {}: {err}",
131            source_path.display()
132        ))
133    })?;
134    let bytes = encode_lisp_source_text_to_binary_pack(cx, codec, source_path.to_path_buf(), text)?;
135    std::fs::write(output_path, bytes).map_err(|err| {
136        sim_kernel::Error::HostError(format!(
137            "failed to write binary lib pack {}: {err}",
138            output_path.display()
139        ))
140    })?;
141    Ok(())
142}
143
144#[cfg(all(feature = "codec-lisp", not(feature = "codec-binary")))]
145fn compile_lisp_source_lib(
146    path: std::path::PathBuf,
147    expr: sim_kernel::Expr,
148) -> Result<Box<dyn Lib>> {
149    let compiled = compile::compile_lisp_source_parts(path, expr)?;
150    #[cfg(feature = "shape")]
151    {
152        Ok(Box::new(SourceLib::new(
153            compiled.manifest,
154            compiled.exports,
155            compiled.macros,
156        )))
157    }
158    #[cfg(not(feature = "shape"))]
159    {
160        if !compiled.macros.is_empty() {
161            return Err(sim_kernel::Error::Lib(
162                "lisp-source defmacro requires the shape feature".to_owned(),
163            ));
164        }
165        Ok(Box::new(ReexportLib::new(
166            compiled.manifest,
167            compiled.exports,
168        )))
169    }
170}
171
172#[cfg(all(feature = "codec-lisp", feature = "codec-binary"))]
173fn compile_lisp_source_lib(
174    path: std::path::PathBuf,
175    expr: sim_kernel::Expr,
176) -> Result<Box<dyn Lib>> {
177    let compiled = compile::compile_lisp_source_parts(path, expr)?;
178    #[cfg(feature = "shape")]
179    {
180        Ok(Box::new(SourceLib::new(
181            compiled.manifest,
182            compiled.exports,
183            compiled.macros,
184        )))
185    }
186    #[cfg(not(feature = "shape"))]
187    {
188        if !compiled.macros.is_empty() {
189            return Err(sim_kernel::Error::Lib(
190                "lisp-source defmacro requires the shape feature".to_owned(),
191            ));
192        }
193        Ok(Box::new(ReexportLib::new(
194            compiled.manifest,
195            compiled.exports,
196        )))
197    }
198}
199
200#[cfg(all(feature = "codec-lisp", feature = "codec-binary"))]
201/// Compiles an already-decoded Lisp source expression into a binary lib pack.
202pub fn compile_lisp_source_pack(
203    path: std::path::PathBuf,
204    expr: sim_kernel::Expr,
205) -> Result<super::binary_pack::BinaryLibPack> {
206    let compiled = compile::compile_lisp_source_parts(path, expr)?;
207    if !compiled.macros.is_empty() {
208        return Err(sim_kernel::Error::Lib(
209            "binary lib packs do not encode Lisp-authored defmacro bodies".to_owned(),
210        ));
211    }
212    Ok(super::binary_pack::BinaryLibPack {
213        manifest: compiled.manifest,
214        exports: compiled.exports,
215    })
216}