open_vaf/
symbol.rs

1/*
2
3 * ******************************************************************************************
4 * Copyright (c) 2019 Pascal Kuthe. This file is part of the OpenVAF project.
5 * It is subject to the license terms in the LICENSE file found in the top-level directory
6 *  of this distribution and at  https://gitlab.com/DSPOM/OpenVAF/blob/master/LICENSE.
7 *  No part of OpenVAF, including this file, may be copied, modified, propagated, or
8 *  distributed except according to the terms contained in the LICENSE file.
9 * *****************************************************************************************
10
11 Adapted from https://github.com/rust-lang/rust src/librustc/symbol.rs under MIT-License
12
13    Permission is hereby granted, free of charge, to any person obtaining a copy
14    of this software and associated documentation files (the "Software"), to deal
15    in the Software without restriction, including without limitation the rights
16    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17    copies of the Software, and to permit persons to whom the Software is
18    furnished to do so, subject to the following conditions:
19
20    The above copyright notice and this permission notice shall be included in all
21    copies or substantial portions of the Software.
22
23    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
24    ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
25    TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
26    PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
27    SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
28    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
30    IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31    DEALINGS IN THE SOFTWARE.
32*/
33
34#![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    /// Constructs a new identifier from a symbol and a span.
60    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    /// Maps a string to an identifier with a dummy span.
83    pub fn from_str(string: &str) -> Self {
84        Self::new(Symbol::intern(string), DUMMY_SPAN)
85    }
86
87    /// Maps a string and a span to an identifier.
88    #[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    /// Convert the name to a `SymbolStr`. This is a slowish operation because
103    /// it requires locking the symbol interner.
104    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/// An interned string.
129///
130/// Internally, a `Symbol` is implemented as an index, and all operations
131/// (including hashing, equality, and ordering) operate on that index.
132#[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    /// Maps a string to its interned representation.
155    pub fn intern(string: &str) -> Self {
156        with_interner(|interner| interner.intern(string))
157    }
158
159    /// Access the symbol's chars. This is a slowish operation because it
160    /// requires locking the symbol interner.
161    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    /// Convert to a `SymbolStr`. This is a slowish operation because it
167    /// requires locking the symbol interner.
168    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// The `&'static str`s in this type actually point into the arena.
197#[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        // It is safe to extend the arena allocation to `'static` because we only access
223        // these while the arena is still alive.
224        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    // Get the symbol as a string. `Symbol::as_str()` should be used in
231    // preference to this function.
232    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/// An alternative to `Symbol`, useful when the chars within the symbol need to
292/// be accessed. It deliberately has limited functionality and should only be
293/// used for temporary values.
294///
295/// Because the interner outlives any thread which uses this type, we can
296/// safely treat `string` which points to interner data, as an immortal string,
297/// as long as this type never crosses between threads.
298//
299// by creating a new thread right after constructing the interner.
300#[derive(Clone, Eq, PartialOrd, Ord)]
301pub struct SymbolStr {
302    string: &'static str,
303}
304
305// This impl allows a `SymbolStr` to be directly equated with a `String` or
306// `&str`.
307impl<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
313/// This impl means that if `ss` is a `SymbolStr`:
314/// - `*ss` is a `str`;
315/// - `&*ss` is a `&str`;
316/// - `&ss as &str` is a `&str`, which means that `&ss` can be passed to a
317///   function expecting a `&str`.
318impl 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}