use crate::vm::error::{CompileError, CompileErrorKind};
use crate::vm::program::MAX_LOCALS;
use std::collections::HashMap;
struct Scope {
names: HashMap<String, u16>,
saved_next: u16,
}
#[derive(Default)]
pub struct ScopeStack {
scopes: Vec<Scope>,
next_slot: u16,
high_water: u16,
}
impl ScopeStack {
pub fn new() -> ScopeStack {
ScopeStack::default()
}
pub fn push_scope(&mut self) {
self.scopes.push(Scope {
names: HashMap::new(),
saved_next: self.next_slot,
});
}
pub fn pop_scope(&mut self) {
let scope = self.scopes.pop().expect("balanced scope stack");
self.next_slot = scope.saved_next;
}
pub fn declare(&mut self, name: &str) -> Result<u16, CompileError> {
let scope = self.scopes.last_mut().expect("function scope is open");
let slot = self.next_slot;
if usize::from(slot) >= MAX_LOCALS {
return Err(CompileError::new(
CompileErrorKind::Limit,
format!("local slots exceed limit of {MAX_LOCALS}"),
None,
));
}
self.next_slot += 1;
self.high_water = self.high_water.max(self.next_slot);
scope.names.insert(name.to_string(), slot);
Ok(slot)
}
pub fn declare_hidden(&mut self) -> Result<u16, CompileError> {
let slot = self.next_slot;
if usize::from(slot) >= MAX_LOCALS {
return Err(CompileError::new(
CompileErrorKind::Limit,
format!("local slots exceed limit of {MAX_LOCALS}"),
None,
));
}
self.next_slot += 1;
self.high_water = self.high_water.max(self.next_slot);
Ok(slot)
}
pub fn resolve(&self, name: &str) -> Option<u16> {
self.scopes
.iter()
.rev()
.find_map(|scope| scope.names.get(name).copied())
}
pub fn high_water(&self) -> u16 {
self.high_water
}
}