ryo_analysis/query/
var_id.rs1use crate::symbol::SymbolId;
10use slotmap::{new_key_type, SecondaryMap, SlotMap};
11
12new_key_type! {
13 pub struct VarId;
22}
23
24#[derive(Debug, Clone, Default)]
48pub struct VarSymbolMapping {
49 var_to_symbol: SlotMap<VarId, Option<SymbolId>>,
52
53 symbol_to_var: SecondaryMap<SymbolId, VarId>,
55}
56
57impl VarSymbolMapping {
58 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn with_capacity(capacity: usize) -> Self {
65 Self {
66 var_to_symbol: SlotMap::with_capacity_and_key(capacity),
67 symbol_to_var: SecondaryMap::with_capacity(capacity),
68 }
69 }
70
71 pub fn register(&mut self, symbol_id: SymbolId) -> VarId {
75 if let Some(&var_id) = self.symbol_to_var.get(symbol_id) {
77 if self.var_to_symbol.contains_key(var_id) {
79 return var_id;
80 }
81 }
83
84 let var_id = self.var_to_symbol.insert(Some(symbol_id));
86 self.symbol_to_var.insert(symbol_id, var_id);
87 var_id
88 }
89
90 pub fn allocate(&mut self) -> VarId {
94 self.var_to_symbol.insert(None)
95 }
96
97 pub fn remove(&mut self, var_id: VarId) -> Option<SymbolId> {
101 if let Some(Some(symbol_id)) = self.var_to_symbol.remove(var_id) {
102 self.symbol_to_var.remove(symbol_id);
103 return Some(symbol_id);
104 }
105 None
106 }
107
108 pub fn remove_by_symbol(&mut self, symbol_id: SymbolId) -> Option<VarId> {
112 if let Some(var_id) = self.symbol_to_var.remove(symbol_id) {
113 self.var_to_symbol.remove(var_id);
114 Some(var_id)
115 } else {
116 None
117 }
118 }
119
120 #[inline]
124 pub fn to_symbol(&self, var_id: VarId) -> Option<SymbolId> {
125 self.var_to_symbol.get(var_id).copied().flatten()
126 }
127
128 #[inline]
132 pub fn to_var(&self, symbol_id: SymbolId) -> Option<VarId> {
133 self.symbol_to_var.get(symbol_id).copied()
134 }
135
136 #[inline]
138 pub fn contains_symbol(&self, symbol_id: SymbolId) -> bool {
139 self.symbol_to_var.contains_key(symbol_id)
140 }
141
142 #[inline]
144 pub fn contains_var(&self, var_id: VarId) -> bool {
145 self.var_to_symbol.contains_key(var_id)
146 }
147
148 #[inline]
150 pub fn len(&self) -> usize {
151 self.var_to_symbol.len()
152 }
153
154 #[inline]
156 pub fn is_empty(&self) -> bool {
157 self.var_to_symbol.is_empty()
158 }
159
160 pub fn iter(&self) -> impl Iterator<Item = (VarId, Option<SymbolId>)> + '_ {
162 self.var_to_symbol.iter().map(|(id, &sym)| (id, sym))
163 }
164
165 pub fn clear(&mut self) {
167 self.var_to_symbol.clear();
168 self.symbol_to_var.clear();
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use slotmap::SlotMap;
176
177 #[test]
178 fn test_var_symbol_mapping_register() {
179 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
180 let sym1 = symbols.insert("x");
181 let sym2 = symbols.insert("y");
182
183 let mut mapping = VarSymbolMapping::new();
184
185 let var1 = mapping.register(sym1);
186 let var2 = mapping.register(sym2);
187
188 assert_ne!(var1, var2);
189 assert_eq!(mapping.len(), 2);
190 }
191
192 #[test]
193 fn test_var_symbol_mapping_idempotent() {
194 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
195 let sym = symbols.insert("x");
196
197 let mut mapping = VarSymbolMapping::new();
198
199 let var1 = mapping.register(sym);
200 let var2 = mapping.register(sym); assert_eq!(var1, var2);
203 assert_eq!(mapping.len(), 1);
204 }
205
206 #[test]
207 fn test_var_symbol_mapping_bidirectional() {
208 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
209 let sym = symbols.insert("x");
210
211 let mut mapping = VarSymbolMapping::new();
212 let var = mapping.register(sym);
213
214 assert_eq!(mapping.to_symbol(var), Some(sym));
216
217 assert_eq!(mapping.to_var(sym), Some(var));
219 }
220
221 #[test]
222 fn test_var_symbol_mapping_contains() {
223 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
224 let sym1 = symbols.insert("x");
225 let sym2 = symbols.insert("y");
226
227 let mut mapping = VarSymbolMapping::new();
228 let var = mapping.register(sym1);
229
230 assert!(mapping.contains_symbol(sym1));
231 assert!(!mapping.contains_symbol(sym2));
232 assert!(mapping.contains_var(var));
233 }
234
235 #[test]
236 fn test_var_symbol_mapping_remove() {
237 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
238 let sym = symbols.insert("x");
239
240 let mut mapping = VarSymbolMapping::new();
241 let var = mapping.register(sym);
242
243 assert_eq!(mapping.len(), 1);
244 assert!(mapping.contains_var(var));
245
246 let removed = mapping.remove(var);
248 assert_eq!(removed, Some(sym));
249 assert_eq!(mapping.len(), 0);
250 assert!(!mapping.contains_var(var));
251 assert!(!mapping.contains_symbol(sym));
252 }
253
254 #[test]
255 fn test_var_symbol_mapping_remove_by_symbol() {
256 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
257 let sym = symbols.insert("x");
258
259 let mut mapping = VarSymbolMapping::new();
260 let var = mapping.register(sym);
261
262 let removed = mapping.remove_by_symbol(sym);
264 assert_eq!(removed, Some(var));
265 assert_eq!(mapping.len(), 0);
266 }
267
268 #[test]
269 fn test_var_symbol_mapping_iter() {
270 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
271 let sym1 = symbols.insert("x");
272 let sym2 = symbols.insert("y");
273
274 let mut mapping = VarSymbolMapping::new();
275 mapping.register(sym1);
276 mapping.register(sym2);
277
278 let pairs: Vec<_> = mapping.iter().collect();
279 assert_eq!(pairs.len(), 2);
280 }
281
282 #[test]
283 fn test_var_symbol_mapping_clear() {
284 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
285 let sym = symbols.insert("x");
286
287 let mut mapping = VarSymbolMapping::new();
288 mapping.register(sym);
289 assert!(!mapping.is_empty());
290
291 mapping.clear();
292 assert!(mapping.is_empty());
293 assert!(!mapping.contains_symbol(sym));
294 }
295
296 #[test]
297 fn test_var_symbol_mapping_reregister_after_remove() {
298 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
299 let sym = symbols.insert("x");
300
301 let mut mapping = VarSymbolMapping::new();
302 let var1 = mapping.register(sym);
303 mapping.remove(var1);
304
305 let var2 = mapping.register(sym);
307 assert_ne!(var1, var2); assert_eq!(mapping.len(), 1);
309 assert!(mapping.contains_var(var2));
310 }
311
312 #[test]
313 fn test_allocate_produces_distinct_ids() {
314 let mut mapping = VarSymbolMapping::new();
315
316 let v1 = mapping.allocate();
317 let v2 = mapping.allocate();
318 let v3 = mapping.allocate();
319
320 assert_ne!(v1, v2);
321 assert_ne!(v2, v3);
322 assert_ne!(v1, v3);
323 assert_eq!(mapping.len(), 3);
324
325 assert_eq!(mapping.to_symbol(v1), None);
327 assert_eq!(mapping.to_symbol(v2), None);
328 }
329
330 #[test]
331 fn test_allocate_and_register_coexist() {
332 let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
333 let sym = symbols.insert("x");
334
335 let mut mapping = VarSymbolMapping::new();
336 let anon = mapping.allocate();
337 let named = mapping.register(sym);
338
339 assert_ne!(anon, named);
340 assert_eq!(mapping.len(), 2);
341 assert_eq!(mapping.to_symbol(anon), None);
342 assert_eq!(mapping.to_symbol(named), Some(sym));
343 assert_eq!(mapping.to_var(sym), Some(named));
344 }
345
346 #[test]
347 fn test_remove_allocated_var() {
348 let mut mapping = VarSymbolMapping::new();
349 let v = mapping.allocate();
350 assert_eq!(mapping.len(), 1);
351
352 let removed = mapping.remove(v);
353 assert_eq!(removed, None); assert_eq!(mapping.len(), 0);
355 assert!(!mapping.contains_var(v));
356 }
357}