76 lines
1.7 KiB
Rust
76 lines
1.7 KiB
Rust
use std::{cell::RefCell, collections::HashMap};
|
|
|
|
use gc::{allocator::GCAllocator, gc_ref::GcRef, trace::GCTrace};
|
|
|
|
use crate::ast::statement::Statement;
|
|
|
|
use super::{
|
|
ast_walker::{Interpret, RuntimeError},
|
|
types::Primitive,
|
|
};
|
|
|
|
#[derive(Default, GCTrace)]
|
|
pub struct Environment {
|
|
variables: HashMap<String, Primitive>,
|
|
parent: Option<GcRef<RefCell<Environment>>>,
|
|
}
|
|
|
|
impl Environment {
|
|
// Update an already existing variable in current scope
|
|
pub fn update_var(&mut self, name: &str, v: Primitive) -> Option<Primitive> {
|
|
if let Some(cur) = self.variables.get_mut(name) {
|
|
Some(std::mem::replace(cur, v))
|
|
} else {
|
|
self.parent
|
|
.as_ref()
|
|
.and_then(|parent| parent.borrow_mut().update_var(name, v))
|
|
}
|
|
}
|
|
|
|
pub fn set_var(&mut self, name: String, v: Primitive) -> Option<Primitive> {
|
|
self.update_var(&name, v.clone())
|
|
.or_else(|| self.variables.insert(name, v))
|
|
}
|
|
|
|
pub fn get_var(&self, name: &str) -> Option<Primitive> {
|
|
self.variables.get(name).cloned().or_else(|| {
|
|
self.parent
|
|
.as_ref()
|
|
.and_then(|v| (**v).borrow().get_var(name))
|
|
})
|
|
}
|
|
}
|
|
|
|
pub struct World {
|
|
env: GcRef<RefCell<Environment>>,
|
|
_gc: GCAllocator,
|
|
}
|
|
|
|
impl World {
|
|
pub fn set_var(&mut self, name: String, v: Primitive) -> Option<Primitive> {
|
|
self.env.borrow_mut().set_var(name, v)
|
|
}
|
|
pub fn get_var(&self, name: &str) -> Option<Primitive> {
|
|
self.env.borrow().get_var(name)
|
|
}
|
|
}
|
|
|
|
impl World {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
pub fn exec(&mut self, nodes: Vec<Statement>) -> Result<Primitive, RuntimeError> {
|
|
nodes
|
|
.into_iter()
|
|
.try_fold(Primitive::Nil, |_, stmnt| stmnt.interpret(self))
|
|
}
|
|
}
|
|
|
|
impl Default for World {
|
|
fn default() -> Self {
|
|
let mut gc = GCAllocator::default();
|
|
let env = gc.alloc(RefCell::default());
|
|
Self { env, _gc: gc }
|
|
}
|
|
}
|