mimium_lang/utils/
environment.rs

1use std::collections::LinkedList;
2
3use crate::interner::Symbol;
4
5type EnvInner<T> = LinkedList<Vec<(Symbol, T)>>;
6
7/// Environment stack used during compilation.
8///
9/// Each scope is represented by a vector of bindings and the whole stack is
10/// maintained as a `LinkedList`.  The top of the stack is the innermost scope.
11#[derive(Clone, Debug, PartialEq)]
12pub struct Environment<T>(pub EnvInner<T>);
13
14/// Result of name lookup on [`Environment`].
15#[derive(Clone, Debug, PartialEq)]
16pub enum LookupRes<T: Clone> {
17    /// Binding exists in the current scope.
18    Local(T),
19    /// Binding is found in an outer scope. `usize` indicates how many scopes
20    /// above the current one the binding resides in.
21    UpValue(usize, T),
22    /// Binding is located in the global scope.
23    Global(T),
24    /// Binding was not found.
25    None,
26}
27impl<T: Clone> Default for Environment<T> {
28    fn default() -> Self {
29        Self(EnvInner::new())
30    }
31}
32impl<T: Clone> Environment<T> {
33    /// Create a new empty environment stack.
34    pub fn new() -> Self {
35        Self(EnvInner::new())
36    }
37
38    /// Returns `true` if the current scope is the outermost global scope.
39    pub fn is_global(&self) -> bool {
40        self.0.len() <= 1
41    }
42
43    /// Push a new empty scope on top of the stack.
44    pub fn extend(&mut self) {
45        self.0.push_front(Vec::new());
46    }
47
48    /// Pop the current scope.
49    pub fn to_outer(&mut self) {
50        let _ = self.0.pop_front();
51    }
52
53    /// Add new bindings to the current scope.
54    pub fn add_bind(&mut self, binds: &[(Symbol, T)]) {
55        assert!(!self.0.is_empty());
56        self.0.front_mut().unwrap().extend_from_slice(binds);
57    }
58
59    /// Perform a lookup with information about where the value was found.
60    pub fn lookup_cls(&self, name: &Symbol) -> LookupRes<&T> {
61        match self
62            .0
63            .iter()
64            .enumerate()
65            .find(|(_level, vec)| vec.iter().any(|(n, _)| n == name))
66            .and_then(|(level, vec)| {
67                vec.iter()
68                    .rfind(|(n, _)| n == name)
69                    .map(|(_, v)| (level, v))
70            }) {
71            None => LookupRes::None,
72            Some((level, e)) if level >= self.0.len() - 1 => LookupRes::Global(e),
73            Some((0, e)) if self.0.len() <= 1 => LookupRes::Global(e),
74            Some((0, e)) => LookupRes::Local(e),
75            Some((level, e)) => LookupRes::UpValue(level, e),
76        }
77    }
78    /// Look up `name` and return the associated value if it exists.
79    ///
80    /// This helper ignores where the binding was found in the environment and
81    /// simply returns its value.
82    pub fn lookup(&self, name: &Symbol) -> Option<&T> {
83        match self.lookup_cls(name) {
84            LookupRes::None => None,
85            LookupRes::Global(e) | LookupRes::Local(e) | LookupRes::UpValue(_, e) => Some(e),
86        }
87    }
88}