Browse Source

Move to new namespace system

We were outgrowing the old namespace system, and need a way to refer to
values without actually borrowing them (this is provided by the new concept
of an `AmlHandle`). For the meantime, this representation is slightly less
efficient (because we're not using handles yet, and need two maps), but in
the future will be much better because we'll be able to throw handles around
to avoid having to clone objects. We can also build a more efficient
representation than the BTreeMap to store the actual name part of the
namespace (the `name_map` field atm) so that it uses far less memory, and
may even be faster to search.
Isaac Woods 5 years ago
parent
commit
186ea65c38

+ 21 - 70
aml_parser/src/lib.rs

@@ -43,6 +43,7 @@ extern crate std;
 mod test_utils;
 
 pub(crate) mod name_object;
+pub(crate) mod namespace;
 pub(crate) mod opcode;
 pub(crate) mod parser;
 pub(crate) mod pkg_length;
@@ -51,9 +52,12 @@ pub(crate) mod type1;
 pub(crate) mod type2;
 pub mod value;
 
-pub use crate::{name_object::AmlName, value::AmlValue};
+pub use crate::{
+    namespace::{AmlHandle, AmlName, Namespace},
+    value::AmlValue,
+};
 
-use alloc::{collections::BTreeMap, string::String};
+use alloc::string::String;
 use log::error;
 use parser::Parser;
 use pkg_length::PkgLength;
@@ -82,10 +86,21 @@ pub enum AmlError {
     WrongParser,
 
     /*
-     * Errors produced querying the namespace.
+     * Errors produced manipulating AML names.
+     */
+    /// Produced when trying to normalize a path that does not point to a valid level of the
+    /// namespace. E.g. `\_SB.^^PCI0` goes above the root of the namespace.
+    InvalidNormalizedName(String),
+    RootHasNoParent,
+
+    /*
+     * Errors produced working with the namespace.
      */
     /// Produced when a path is given that does not point to an object in the AML namespace.
     ObjectDoesNotExist(String),
+    HandleDoesNotExist(AmlHandle),
+    /// Produced when two values with the same name are added to the namespace.
+    NameCollision(AmlName),
 
     /*
      * Errors produced executing control methods.
@@ -99,7 +114,7 @@ pub enum AmlError {
 
 #[derive(Debug)]
 pub struct AmlContext {
-    pub namespace: BTreeMap<AmlName, AmlValue>,
+    pub namespace: Namespace,
     current_scope: AmlName,
 
     /*
@@ -123,7 +138,7 @@ pub struct AmlContext {
 impl AmlContext {
     pub fn new() -> AmlContext {
         AmlContext {
-            namespace: BTreeMap::new(),
+            namespace: Namespace::new(),
             current_scope: AmlName::root(),
             local_0: None,
             local_1: None,
@@ -154,70 +169,6 @@ impl AmlContext {
 
     /// Invoke a method referred to by its path in the namespace, with the given arguments.
     pub fn invoke_method(&mut self, path: &AmlName, args: Args) -> Result<AmlValue, AmlError> {
-        let method = match self.lookup(path) {
-            // Unfortunately, we have to clone the method object to end the borrow on the context.
-            Some(object) => object.clone(),
-            None => return Err(AmlError::ObjectDoesNotExist(path.as_string())),
-        };
-
-        method.invoke(self, args, path.clone())
-    }
-
-    /// Resolves a given path relative to the current scope (if the given path is not absolute).
-    /// If you want to use this to index the namespace (and expect things like search rules to
-    /// work), use `lookup`.
-    pub fn resolve_path(&self, path: &AmlName) -> AmlName {
-        // TODO: we should normalize the path by resolving prefix chars etc.
-
-        // If the path is absolute, just return it.
-        if path.is_absolute() {
-            return path.clone();
-        }
-
-        // Otherwise, it's relative to the current scope so append it onto that.
-        self.current_scope.clone() + path.clone()
-    }
-
-    /// Lookup the object at the given path of the namespace. If the given path is not absolute, it
-    /// is resolved against the current scope. Returns `None` if no object exists at that path. If
-    /// `path` is a single name segment and does not appear in the current scope, the search rules
-    /// describes in §5.3 of the ACPI specification.
-    pub fn lookup(&self, path: &AmlName) -> Option<&AmlValue> {
-        if path.search_rules_apply() {
-            /*
-             * If search rules apply, we need to recursively look through the namespace. If the
-             * given name does not occur in the current scope, we look at the parent scope, until
-             * we either find the name, or reach the root of the namespace.
-             */
-            let mut scope = self.current_scope.clone();
-            assert!(scope.is_absolute());
-            loop {
-                // Search for the name at this namespace level. If we find it, we're done.
-                let resolved_path = scope.clone() + path.clone();
-                if let Some(value) = self.namespace.get(&resolved_path) {
-                    return Some(value);
-                }
-
-                // If we don't find it, go up a level in the namespace and search for it there,
-                // recursively.
-                match scope.parent() {
-                    Some(parent) => scope = parent,
-                    // If we still haven't found the value and have run out of parents, return `None`.
-                    None => return None,
-                }
-            }
-            None
-        } else {
-            // If special search rules don't apply, simply resolve it against the current scope
-            let resolved_path = self.resolve_path(&path);
-            self.namespace.get(&resolved_path)
-        }
-    }
-
-    /// Add an `AmlValue` to the namespace. `path` can either be absolute, or relative (in which
-    /// case it's treated as relative to the current scope).
-    pub fn add_to_namespace(&mut self, path: AmlName, value: AmlValue) {
-        let resolved_path = self.resolve_path(&path);
-        self.namespace.insert(resolved_path, value);
+        self.namespace.get_by_path(path)?.clone().invoke(self, args, path.clone())
     }
 }

