Skip to main content

xsd_schema/xpath/
arena.rs

1//! Arena-based storage for XPath AST nodes.
2//!
3//! This module provides an arena allocator for AST nodes, allowing efficient
4//! storage and reference by ID. This approach avoids recursive ownership issues
5//! and enables efficient tree manipulation.
6
7use crate::xpath::ast::AstNode;
8
9/// Unique identifier for an AST node within an arena.
10///
11/// This is a simple index into the arena's node vector. The value 0 is reserved
12/// as a sentinel for "no node" in some contexts.
13pub type AstNodeId = u32;
14
15/// Sentinel value indicating no node (used for optional references).
16pub const NO_NODE: AstNodeId = u32::MAX;
17
18/// Source location span within the input string.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20pub struct SourceSpan {
21    /// Start byte offset (inclusive).
22    pub start: usize,
23    /// End byte offset (exclusive).
24    pub end: usize,
25}
26
27impl SourceSpan {
28    /// Create a new source span.
29    #[inline]
30    pub fn new(start: usize, end: usize) -> Self {
31        Self { start, end }
32    }
33
34    /// Create an empty span at a single position.
35    #[inline]
36    pub fn at(pos: usize) -> Self {
37        Self {
38            start: pos,
39            end: pos,
40        }
41    }
42
43    /// Merge two spans into one that covers both.
44    #[inline]
45    pub fn merge(self, other: Self) -> Self {
46        Self {
47            start: self.start.min(other.start),
48            end: self.end.max(other.end),
49        }
50    }
51
52    /// Check if this span is empty (zero length).
53    #[inline]
54    pub fn is_empty(&self) -> bool {
55        self.start >= self.end
56    }
57
58    /// Get the length of this span in bytes.
59    #[inline]
60    pub fn len(&self) -> usize {
61        self.end.saturating_sub(self.start)
62    }
63}
64
65/// Arena for storing AST nodes.
66///
67/// Nodes are stored in a contiguous vector and referenced by `AstNodeId`.
68/// This enables efficient allocation and avoids recursive Box structures.
69#[derive(Debug, Default, Clone)]
70pub struct AstArena {
71    nodes: Vec<AstNode>,
72}
73
74impl AstArena {
75    /// Create a new empty arena.
76    #[inline]
77    pub fn new() -> Self {
78        Self { nodes: Vec::new() }
79    }
80
81    /// Create an arena with pre-allocated capacity.
82    #[inline]
83    pub fn with_capacity(capacity: usize) -> Self {
84        Self {
85            nodes: Vec::with_capacity(capacity),
86        }
87    }
88
89    /// Add a node to the arena and return its ID.
90    #[inline]
91    pub fn add(&mut self, node: AstNode) -> AstNodeId {
92        let id = self.nodes.len() as AstNodeId;
93        self.nodes.push(node);
94        id
95    }
96
97    /// Get a reference to a node by ID.
98    ///
99    /// # Panics
100    /// Panics if the ID is out of bounds.
101    #[inline]
102    pub fn get(&self, id: AstNodeId) -> &AstNode {
103        &self.nodes[id as usize]
104    }
105
106    /// Get a mutable reference to a node by ID.
107    ///
108    /// # Panics
109    /// Panics if the ID is out of bounds.
110    #[inline]
111    pub fn get_mut(&mut self, id: AstNodeId) -> &mut AstNode {
112        &mut self.nodes[id as usize]
113    }
114
115    /// Try to get a reference to a node by ID.
116    #[inline]
117    pub fn try_get(&self, id: AstNodeId) -> Option<&AstNode> {
118        self.nodes.get(id as usize)
119    }
120
121    /// Try to get a mutable reference to a node by ID.
122    #[inline]
123    pub fn try_get_mut(&mut self, id: AstNodeId) -> Option<&mut AstNode> {
124        self.nodes.get_mut(id as usize)
125    }
126
127    /// Get the number of nodes in the arena.
128    #[inline]
129    pub fn len(&self) -> usize {
130        self.nodes.len()
131    }
132
133    /// Check if the arena is empty.
134    #[inline]
135    pub fn is_empty(&self) -> bool {
136        self.nodes.is_empty()
137    }
138
139    /// Clear all nodes from the arena.
140    #[inline]
141    pub fn clear(&mut self) {
142        self.nodes.clear();
143    }
144
145    /// Iterate over all nodes in the arena.
146    #[inline]
147    pub fn iter(&self) -> impl Iterator<Item = (AstNodeId, &AstNode)> {
148        self.nodes
149            .iter()
150            .enumerate()
151            .map(|(i, n)| (i as AstNodeId, n))
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use crate::xpath::ast::{AstNode, ValueNode};
159
160    #[test]
161    fn test_source_span() {
162        let span = SourceSpan::new(10, 20);
163        assert_eq!(span.start, 10);
164        assert_eq!(span.end, 20);
165        assert_eq!(span.len(), 10);
166        assert!(!span.is_empty());
167
168        let empty = SourceSpan::at(5);
169        assert!(empty.is_empty());
170        assert_eq!(empty.len(), 0);
171    }
172
173    #[test]
174    fn test_span_merge() {
175        let a = SourceSpan::new(10, 20);
176        let b = SourceSpan::new(15, 30);
177        let merged = a.merge(b);
178        assert_eq!(merged.start, 10);
179        assert_eq!(merged.end, 30);
180    }
181
182    #[test]
183    fn test_arena_basic() {
184        let mut arena = AstArena::new();
185        assert!(arena.is_empty());
186
187        let id1 = arena.add(AstNode::Value(ValueNode::Empty));
188        let id2 = arena.add(AstNode::Value(ValueNode::Boolean(true)));
189
190        assert_eq!(arena.len(), 2);
191        assert_eq!(id1, 0);
192        assert_eq!(id2, 1);
193    }
194
195    #[test]
196    fn test_arena_get() {
197        let mut arena = AstArena::new();
198        let id = arena.add(AstNode::Value(ValueNode::String("test".to_string())));
199
200        match arena.get(id) {
201            AstNode::Value(ValueNode::String(s)) => assert_eq!(s, "test"),
202            _ => panic!("Unexpected node type"),
203        }
204    }
205
206    #[test]
207    fn test_arena_try_get() {
208        let arena = AstArena::new();
209        assert!(arena.try_get(0).is_none());
210        assert!(arena.try_get(100).is_none());
211    }
212
213    #[test]
214    fn test_arena_iter() {
215        let mut arena = AstArena::new();
216        arena.add(AstNode::Value(ValueNode::Empty));
217        arena.add(AstNode::Value(ValueNode::Boolean(true)));
218
219        let ids: Vec<_> = arena.iter().map(|(id, _)| id).collect();
220        assert_eq!(ids, vec![0, 1]);
221    }
222}