cl_interpret/
env.rs

1//! Lexical and non-lexical scoping for variables
2
3use crate::builtin::Builtin;
4
5use super::{
6    Callable, Interpret,
7    builtin::{Builtins, Math},
8    convalue::ConValue,
9    error::{Error, IResult},
10    function::Function,
11};
12use cl_ast::{Function as FnDecl, Sym};
13use std::{
14    collections::HashMap,
15    fmt::Display,
16    ops::{Deref, DerefMut},
17    rc::Rc,
18};
19
20type StackFrame = HashMap<Sym, Option<ConValue>>;
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23pub enum Place {
24    Global(Sym),
25    Local(usize),
26}
27
28impl Display for Place {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            Place::Global(name) => name.fmt(f),
32            Place::Local(id) => id.fmt(f),
33        }
34    }
35}
36
37#[derive(Clone, Debug, Default)]
38struct EnvFrame {
39    /// The length of the array when this stack frame was constructed
40    pub name: Option<&'static str>,
41    pub base: usize,
42    pub binds: HashMap<Sym, usize>,
43}
44
45/// Implements a nested lexical scope
46#[derive(Clone, Debug)]
47pub struct Environment {
48    global: HashMap<Sym, Option<ConValue>>,
49    values: Vec<Option<ConValue>>,
50    frames: Vec<EnvFrame>,
51}
52
53impl Display for Environment {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        for EnvFrame { name, base: _, binds } in self.frames.iter().rev() {
56            writeln!(
57                f,
58                "--- {} ---",
59                if let Some(name) = name { name } else { "" }
60            )?;
61            for (var, val) in binds {
62                write!(f, "{var}: ")?;
63                match self.values.get(*val) {
64                    Some(Some(value)) => writeln!(f, "\t{value}"),
65                    Some(None) => writeln!(f, "<undefined>"),
66                    None => writeln!(f, "ERROR: {var} address blows the stack!"),
67                }?
68            }
69        }
70        Ok(())
71    }
72}
73impl Default for Environment {
74    fn default() -> Self {
75        let mut this = Self::no_builtins();
76        this.add_builtins(Builtins).add_builtins(Math);
77        this
78    }
79}
80
81impl Environment {
82    pub fn new() -> Self {
83        Self::default()
84    }
85    /// Creates an [Environment] with no [builtins](super::builtin)
86    pub fn no_builtins() -> Self {
87        Self { values: Vec::new(), global: HashMap::new(), frames: vec![] }
88    }
89
90    /// Reflexively evaluates a node
91    pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
92        node.interpret(self)
93    }
94
95    /// Calls a function inside the interpreter's scope,
96    /// and returns the result
97    pub fn call(&mut self, name: Sym, args: &[ConValue]) -> IResult<ConValue> {
98        let function = self.get(name)?;
99        function.call(self, args)
100    }
101
102    /// Binds a value to the given name in the current scope.
103    pub fn bind(&mut self, name: &str, value: impl Into<ConValue>) {
104        self.insert(name.into(), Some(value.into()));
105    }
106
107    /// Gets all registered globals, bound or unbound.
108    pub fn globals(&self) -> &HashMap<Sym, Option<ConValue>> {
109        &self.global
110    }
111
112    /// Adds builtins
113    ///
114    /// # Panics
115    ///
116    /// Will panic if globals table is non-empty!
117    pub fn add_builtins(&mut self, builtins: &'static [Builtin]) -> &mut Self {
118        let Self { global, .. } = self;
119        for builtin in builtins {
120            global.insert(builtin.name(), Some(builtin.into()));
121        }
122        self
123    }
124
125    pub fn push_frame(&mut self, name: &'static str, frame: StackFrame) {
126        self.enter(name);
127        for (k, v) in frame {
128            self.insert(k, v);
129        }
130    }
131
132    pub fn pop_frame(&mut self) -> Option<(StackFrame, &'static str)> {
133        let mut out = HashMap::new();
134        let EnvFrame { name, base, binds } = self.frames.pop()?;
135        for (k, v) in binds {
136            out.insert(k, self.values.get_mut(v).and_then(std::mem::take));
137        }
138        self.values.truncate(base);
139        Some((out, name.unwrap_or("")))
140    }
141
142    /// Enters a nested scope, returning a [`Frame`] stack-guard.
143    ///
144    /// [`Frame`] implements Deref/DerefMut for [`Environment`].
145    pub fn frame(&mut self, name: &'static str) -> Frame {
146        Frame::new(self, name)
147    }
148
149    /// Resolves a variable mutably.
150    ///
151    /// Returns a mutable reference to the variable's record, if it exists.
152    pub fn get_mut(&mut self, name: Sym) -> IResult<&mut Option<ConValue>> {
153        let at = self.id_of(name)?;
154        self.get_id_mut(at).ok_or(Error::NotDefined(name))
155    }
156
157    /// Resolves a variable immutably.
158    ///
159    /// Returns a reference to the variable's contents, if it is defined and initialized.
160    pub fn get(&self, name: Sym) -> IResult<ConValue> {
161        let id = self.id_of(name)?;
162        let res = match id {
163            Place::Global(name) => self.global.get(&name),
164            Place::Local(id) => self.values.get(id),
165        };
166        match res.ok_or(Error::NotDefined(name))? {
167            Some(value) => Ok(value.clone()),
168            None => Err(Error::NotInitialized(name)),
169        }
170    }
171
172    /// Resolves the [Place] associated with a [Sym]
173    pub fn id_of(&self, name: Sym) -> IResult<Place> {
174        for EnvFrame { binds, .. } in self.frames.iter().rev() {
175            if let Some(id) = binds.get(&name).copied() {
176                return Ok(Place::Local(id));
177            }
178        }
179        Ok(Place::Global(name))
180    }
181
182    pub fn get_id(&self, at: Place) -> Option<&ConValue> {
183        let res = match at {
184            Place::Global(name) => self.global.get(&name),
185            Place::Local(id) => self.values.get(id),
186        }?;
187        res.as_ref()
188    }
189
190    pub fn get_id_mut(&mut self, at: Place) -> Option<&mut Option<ConValue>> {
191        match at {
192            Place::Global(name) => self.global.get_mut(&name),
193            Place::Local(id) => self.values.get_mut(id),
194        }
195    }
196
197    /// Inserts a new [ConValue] into this [Environment]
198    pub fn insert(&mut self, k: Sym, v: Option<ConValue>) {
199        if self.bind_raw(k, self.values.len()).is_some() {
200            self.values.push(v);
201        } else {
202            self.global.insert(k, v);
203        }
204    }
205
206    /// A convenience function for registering a [FnDecl] as a [Function]
207    pub fn insert_fn(&mut self, decl: &FnDecl) {
208        let FnDecl { name, .. } = decl;
209        let (name, function) = (*name, Rc::new(Function::new(decl)));
210        self.insert(name, Some(ConValue::Function(function.clone())));
211        // Tell the function to lift its upvars now, after it's been declared
212        function.lift_upvars(self);
213    }
214
215    /// Allocates a local variable
216    pub fn stack_alloc(&mut self, value: ConValue) -> IResult<usize> {
217        let adr = self.values.len();
218        self.values.push(Some(value));
219        Ok(adr)
220    }
221
222    pub fn bind_raw(&mut self, name: Sym, id: usize) -> Option<()> {
223        let EnvFrame { name: _, base: _, binds } = self.frames.last_mut()?;
224        binds.insert(name, id);
225        Some(())
226    }
227}
228
229/// Functions which aid in the implementation of [`Frame`]
230impl Environment {
231    /// Enters a scope, creating a new namespace for variables
232    fn enter(&mut self, name: &'static str) -> &mut Self {
233        let new_frame =
234            EnvFrame { name: Some(name), base: self.values.len(), binds: HashMap::new() };
235        self.frames.push(new_frame);
236        self
237    }
238
239    /// Exits the scope, destroying all local variables and
240    /// returning the outer scope, if there is one
241    fn exit(&mut self) -> &mut Self {
242        if let Some(frame) = self.frames.pop() {
243            self.values.truncate(frame.base);
244        }
245        self
246    }
247}
248
249/// Represents a stack frame
250#[derive(Debug)]
251pub struct Frame<'scope> {
252    scope: &'scope mut Environment,
253}
254impl<'scope> Frame<'scope> {
255    fn new(scope: &'scope mut Environment, name: &'static str) -> Self {
256        Self { scope: scope.enter(name) }
257    }
258}
259impl Deref for Frame<'_> {
260    type Target = Environment;
261    fn deref(&self) -> &Self::Target {
262        self.scope
263    }
264}
265impl DerefMut for Frame<'_> {
266    fn deref_mut(&mut self) -> &mut Self::Target {
267        self.scope
268    }
269}
270impl Drop for Frame<'_> {
271    fn drop(&mut self) {
272        self.scope.exit();
273    }
274}