+ 4 - 186
aml_parser/src/name_object.rs

@@ -1,138 +1,12 @@
 use crate::{
+    namespace::{AmlName, NameComponent},
     opcode::{opcode, DUAL_NAME_PREFIX, MULTI_NAME_PREFIX, NULL_NAME, PREFIX_CHAR, ROOT_CHAR},
     parser::{choice, comment_scope_verbose, consume, n_of, take, Parser},
     AmlContext,
     AmlError,
 };
-use alloc::{
-    string::{String, ToString},
-    vec::Vec,
-};
-use core::{fmt, ops::Add, str};
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct AmlName(pub(crate) Vec<NameComponent>);
-
-impl AmlName {
-    pub fn root() -> AmlName {
-        AmlName(alloc::vec![NameComponent::Root])
-    }
-
-    pub fn from_name_seg(seg: NameSeg) -> AmlName {
-        AmlName(alloc::vec![NameComponent::Segment(seg)])
-    }
-
-    /// Convert a string representation of an AML name into an `AmlName`. Returns `None` if the
-    /// passed string is not a valid AML path.
-    pub fn from_str(mut string: &str) -> Option<AmlName> {
-        if string.len() == 0 {
-            return None;
-        }
-
-        let mut components = Vec::new();
-
-        // If it starts with a \, make it an absolute name
-        if string.starts_with('\\') {
-            components.push(NameComponent::Root);
-            string = &string[1..];
-        }
-
-        if string.len() > 0 {
-            // Divide the rest of it into segments, and parse those
-            for mut part in string.split('.') {
-                // Handle prefix chars
-                while part.starts_with('^') {
-                    components.push(NameComponent::Prefix);
-                    part = &part[1..];
-                }
-
-                components.push(NameComponent::Segment(NameSeg::from_str(part)?));
-            }
-        }
-
-        Some(AmlName(components))
-    }
-
-    pub fn as_string(&self) -> String {
-        self.0
-            .iter()
-            .fold(String::new(), |name, component| match component {
-                NameComponent::Root => name + "\\",
-                NameComponent::Prefix => name + "^",
-                NameComponent::Segment(seg) => name + seg.as_str() + ".",
-            })
-            .trim_end_matches('.')
-            .to_string()
-    }
-
-    pub fn is_absolute(&self) -> bool {
-        self.0.first() == Some(&NameComponent::Root)
-    }
-
-    /// Returns `true` if this path is at the root of the namespace (`\`).
-    pub fn is_at_root(&self) -> bool {
-        self.0 == &[NameComponent::Root]
-    }
-
-    /// Special rules apply when searching for certain paths (specifically, those that are made up
-    /// of a single name segment). Returns `true` if those rules apply.
-    pub fn search_rules_apply(&self) -> bool {
-        if self.0.len() != 1 {
-            return false;
-        }
-
-        match self.0[0] {
-            NameComponent::Segment(_) => true,
-            _ => false,
-        }
-    }
-
-    /// Normalize an AML path, resolving prefix chars. Returns `None` if the path normalizes to an
-    /// invalid path (e.g. `\^_FOO`)
-    pub fn normalize(self) -> Option<AmlName> {
-        // TODO: currently, this doesn't do anything. Work out a nice way of handling prefix chars.
-        Some(self)
-    }
-
-    /// Get the parent of this `AmlName`. For example, the parent of `\_SB.PCI0._PRT` is `\_SB.PCI0`. The root
-    /// path has no parent, and so returns `None`.
-    pub fn parent(&self) -> Option<AmlName> {
-        // Firstly, normalize the path so we don't have to deal with prefix chars
-        let mut normalized_self = self.clone().normalize()?;
-
-        match normalized_self.0.last() {
-            None => None,
-            Some(NameComponent::Root) => None,
-            Some(NameComponent::Segment(_)) => {
-                normalized_self.0.pop();
-                Some(normalized_self)
-            }
-            Some(NameComponent::Prefix) => unreachable!(), // Prefix chars are removed by normalization
-        }
-    }
-}
-
-impl Add for AmlName {
-    type Output = AmlName;
-
-    fn add(mut self, other: Self) -> Self {
-        self.0.extend_from_slice(&(other.0));
-        self
-    }
-}
-
-impl fmt::Display for AmlName {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.as_string())
-    }
-}
-
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub enum NameComponent {
-    Root,
-    Prefix,
-    Segment(NameSeg),
-}
+use alloc::vec::Vec;
+use core::{fmt, str};
 
 pub fn name_string<'a, 'c>() -> impl Parser<'a, 'c, AmlName>
 where
