1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
//! Arena-allocated parse tree node
//!
//! This module provides the `Node<'arena>` type, a lightweight wrapper
//! around arena-allocated `TreeNodeData`. Node provides a safe, ergonomic
//! API for traversing parse trees without manual handle management.
//!
//! # Example
//!
//! ```ignore
//! use adze::parser_v4::Parser;
//!
//! let mut parser = Parser::new(grammar, parse_table, "example".to_string());
//! let root = parser.parse_tree("1 + 2")?;
//!
//! // `root` is a ParseNode value that can be traversed without `Node` APIs.
//!
//! // Traverse children
//! for child in root.children() {
//! println!("Child symbol: {}", child.symbol());
//! }
//! ```
//!
//! Related: docs/specs/NODE_ARENA_SPEC.md
use crate::arena_allocator::{NodeHandle, TreeArena};
use crate::tree_node_data::TreeNodeData;
use std::cell::RefCell;
use std::collections::HashMap;
type NodeDataCacheKey = (usize, NodeHandle);
thread_local! {
static NODE_DATA_CACHE: RefCell<HashMap<NodeDataCacheKey, &'static TreeNodeData>> =
RefCell::new(HashMap::new());
}
/// A node in the parse tree
///
/// Node is a lightweight wrapper around a NodeHandle that provides
/// safe access to arena-allocated TreeNodeData. The lifetime parameter
/// ties the node to the arena, preventing use-after-free.
///
/// # Lifetime
///
/// The `'arena` lifetime ensures nodes cannot outlive the arena
/// they reference. This is enforced at compile time with zero
/// runtime overhead.
///
/// # Size
///
/// Node is 16 bytes on 64-bit systems:
/// - NodeHandle: 8 bytes (u32 + u32)
/// - &'arena TreeArena: 8 bytes (pointer)
///
/// # Copy Semantics
///
/// Node implements Copy because it only contains:
/// - A handle (Copy)
/// - A reference (Copy)
///
/// This allows efficient passing and duplication without clone().
#[derive(Copy, Clone, Debug)]
pub struct Node<'arena> {
#[allow(dead_code)] // Will be used in Day 5 parse() integration
handle: NodeHandle,
arena: &'arena TreeArena,
}
impl<'arena> Node<'arena> {
/// Create a new node from handle and arena reference
///
/// # Safety
///
/// Internal use only. Handle must be valid for the arena.
/// This is enforced by only exposing this method within the crate.
pub(crate) fn new(handle: NodeHandle, arena: &'arena TreeArena) -> Self {
Node { handle, arena }
}
/// Get direct access to underlying TreeNodeData
///
/// For advanced use cases that need raw data access.
///
/// # Performance
///
/// O(1) - direct arena lookup by handle.
///
/// # Note
///
/// Temporary implementation returns fallback metadata derived from the node
/// symbol while full parse-tree data integration is completed.
pub fn data(&self) -> &'arena TreeNodeData {
let key: NodeDataCacheKey = (self.arena as *const _ as usize, self.handle);
NODE_DATA_CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
if let Some(data) = cache.get(&key) {
return *data;
}
let symbol = self.raw_node().value() as u16;
let data = Box::leak(Box::new(TreeNodeData::new(symbol, 0, 0)));
cache.insert(key, data);
data
})
}
fn raw_node(&self) -> &crate::arena_allocator::TreeNode {
self.arena.get(self.handle).get_ref()
}
/// Get the node's symbol/kind ID
///
/// # Performance
///
/// O(1) - delegates to TreeNodeData::symbol().
pub fn symbol(&self) -> u16 {
self.raw_node().value() as u16
}
/// Get the node's byte range in the source
///
/// Returns (start_byte, end_byte) tuple.
///
/// # Performance
///
/// O(1) - delegates to TreeNodeData::byte_range().
pub fn byte_range(&self) -> (u32, u32) {
(0, 0)
}
/// Get start byte position
///
/// # Performance
///
/// O(1) - direct field access via data().
pub fn start_byte(&self) -> u32 {
self.byte_range().0
}
/// Get end byte position
///
/// # Performance
///
/// O(1) - direct field access via data().
pub fn end_byte(&self) -> u32 {
self.byte_range().1
}
/// Check if this is a named node
///
/// Named nodes appear in the grammar explicitly.
/// Anonymous nodes are punctuation/keywords.
///
/// # Performance
///
/// O(1) - bit check in flags.
pub fn is_named(&self) -> bool {
false
}
/// Check if this node is missing (error recovery)
///
/// Missing nodes are inserted by the parser during error recovery.
///
/// # Performance
///
/// O(1) - bit check in flags.
pub fn is_missing(&self) -> bool {
false
}
/// Check if this node is extra (trivia)
///
/// Extra nodes are comments, whitespace, etc.
///
/// # Performance
///
/// O(1) - bit check in flags.
pub fn is_extra(&self) -> bool {
false
}
/// Check if this node contains errors
///
/// Returns true if this node or any descendant has an error.
///
/// # Performance
///
/// O(1) - bit check in flags.
pub fn has_error(&self) -> bool {
false
}
/// Get child count
///
/// Returns the total number of children, including both named
/// and anonymous children.
///
/// # Performance
///
/// O(1) - delegates to TreeNodeData::child_count().
pub fn child_count(&self) -> usize {
self.raw_node().children().len()
}
/// Get named child count
///
/// Returns the number of named children only.
///
/// # Performance
///
/// O(1) - direct field access via data().
pub fn named_child_count(&self) -> usize {
0
}
/// Get child by index
///
/// Returns None if index >= child_count().
///
/// # Performance
///
/// O(1) - array index + Node creation.
pub fn child(&self, index: usize) -> Option<Node<'arena>> {
self.raw_node()
.children()
.get(index)
.copied()
.map(|handle| Node::new(handle, self.arena))
}
/// Get named child by index
///
/// Returns None in this stage because named-field metadata is not yet
/// populated from `TreeNode`.
///
/// # Performance
///
/// O(1).
pub fn named_child(&self, _index: usize) -> Option<Node<'arena>> {
None
}
/// Get field ID if this node has one.
///
/// Field IDs are not tracked in the current arena-backed tree.
///
/// Field IDs are used for named fields in the grammar.
///
/// # Performance
///
/// O(1) - direct field access via data().
pub fn field_id(&self) -> Option<u16> {
None
}
/// Iterate over all children
///
/// Returns an iterator that yields all children in order.
///
/// # Performance
///
/// O(1) to create iterator, O(n) to consume.
///
/// # Note
///
/// Full implementation in Day 5 when TreeArena stores TreeNodeData.
pub fn children(&self) -> NodeChildren<'arena> {
NodeChildren {
handles: self.arena.get(self.handle).get_ref().children(),
arena: self.arena,
index: 0,
}
}
/// Iterate over named children only
///
/// Returns an iterator that yields only named children.
///
/// # Performance
///
/// O(1) to create iterator, O(n) to consume (filters all children).
pub fn named_children(&self) -> NamedChildren<'arena> {
NamedChildren {
inner: self.children(),
}
}
}
/// Iterator over all children of a node
///
/// Created by `Node::children()`.
#[derive(Clone)]
pub struct NodeChildren<'arena> {
handles: &'arena [NodeHandle],
arena: &'arena TreeArena,
index: usize,
}
impl<'arena> Iterator for NodeChildren<'arena> {
type Item = Node<'arena>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.handles.len() {
let handle = self.handles[self.index];
self.index += 1;
Some(Node::new(handle, self.arena))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.handles.len() - self.index;
(remaining, Some(remaining))
}
}
impl<'arena> ExactSizeIterator for NodeChildren<'arena> {
fn len(&self) -> usize {
self.handles.len() - self.index
}
}
/// Iterator over named children only
///
/// Created by `Node::named_children()`.
#[derive(Clone)]
pub struct NamedChildren<'arena> {
inner: NodeChildren<'arena>,
}
impl<'arena> Iterator for NamedChildren<'arena> {
type Item = Node<'arena>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.find(|node| node.is_named())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_size() {
use std::mem::size_of;
assert_eq!(size_of::<Node>(), 16, "Node should be 16 bytes");
}
#[test]
fn test_node_is_copy() {
// This test compiles if Node is Copy
fn takes_copy<T: Copy>(_: T) {}
let mut arena = TreeArena::new();
// Use TreeNode::leaf() which matches arena.alloc() signature
let handle = arena.alloc(crate::arena_allocator::TreeNode::leaf(42));
let node = Node::new(handle, &arena);
takes_copy(node);
}
#[test]
fn test_node_data_returns_cached_fallback() {
let mut arena = TreeArena::new();
let handle = arena.alloc(crate::arena_allocator::TreeNode::leaf(42));
let node = Node::new(handle, &arena);
let data = node.data();
let data2 = node.data();
assert_eq!(data.symbol(), 42);
assert_eq!(data2.symbol(), 42);
assert!(std::ptr::eq(data, data2));
}
#[test]
fn test_node_children_iterate_from_tree_node() {
let mut arena = TreeArena::new();
let child = arena.alloc(crate::arena_allocator::TreeNode::leaf(7));
let handle = arena.alloc(crate::arena_allocator::TreeNode::branch(vec![child]));
let node = Node::new(handle, &arena);
let children: Vec<_> = node.children().collect::<Vec<_>>();
assert_eq!(children.len(), 1);
assert_eq!(children[0].symbol(), 7);
}
}