Wire functions through everything, with some unimplemented, and add a basic scoped map.

This commit is contained in:
2023-10-07 11:06:28 +02:00
parent eba5227ebc
commit 736d27953f
18 changed files with 392 additions and 97 deletions

81
src/util/scoped_map.rs Normal file
View 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()
}
}