use std::collections::BTreeMap;
use std::sync::Arc;
use crate::Literal;
#[derive(Debug)]
struct Binding {
name: Arc<str>,
value: Literal,
outer: Option<Arc<Self>>,
}
#[derive(Clone, Default)]
pub struct Env {
innermost: Option<Arc<Binding>>,
}
impl Env {
#[must_use]
pub const fn new() -> Self {
Self { innermost: None }
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&Literal> {
let mut cursor = self.innermost.as_deref();
while let Some(binding) = cursor {
if &*binding.name == name {
return Some(&binding.value);
}
cursor = binding.outer.as_deref();
}
None
}
#[must_use]
pub fn extend(&self, name: Arc<str>, value: Literal) -> Self {
Self {
innermost: Some(Arc::new(Binding {
name,
value,
outer: self.innermost.clone(),
})),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.iter().count()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.innermost.is_none()
}
pub fn iter(&self) -> impl Iterator<Item = (&Arc<str>, &Literal)> {
self.canonical().into_values()
}
fn canonical(&self) -> BTreeMap<&str, (&Arc<str>, &Literal)> {
let mut visible: BTreeMap<&str, (&Arc<str>, &Literal)> = BTreeMap::new();
let mut cursor = self.innermost.as_deref();
while let Some(binding) = cursor {
visible
.entry(&binding.name)
.or_insert((&binding.name, &binding.value));
cursor = binding.outer.as_deref();
}
visible
}
}
impl std::fmt::Debug for Env {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl PartialEq for Env {
fn eq(&self, other: &Self) -> bool {
self.iter().eq(other.iter())
}
}
impl Eq for Env {}
impl std::hash::Hash for Env {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
for (name, value) in self.iter() {
name.hash(state);
value.hash(state);
}
}
}
impl serde::Serialize for Env {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq as _;
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for (name, value) in self.iter() {
seq.serialize_element(&(name, value))?;
}
seq.end()
}
}
impl<'de> serde::Deserialize<'de> for Env {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let pairs: Vec<(Arc<str>, Literal)> = serde::Deserialize::deserialize(deserializer)?;
Ok(pairs.into_iter().collect())
}
}
impl FromIterator<(Arc<str>, Literal)> for Env {
fn from_iter<T: IntoIterator<Item = (Arc<str>, Literal)>>(iter: T) -> Self {
iter.into_iter()
.fold(Self::new(), |env, (name, value)| env.extend(name, value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extend_shadows() {
let env = Env::new().extend(Arc::from("x"), Literal::Int(1));
let env2 = env.extend(Arc::from("x"), Literal::Int(2));
assert_eq!(env.get("x"), Some(&Literal::Int(1)));
assert_eq!(env2.get("x"), Some(&Literal::Int(2)));
}
#[test]
fn missing_variable() {
let env = Env::new();
assert_eq!(env.get("x"), None);
}
#[test]
fn a_shadowed_name_is_counted_and_yielded_once() {
let env = Env::new()
.extend(Arc::from("x"), Literal::Int(1))
.extend(Arc::from("y"), Literal::Int(2))
.extend(Arc::from("x"), Literal::Int(3));
assert_eq!(env.len(), 2);
let mut seen: Vec<(&str, &Literal)> =
env.iter().map(|(name, value)| (&**name, value)).collect();
seen.sort_by_key(|(name, _)| *name);
assert_eq!(seen, vec![("x", &Literal::Int(3)), ("y", &Literal::Int(2))]);
}
#[test]
fn the_order_bindings_arrived_in_does_not_change_the_environment() {
let forward: Env = [
(Arc::from("a"), Literal::Int(1)),
(Arc::from("b"), Literal::Int(2)),
]
.into_iter()
.collect();
let backward: Env = [
(Arc::from("b"), Literal::Int(2)),
(Arc::from("a"), Literal::Int(1)),
]
.into_iter()
.collect();
assert_eq!(forward, backward);
}
#[test]
fn collecting_lets_the_last_binding_win() {
let env: Env = [
(Arc::from("x"), Literal::Int(1)),
(Arc::from("x"), Literal::Int(2)),
]
.into_iter()
.collect();
assert_eq!(env.get("x"), Some(&Literal::Int(2)));
assert_eq!(env.len(), 1);
}
#[test]
fn an_environment_round_trips_through_serde() {
let env: Env = [
(Arc::from("a"), Literal::Int(1)),
(Arc::from("b"), Literal::Str("two".into())),
]
.into_iter()
.collect();
let json = serde_json::to_string(&env).unwrap_or_else(|e| panic!("serialize: {e}"));
let back: Env = serde_json::from_str(&json).unwrap_or_else(|e| panic!("deserialize: {e}"));
assert_eq!(env, back);
}
}