Wire functions through everything, with some unimplemented, and add a basic scoped map.
This commit is contained in:
81
src/util/scoped_map.rs
Normal file
81
src/util/scoped_map.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use std::{borrow::Borrow, collections::HashMap, hash::Hash};
|
||||
|
||||
/// A version of [`std::collections::HashMap`] with a built-in notion of scope.
|
||||
pub struct ScopedMap<K: Eq + Hash + PartialEq, V> {
|
||||
scopes: Vec<HashMap<K, V>>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user