135 lines
3.9 KiB
Rust
135 lines
3.9 KiB
Rust
use std::borrow::Borrow;
|
|
use std::collections::HashMap;
|
|
use std::hash::Hash;
|
|
|
|
/// A version of [`std::collections::HashMap`] with a built-in notion of scope.
|
|
#[derive(Clone)]
|
|
pub struct ScopedMap<K: Eq + Hash + PartialEq, V> {
|
|
scopes: Vec<HashMap<K, V>>,
|
|
}
|
|
|
|
impl<K: Eq + Hash + PartialEq, V> Default for ScopedMap<K, V> {
|
|
fn default() -> Self {
|
|
ScopedMap::new()
|
|
}
|
|
}
|
|
|
|
impl<K: Eq + Hash + PartialEq, V> ScopedMap<K, V> {
|
|
/// Generate a new scoped map.
|
|
///
|
|
/// In addition to generate the map structure, this method also generates
|
|
/// an initial scope for use by the caller.
|
|
pub fn new() -> ScopedMap<K, V> {
|
|
ScopedMap {
|
|
scopes: vec![HashMap::new()],
|
|
}
|
|
}
|
|
|
|
/// Get a value from the scoped map.
|
|
pub fn get<Q>(&self, k: &Q) -> Option<&V>
|
|
where
|
|
K: Borrow<Q>,
|
|
Q: Hash + Eq + ?Sized,
|
|
{
|
|
for map in self.scopes.iter().rev() {
|
|
match map.get(k) {
|
|
None => continue,
|
|
Some(v) => return Some(v),
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Returns true if the map contains the given key.
|
|
pub fn contains_key(&self, k: &K) -> bool {
|
|
self.scopes.iter().any(|x| x.contains_key(k))
|
|
}
|
|
|
|
/// Insert a value into the current binding scope.
|
|
///
|
|
/// If this variable is bound in the current scope, then its value will be
|
|
/// overridden. If it's bound in a previous scope, however, that value will
|
|
/// be shadowed, so that its value will preserved if/when the current scope
|
|
/// is popped.
|
|
pub fn insert(&mut self, k: K, v: V) {
|
|
self.scopes
|
|
.last_mut()
|
|
.expect("tried to insert into ScopedMap with no scopes")
|
|
.insert(k, v);
|
|
}
|
|
|
|
/// Create a new scope.
|
|
///
|
|
/// Modifications to this scope will shadow all previous scopes without
|
|
/// modifying them. Consider the following examples:
|
|
///
|
|
/// ```
|
|
/// use ngr::util::scoped_map::ScopedMap;
|
|
///
|
|
/// let mut example1 = ScopedMap::new();
|
|
/// example1.insert(1, true);
|
|
/// example1.insert(1, false);
|
|
/// assert_eq!(Some(&false), example1.get(&1));
|
|
/// let mut example2 = ScopedMap::new();
|
|
/// example2.insert(1, true);
|
|
/// example2.new_scope();
|
|
/// example2.insert(1, false);
|
|
/// assert_eq!(Some(&false), example2.get(&1));
|
|
/// example2.release_scope().expect("scope releases");
|
|
/// assert_eq!(Some(&true), example2.get(&1));
|
|
/// ```
|
|
pub fn new_scope(&mut self) {
|
|
self.scopes.push(HashMap::new());
|
|
}
|
|
|
|
/// Pop the current scope, returning to whatever was bound in the previous
|
|
/// scope. If there is no prior scope, `None` will be returned.
|
|
pub fn release_scope(&mut self) -> Option<HashMap<K, V>> {
|
|
self.scopes.pop()
|
|
}
|
|
|
|
/// Create a new scoped set by mapping over the values of this one.
|
|
pub fn map_values<F, W>(self, f: F) -> ScopedMap<K, W>
|
|
where
|
|
F: Fn(V) -> W,
|
|
{
|
|
let mut scopes = Vec::with_capacity(self.scopes.len());
|
|
|
|
for scope in self.scopes {
|
|
let mut map = HashMap::with_capacity(scope.len());
|
|
|
|
for (k, v) in scope {
|
|
map.insert(k, f(v));
|
|
}
|
|
|
|
scopes.push(map);
|
|
}
|
|
|
|
ScopedMap { scopes }
|
|
}
|
|
|
|
/// Returns true if this map is completely empty, at every level of
|
|
/// scope.
|
|
pub fn is_empty(&self) -> bool {
|
|
self.scopes.iter().all(|x| x.is_empty())
|
|
}
|
|
}
|
|
|
|
impl<K: Clone + Eq + Hash, V: Clone> ScopedMap<K, V> {
|
|
/// Returns the set of all variables bound at this time, with shadowed
|
|
/// variables hidden.
|
|
pub fn bindings(&self) -> HashMap<K, V> {
|
|
let mut result = HashMap::new();
|
|
|
|
for scope in self.scopes.iter().rev() {
|
|
for (key, value) in scope.iter() {
|
|
if !result.contains_key(key) {
|
|
result.insert(key.clone(), value.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
result
|
|
}
|
|
}
|