use std::collections::HashMap;
#[derive(Debug)]
pub struct ScopeStack {
scopes: Vec<Scope>,
}
#[derive(Debug)]
struct Scope {
locals: HashMap<String, LocalDef>,
}
#[derive(Debug)]
pub struct LocalDef {
pub line: usize,
pub column: usize,
pub referenced: bool,
pub class_type: Option<String>,
}
impl Default for ScopeStack {
fn default() -> Self {
Self::new()
}
}
impl ScopeStack {
pub fn new() -> Self {
Self {
scopes: vec![Scope {
locals: HashMap::new(),
}],
}
}
pub fn push_scope(&mut self) {
self.scopes.push(Scope {
locals: HashMap::new(),
});
}
pub fn pop_scope(&mut self) -> Vec<(String, LocalDef)> {
let scope = self.scopes.pop().unwrap_or(Scope {
locals: HashMap::new(),
});
scope
.locals
.into_iter()
.filter(|(_, def)| !def.referenced)
.collect()
}
pub fn define_local(&mut self, name: &str, line: usize, column: usize) {
if let Some(scope) = self.scopes.last_mut() {
scope.locals.insert(
name.to_string(),
LocalDef {
line,
column,
referenced: false,
class_type: None,
},
);
}
}
pub fn define_local_typed(
&mut self,
name: &str,
line: usize,
column: usize,
class_type: String,
) {
if let Some(scope) = self.scopes.last_mut() {
scope.locals.insert(
name.to_string(),
LocalDef {
line,
column,
referenced: false,
class_type: Some(class_type),
},
);
}
}
pub fn class_type_of(&self, name: &str) -> Option<&str> {
for scope in self.scopes.iter().rev() {
if let Some(def) = scope.locals.get(name) {
return def.class_type.as_deref();
}
}
None
}
pub fn resolve_and_mark(&mut self, name: &str) -> bool {
for scope in self.scopes.iter_mut().rev() {
if let Some(def) = scope.locals.get_mut(name) {
def.referenced = true;
return true;
}
}
false
}
pub fn is_defined(&self, name: &str) -> bool {
self.scopes
.iter()
.rev()
.any(|scope| scope.locals.contains_key(name))
}
}