diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-10 20:01:18 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-07-10 23:10:17 +0200 |
| commit | 6a4823461f491aef63451f097ddfe5602e0b2157 (patch) | |
| tree | ad11b0ad169d030942d950573c729d50f7b3291b /src/eval/dict.rs | |
| parent | 36b3067c19c8743032a44f888ee48702b88d135b (diff) | |
Reference-count complex values
Rename some nodes types
Diffstat (limited to 'src/eval/dict.rs')
| -rw-r--r-- | src/eval/dict.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/eval/dict.rs b/src/eval/dict.rs new file mode 100644 index 00000000..bf99ea17 --- /dev/null +++ b/src/eval/dict.rs @@ -0,0 +1,129 @@ +use std::collections::BTreeMap; +use std::fmt::{self, Debug, Formatter}; +use std::iter::FromIterator; +use std::ops::{Add, AddAssign}; +use std::rc::Rc; + +use super::Value; +use crate::eco::EcoString; + +/// Create a new [`Dict`] from key-value pairs. +#[macro_export] +macro_rules! dict { + ($($key:expr => $value:expr),* $(,)?) => {{ + #[allow(unused_mut)] + let mut map = std::collections::BTreeMap::new(); + $(map.insert($crate::eco::EcoString::from($key), $crate::eval::Value::from($value));)* + $crate::eval::Dict::from_map(map) + }}; +} + +/// A variably-typed dictionary with clone-on-write value semantics. +#[derive(Clone, PartialEq)] +pub struct Dict { + map: Rc<BTreeMap<EcoString, Value>>, +} + +impl Dict { + /// Create a new, empty dictionary. + pub fn new() -> Self { + Self::default() + } + + /// Create a new dictionary from a mapping of strings to values. + pub fn from_map(map: BTreeMap<EcoString, Value>) -> Self { + Self { map: Rc::new(map) } + } + + /// Whether the dictionary is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// The number of pairs in the dictionary. + pub fn len(&self) -> usize { + self.map.len() + } + + /// Borrow the value the given `key` maps to. + pub fn get(&self, key: &str) -> Option<&Value> { + self.map.get(key) + } + + /// Mutably borrow the value the given `key` maps to. + pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> { + Rc::make_mut(&mut self.map).get_mut(key) + } + + /// Insert a mapping from the given `key` to the given `value`. + pub fn insert(&mut self, key: EcoString, value: Value) { + Rc::make_mut(&mut self.map).insert(key, value); + } + + /// Extend the dictionary with the values from another dictionary. + pub fn extend(&mut self, other: &Dict) { + Rc::make_mut(&mut self.map).extend(other.into_iter()) + } + + /// Clear the dictionary. + pub fn clear(&mut self) { + if Rc::strong_count(&mut self.map) == 1 { + Rc::make_mut(&mut self.map).clear(); + } else { + *self = Self::new(); + } + } + + /// Iterate over pairs of the contained keys and values. + pub fn into_iter(&self) -> impl Iterator<Item = (EcoString, Value)> + Clone + '_ { + // TODO: Actually consume the map if the ref-count is 1? + self.iter().map(|(k, v)| (k.clone(), v.clone())) + } + + /// Iterate over pairs of references to the contained keys and values. + pub fn iter(&self) -> std::collections::btree_map::Iter<EcoString, Value> { + self.map.iter() + } +} + +impl Default for Dict { + fn default() -> Self { + Self::new() + } +} + +impl Debug for Dict { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_map().entries(self.map.iter()).finish() + } +} + +impl FromIterator<(EcoString, Value)> for Dict { + fn from_iter<T: IntoIterator<Item = (EcoString, Value)>>(iter: T) -> Self { + Dict { map: Rc::new(iter.into_iter().collect()) } + } +} + +impl<'a> IntoIterator for &'a Dict { + type Item = (&'a EcoString, &'a Value); + type IntoIter = std::collections::btree_map::Iter<'a, EcoString, Value>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl Add<&Dict> for Dict { + type Output = Self; + + fn add(mut self, rhs: &Dict) -> Self::Output { + self.extend(rhs); + self + } +} + +impl AddAssign<&Dict> for Dict { + fn add_assign(&mut self, rhs: &Dict) { + self.extend(rhs); + } +} |
