scheme_rs/
env.rs

1use futures::future::BoxFuture;
2use std::collections::HashMap;
3
4use crate::{
5    builtin::Builtin,
6    compile::CompileError,
7    error::RuntimeError,
8    gc::{init_gc, Gc, Trace},
9    lex::{LexError, Token},
10    parse::ParseError,
11    syntax::{Identifier, Mark, ParsedSyntax},
12    value::Value,
13};
14
15#[derive(derive_more::Debug, Trace)]
16pub struct LexicalContour {
17    #[debug(skip)]
18    pub up: Env,
19    // pub mark: Mark,
20    #[debug("{:?}", vars.keys().cloned().collect::<Vec<_>>())]
21    vars: HashMap<Identifier, Gc<Value>>,
22    #[debug("{:?}", macros.keys().cloned().collect::<Vec<_>>())]
23    macros: HashMap<Identifier, Gc<Value>>,
24}
25
26impl LexicalContour {
27    pub fn is_bound<'a>(&'a self, ident: &'a Identifier) -> BoxFuture<'a, bool> {
28        Box::pin(async move {
29            self.vars.contains_key(ident)
30                || self.macros.contains_key(ident)
31                || self.up.is_bound(ident).await
32        })
33    }
34
35    fn fetch_var<'a>(&'a self, ident: &'a Identifier) -> BoxFuture<'a, Option<Gc<Value>>> {
36        Box::pin(async move {
37            if let Some(var) = self.vars.get(ident) {
38                return Some(var.clone());
39            }
40            // Macros are also variables
41            if let Some(var) = self.macros.get(ident) {
42                return Some(var.clone());
43            }
44            // Check the next lexical scope up
45            self.up.fetch_var(ident).await
46        })
47    }
48
49    fn fetch_macro<'a>(&'a self, ident: &'a Identifier) -> BoxFuture<'a, Option<MacroLookup>> {
50        Box::pin(async move {
51            // Only check the macro definitions
52            if let Some(var) = self.macros.get(ident) {
53                return Some(MacroLookup::WithoutEnv(var.clone()));
54            }
55            self.up.fetch_macro(ident).await.map(MacroLookup::WithEnv)
56        })
57    }
58
59    pub fn def_var(&mut self, ident: &Identifier, value: Gc<Value>) {
60        // If the identifier is defined as a macro, remove it.
61        if self.macros.contains_key(ident) {
62            self.macros.remove(ident);
63        }
64        self.vars.insert(ident.clone(), value);
65    }
66
67    pub fn def_macro(&mut self, ident: &Identifier, value: Gc<Value>) {
68        // If the identifier is defined as a variable, remove it
69        if self.vars.contains_key(ident) {
70            self.vars.remove(ident);
71        }
72        self.macros.insert(ident.clone(), value);
73    }
74}
75
76#[derive(derive_more::Debug, Trace)]
77pub struct ExpansionContext {
78    #[debug(skip)]
79    up: Env,
80    mark: Mark,
81    #[debug(skip)]
82    macro_env: Env,
83}
84
85impl ExpansionContext {
86    pub fn is_bound<'a>(&'a self, ident: &'a Identifier) -> BoxFuture<'a, bool> {
87        Box::pin(async move {
88            if ident.marks.contains(&self.mark) {
89                let mut stripped = ident.clone();
90                stripped.mark(self.mark);
91                self.macro_env.is_bound(&stripped).await
92            } else {
93                self.up.is_bound(ident).await
94            }
95        })
96    }
97
98    pub fn fetch_var<'a>(&'a self, ident: &'a Identifier) -> BoxFuture<'a, Option<Gc<Value>>> {
99        Box::pin(async move {
100            // If the ident contains this mark, it comes from the macro and
101            // we must fetch from the macro's environment.
102            if ident.marks.contains(&self.mark) {
103                let mut stripped = ident.clone();
104                stripped.mark(self.mark);
105                self.macro_env.fetch_var(&stripped).await
106            } else {
107                self.up.fetch_var(ident).await
108            }
109        })
110    }
111
112    pub fn fetch_macro<'a>(
113        &'a self,
114        ident: &'a Identifier,
115    ) -> BoxFuture<'a, Option<(Env, Gc<Value>)>> {
116        Box::pin(async move {
117            if ident.marks.contains(&self.mark) {
118                let mut stripped = ident.clone();
119                stripped.mark(self.mark);
120                self.macro_env.fetch_macro(&stripped).await
121            } else {
122                self.up.fetch_macro(ident).await
123            }
124        })
125    }
126}
127
128#[derive(Clone, Trace, derive_more::Debug)]
129pub enum Env {
130    /// This is the top level environment
131    Top,
132    /// This is an expansion context
133    Expansion(#[debug(skip)] Gc<ExpansionContext>),
134    /// This is a lexical contour
135    LexicalContour(#[debug(skip)] Gc<LexicalContour>),
136}
137
138impl Env {
139    pub async fn is_bound(&self, ident: &Identifier) -> bool {
140        match self {
141            Self::Top => false,
142            Self::Expansion(expansion) => expansion.read().await.is_bound(ident).await,
143            Self::LexicalContour(env) => env.read().await.is_bound(ident).await,
144        }
145    }
146
147    pub async fn fetch_var(&self, ident: &Identifier) -> Option<Gc<Value>> {
148        match self {
149            Self::Top => None,
150            Self::Expansion(expansion) => expansion.read().await.fetch_var(ident).await,
151            Self::LexicalContour(env) => env.read().await.fetch_var(ident).await,
152        }
153    }
154
155    pub async fn fetch_macro(&self, ident: &Identifier) -> Option<(Env, Gc<Value>)> {
156        match self {
157            Self::Top => None,
158            Self::Expansion(expansion) => expansion.read().await.fetch_macro(ident).await,
159            Self::LexicalContour(env) => match env.read().await.fetch_macro(ident).await {
160                Some(MacroLookup::WithEnv((env, value))) => Some((env, value)),
161                Some(MacroLookup::WithoutEnv(value)) => Some((self.clone(), value)),
162                _ => None,
163            },
164        }
165    }
166
167    pub async fn top() -> Self {
168        // We should probably find another place to init_gc, but this is honestly fine
169        init_gc();
170
171        let mut top = Self::Top.new_lexical_contour();
172        // Install the builtins:
173        for builtin in inventory::iter::<Builtin> {
174            builtin.install(&mut top);
175        }
176        let top = Self::LexicalContour(Gc::new(top));
177        // Install the stdlib:
178        let _ = top.eval(include_str!("stdlib.scm")).await.unwrap();
179        top
180    }
181
182    pub fn new_lexical_contour(&self) -> LexicalContour {
183        LexicalContour {
184            up: self.clone(),
185            // mark,
186            vars: HashMap::default(),
187            macros: HashMap::default(),
188        }
189    }
190
191    pub fn new_expansion_context(&self, mark: Mark, macro_env: Env) -> ExpansionContext {
192        ExpansionContext {
193            up: self.clone(),
194            mark,
195            macro_env,
196        }
197    }
198
199    pub fn def_var<'a>(&'a self, ident: &'a Identifier, value: Gc<Value>) -> BoxFuture<'a, ()> {
200        Box::pin(async move {
201            match self {
202                Self::Top => unreachable!(),
203                Self::Expansion(expansion) => expansion.read().await.up.def_var(ident, value).await,
204                Self::LexicalContour(contour) => contour.write().await.def_var(ident, value),
205            }
206        })
207    }
208
209    pub fn def_macro<'a>(&'a self, ident: &'a Identifier, value: Gc<Value>) -> BoxFuture<'a, ()> {
210        Box::pin(async move {
211            match self {
212                Self::Top => unreachable!(),
213                Self::Expansion(expansion) => {
214                    expansion.read().await.up.def_macro(ident, value).await
215                }
216                Self::LexicalContour(contour) => contour.write().await.def_macro(ident, value),
217            }
218        })
219    }
220
221    /// Evaluate a string, returning all of the results in a Vec
222    pub async fn eval<'e>(&self, exprs: &'e str) -> Result<Vec<Vec<Gc<Value>>>, EvalError<'e>> {
223        let tokens = Token::tokenize_str(exprs)?;
224        let sexprs = ParsedSyntax::parse(&tokens)?;
225        let mut results = Vec::new();
226        for sexpr in sexprs {
227            let result = sexpr.compile(self, &None).await?.eval(self, &None).await?;
228            results.push(result);
229        }
230        Ok(results)
231    }
232}
233
234impl From<Gc<ExpansionContext>> for Env {
235    fn from(env: Gc<ExpansionContext>) -> Self {
236        Self::Expansion(env)
237    }
238}
239
240impl From<Gc<LexicalContour>> for Env {
241    fn from(env: Gc<LexicalContour>) -> Self {
242        Self::LexicalContour(env)
243    }
244}
245
246enum MacroLookup {
247    WithEnv((Env, Gc<Value>)),
248    WithoutEnv(Gc<Value>),
249}
250
251#[derive(derive_more::From, Debug)]
252pub enum EvalError<'e> {
253    LexError(LexError<'e>),
254    ParseError(ParseError<'e>),
255    CompileError(CompileError),
256    RuntimeError(RuntimeError),
257}