use alloc::vec::Vec;
pub trait StackMap<'a, V>
where
V: Default + Copy,
{
#[must_use]
fn lookup(
&self,
key: &'a str,
) -> Option<V>;
#[must_use]
fn export_key_values<const K: usize>(
&self,
keys: &'a [&str; K],
) -> [V; K] {
let mut values: [V; K] = [Default::default(); K];
for i in 0..K {
let key = keys[i];
values[i] = match self.lookup(key) {
Some(v) => v,
None => panic!("No value for key \"{key}\""),
};
}
values
}
}
pub type StackEnvironment<'a> = &'a [(&'a str, usize)];
impl<'a> StackMap<'a, usize> for StackEnvironment<'a> {
#[inline]
fn lookup(
&self,
key: &'a str,
) -> Option<usize> {
for &(k, v) in self.iter() {
if k == key {
return Some(v);
}
}
None
}
}
pub trait MutableStackMap<'a, V>: StackMap<'a, V>
where
V: Default + Copy,
{
fn bind(
&mut self,
key: &'a str,
value: V,
);
}
pub struct MutableStackEnvironment<'a> {
pub backing: StackEnvironment<'a>,
pub updates: Vec<(&'a str, usize)>,
}
impl<'a> MutableStackEnvironment<'a> {
#[inline]
#[must_use]
fn updates_first_lookup(
&self,
key: &'a str,
) -> Option<usize> {
self.updates
.as_slice()
.lookup(key)
.or_else(|| self.backing.lookup(key))
}
}
impl<'a> StackMap<'a, usize> for MutableStackEnvironment<'a> {
#[inline]
fn lookup(
&self,
key: &str,
) -> Option<usize> {
self.backing
.lookup(key)
.or_else(|| self.updates.as_slice().lookup(key))
}
#[inline]
fn export_key_values<const K: usize>(
&self,
keys: &'a [&str; K],
) -> [usize; K] {
let mut values: [usize; K] = [Default::default(); K];
for i in 0..K {
let key = keys[i];
values[i] = match self.updates_first_lookup(key) {
Some(v) => v,
None => panic!("No value for key \"{key}\""),
};
}
values
}
}
impl<'a> MutableStackMap<'a, usize> for MutableStackEnvironment<'a> {
#[inline]
fn bind(
&mut self,
key: &'a str,
value: usize,
) {
#[cfg(debug_assertions)]
assert!(self.lookup(key).is_none(), "double-bind: {key}");
self.updates.push((key, value))
}
}
impl<'a> MutableStackEnvironment<'a> {
#[inline(always)]
pub fn new(bindings: StackEnvironment<'a>) -> Self {
MutableStackEnvironment {
backing: bindings,
updates: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stack_bindings() {
{
let env: StackEnvironment = &[("a", 1), ("b", 2)];
assert_eq!(env.lookup("a"), Some(1));
assert_eq!(env.lookup("b"), Some(2));
assert_eq!(env.lookup("c"), None);
}
{
static ENV: StackEnvironment = &[("a", 1), ("b", 2)];
assert_eq!(ENV.lookup("a"), Some(1));
assert_eq!(ENV.lookup("b"), Some(2));
assert_eq!(ENV.lookup("c"), None);
let keys = ["b", "a"];
assert_eq!(ENV.export_key_values(&keys), [2, 1]);
}
}
#[should_panic(expected = "No value for key \"d\"")]
#[test]
fn test_stack_env_export_key_values_panic() {
let env: StackEnvironment = &[("a", 1), ("b", 2)];
let keys = ["a", "b", "d"]; let _ = env.export_key_values(&keys); }
#[test]
fn test_mutable_stack_bindings() {
let mut env = MutableStackEnvironment::new(&[("a", 1), ("b", 2)]);
assert_eq!(env.lookup("a"), Some(1));
assert_eq!(env.lookup("b"), Some(2));
assert_eq!(env.lookup("c"), None);
env.bind("c", 3);
assert_eq!(env.lookup("c"), Some(3));
let keys = ["a", "b", "c"];
let values = env.export_key_values(&keys);
assert_eq!(values, [1, 2, 3]);
}
#[should_panic(expected = "No value for key \"d\"")]
#[test]
fn test_muttable_stack_env_export_key_values_panic() {
let env = MutableStackEnvironment::new(&[("a", 1), ("b", 2)]);
let keys = ["a", "b", "d"]; let _ = env.export_key_values(&keys); }
#[should_panic(expected = "double-bind: a")]
#[test]
#[cfg(debug_assertions)]
fn test_double_bind_panic() {
let mut env = MutableStackEnvironment::new(&[("a", 1), ("b", 2)]);
env.bind("a", 3);
}
}