mangle_analysis/
name_trie.rs1use mangle_ir::{Inst, InstId, Ir};
22use rustc_hash::FxHashMap;
23
24use crate::type_expr;
25
26#[derive(Debug, Default)]
28pub struct NameTrie {
29 children: FxHashMap<String, NameTrie>,
30 is_terminal: bool,
31}
32
33impl NameTrie {
34 pub fn new() -> Self {
35 Self::default()
36 }
37
38 pub fn add(&mut self, name: &str) {
40 let parts = split_name(name);
41 let mut node = self;
42 for part in parts {
43 node = node.children.entry(part.to_string()).or_default();
44 }
45 node.is_terminal = true;
46 }
47
48 pub fn contains(&self, name: &str) -> bool {
50 let parts = split_name(name);
51 let mut node = self;
52 for part in parts {
53 match node.children.get(part) {
54 Some(child) => node = child,
55 None => return false,
56 }
57 }
58 node.is_terminal
59 }
60
61 pub fn prefix_name(&self, name: &str) -> String {
67 let parts = split_name(name);
68 let mut node = self;
69 let mut last_terminal_idx: Option<usize> = None;
70
71 for (i, part) in parts.iter().enumerate() {
72 match node.children.get(*part) {
73 Some(child) => {
74 if child.is_terminal {
75 last_terminal_idx = Some(i);
76 }
77 node = child;
78 }
79 None => break,
80 }
81 }
82
83 match last_terminal_idx {
84 Some(idx) => {
85 let prefix_parts = &parts[..=idx];
86 format!("/{}", prefix_parts.join("/"))
87 }
88 None => "/name".to_string(),
89 }
90 }
91
92 pub fn collect(&mut self, ir: &Ir, id: InstId) {
98 match ir.get(id) {
99 Inst::Name(n) => {
100 let name = ir.resolve_name(*n);
101 if !type_expr::is_base_type(ir, id) && name.starts_with('/') {
102 self.add(name);
103 }
104 }
105 Inst::ApplyFn { function, args } => {
106 let fname = ir.resolve_name(*function);
107 if fname == type_expr::FN_TAGGED_UNION && args.len() >= 3 {
108 for i in (2..args.len()).step_by(2) {
111 self.collect(ir, args[i]);
112 }
113 } else {
114 for arg in args {
115 self.collect(ir, *arg);
116 }
117 }
118 }
119 _ => {}
120 }
121 }
122}
123
124fn split_name(name: &str) -> Vec<&str> {
126 name.split('/').filter(|s| !s.is_empty()).collect()
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn basic_trie_operations() {
135 let mut trie = NameTrie::new();
136 trie.add("/animal");
137 trie.add("/animal/dog");
138 trie.add("/color");
139
140 assert!(trie.contains("/animal"));
141 assert!(trie.contains("/animal/dog"));
142 assert!(trie.contains("/color"));
143 assert!(!trie.contains("/animal/cat"));
144 assert!(!trie.contains("/plant"));
145 }
146
147 #[test]
148 fn prefix_name_lookup() {
149 let mut trie = NameTrie::new();
150 trie.add("/animal");
151 trie.add("/animal/dog");
152
153 assert_eq!(trie.prefix_name("/animal/dog/poodle"), "/animal/dog");
154 assert_eq!(trie.prefix_name("/animal/cat"), "/animal");
155 assert_eq!(trie.prefix_name("/animal"), "/animal");
156 assert_eq!(trie.prefix_name("/plant/rose"), "/name");
157 }
158
159 #[test]
160 fn collect_from_type_expr() {
161 let mut ir = Ir::new();
162 let mut trie = NameTrie::new();
163
164 let x = {
166 let n = ir.intern_name("/x");
167 ir.add_inst(Inst::Name(n))
168 };
169 let animal = {
170 let n = ir.intern_name("/animal");
171 ir.add_inst(Inst::Name(n))
172 };
173 let y = {
174 let n = ir.intern_name("/y");
175 ir.add_inst(Inst::Name(n))
176 };
177 let color = {
178 let n = ir.intern_name("/color");
179 ir.add_inst(Inst::Name(n))
180 };
181 let struct_name = ir.intern_name("fn:Struct");
182 let struct_type = ir.add_inst(Inst::ApplyFn {
183 function: struct_name,
184 args: vec![x, animal, y, color],
185 });
186
187 trie.collect(&ir, struct_type);
188 assert!(trie.contains("/x"));
189 assert!(trie.contains("/animal"));
190 assert!(trie.contains("/y"));
191 assert!(trie.contains("/color"));
192 }
193
194 #[test]
195 fn collect_from_tagged_union_skips_tags() {
196 let mut ir = Ir::new();
197 let mut trie = NameTrie::new();
198
199 let kind = {
201 let n = ir.intern_name("/kind");
202 ir.add_inst(Inst::Name(n))
203 };
204 let move_ = {
205 let n = ir.intern_name("/move");
206 ir.add_inst(Inst::Name(n))
207 };
208 let x = {
209 let n = ir.intern_name("/x");
210 ir.add_inst(Inst::Name(n))
211 };
212 let number = {
213 let n = ir.intern_name("/number");
214 ir.add_inst(Inst::Name(n))
215 };
216 let struct_name = ir.intern_name("fn:Struct");
217 let move_struct = ir.add_inst(Inst::ApplyFn {
218 function: struct_name,
219 args: vec![x, number],
220 });
221 let quit = {
222 let n = ir.intern_name("/quit");
223 ir.add_inst(Inst::Name(n))
224 };
225 let quit_struct = ir.add_inst(Inst::ApplyFn {
226 function: struct_name,
227 args: vec![],
228 });
229 let tu_name = ir.intern_name("fn:TaggedUnion");
230 let tu = ir.add_inst(Inst::ApplyFn {
231 function: tu_name,
232 args: vec![kind, move_, move_struct, quit, quit_struct],
233 });
234
235 trie.collect(&ir, tu);
236
237 assert!(trie.contains("/x"));
239 assert!(!trie.contains("/number"));
241 assert!(!trie.contains("/kind"));
243 assert!(!trie.contains("/move"));
244 assert!(!trie.contains("/quit"));
245 }
246}