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