use std::{
collections::BTreeMap,
sync::{Arc, Mutex, MutexGuard},
};
use sim_kernel::{Error, Result, Symbol, Value};
type DynamicFrame = BTreeMap<Symbol, Value>;
#[derive(Clone, Debug, Default)]
pub struct DynamicEnv {
frames: Arc<Mutex<Vec<DynamicFrame>>>,
}
impl DynamicEnv {
pub fn new() -> Self {
Self::default()
}
pub fn lookup(&self, name: &Symbol) -> Result<Option<Value>> {
Ok(self
.frames()?
.iter()
.rev()
.find_map(|frame| frame.get(name).cloned()))
}
pub fn with_bindings<T>(
&self,
bindings: Vec<(Symbol, Value)>,
body: impl FnOnce() -> Result<T>,
) -> Result<T> {
self.frames()?.push(bindings.into_iter().collect());
let _guard = DynamicFrameGuard {
frames: self.frames.clone(),
};
body()
}
fn frames(&self) -> Result<MutexGuard<'_, Vec<DynamicFrame>>> {
self.frames
.lock()
.map_err(|_| Error::Eval("dynamic binding frame lock is poisoned".to_owned()))
}
}
struct DynamicFrameGuard {
frames: Arc<Mutex<Vec<DynamicFrame>>>,
}
impl Drop for DynamicFrameGuard {
fn drop(&mut self) {
if let Ok(mut frames) = self.frames.lock() {
frames.pop();
}
}
}
#[derive(Clone, Debug)]
pub struct Parameter {
name: Symbol,
default: Value,
dynamic: DynamicEnv,
}
impl Parameter {
pub fn new(name: Symbol, default: Value) -> Self {
Self::with_dynamic_env(name, default, DynamicEnv::new())
}
pub fn with_dynamic_env(name: Symbol, default: Value, dynamic: DynamicEnv) -> Self {
Self {
name,
default,
dynamic,
}
}
pub fn name(&self) -> &Symbol {
&self.name
}
pub fn get(&self) -> Result<Value> {
Ok(self
.dynamic
.lookup(&self.name)?
.unwrap_or_else(|| self.default.clone()))
}
pub fn with_value<T>(&self, value: Value, body: impl FnOnce() -> Result<T>) -> Result<T> {
self.dynamic
.with_bindings(vec![(self.name.clone(), value)], body)
}
}