Skip to main content

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