Skip to content

Aml: Add fold/level_fold/find_map iterators and lookup handle->name #181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aml/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ pub enum AmlError {
/// `AmlName` is the name of the entire sub-level/value.
LevelDoesNotExist(AmlName),
ValueDoesNotExist(AmlName),
HandleDoesNotExist(AmlHandle),
/// Produced when two values with the same name are added to the namespace.
NameCollision(AmlName),
TriedToRemoveRootNamespace,
Expand Down
142 changes: 142 additions & 0 deletions aml/src/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,105 @@ impl Namespace {

Ok(())
}

/// Traverse the namespace, calling `f` on each namespace level, to calculate a result of type T.
/// `f` returns a `Result<(bool, T), AmlError>`
/// Errors terminate the traversal on the path and are propagated.
/// The boolean part of the result indicates if this branch should recurse, and is
/// independent of the result.
/// It's assumed that T will be an Option or Result type, but it's left to the
/// caller to decide.
pub fn level_fold<F, T>(&mut self, init: T, mut f: F) -> Result<T, AmlError>
where
F: FnMut((bool, T), &AmlName, &NamespaceLevel) -> Result<(bool, T), AmlError>,
{
fn fold_levels<F, T>(l_init: (bool, T), scope: &AmlName, level: &NamespaceLevel, func: &mut F) -> Result<(bool, T), AmlError>
where
F: FnMut((bool, T), &AmlName, &NamespaceLevel) -> Result<(bool, T), AmlError>,
{
level.children.iter().fold(Ok(l_init), |accum, (name, child)| {
let name = AmlName::from_name_seg(*name).resolve(scope)?;

match func(accum?, &name, child)? {
(true, a) => {
fold_levels((true, a), &name, child, func)
}
(false, a) => {
Ok((false, a))
}
}
})
}

match f((true, init), &AmlName::root(), & self.root)? {
(true, accum) => {
let (_, folded) = fold_levels((true, accum), &AmlName::root(), &self.root, &mut f)?;
Ok(folded)
}
(false, accum) => {
Ok(accum)
}
}
}

/// Traverse the namespace, calling `f` on each namespace entry,
/// to calculate a cumulative result of type T.
/// `f` returns a `Result<T, AmlError>`
/// Errors terminate the traversal on the path and are propagated.
/// It's assumed that T will be an Option or Result type, but it's left to the
/// caller to decide.
pub fn fold<F, T>(&mut self, init: T, mut f: F) -> Result<T, AmlError>
where
F: FnMut(T, &AmlName, &AmlHandle) -> Result<T, AmlError>,
{
let folded = self.level_fold(init, |l_accum, scope, level| {
let (_, v_accum) = l_accum;
let folded = level.values.iter().fold(Ok(v_accum), |accum, (seg, handle)| {
f(accum?, &AmlName::from_name_seg(*seg).resolve(scope)?, handle)
})?;
Ok((!level.children.is_empty(), folded))
})?;
Ok(folded)
}

/// Traverse the namespace, calling 'f' on each namespace entry,
/// and return the first non-None result.
pub fn find_map<F, T>(&mut self, mut f: F) -> Result<Option<T>, AmlError>
where
F: FnMut(&AmlName, &AmlHandle) -> Result<Option<T>, AmlError>,
{
self.level_fold(None, |l_accum, scope, level| {
let (_, v_accum) = l_accum;
if v_accum.is_some() {
return Ok((false, v_accum));
}
let folded = level.values.iter().fold(Ok(v_accum), |accum, (seg, handle)| {
if let Ok(Some(t)) = accum {
Ok(Some(t))
} else {
f(&AmlName::from_name_seg(*seg).resolve(scope)?, handle)
}
})?;
Ok((!level.children.is_empty(), folded))
})
}

/// Find the name for a given handle.
/// The first name found is returned. This may be an alias.
pub fn lookup(&mut self, handle: &AmlHandle) -> Result<AmlName, AmlError> {
let folded = self.find_map(|name, h| {
if h == handle {
Ok(Some(name.clone()))
} else {
Ok(None)
}
})?;
if folded.is_some() {
Ok(folded.unwrap())
} else {
Err(AmlError::HandleDoesNotExist(*handle))
}
}
}

impl fmt::Debug for Namespace {
Expand Down Expand Up @@ -790,4 +889,47 @@ mod tests {
assert_eq!(last_seg, NameSeg::from_str("FOO").unwrap());
}
}

#[test]
fn test_lookup() {
let mut namespace = Namespace::new();

let _ = namespace.add_value(AmlName::from_str("\\FOO").unwrap(), AmlValue::Integer(1));
let h1 = namespace.get_handle(&AmlName::from_str("\\FOO", ).unwrap()).unwrap();
assert_eq!(namespace.lookup(&h1), AmlName::from_str("\\FOO"));
let _ = namespace.add_level(AmlName::from_str("\\L1").unwrap(), LevelType::Scope);
let _ = namespace.add_value(AmlName::from_str("\\L1.BAR").unwrap(), AmlValue::Integer(2));
let h2 = namespace.get_handle(&AmlName::from_str("\\L1.BAR").unwrap()).unwrap();
assert_eq!(namespace.lookup(&h2), AmlName::from_str("\\L1.BAR"));
assert_eq!(namespace.lookup(&AmlHandle(100)), Err(AmlError::HandleDoesNotExist(AmlHandle(100))));
}

#[test]
fn test_fold() {
fn fold_lookup(namespace: &mut Namespace, handle: &AmlHandle) -> Result<AmlName, AmlError> {
let folded = namespace.fold(None, |accum, name, h| {
if accum.is_none() && h == handle {
Ok(Some(name.clone()))
} else {
Ok(accum)
}
})?;
if folded.is_some() {
Ok(folded.unwrap())
} else {
Err(AmlError::HandleDoesNotExist(*handle))
}
}

let mut namespace = Namespace::new();

let _ = namespace.add_value(AmlName::from_str("\\FOO").unwrap(), AmlValue::Integer(1));
let h1 = namespace.get_handle(&AmlName::from_str("\\FOO", ).unwrap()).unwrap();
assert_eq!(fold_lookup(&mut namespace, &h1), AmlName::from_str("\\FOO"));
let _ = namespace.add_level(AmlName::from_str("\\L1").unwrap(), LevelType::Scope);
let _ = namespace.add_value(AmlName::from_str("\\L1.BAR").unwrap(), AmlValue::Integer(2));
let h2 = namespace.get_handle(&AmlName::from_str("\\L1.BAR").unwrap()).unwrap();
assert_eq!(fold_lookup(&mut namespace, &h2), AmlName::from_str("\\L1.BAR"));
assert_eq!(fold_lookup(&mut namespace, &AmlHandle(100)), Err(AmlError::HandleDoesNotExist(AmlHandle(100))));
}
}
2 changes: 1 addition & 1 deletion aml_tester/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* - For failing tests, print out a nice summary of the errors for each file
*/

use aml::{AmlContext, DebugVerbosity};
use aml::{AmlContext, DebugVerbosity, AmlName};
use clap::{Arg, ArgAction, ArgGroup};
use std::{
collections::HashSet,
Expand Down