use crate::core::Value;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct StackFrame {
variables: HashMap<String, Value>,
#[allow(dead_code)]
name: String,
}
impl StackFrame {
pub fn new(name: impl Into<String>) -> Self {
Self {
variables: HashMap::new(),
name: name.into(),
}
}
}
#[derive(Debug)]
pub struct Environment {
frames: Vec<StackFrame>,
}
impl Environment {
pub fn new() -> Self {
Self {
frames: vec![StackFrame::new("global")],
}
}
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}
impl Environment {
pub fn push_frame(&mut self, name: impl Into<String>) {
self.frames.push(StackFrame::new(name));
}
pub fn pop_frame(&mut self) {
if self.frames.len() > 1 {
self.frames.pop();
}
}
pub fn define(&mut self, name: &str, value: Value) {
if let Some(frame) = self.frames.last_mut() {
frame.variables.insert(name.trim().to_lowercase(), value);
}
}
pub fn define_global(&mut self, name: &str, value: Value) {
if let Some(frame) = self.frames.first_mut() {
frame.variables.insert(name.trim().to_lowercase(), value);
}
}
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), String> {
let key = name.trim().to_lowercase();
for frame in self.frames.iter_mut().rev() {
if let std::collections::hash_map::Entry::Occupied(mut e) =
frame.variables.entry(key.clone())
{
e.insert(value);
return Ok(());
}
}
Err(format!("Undefined variable '{}'", name))
}
pub fn get(&self, name: &str) -> Option<&Value> {
let key = name.trim().to_lowercase();
for frame in self.frames.iter().rev() {
if let Some(val) = frame.variables.get(&key) {
return Some(val);
}
}
None
}
pub fn frames(&self) -> &[StackFrame] {
&self.frames
}
}