zscript_parser/
interner.rs

1use 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    // try to get the interned symbol without having to mutably lock,
82    // so that threads will only block others when they see a new symbol
83    {
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    // try to get the interned symbol without having to mutably lock,
96    // so that threads will only block others when they see a new symbol
97    {
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}