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