@@ -224,7 +98,7 @@ where
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub struct NameSeg([u8; 4]);
+pub struct NameSeg(pub(crate) [u8; 4]);
 
 impl NameSeg {
     pub(crate) fn from_str(string: &str) -> Option<NameSeg> {
@@ -349,60 +223,4 @@ mod tests {
             &[]
         );
     }
-
-    #[test]
-    fn test_aml_name_from_str() {
-        assert_eq!(AmlName::from_str(""), None);
-        assert_eq!(AmlName::from_str("\\"), Some(AmlName::root()));
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0"),
-            Some(AmlName(alloc::vec![
-                NameComponent::Root,
-                NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
-                NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
-            ]))
-        );
-        assert_eq!(
-            AmlName::from_str("\\_SB.^^^PCI0"),
-            Some(AmlName(alloc::vec![
-                NameComponent::Root,
-                NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
-                NameComponent::Prefix,
-                NameComponent::Prefix,
-                NameComponent::Prefix,
-                NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
-            ]))
-        );
-    }
-
-    #[test]
-    fn test_is_at_root() {
-        assert_eq!(AmlName::root().is_at_root(), true);
-        assert_eq!(AmlName::from_str("\\_SB").unwrap().is_at_root(), false);
-        assert_eq!(AmlName::from_str("\\_SB.PCI0.^VGA").unwrap().is_at_root(), false);
-    }
-
-    #[test]
-    fn test_search_rules_apply() {
-        assert_eq!(AmlName::root().search_rules_apply(), false);
-        assert_eq!(AmlName::from_str("\\_SB").unwrap().search_rules_apply(), false);
-        assert_eq!(AmlName::from_str("^VGA").unwrap().search_rules_apply(), false);
-        assert_eq!(AmlName::from_str("_SB.PCI0.VGA").unwrap().search_rules_apply(), false);
-        assert_eq!(AmlName::from_str("VGA").unwrap().search_rules_apply(), true);
-        assert_eq!(AmlName::from_str("_SB").unwrap().search_rules_apply(), true);
-    }
-
-    #[test]
-    fn test_aml_name_parent() {
-        assert_eq!(AmlName::from_str("\\").unwrap().parent(), None);
-        assert_eq!(AmlName::from_str("\\_SB").unwrap().parent(), Some(AmlName::root()));
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0").unwrap().parent(),
-            Some(AmlName::from_str("\\_SB").unwrap())
-        );
-        assert_eq!(
-            AmlName::from_str("\\_SB.PCI0").unwrap().parent().unwrap().parent(),
-            Some(AmlName::root())
-        );
-    }
 }

