zscript_parser/
interner.rs1use once_cell::sync::Lazy;
2use parking_lot::RwLock;
3use std::collections::HashMap;
4
5#[cfg(feature = "serialize")]
6use serde::{Serialize, Serializer};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
9pub struct Symbol<const CASE_SENSITIVE: bool>(usize);
10
11#[derive(Debug, Clone, Default)]
12pub struct Interner<const CASE_SENSITIVE: bool> {
13 symbol_map: HashMap<Box<str>, Symbol<CASE_SENSITIVE>>,
14 string_map: Vec<Box<str>>,
15}
16
17impl<const CASE_SENSITIVE: bool> std::fmt::Display for Interner<CASE_SENSITIVE> {
18 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
19 writeln!(f, "{{")?;
20 for (i, s) in self.string_map.iter().enumerate() {
21 writeln!(f, " {} => {:?},", i, s)?;
22 }
23 write!(f, "}}")?;
24 Ok(())
25 }
26}
27
28impl<const CASE_SENSITIVE: bool> Interner<CASE_SENSITIVE> {
29 pub fn interned(&mut self, string: &str) -> Symbol<CASE_SENSITIVE> {
30 if CASE_SENSITIVE {
31 if let Some(s) = self.symbol_map.get(&*string) {
32 return *s;
33 }
34 } else if let Some(s) = self.symbol_map.get(&*string.to_lowercase()) {
35 return *s;
36 }
37
38 let new_sym = Symbol(self.string_map.len());
39 if CASE_SENSITIVE {
40 self.symbol_map
41 .insert(string.to_string().into_boxed_str(), new_sym);
42 } else {
43 self.symbol_map
44 .insert(string.to_lowercase().into_boxed_str(), new_sym);
45 }
46 self.string_map.push(string.to_string().into_boxed_str());
47
48 new_sym
49 }
50
51 pub fn try_interned(&self, string: &str) -> Option<Symbol<CASE_SENSITIVE>> {
52 if CASE_SENSITIVE {
53 if let Some(s) = self.symbol_map.get(&*string) {
54 return Some(*s);
55 }
56 } else if let Some(s) = self.symbol_map.get(&*string.to_lowercase()) {
57 return Some(*s);
58 }
59 None
60 }
61
62 pub fn string(&self, symbol: Symbol<CASE_SENSITIVE>) -> &str {
63 &self.string_map[symbol.0]
64 }
65}
66
67static NAME_INTERNER: Lazy<RwLock<NameInterner>> = Lazy::new(|| {
68 RwLock::new(NameInterner {
69 symbol_map: HashMap::new(),
70 string_map: vec![],
71 })
72});
73static STRING_INTERNER: Lazy<RwLock<StringInterner>> = Lazy::new(|| {
74 RwLock::new(StringInterner {
75 symbol_map: HashMap::new(),
76 string_map: vec![],
77 })
78});
79
80pub fn intern_name(s: &str) -> NameSymbol {
81 {
84 let l = NAME_INTERNER.read();
85 if let Some(s) = l.try_interned(s) {
86 return s;
87 }
88 }
89 {
90 let mut l = NAME_INTERNER.write();
91 l.interned(s)
92 }
93}
94pub fn intern_string(s: &str) -> StringSymbol {
95 {
98 let l = STRING_INTERNER.read();
99 if let Some(s) = l.try_interned(s) {
100 return s;
101 }
102 }
103 {
104 let mut l = STRING_INTERNER.write();
105 l.interned(s)
106 }
107}
108
109pub type NameInterner = Interner<false>;
110pub type NameSymbol = Symbol<false>;
111
112impl std::fmt::Display for NameSymbol {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "{:?}", NAME_INTERNER.read().string(*self))
115 }
116}
117
118impl NameSymbol {
119 pub fn string(&self) -> impl std::ops::Deref<Target = str> + 'static {
120 parking_lot::RwLockReadGuard::map(NAME_INTERNER.read(), |s| s.string(*self))
121 }
122}
123
124#[cfg(feature = "serialize")]
125impl Serialize for NameSymbol {
126 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127 where
128 S: Serializer,
129 {
130 serializer.serialize_str(NAME_INTERNER.read().string(*self))
131 }
132}
133
134pub type StringInterner = Interner<true>;
135pub type StringSymbol = Symbol<true>;
136
137impl std::fmt::Display for StringSymbol {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "{:?}", STRING_INTERNER.read().string(*self))
140 }
141}
142
143impl StringSymbol {
144 pub fn string(&self) -> impl std::ops::Deref<Target = str> + 'static {
145 parking_lot::RwLockReadGuard::map(STRING_INTERNER.read(), |s| s.string(*self))
146 }
147}
148
149#[cfg(feature = "serialize")]
150impl Serialize for StringSymbol {
151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152 where
153 S: Serializer,
154 {
155 serializer.serialize_str(STRING_INTERNER.read().string(*self))
156 }
157}