1#![allow(clippy::module_name_repetitions)]
35
36use core::fmt;
37use std::clone::Clone;
38use std::convert::Into;
39use std::hash::{Hash, Hasher};
40use std::iter::Iterator;
41use more_asserts::assert_le;
42
43use ahash::AHashMap;
44use bumpalo::Bump;
45
46use crate::symbol::keywords::EMPTY_SYMBOL;
47use crate::symbol::statics::with_interner;
48use crate::Span;
49
50#[derive(Copy, Clone, Debug)]
51pub struct Ident {
52 pub name: Symbol,
53 pub span: Span,
54}
55const DUMMY_SPAN: Span = Span::new_short_empty_span(0);
56impl Ident {
57 #[inline]
58 #[must_use]
59 pub const fn new(name: Symbol, span: Span) -> Self {
61 Self { name, span }
62 }
63
64 #[must_use]
65 pub const fn empty() -> Self {
66 Self {
67 name: EMPTY_SYMBOL,
68 span: DUMMY_SPAN,
69 }
70 }
71
72 #[must_use]
73 pub const fn spanned_empty(span: Span) -> Self {
74 Self {
75 name: EMPTY_SYMBOL,
76 span,
77 }
78 }
79
80 #[allow(clippy::should_implement_trait)]
81 #[must_use]
82 pub fn from_str(string: &str) -> Self {
84 Self::new(Symbol::intern(string), DUMMY_SPAN)
85 }
86
87 #[must_use]
89 pub fn from_str_and_span(string: &str, span: Span) -> Self {
90 Self::new(Symbol::intern(string), span)
91 }
92
93 #[must_use]
94 pub fn without_first_quote(self) -> Self {
95 Self::new(
96 Symbol::intern(self.as_str().trim_start_matches('\'')),
97 self.span,
98 )
99 }
100
101 #[must_use]
102 pub fn as_str(self) -> SymbolStr {
105 self.name.as_str()
106 }
107}
108
109impl PartialEq for Ident {
110 fn eq(&self, rhs: &Self) -> bool {
111 self.name == rhs.name
112 }
113}
114impl Eq for Ident {}
115
116impl Hash for Ident {
117 fn hash<H: Hasher>(&self, state: &mut H) {
118 self.name.hash(state);
119 }
120}
121
122impl fmt::Display for Ident {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 fmt::Display::fmt(&self.name, f)
125 }
126}
127
128#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
133pub struct Symbol(SymbolIndex);
134
135#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
136struct SymbolIndex(u32);
137impl SymbolIndex {
138 pub const fn as_usize(self) -> usize {
139 self.0 as usize
140 }
141
142 pub const fn as_u32(self) -> u32 {
143 self.0
144 }
145}
146
147impl Symbol {
148 #[must_use]
149 const fn new(n: u32) -> Self {
150 Self(SymbolIndex(n))
151 }
152
153 #[must_use]
154 pub fn intern(string: &str) -> Self {
156 with_interner(|interner| interner.intern(string))
157 }
158
159 pub fn with<F: FnOnce(&str) -> R, R>(self, f: F) -> R {
162 with_interner(|interner| f(interner.get(self)))
163 }
164
165 #[must_use]
166 pub fn as_str(self) -> SymbolStr {
169 with_interner(|interner| unsafe {
170 SymbolStr {
171 string: std::mem::transmute::<&str, &str>(interner.get(self)),
172 }
173 })
174 }
175
176 #[must_use]
177 pub const fn as_u32(self) -> u32 {
178 let index = self.0;
179 index.0
180 }
181}
182
183impl fmt::Debug for Symbol {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 self.with(|str| fmt::Debug::fmt(&str, f))?;
186 fmt::Debug::fmt(&self.0, f)
187 }
188}
189
190impl fmt::Display for Symbol {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 self.with(|str| fmt::Display::fmt(&str, f))
193 }
194}
195
196#[derive(Default)]
198pub struct Interner {
199 arena: Bump,
200 names: AHashMap<&'static str, Symbol>,
201 strings: Vec<&'static str>,
202}
203
204impl Interner {
205 fn prefill(init: &[&'static str]) -> Self {
206 Self {
207 strings: init.into(),
208 names: init.iter().copied().zip((0..).map(Symbol::new)).collect(),
209 ..Default::default()
210 }
211 }
212
213 pub fn intern(&mut self, string: &str) -> Symbol {
214 if let Some(&name) = self.names.get(string) {
215 return name;
216 }
217
218 assert_le!(self.strings.len(),u32::MAX as usize);
219 #[allow(clippy::cast_possible_truncation)]
220 let name = Symbol::new(self.strings.len() as u32);
221 let string = self.arena.alloc_str(string);
222 let string: &'static str = unsafe { &*(string as *const str) };
225 self.strings.push(string);
226 self.names.insert(string, name);
227 name
228 }
229
230 pub fn get(&self, symbol: Symbol) -> &str {
233 self.strings[symbol.0.as_usize()]
234 }
235}
236pub mod keywords {
237 use crate::symbol::{Symbol, SymbolIndex};
238
239 pub(super) static EMPTY_SYMBOL_STR: &str = " ";
240 pub const EMPTY_SYMBOL: Symbol = Symbol(SymbolIndex(0));
241
242 pub(super) static FLOW_STR: &str = "flow";
243 pub const FLOW: Symbol = Symbol(SymbolIndex(1));
244 pub(super) static POTENTIAL_STR: &str = "potential";
245 pub const POTENTIAL: Symbol = Symbol(SymbolIndex(2));
246 pub(super) static TEMPERATURE_STR: &str = "$temperature";
247 pub const TEMPERATURE: Symbol = Symbol(SymbolIndex(3));
248 pub(super) static ABSTOL_STR: &str = "abstol";
249 pub const ABSTOL: Symbol = Symbol(SymbolIndex(4));
250 pub(super) static ACCESS_STR: &str = "access";
251 pub const ACCESS: Symbol = Symbol(SymbolIndex(5));
252 pub(super) static UNITS_STR: &str = "units";
253 pub const UNITS: Symbol = Symbol(SymbolIndex(6));
254 pub(super) static IDT_NATURE_STR: &str = "idt_nature";
255 pub const IDT_NATURE: Symbol = Symbol(SymbolIndex(7));
256 pub(super) static DDT_NATURE_STR: &str = "ddt_nature";
257 pub const DDT_NATURE: Symbol = Symbol(SymbolIndex(8));
258 pub(super) static DESC_STR: &str = "desc";
259 pub const DESC: Symbol = Symbol(SymbolIndex(9));
260
261 pub(super) static IMPLICIT_SOLVER_STR: &str = "ImplicitFunctionSolver";
262 pub const IMPLICIT_SOLVER: Symbol = Symbol(SymbolIndex(9));
263}
264mod statics {
265 #![allow(clippy::wildcard_imports)]
266 use parking_lot::Mutex;
267 use crate::symbol::keywords::*;
268 use crate::symbol::Interner;
269
270 lazy_static! {
271 static ref INTERNER: Mutex<Interner> = Mutex::new(Interner::prefill(&[
272 EMPTY_SYMBOL_STR,
273 FLOW_STR,
274 POTENTIAL_STR,
275 TEMPERATURE_STR,
276 ABSTOL_STR,
277 ACCESS_STR,
278 UNITS_STR,
279 IDT_NATURE_STR,
280 DDT_NATURE_STR,
281 IMPLICIT_SOLVER_STR,
282 DESC_STR
283 ]));
284 }
285 #[inline]
286 pub(super) fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
287 f(&mut *INTERNER.lock())
288 }
289}
290
291#[derive(Clone, Eq, PartialOrd, Ord)]
301pub struct SymbolStr {
302 string: &'static str,
303}
304
305impl<T: std::ops::Deref<Target = str>> std::cmp::PartialEq<T> for SymbolStr {
308 fn eq(&self, other: &T) -> bool {
309 self.string == other.deref()
310 }
311}
312
313impl std::ops::Deref for SymbolStr {
319 type Target = str;
320 #[inline]
321 fn deref(&self) -> &str {
322 self.string
323 }
324}
325
326impl fmt::Debug for SymbolStr {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 fmt::Debug::fmt(self.string, f)
329 }
330}
331
332impl fmt::Display for SymbolStr {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 fmt::Display::fmt(self.string, f)
335 }
336}