+ 307 - 0
aml_parser/src/namespace.rs

@@ -0,0 +1,307 @@
+use crate::{name_object::NameSeg, value::AmlValue, AmlError};
+use alloc::{
+    collections::BTreeMap,
+    string::{String, ToString},
+    vec::Vec,
+};
+use core::fmt;
+
+/// A handle is used to refer to an AML value without actually borrowing it until you need to
+/// access it (this makes borrowing situation much easier as you only have to consider who's
+/// borrowing the namespace). They can also be cached to avoid expensive namespace lookups.
+///
+/// Handles are never reused (the handle to a removed object will never be reused to point to a new
+/// object). This ensures handles cached by the library consumer will never point to an object they
+/// did not originally point to, but also means that, in theory, we can run out of handles on a
+/// very-long-running system (we are yet to see if this is a problem, practically).
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub struct AmlHandle(u32);
+
+impl AmlHandle {
+    pub(self) fn increment(&mut self) {
+        self.0 += 1;
+    }
+}
+
+pub struct Namespace {
+    /// This is a running count of ids, which are never reused. This is incremented every time we
+    /// add a new object to the namespace. We can then remove objects, freeing their memory, without
+    /// risking using the same id for two objects.
+    next_handle: AmlHandle,
+
+    /// This maps handles to actual values, and is used to access the actual AML values.
+    object_map: BTreeMap<AmlHandle, AmlValue>,
+
+    /// This maps names to handles, and should be used when you don't already have the handle to a
+    /// value.
+    // XXX: in the future, this could be replaced with a better data structure that doesn't store
+    // the entire name for each id. Instead, it would be a tree-like structure that stores each
+    // name segment, with a list of objects and their names, and a list of child scopes at that
+    // level.
+    name_map: BTreeMap<AmlName, AmlHandle>,
+}
+
+impl Namespace {
+    pub fn new() -> Namespace {
+        Namespace { next_handle: AmlHandle(0), object_map: BTreeMap::new(), name_map: BTreeMap::new() }
+    }
+
+    /// Add a value to the namespace at the given path, which must be a normalized, absolute AML
+    /// name. If you want to add at a path relative to a given scope, use `add_at_resolved_path`
+    /// instead.
+    pub fn add(&mut self, path: AmlName, value: AmlValue) -> Result<AmlHandle, AmlError> {
+        assert!(path.is_absolute());
+
+        if self.name_map.contains_key(&path) {
+            return Err(AmlError::NameCollision(path.clone()));
+        }
+
+        let handle = self.next_handle;
+        self.next_handle.increment();
+
+        self.object_map.insert(handle, value);
+        self.name_map.insert(path, handle);
+
+        Ok(handle)
+    }
+
+    /// Helper method for adding a value to the namespace at a path that is relative to the given
+    /// scope. This operation involves a lot of error handling in parts of the parser, so is
+    /// encapsulated here.
+    pub fn add_at_resolved_path(
+        &mut self,
+        path: AmlName,
+        scope: &AmlName,
+        value: AmlValue,
+    ) -> Result<AmlHandle, AmlError> {
+        self.add(path.resolve(scope)?, value)
+    }
+
+    pub fn get(&self, handle: AmlHandle) -> Result<&AmlValue, AmlError> {
+        self.object_map.get(&handle).ok_or(AmlError::HandleDoesNotExist(handle))
+    }
+
+    pub fn get_by_path(&self, path: &AmlName) -> Result<&AmlValue, AmlError> {
+        let handle = self.name_map.get(path).ok_or(AmlError::ObjectDoesNotExist(path.as_string()))?;
+        self.get(*handle).map_err(|_| AmlError::ObjectDoesNotExist(path.as_string()))
+    }
+
+    /// Search for an object at the given path of the namespace, applying the search rules
+    /// described in §5.3 of the ACPI specification, if they are applicable.
+    pub fn search(&self, path: &AmlName, starting_scope: &AmlName) -> Result<&AmlValue, AmlError> {
+        if path.search_rules_apply() {
+            /*
+             * If search rules apply, we need to recursively look through the namespace. If the
+             * given name does not occur in the current scope, we look at the parent scope, until
+             * we either find the name, or reach the root of the namespace.
+             */
+            let mut scope = starting_scope.clone();
+            assert!(scope.is_absolute());
+            loop {
+                // Search for the name at this namespace level. If we find it, we're done.
+                if let Ok(value) = self.get_by_path(&path.resolve(&scope).unwrap()) {
+                    return Ok(value);
+                }
+
+                // If we don't find it, go up a level in the namespace and search for it there,
+                // recursively.
+                match scope.parent() {
+                    Ok(parent) => scope = parent,
+                    // If we still haven't found the value and have run out of parents, return `None`.
+                    Err(AmlError::RootHasNoParent) => {
+                        return Err(AmlError::ObjectDoesNotExist(path.as_string()))
+                    }
+                    Err(err) => return Err(err),
+                }
+            }
+        } else {
+            // If search rules don't apply, simply resolve it against the starting scope
+            self.get_by_path(&path.resolve(starting_scope)?)
+        }
+    }
+}
+
+impl fmt::Debug for Namespace {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        for (name, handle) in self.name_map.iter() {
+            write!(f, "{}: {:?}\n", name, self.object_map.get(handle).unwrap())?;
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub struct AmlName(pub(crate) Vec<NameComponent>);
+
+impl AmlName {
+    pub fn root() -> AmlName {
+        AmlName(alloc::vec![NameComponent::Root])
+    }
+
+    pub fn from_name_seg(seg: NameSeg) -> AmlName {
+        AmlName(alloc::vec![NameComponent::Segment(seg)])
+    }
+
+    /// Convert a string representation of an AML name into an `AmlName`. Returns `None` if the
+    /// passed string is not a valid AML path.
+    pub fn from_str(mut string: &str) -> Option<AmlName> {
+        if string.len() == 0 {
+            return None;
+        }
+
+        let mut components = Vec::new();
+
+        // If it starts with a \, make it an absolute name
+        if string.starts_with('\\') {
+            components.push(NameComponent::Root);
+            string = &string[1..];
+        }
+
+        if string.len() > 0 {
+            // Divide the rest of it into segments, and parse those
+            for mut part in string.split('.') {
+                // Handle prefix chars
+                while part.starts_with('^') {
+                    components.push(NameComponent::Prefix);
+                    part = &part[1..];
+                }
+
+                components.push(NameComponent::Segment(NameSeg::from_str(part)?));
+            }
+        }
+
+        Some(AmlName(components))
+    }
+
+    pub fn as_string(&self) -> String {
+        self.0
+            .iter()
+            .fold(String::new(), |name, component| match component {
+                NameComponent::Root => name + "\\",
+                NameComponent::Prefix => name + "^",
+                NameComponent::Segment(seg) => name + seg.as_str() + ".",
+            })
+            .trim_end_matches('.')
+            .to_string()
+    }
+
+    pub fn is_absolute(&self) -> bool {
+        self.0.first() == Some(&NameComponent::Root)
+    }
+
+    /// Special rules apply when searching for certain paths (specifically, those that are made up
+    /// of a single name segment). Returns `true` if those rules apply.
+    pub fn search_rules_apply(&self) -> bool {
+        if self.0.len() != 1 {
+            return false;
+        }
+
+        match self.0[0] {
+            NameComponent::Segment(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Normalize an AML path, resolving prefix chars. Returns `None` if the path normalizes to an
+    /// invalid path (e.g. `\^_FOO`)
+    pub fn normalize(self) -> Result<AmlName, AmlError> {
+        // TODO: currently, this doesn't do anything. Work out a nice way of handling prefix chars.
+        // If the name can't be normalized, emit AmlError::InvalidNormalizedName
+        Ok(self)
+    }
+
+    /// Get the parent of this `AmlName`. For example, the parent of `\_SB.PCI0._PRT` is `\_SB.PCI0`. The root
+    /// path has no parent, and so returns `None`.
+    pub fn parent(&self) -> Result<AmlName, AmlError> {
+        // Firstly, normalize the path so we don't have to deal with prefix chars
+        let mut normalized_self = self.clone().normalize()?;
+
+        match normalized_self.0.last() {
+            None | Some(NameComponent::Root) => Err(AmlError::RootHasNoParent),
+            Some(NameComponent::Segment(_)) => {
+                normalized_self.0.pop();
+                Ok(normalized_self)
+            }
+            Some(NameComponent::Prefix) => unreachable!(), // Prefix chars are removed by normalization
+        }
+    }
+
+    /// Resolve this path against a given scope, making it absolute. If the path is absolute, it is
+    /// returned directly. The path is also normalized.
+    pub fn resolve(&self, scope: &AmlName) -> Result<AmlName, AmlError> {
+        assert!(scope.is_absolute());
+
+        if self.is_absolute() {
+            return Ok(self.clone());
+        }
+
+        let mut resolved_path = scope.clone();
+        resolved_path.0.extend_from_slice(&(self.0));
+        resolved_path.normalize()
+    }
+}
+
+impl fmt::Display for AmlName {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", self.as_string())
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum NameComponent {
+    Root,
+    Prefix,
+    Segment(NameSeg),
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_aml_name_from_str() {
+        assert_eq!(AmlName::from_str(""), None);
+        assert_eq!(AmlName::from_str("\\"), Some(AmlName::root()));
+        assert_eq!(
+            AmlName::from_str("\\_SB.PCI0"),
+            Some(AmlName(alloc::vec![
+                NameComponent::Root,
+                NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
+                NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
+            ]))
+        );
+        assert_eq!(
+            AmlName::from_str("\\_SB.^^^PCI0"),
+            Some(AmlName(alloc::vec![
+                NameComponent::Root,
+                NameComponent::Segment(NameSeg([b'_', b'S', b'B', b'_'])),
+                NameComponent::Prefix,
+                NameComponent::Prefix,
+                NameComponent::Prefix,
+                NameComponent::Segment(NameSeg([b'P', b'C', b'I', b'0']))
+            ]))
+        );
+    }
+
+    #[test]
+    fn test_search_rules_apply() {
+        assert_eq!(AmlName::root().search_rules_apply(), false);
+        assert_eq!(AmlName::from_str("\\_SB").unwrap().search_rules_apply(), false);
+        assert_eq!(AmlName::from_str("^VGA").unwrap().search_rules_apply(), false);
+        assert_eq!(AmlName::from_str("_SB.PCI0.VGA").unwrap().search_rules_apply(), false);
+        assert_eq!(AmlName::from_str("VGA").unwrap().search_rules_apply(), true);
+        assert_eq!(AmlName::from_str("_SB").unwrap().search_rules_apply(), true);
+    }
+
+    #[test]
+    fn test_aml_name_parent() {
+        assert_eq!(AmlName::from_str("\\").unwrap().parent(), Err(AmlError::RootHasNoParent));
+        assert_eq!(AmlName::from_str("\\_SB").unwrap().parent(), Ok(AmlName::root()));
+        assert_eq!(
+            AmlName::from_str("\\_SB.PCI0").unwrap().parent(),
+            Ok(AmlName::from_str("\\_SB").unwrap())
+        );
+        assert_eq!(AmlName::from_str("\\_SB.PCI0").unwrap().parent().unwrap().parent(), Ok(AmlName::root()));
+    }
+}

+ 12 - 0
aml_parser/src/parser.rs

@@ -437,6 +437,18 @@ pub(crate) macro make_parser_concrete($parser: expr) {
     |input, context| ($parser).parse(input, context)
 }
 
+/// Helper macro for use within `map_with_context` as an alternative to "trying" an expression.
+///
+/// ### Example
+/// Problem: `expr?` won't work because the expected return type is `(Result<R, AmlError>, &mut AmlContext)`
+/// Solution: use `try_with_context!(context, expr)` instead.
+pub(crate) macro try_with_context($context: expr, $expr: expr) {
+    match $expr {
+        Ok(result) => result,
+        Err(err) => return (Err(err), $context),
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;

+ 74 - 22
aml_parser/src/term_object.rs

@@ -1,5 +1,6 @@
 use crate::{
-    name_object::{name_seg, name_string, AmlName},
+    name_object::{name_seg, name_string},
+    namespace::AmlName,
     opcode::{self, ext_opcode, opcode},
     parser::{
         choice,
@@ -11,6 +12,7 @@ use crate::{
         take_u16,
         take_u32,
         take_u64,
+        try_with_context,
         ParseResult,
         Parser,
     },
@@ -104,7 +106,14 @@ where
         .then(comment_scope(
             "DefName",
             name_string().then(data_ref_object()).map_with_context(|(name, data_ref_object), context| {
-                context.add_to_namespace(name, AmlValue::Name(box data_ref_object));
+                try_with_context!(
+                    context,
+                    context.namespace.add_at_resolved_path(
+                        name,
+                        &context.current_scope,
+                        AmlValue::Name(box data_ref_object)
+                    )
+                );
                 (Ok(()), context)
             }),
         ))
@@ -125,7 +134,7 @@ where
                 .then(name_string())
                 .map_with_context(|(length, name), context| {
                     let previous_scope = context.current_scope.clone();
-                    context.current_scope = context.resolve_path(&name);
+                    context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
                     (Ok((length, name, previous_scope)), context)
                 })
                 .feed(|(pkg_length, name, previous_scope)| {
@@ -188,7 +197,14 @@ where
                         Err(err) => return (Err(err), context),
                     };
 
-                    context.add_to_namespace(name, AmlValue::OpRegion { region, offset, length });
+                    try_with_context!(
+                        context,
+                        context.namespace.add_at_resolved_path(
+                            name,
+                            &context.current_scope,
+                            AmlValue::OpRegion { region, offset, length }
+                        )
+                    );
                     (Ok(()), context)
                 },
             ),
@@ -272,14 +288,18 @@ where
     //     .map_with_context(|(((), access_type), access_attrib), context| (Ok(    , context));
 
     let named_field = name_seg().then(pkg_length()).map_with_context(move |(name_seg, length), context| {
-        context.add_to_namespace(
-            AmlName::from_name_seg(name_seg),
-            AmlValue::Field {
-                region: region_name.clone(),
-                flags,
-                offset: current_offset,
-                length: length.raw_length as u64,
-            },
+        try_with_context!(
+            context,
+            context.namespace.add_at_resolved_path(
+                AmlName::from_name_seg(name_seg),
+                &context.current_scope,
+                AmlValue::Field {
+                    region: region_name.clone(),
+                    flags,
+                    offset: current_offset,
+                    length: length.raw_length as u64,
+                },
+            )
         );
 
         (Ok(length.raw_length as u64), context)
@@ -308,9 +328,13 @@ where
                     take_to_end_of_pkglength(length).map(move |code| Ok((name.clone(), flags, code)))
                 })
                 .map_with_context(|(name, flags, code), context| {
-                    context.add_to_namespace(
-                        name,
-                        AmlValue::Method { flags: MethodFlags::new(flags), code: code.to_vec() },
+                    try_with_context!(
+                        context,
+                        context.namespace.add_at_resolved_path(
+                            name,
+                            &context.current_scope,
+                            AmlValue::Method { flags: MethodFlags::new(flags), code: code.to_vec() },
+                        )
                     );
                     (Ok(()), context)
                 }),
@@ -331,10 +355,17 @@ where
             pkg_length()
                 .then(name_string())
                 .map_with_context(|(length, name), context| {
-                    context.add_to_namespace(name.clone(), AmlValue::Device);
+                    try_with_context!(
+                        context,
+                        context.namespace.add_at_resolved_path(
+                            name.clone(),
+                            &context.current_scope,
+                            AmlValue::Device
+                        )
+                    );
 
                     let previous_scope = context.current_scope.clone();
-                    context.current_scope = context.resolve_path(&name);
+                    context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
 
                     (Ok((length, previous_scope)), context)
                 })
@@ -365,11 +396,25 @@ where
                 .then(take())
                 .then(take_u32())
                 .then(take())
-                .feed(|((((pkg_length, name), proc_id), pblk_address), pblk_len)| {
-                    term_list(pkg_length).map(move |_| Ok((name.clone(), proc_id, pblk_address, pblk_len)))
+                .map_with_context(|((((pkg_length, name), proc_id), pblk_address), pblk_len), context| {
+                    try_with_context!(
+                        context,
+                        context.namespace.add_at_resolved_path(
+                            name.clone(),
+                            &context.current_scope,
+                            AmlValue::Processor { id: proc_id, pblk_address, pblk_len }
+                        )
+                    );
+                    let previous_scope = context.current_scope.clone();
+                    context.current_scope = try_with_context!(context, name.resolve(&context.current_scope));
+
+                    (Ok((previous_scope, pkg_length)), context)
+                })
+                .feed(move |(previous_scope, pkg_length)| {
+                    term_list(pkg_length).map(move |_| Ok(previous_scope.clone()))
                 })
-                .map_with_context(|(name, id, pblk_address, pblk_len), context| {
-                    context.add_to_namespace(name, AmlValue::Processor { id, pblk_address, pblk_len });
+                .map_with_context(|previous_scope, context| {
+                    context.current_scope = previous_scope;
                     (Ok(()), context)
                 }),
         ))
@@ -389,7 +434,14 @@ where
         .then(comment_scope(
             "DefMutex",
             name_string().then(take()).map_with_context(|(name, sync_level), context| {
-                context.add_to_namespace(name, AmlValue::Mutex { sync_level });
+                try_with_context!(
+                    context,
+                    context.namespace.add_at_resolved_path(
+                        name,
+                        &context.current_scope,
+                        AmlValue::Mutex { sync_level }
+                    )
+                );
                 (Ok(()), context)
             }),
         ))

+ 4 - 6
aml_parser/src/type2.rs

@@ -1,7 +1,7 @@
 use crate::{
     name_object::name_string,
     opcode::{self, opcode},
-    parser::{choice, comment_scope_verbose, ParseResult, Parser},
+    parser::{choice, comment_scope_verbose, try_with_context, ParseResult, Parser},
     term_object::term_arg,
     value::AmlValue,
     AmlError,
@@ -66,11 +66,9 @@ where
         "MethodInvocation",
         name_string()
             .map_with_context(move |name, context| {
-                let object = match context.lookup(&name) {
-                    Some(object) => (*object).clone(),
-                    None => return (Err(AmlError::ObjectDoesNotExist(name.as_string())), context),
-                };
-
+                let object =
+                    try_with_context!(context, context.namespace.search(&name, &context.current_scope))
+                        .clone();
                 (Ok(object), context)
             })
             .feed(|object| {

+ 1 - 1
aml_parser/src/value.rs

@@ -1,5 +1,5 @@
 use crate::{
-    name_object::AmlName,
+    namespace::AmlName,
     parser::Parser,
     pkg_length::PkgLength,
     term_object::term_list,