Skip to main content

layer_conform_core/
tree.rs

1//! Neutral AST tree IR shared across language adapters.
2
3use compact_str::CompactString;
4
5#[repr(u32)]
6#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
7pub enum NodeKind {
8    Program,
9    FunctionDeclaration,
10    ArrowFunction,
11    Method,
12    CallExpression,
13    MemberExpression,
14    JsxElement,
15    Identifier,
16    Literal,
17    ImportSpecifier,
18    Block,
19    Other,
20}
21
22#[derive(Clone, Debug)]
23pub struct TreeNode {
24    pub kind: NodeKind,
25    pub value: Option<CompactString>,
26    pub children: Vec<Box<TreeNode>>,
27    pub id: u32,
28    pub subtree_size: u32,
29}
30
31impl TreeNode {
32    /// 子なしリーフを作る。`id` と `subtree_size` は finalize で確定する。
33    pub fn leaf(kind: NodeKind, value: Option<CompactString>) -> Self {
34        Self { kind, value, children: Vec::new(), id: 0, subtree_size: 0 }
35    }
36
37    /// 子を持つノードを作る。`id` と `subtree_size` は finalize で確定する。
38    pub fn branch(kind: NodeKind, children: Vec<TreeNode>) -> Self {
39        Self {
40            kind,
41            value: None,
42            children: children.into_iter().map(Box::new).collect(),
43            id: 0,
44            subtree_size: 0,
45        }
46    }
47
48    /// preorder traversal で id を採番し、bottom-up で `subtree_size` を確定する。
49    /// 構築完了後に 1 度だけ呼ぶ。
50    pub fn finalize(&mut self) {
51        let mut next_id: u32 = 0;
52        Self::finalize_recurse(self, &mut next_id);
53    }
54
55    fn finalize_recurse(node: &mut TreeNode, next_id: &mut u32) {
56        node.id = *next_id;
57        *next_id += 1;
58        let mut size: u32 = 1;
59        for child in &mut node.children {
60            Self::finalize_recurse(child, next_id);
61            size += child.subtree_size;
62        }
63        node.subtree_size = size;
64    }
65
66    /// blake3(canonical bytes) を返す。
67    /// フォーマット: kind(u32 LE) | value 長(u32 LE) | value bytes | children 数(u32 LE) | 子を再帰
68    /// id / `subtree_size` は入力に含めない (構築順で変動するため)。
69    pub fn canonical_hash(&self) -> [u8; 32] {
70        let mut hasher = blake3::Hasher::new();
71        Self::write_canonical(self, &mut hasher);
72        *hasher.finalize().as_bytes()
73    }
74
75    fn write_canonical(node: &TreeNode, hasher: &mut blake3::Hasher) {
76        hasher.update(&(node.kind as u32).to_le_bytes());
77        let v = node.value.as_deref().unwrap_or("");
78        let v_bytes = v.as_bytes();
79        hasher.update(&(v_bytes.len() as u32).to_le_bytes());
80        hasher.update(v_bytes);
81        hasher.update(&(node.children.len() as u32).to_le_bytes());
82        for child in &node.children {
83            Self::write_canonical(child, hasher);
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn node_kind_discriminants_are_stable() {
94        // discriminant は baseline hash の入力に使われるため安定が必要。
95        // 値が変わったら NodeKind::* の順序を変えていないか要確認。
96        assert_eq!(NodeKind::Program as u32, 0);
97        assert_eq!(NodeKind::FunctionDeclaration as u32, 1);
98        assert_eq!(NodeKind::ArrowFunction as u32, 2);
99    }
100
101    #[test]
102    fn node_kind_is_copy_and_eq() {
103        let a = NodeKind::Identifier;
104        let b = a;
105        assert_eq!(a, b);
106    }
107
108    #[test]
109    fn leaf_constructor_has_no_children() {
110        let n = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
111        assert_eq!(n.kind, NodeKind::Identifier);
112        assert_eq!(n.value.as_deref(), Some("x"));
113        assert_eq!(n.children.len(), 0);
114    }
115
116    #[test]
117    fn branch_constructor_owns_children() {
118        let leaf = TreeNode::leaf(NodeKind::Identifier, None);
119        let branch = TreeNode::branch(NodeKind::Block, vec![leaf]);
120        assert_eq!(branch.children.len(), 1);
121        assert_eq!(branch.children[0].kind, NodeKind::Identifier);
122    }
123
124    #[test]
125    fn finalize_assigns_preorder_ids() {
126        // tree:
127        //     Block (id=0, size=3)
128        //     ├── Identifier (id=1, size=1)
129        //     └── Identifier (id=2, size=1)
130        let leaf1 = TreeNode::leaf(NodeKind::Identifier, Some("a".into()));
131        let leaf2 = TreeNode::leaf(NodeKind::Identifier, Some("b".into()));
132        let mut root = TreeNode::branch(NodeKind::Block, vec![leaf1, leaf2]);
133        root.finalize();
134        assert_eq!(root.id, 0);
135        assert_eq!(root.subtree_size, 3);
136        assert_eq!(root.children[0].id, 1);
137        assert_eq!(root.children[0].subtree_size, 1);
138        assert_eq!(root.children[1].id, 2);
139        assert_eq!(root.children[1].subtree_size, 1);
140    }
141
142    #[test]
143    fn finalize_handles_nested_subtrees() {
144        // tree:
145        //     Block (id=0, size=4)
146        //     └── Block (id=1, size=3)
147        //         ├── Identifier (id=2, size=1)
148        //         └── Identifier (id=3, size=1)
149        let leaf1 = TreeNode::leaf(NodeKind::Identifier, None);
150        let leaf2 = TreeNode::leaf(NodeKind::Identifier, None);
151        let inner = TreeNode::branch(NodeKind::Block, vec![leaf1, leaf2]);
152        let mut root = TreeNode::branch(NodeKind::Block, vec![inner]);
153        root.finalize();
154        assert_eq!(root.subtree_size, 4);
155        assert_eq!(root.children[0].subtree_size, 3);
156    }
157
158    #[test]
159    fn canonical_hash_is_deterministic() {
160        let mut tree = TreeNode::branch(
161            NodeKind::Block,
162            vec![TreeNode::leaf(NodeKind::Identifier, Some("x".into()))],
163        );
164        tree.finalize();
165        let h1 = tree.canonical_hash();
166        let h2 = tree.canonical_hash();
167        assert_eq!(h1, h2);
168    }
169
170    #[test]
171    fn canonical_hash_differs_for_different_kinds() {
172        let mut t1 = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
173        t1.finalize();
174        let mut t2 = TreeNode::leaf(NodeKind::Literal, Some("x".into()));
175        t2.finalize();
176        assert_ne!(t1.canonical_hash(), t2.canonical_hash());
177    }
178
179    #[test]
180    fn canonical_hash_differs_for_different_values() {
181        let mut t1 = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
182        t1.finalize();
183        let mut t2 = TreeNode::leaf(NodeKind::Identifier, Some("y".into()));
184        t2.finalize();
185        assert_ne!(t1.canonical_hash(), t2.canonical_hash());
186    }
187
188    #[test]
189    fn canonical_hash_ignores_id_and_size() {
190        // id と subtree_size は構築順で変わるが、ハッシュには影響しないことを確認。
191        let mut t1 = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
192        t1.id = 100;
193        t1.subtree_size = 999;
194        let h1 = t1.canonical_hash();
195        t1.id = 0;
196        t1.subtree_size = 1;
197        let h2 = t1.canonical_hash();
198        assert_eq!(h1, h2);
199    }
200}