phoenix_lang/
resolver.rs

1use serde::{Deserialize, Serialize};
2
3use crate::chunk::FunctionType;
4
5/// Manages the declaration and definition of local variables
6///
7/// One ResolverNode is created and pushed onto the stack for each function definition. It then gets popped off once its definition ends, so the value on top of each ResolverNode
8/// on the stack will always be its parent
9///
10/// Normal resolution is handled by the current ResolverNode.
11/// Resolution that involves closures (upvalues) is done by the Resolver (since it will potentially involve many ResolverNodes)
12#[derive(Debug, Clone)]
13pub struct Resolver {
14    stack: Vec<ResolverNode>,
15}
16
17impl Default for Resolver {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23/// Used by Resolver to generate simple functions that just call the same function on the current ResolverNode
24macro_rules! delegate_to_latest {
25    ($fn_name: ident, $ret: ty) => {
26        /// Calls this function on the current ResolverNode
27        pub fn $fn_name(&mut self) -> $ret {
28            self.current_node().$fn_name()
29        }
30    };
31    ($fn_name: ident, $ret: ty, $param: ty) => {
32        /// Calls this function on the current ResolverNode
33        pub fn $fn_name(&mut self, x: $param) -> $ret {
34            self.current_node().$fn_name(x)
35        }
36    };
37}
38
39impl Resolver {
40    fn current_node(&mut self) -> &mut ResolverNode {
41        self.stack.last_mut().unwrap()
42    }
43
44    delegate_to_latest!(begin_scope, ());
45    delegate_to_latest!(end_scope, usize);
46    delegate_to_latest!(is_global, bool);
47    delegate_to_latest!(mark_initialized, ());
48    delegate_to_latest!(declare_variable, bool, String);
49    delegate_to_latest!(resolve_local, Option<Option<usize>>, &str);
50
51    /// Calls [Resolver::recursive_resolve] to handle the flattening of upvalues
52    ///
53    /// Returns the index of the UpValue in the upvalues Vec
54    pub fn resolve_upvalue(&mut self, name: &str) -> Option<usize> {
55        let n = self.stack.len();
56        if n >= 3 {
57            // Depth must be 3, global scope -> function scope -> internal scope (the first place we can have upvalues)
58            self.recursive_resolve(name, n - 1)
59        } else {
60            None
61        }
62    }
63
64    /// Recursively checks parents for the given name, using child_index to index into the ResolverNode stack
65    ///
66    /// Attempt to resolve the variable name by looking at the locals of the parent (which sits at child_index - 1)
67    /// If found, add an upvalue to self.upvalues and return the index in the UpValues vec
68    fn recursive_resolve(&mut self, name: &str, child_index: usize) -> Option<usize> {
69        if child_index == 0 {
70            return None;
71        } // Base case: Everyone failed to resolve the upvalue
72
73        let parent = self.stack.get(child_index - 1)?;
74        let mut upval_index = None;
75        for (i, local) in parent.locals.iter().enumerate() {
76            if local.name.eq(name) {
77                upval_index = Some(i);
78                break;
79            }
80        }
81
82        if let Some(index) = upval_index {
83            let child = self.stack.get_mut(child_index)?;
84            Some(child.add_upvalue(index, true))
85        } else if let Some(index) = self.recursive_resolve(name, child_index - 1) {
86            let child = self.stack.get_mut(child_index)?;
87            return Some(child.add_upvalue(index, false));
88        } else {
89            None
90        }
91    }
92
93    /// Push a new ResolverNode for the new function scope
94    pub fn push(&mut self, fn_type: FunctionType) {
95        let mut locals = Vec::new();
96        let first_local = match fn_type {
97            FunctionType::Method | FunctionType::Initializer => Local {
98                name: String::from("this"),
99                depth: Some(1),
100            }, // Fill the first slot with a magically initialized "this" which will contain the PhoenixPointer to itself
101            _ => Local {
102                name: String::from(""),
103                depth: None,
104            }, // Fill the first slot with a blank to be filled with the closure
105        };
106        locals.push(first_local);
107
108        let new = ResolverNode {
109            upvalues: Vec::new(),
110            locals,
111            scope_depth: self.stack.last().unwrap().scope_depth, // Child is responsible for calling begin and end scope
112        };
113
114        self.stack.push(new);
115    }
116
117    /// Remove the latest ResolverNode and return the UpValues resolved in that scope
118    pub fn pop(&mut self) -> Vec<UpValue> {
119        let latest = self.stack.pop().unwrap(); // Fixme: make this not panic?
120        latest.upvalues
121    }
122
123    pub fn new() -> Resolver {
124        let locals = vec![Local {
125            // Placeholder local variable for VM use -> Will be filled by the corresponding PhoenixFunction for the CallFrame
126            name: String::from(""),
127            depth: None,
128        }];
129
130        let top = ResolverNode {
131            upvalues: Vec::new(),
132            locals,
133            scope_depth: 0,
134        };
135
136        let stack = vec![top];
137
138        Resolver { stack }
139    }
140}
141
142#[derive(Debug, Clone)]
143struct ResolverNode {
144    upvalues: Vec<UpValue>,
145    locals: Vec<Local>,
146    scope_depth: usize,
147}
148
149impl ResolverNode {
150    pub fn begin_scope(&mut self) {
151        self.scope_depth += 1;
152    }
153
154    /// MUST BE CALLED BY Compiler::end_scope()
155    ///
156    /// Decrements the scope depth and pops off the values that went out of scope
157    /// Todo:
158    /// *  Make this less uggo
159    /// *  Use a trait or something to limit the visibility somehow?
160    pub fn end_scope(&mut self) -> usize {
161        self.scope_depth -= 1;
162        let mut pops = 0;
163        for local in self.locals.iter().rev() {
164            if let Some(x) = local.depth {
165                if x > self.scope_depth {
166                    pops += 1;
167                } else {
168                    break;
169                }
170            }
171        }
172        for _ in 0..pops {
173            self.locals.pop();
174        }
175        pops
176    }
177
178    pub fn is_global(&self) -> bool {
179        self.scope_depth == 0
180    }
181
182    fn add_local(&mut self, name: String) {
183        let local = Local { name, depth: None };
184        self.locals.push(local);
185    }
186
187    /// Marks the last local variable as initialized by giving it a depth
188    /// if the current scope is not global
189    pub fn mark_initialized(&mut self) {
190        if self.scope_depth == 0 {
191            return;
192        }
193        self.locals.last_mut().unwrap().depth = Some(self.scope_depth);
194    }
195
196    /// MUST BE CALLED BY Compiler::declare_variable()
197    /// Declare new local variables by pushing them onto self.locals
198    ///
199    /// New locals are set to a special "uninitialized" state until define_variable() is called
200    ///
201    /// If the scope is global, do nothing
202    pub fn declare_variable(&mut self, str_val: String) -> bool {
203        if !self.is_global() {
204            // Must not be in the global scope in order to define local vars
205            let mut found_eq = false; // Is this the idiomatic way of doing this?? I have no idea
206
207            for local in self.locals.iter() {
208                if let Some(x) = local.depth {
209                    if x < self.scope_depth {
210                        break;
211                    }
212                }
213                if str_val.eq(&local.name) {
214                    found_eq = true;
215                    break;
216                }
217            }
218
219            self.add_local(str_val);
220            !found_eq
221        } else {
222            true
223        }
224    }
225
226    /// Walk and look for a local variable with the same name, None if the var is not found (treat as global)
227    ///
228    /// *  Err(_) => Syntax error detected, throw an error
229    /// *  Ok(None) => Resolution failed
230    /// *  Ok(Some(index)) => found
231    ///
232    /// Fixme: Should probably make this a Option<Option<usize>>
233    pub fn resolve_local(&self, name: &str) -> Option<Option<usize>> {
234        let mut error = false;
235        for (i, local) in self.locals.iter().enumerate() {
236            if local.name.eq(name) {
237                if local.depth.is_none() {
238                    error = true;
239                    break;
240                } else {
241                    return Some(Some(i));
242                }
243            }
244        }
245
246        if error {
247            None
248        } else {
249            Some(None)
250        }
251    }
252
253    fn add_upvalue(&mut self, index: usize, is_local: bool) -> usize {
254        for (i, existing_upvalue) in self.upvalues.iter().enumerate() {
255            if existing_upvalue.index == index {
256                return i;
257            }
258        }
259
260        self.upvalues.push(UpValue { is_local, index });
261        self.upvalues.len() - 1
262    }
263}
264
265#[derive(Debug, Clone)]
266pub struct Local {
267    name: String,
268    depth: Option<usize>,
269}
270
271/// Similar to local, but for upvalues
272/// An upvalue refers to a local variable in an enclosing function
273#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
274pub struct UpValue {
275    pub is_local: bool,
276    pub index: usize,
277}