midenc_hir_symbol/
lib.rs

1#![no_std]
2#![deny(warnings)]
3
4extern crate alloc;
5#[cfg(feature = "std")]
6extern crate std;
7
8pub mod sync;
9
10use alloc::{
11    boxed::Box,
12    collections::BTreeMap,
13    string::{String, ToString},
14    vec::Vec,
15};
16use core::{fmt, mem, ops::Deref, str};
17
18use miden_formatting::prettier::PrettyPrint;
19
20pub mod symbols {
21    include!(env!("SYMBOLS_RS"));
22}
23
24static SYMBOL_TABLE: sync::LazyLock<SymbolTable> = sync::LazyLock::new(SymbolTable::default);
25
26#[derive(Default)]
27struct SymbolTable {
28    interner: sync::RwLock<Interner>,
29}
30
31/// A symbol is an interned string.
32#[derive(Clone, Copy, PartialEq, Eq, Hash)]
33pub struct Symbol(SymbolIndex);
34
35#[cfg(feature = "serde")]
36impl serde::Serialize for Symbol {
37    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
38    where
39        S: serde::Serializer,
40    {
41        self.as_str().serialize(serializer)
42    }
43}
44
45#[cfg(feature = "serde")]
46impl<'de> serde::Deserialize<'de> for Symbol {
47    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
48    where
49        D: serde::Deserializer<'de>,
50    {
51        struct SymbolVisitor;
52        impl serde::de::Visitor<'_> for SymbolVisitor {
53            type Value = Symbol;
54
55            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
56                formatter.write_str("symbol")
57            }
58
59            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
60            where
61                E: serde::de::Error,
62            {
63                Ok(Symbol::intern(v))
64            }
65        }
66        deserializer.deserialize_str(SymbolVisitor)
67    }
68}
69
70impl Symbol {
71    #[inline]
72    pub const fn new(n: u32) -> Self {
73        Self(SymbolIndex::new(n))
74    }
75
76    /// Maps a string to its interned representation.
77    pub fn intern(string: impl ToString) -> Self {
78        let string = string.to_string();
79        with_interner(|interner| interner.intern(string))
80    }
81
82    pub fn as_str(self) -> &'static str {
83        with_read_only_interner(|interner| unsafe {
84            // This is safe because the interned string will live for the
85            // lifetime of the program
86            mem::transmute::<&str, &'static str>(interner.get(self))
87        })
88    }
89
90    #[inline]
91    pub fn as_u32(self) -> u32 {
92        self.0.as_u32()
93    }
94
95    #[inline]
96    pub fn as_usize(self) -> usize {
97        self.0.as_usize()
98    }
99
100    /// Returns true if this symbol is a keyword in the IR textual format
101    #[inline]
102    pub fn is_keyword(self) -> bool {
103        symbols::is_keyword(self)
104    }
105}
106impl fmt::Debug for Symbol {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        write!(f, "{}({:?})", self, self.0)
109    }
110}
111impl fmt::Display for Symbol {
112    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113        fmt::Display::fmt(&self.as_str(), f)
114    }
115}
116impl PrettyPrint for Symbol {
117    fn render(&self) -> miden_formatting::prettier::Document {
118        use miden_formatting::prettier::*;
119        const_text(self.as_str())
120    }
121}
122impl PartialOrd for Symbol {
123    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
124        Some(self.cmp(other))
125    }
126}
127impl Ord for Symbol {
128    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
129        self.as_str().cmp(other.as_str())
130    }
131}
132impl AsRef<str> for Symbol {
133    #[inline(always)]
134    fn as_ref(&self) -> &str {
135        self.as_str()
136    }
137}
138impl core::borrow::Borrow<str> for Symbol {
139    #[inline(always)]
140    fn borrow(&self) -> &str {
141        self.as_str()
142    }
143}
144impl<T: Deref<Target = str>> PartialEq<T> for Symbol {
145    fn eq(&self, other: &T) -> bool {
146        self.as_str() == other.deref()
147    }
148}
149impl From<&'static str> for Symbol {
150    fn from(s: &'static str) -> Self {
151        with_interner(|interner| interner.insert(s))
152    }
153}
154impl From<String> for Symbol {
155    fn from(s: String) -> Self {
156        Self::intern(s)
157    }
158}
159impl From<Box<str>> for Symbol {
160    fn from(s: Box<str>) -> Self {
161        Self::intern(s)
162    }
163}
164impl From<alloc::borrow::Cow<'static, str>> for Symbol {
165    fn from(s: alloc::borrow::Cow<'static, str>) -> Self {
166        use alloc::borrow::Cow;
167        match s {
168            Cow::Borrowed(s) => s.into(),
169            Cow::Owned(s) => Self::intern(s),
170        }
171    }
172}
173#[cfg(feature = "compact_str")]
174impl From<compact_str::CompactString> for Symbol {
175    fn from(s: compact_str::CompactString) -> Self {
176        Self::intern(s.into_string())
177    }
178}
179
180#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
181struct SymbolIndex(u32);
182impl SymbolIndex {
183    // shave off 256 indices at the end to allow space for packing these indices into enums
184    pub const MAX_AS_U32: u32 = 0xffff_ff00;
185
186    #[inline]
187    const fn new(n: u32) -> Self {
188        assert!(n <= Self::MAX_AS_U32, "out of range value used");
189
190        SymbolIndex(n)
191    }
192
193    #[inline]
194    pub fn as_u32(self) -> u32 {
195        self.0
196    }
197
198    #[inline]
199    pub fn as_usize(self) -> usize {
200        self.0 as usize
201    }
202}
203impl From<SymbolIndex> for u32 {
204    #[inline]
205    fn from(v: SymbolIndex) -> u32 {
206        v.as_u32()
207    }
208}
209impl From<SymbolIndex> for usize {
210    #[inline]
211    fn from(v: SymbolIndex) -> usize {
212        v.as_usize()
213    }
214}
215
216struct Interner {
217    pub names: BTreeMap<&'static str, Symbol>,
218    pub strings: Vec<&'static str>,
219}
220
221impl Default for Interner {
222    fn default() -> Self {
223        let mut this = Self {
224            names: BTreeMap::default(),
225            strings: Vec::with_capacity(symbols::__SYMBOLS.len()),
226        };
227        for (sym, s) in symbols::__SYMBOLS {
228            this.names.insert(s, *sym);
229            this.strings.push(s);
230        }
231        this
232    }
233}
234
235impl Interner {
236    pub fn intern(&mut self, string: String) -> Symbol {
237        if let Some(&name) = self.names.get(string.as_str()) {
238            return name;
239        }
240
241        let name = Symbol::new(self.strings.len() as u32);
242
243        let string = string.into_boxed_str();
244        let string: &'static str = Box::leak(string);
245        self.strings.push(string);
246        self.names.insert(string, name);
247        name
248    }
249
250    pub fn insert(&mut self, s: &'static str) -> Symbol {
251        if let Some(&name) = self.names.get(s) {
252            return name;
253        }
254        let name = Symbol::new(self.strings.len() as u32);
255        self.strings.push(s);
256        self.names.insert(s, name);
257        name
258    }
259
260    pub fn get(&self, symbol: Symbol) -> &'static str {
261        self.strings[symbol.0.as_usize()]
262    }
263}
264
265// If an interner exists, return it. Otherwise, prepare a fresh one.
266#[inline]
267fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
268    let mut table = SYMBOL_TABLE.interner.write();
269    f(&mut table)
270}
271
272#[inline]
273fn with_read_only_interner<T, F: FnOnce(&Interner) -> T>(f: F) -> T {
274    let table = SYMBOL_TABLE.interner.read();
275    f(&table)
276}