patch_prolog_shared/
interner.rs1use crate::atom::{AtomId, WELL_KNOWN_ATOMS};
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
13pub struct StringInterner {
14 to_id: HashMap<String, AtomId>,
15 to_str: Vec<String>,
16}
17
18impl StringInterner {
19 pub fn new() -> Self {
20 let mut interner = StringInterner {
21 to_id: HashMap::new(),
22 to_str: Vec::new(),
23 };
24 for name in WELL_KNOWN_ATOMS {
25 interner.intern(name);
26 }
27 interner
28 }
29
30 pub fn intern(&mut self, s: &str) -> AtomId {
32 if let Some(&id) = self.to_id.get(s) {
33 return id;
34 }
35 let id = self.to_str.len() as AtomId;
36 self.to_str.push(s.to_string());
37 self.to_id.insert(s.to_string(), id);
38 id
39 }
40
41 pub fn resolve(&self, id: AtomId) -> &str {
43 &self.to_str[id as usize]
44 }
45
46 pub fn try_resolve(&self, id: AtomId) -> Option<&str> {
48 self.to_str.get(id as usize).map(|s| s.as_str())
49 }
50
51 pub fn lookup(&self, s: &str) -> Option<AtomId> {
53 self.to_id.get(s).copied()
54 }
55
56 pub fn len(&self) -> usize {
57 self.to_str.len()
58 }
59
60 pub fn is_empty(&self) -> bool {
61 self.to_str.is_empty()
62 }
63
64 pub fn iter(&self) -> impl Iterator<Item = &str> {
66 self.to_str.iter().map(|s| s.as_str())
67 }
68}
69
70impl Default for StringInterner {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::atom::{ATOM_DOT, ATOM_NIL, ATOM_TRUE};
80
81 #[test]
82 fn well_known_atoms_have_fixed_ids() {
83 let mut i = StringInterner::new();
84 assert_eq!(i.intern("[]"), ATOM_NIL);
85 assert_eq!(i.intern("."), ATOM_DOT);
86 assert_eq!(i.intern("true"), ATOM_TRUE);
87 assert_eq!(i.resolve(ATOM_NIL), "[]");
88 }
89
90 #[test]
91 fn test_string_interner_basic() {
92 let mut interner = StringInterner::new();
93 let a = interner.intern("hello");
94 let b = interner.intern("world");
95 let c = interner.intern("hello"); assert_eq!(a, c);
98 assert_ne!(a, b);
99 assert_eq!(interner.resolve(a), "hello");
100 assert_eq!(interner.resolve(b), "world");
101 }
102
103 #[test]
104 fn test_string_interner_lookup() {
105 let mut interner = StringInterner::new();
106 let id = interner.intern("foo");
107
108 assert_eq!(interner.lookup("foo"), Some(id));
109 assert_eq!(interner.lookup("bar"), None);
110 }
111}