Skip to main content

sim_lib_binding/
lexical.rs

1use std::{
2    collections::BTreeMap,
3    sync::{Arc, Mutex, MutexGuard},
4};
5
6use sim_kernel::{
7    Args, Callable, ClassRef, Cx, Error, Object, ObjectCompat, Result, Symbol, Value,
8};
9
10type BindingSlot = Arc<Mutex<Option<Value>>>;
11
12/// Computes a binding's initial value within a (possibly partial) scope.
13///
14/// Used by `let*` and `letrec` so each initializer can observe the bindings
15/// already established in the same frame.
16pub type BindingInitializer =
17    Box<dyn Fn(&mut Cx, &LexicalEnv) -> Result<Value> + Send + Sync + 'static>;
18
19type LexicalBody =
20    Arc<dyn Fn(&mut Cx, &LexicalEnv, Vec<Value>) -> Result<Value> + Send + Sync + 'static>;
21
22/// A lexical scope: a frame of name-to-value slots chained to its parent.
23///
24/// Cloning shares the same frame; [`child`](LexicalEnv::child) opens a nested
25/// scope. Slots support deferred initialization so `letrec` can predefine names
26/// before computing their values.
27#[derive(Clone, Debug)]
28pub struct LexicalEnv {
29    frame: Arc<LexicalFrame>,
30}
31
32#[derive(Debug)]
33struct LexicalFrame {
34    parent: Option<LexicalEnv>,
35    slots: Mutex<BTreeMap<Symbol, BindingSlot>>,
36}
37
38impl Default for LexicalEnv {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl LexicalEnv {
45    /// Creates a fresh root scope with no parent and no bindings.
46    pub fn new() -> Self {
47        Self {
48            frame: Arc::new(LexicalFrame {
49                parent: None,
50                slots: Mutex::new(BTreeMap::new()),
51            }),
52        }
53    }
54
55    /// Opens a nested scope whose lookups fall through to this one.
56    pub fn child(&self) -> Self {
57        Self {
58            frame: Arc::new(LexicalFrame {
59                parent: Some(self.clone()),
60                slots: Mutex::new(BTreeMap::new()),
61            }),
62        }
63    }
64
65    /// Binds `name` to `value` in this frame.
66    ///
67    /// Errors if `name` is already bound in the same frame (shadowing requires
68    /// a [`child`](LexicalEnv::child) scope).
69    pub fn define(&self, name: Symbol, value: Value) -> Result<()> {
70        self.define_slot(name, Some(value))
71    }
72
73    /// Resolves `name` through this frame and its parents.
74    ///
75    /// Errors if the name is undefined or was predefined but never initialized.
76    pub fn lookup(&self, name: &Symbol) -> Result<Value> {
77        let Some(slot) = self.lookup_slot(name)? else {
78            return Err(Error::Eval(format!(
79                "lexical binding {name} is not defined"
80            )));
81        };
82        slot.lock()
83            .map_err(|_| Error::Eval(format!("lexical binding {name} lock is poisoned")))?
84            .clone()
85            .ok_or_else(|| Error::Eval(format!("lexical binding {name} is not initialized")))
86    }
87
88    fn predefine(&self, name: Symbol) -> Result<()> {
89        self.define_slot(name, None)
90    }
91
92    fn set(&self, name: &Symbol, value: Value) -> Result<()> {
93        let Some(slot) = self.lookup_slot(name)? else {
94            return Err(Error::Eval(format!(
95                "lexical binding {name} is not defined"
96            )));
97        };
98        *slot
99            .lock()
100            .map_err(|_| Error::Eval(format!("lexical binding {name} lock is poisoned")))? =
101            Some(value);
102        Ok(())
103    }
104
105    fn define_slot(&self, name: Symbol, value: Option<Value>) -> Result<()> {
106        let mut slots = self.slots()?;
107        if slots.contains_key(&name) {
108            return Err(Error::Eval(format!(
109                "lexical binding {name} is already defined in this frame"
110            )));
111        }
112        slots.insert(name, Arc::new(Mutex::new(value)));
113        Ok(())
114    }
115
116    fn lookup_slot(&self, name: &Symbol) -> Result<Option<BindingSlot>> {
117        if let Some(slot) = self.slots()?.get(name).cloned() {
118            return Ok(Some(slot));
119        }
120        match &self.frame.parent {
121            Some(parent) => parent.lookup_slot(name),
122            None => Ok(None),
123        }
124    }
125
126    fn slots(&self) -> Result<MutexGuard<'_, BTreeMap<Symbol, BindingSlot>>> {
127        self.frame
128            .slots
129            .lock()
130            .map_err(|_| Error::Eval("lexical binding frame lock is poisoned".to_owned()))
131    }
132}
133
134/// Evaluates a `let` form: parallel bindings in a fresh child scope.
135///
136/// All values are supplied up front, so no binding can observe another; `body`
137/// then runs in the child scope.
138pub fn eval_let(
139    cx: &mut Cx,
140    outer: &LexicalEnv,
141    bindings: Vec<(Symbol, Value)>,
142    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
143) -> Result<Value> {
144    let env = outer.child();
145    for (name, value) in bindings {
146        env.define(name, value)?;
147    }
148    body(cx, &env)
149}
150
151/// Evaluates a `let*` form: sequential bindings in a fresh child scope.
152///
153/// Each [`BindingInitializer`] runs in order and sees the bindings established
154/// before it.
155pub fn eval_let_star(
156    cx: &mut Cx,
157    outer: &LexicalEnv,
158    bindings: Vec<(Symbol, BindingInitializer)>,
159    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
160) -> Result<Value> {
161    let env = outer.child();
162    for (name, initializer) in bindings {
163        let value = initializer(cx, &env)?;
164        env.define(name, value)?;
165    }
166    body(cx, &env)
167}
168
169/// Evaluates a `letrec` form: mutually recursive bindings in a child scope.
170///
171/// All names are predefined before any initializer runs, so each
172/// [`BindingInitializer`] may reference every binding in the frame (including
173/// itself and later ones), enabling mutual recursion.
174pub fn eval_letrec(
175    cx: &mut Cx,
176    outer: &LexicalEnv,
177    bindings: Vec<(Symbol, BindingInitializer)>,
178    body: impl FnOnce(&mut Cx, &LexicalEnv) -> Result<Value>,
179) -> Result<Value> {
180    let env = outer.child();
181    let names = bindings
182        .iter()
183        .map(|(name, _)| name.clone())
184        .collect::<Vec<_>>();
185    for name in &names {
186        env.predefine(name.clone())?;
187    }
188    for ((_, initializer), name) in bindings.into_iter().zip(names.iter()) {
189        let value = initializer(cx, &env)?;
190        env.set(name, value)?;
191    }
192    body(cx, &env)
193}
194
195/// A closure that captures a [`LexicalEnv`] and is callable as a runtime object.
196///
197/// The kernel defines the `Object`/`Callable` contracts; this type realizes
198/// them for a body that closes over its defining lexical scope. It is the
199/// binding organ's representation of a lexically scoped function value.
200#[derive(Clone)]
201pub struct LexicalFunction {
202    name: Symbol,
203    env: LexicalEnv,
204    body: LexicalBody,
205}
206
207impl LexicalFunction {
208    /// Creates a function closing over `env`, identified by `name`.
209    pub fn new(name: Symbol, env: LexicalEnv, body: LexicalBody) -> Self {
210        Self { name, env, body }
211    }
212
213    /// Returns the function's name.
214    pub fn name(&self) -> &Symbol {
215        &self.name
216    }
217}
218
219impl Object for LexicalFunction {
220    fn display(&self, _cx: &mut Cx) -> Result<String> {
221        Ok(format!("#<binding-function {}>", self.name))
222    }
223
224    fn as_any(&self) -> &dyn std::any::Any {
225        self
226    }
227}
228
229impl ObjectCompat for LexicalFunction {
230    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
231        cx.resolve_class(&Symbol::qualified("core", "Function"))
232    }
233
234    fn as_callable(&self) -> Option<&dyn Callable> {
235        Some(self)
236    }
237}
238
239impl Callable for LexicalFunction {
240    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
241        (self.body)(cx, &self.env, args.into_vec())
242    }
243}
244
245/// Wraps a [`LexicalFunction`] as an opaque, callable runtime [`Value`].
246///
247/// The kernel factory defines opaque-object construction; this helper packages
248/// a name, captured scope, and body into a callable value for the host eval.
249pub fn lexical_function_value(
250    cx: &mut Cx,
251    name: Symbol,
252    env: LexicalEnv,
253    body: LexicalBody,
254) -> Result<Value> {
255    cx.factory()
256        .opaque(Arc::new(LexicalFunction::new(name, env, body)))
257}