use serde::{Deserialize, Serialize};
use crate::chunk::FunctionType;
#[derive(Debug, Clone)]
pub struct Resolver {
stack: Vec<ResolverNode>,
}
impl Default for Resolver {
fn default() -> Self {
Self::new()
}
}
macro_rules! delegate_to_latest {
($fn_name: ident, $ret: ty) => {
pub fn $fn_name(&mut self) -> $ret {
self.current_node().$fn_name()
}
};
($fn_name: ident, $ret: ty, $param: ty) => {
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);
pub fn resolve_upvalue(&mut self, name: &str) -> Option<usize> {
let n = self.stack.len();
if n >= 3 {
self.recursive_resolve(name, n - 1)
} else {
None
}
}
fn recursive_resolve(&mut self, name: &str, child_index: usize) -> Option<usize> {
if child_index == 0 {
return None;
}
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
}
}
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),
}, _ => Local {
name: String::from(""),
depth: None,
}, };
locals.push(first_local);
let new = ResolverNode {
upvalues: Vec::new(),
locals,
scope_depth: self.stack.last().unwrap().scope_depth, };
self.stack.push(new);
}
pub fn pop(&mut self) -> Vec<UpValue> {
let latest = self.stack.pop().unwrap(); latest.upvalues
}
pub fn new() -> Resolver {
let locals = vec![Local {
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;
}
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);
}
pub fn mark_initialized(&mut self) {
if self.scope_depth == 0 {
return;
}
self.locals.last_mut().unwrap().depth = Some(self.scope_depth);
}
pub fn declare_variable(&mut self, str_val: String) -> bool {
if !self.is_global() {
let mut found_eq = false;
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
}
}
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>,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct UpValue {
pub is_local: bool,
pub index: usize,
}