leo_span/
symbol.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use crate::source_map::SourceMap;
18
19use core::{
20    borrow::Borrow,
21    cmp::PartialEq,
22    fmt,
23    hash::{Hash, Hasher},
24    num::NonZeroU32,
25    ops::Deref,
26    str,
27};
28use fxhash::FxBuildHasher;
29use indexmap::IndexSet;
30use serde::{Deserialize, Deserializer, Serialize, Serializer};
31use std::cell::RefCell;
32
33/// A helper for `symbols` defined below.
34/// The macro's job is to bind conveniently usable `const` items to the symbol names provided.
35/// For example, with `symbol { a, b }` you'd have `sym::a` and `sym::b`.
36macro_rules! consts {
37    ($val: expr, $sym:ident $(,)?) => {
38        #[allow(non_upper_case_globals)]
39        pub const $sym: $crate::symbol::Symbol = $crate::symbol::Symbol::new($val);
40    };
41    ($val: expr, $sym:ident: $_s:literal $(,)?) => {
42        consts!($val, $sym);
43    };
44    ($val: expr, $sym:ident: $_s:literal, $($rest:tt)*) => {
45        consts!($val, $sym);
46        consts!($val + 1, $($rest)*);
47    };
48    ($val: expr, $sym:ident, $($rest:tt)*) => {
49        consts!($val, $sym);
50        consts!($val + 1, $($rest)*);
51    };
52}
53
54/// A helper for `symbols` defined below.
55/// The macro's job is to merge all the hard-coded strings into a single array of strings.
56/// The strategy applied is [push-down accumulation](https://danielkeep.github.io/tlborm/book/pat-push-down-accumulation.html).
57macro_rules! strings {
58    // Final step 0) in the push-down accumulation.
59    // Here, the actual strings gathered in `$acc` are made into an array.
60    // We have to use this approach because e.g., `$e1 , $e2` is not recognized by any syntactic
61    // category in Rust, so a macro cannot produce it.
62    ([$($acc:expr),*] []) => {
63        [$($acc),*]
64    };
65    // Recursive case 1): Handles e.g., `x: "frodo"` and `y: "sam"`
66    // in `symbols! { x: "frodo", y: "sam", z }`.
67    ([$($acc:expr),*] [$_sym:ident: $string:literal, $($rest:tt)*]) => {
68        strings!([$($acc,)* $string] [$($rest)*])
69    };
70    // Recursive case 2): Handles e.g., `x` and `y` in `symbols! { x, y, z }`.
71    ([$($acc:expr),*] [$sym:ident, $($rest:tt)*]) => {
72        strings!([$($acc,)* stringify!($sym)] [$($rest)*])
73    };
74    // Base case 3): As below, but with a specified string, e.g., `symbols! { x, y: "gandalf" }`.
75    ([$($acc:expr),*] [$_sym:ident: $string:literal $(,)?]) => {
76        strings!([$($acc,)* $string] [])
77    };
78    // Base case 4): A single identifier left.
79    // So in e.g., `symbols! { x, y }`, this handles `y` with `x` already in `$acc`.
80    ([$($acc:expr),*] [$sym:ident $(,)?]) => {
81        strings!([$($acc,)* stringify!($sym)] [])
82    };
83}
84
85/// Creates predefined symbols used throughout the Leo compiler and language.
86/// Broadly speaking, any hard-coded string in the compiler should be defined here.
87///
88/// The macro accepts symbols separated by commas,
89/// and a symbol is either specified as a Rust identifier, in which case it is `stringify!`ed,
90/// or as `ident: "string"` where `"string"` is the actual hard-coded string.
91/// The latter case can be used when the hard-coded string is not a valid identifier.
92/// In either case, a `const $ident: Symbol` will be created that you can access as `sym::$ident`.
93macro_rules! symbols {
94    ($($symbols:tt)*) => {
95        const PRE_DEFINED: &[&str] = &strings!([] [$($symbols)*]);
96
97        pub mod sym {
98            consts!(0, $($symbols)*);
99        }
100    };
101}
102
103symbols! {
104    // unary operators
105    abs,
106    abs_wrapped,
107    double,
108    inv,
109    neg,
110    not,
111    square,
112    square_root,
113
114    // binary operators
115    add,
116    add_wrapped,
117    and,
118    div,
119    div_wrapped,
120    eq,
121    gte,
122    gt,
123    lte,
124    lt,
125    Mod: "mod",
126    mul,
127    mul_wrapped,
128    nand,
129    neq,
130    nor,
131    or,
132    pow,
133    pow_wrapped,
134    rem,
135    rem_wrapped,
136    shl,
137    shl_wrapped,
138    shr,
139    shr_wrapped,
140    sub,
141    sub_wrapped,
142    xor,
143
144    // core constants
145    GEN,
146
147    // core functions
148    BHP256,
149    BHP512,
150    BHP768,
151    BHP1024,
152    ChaCha,
153    commit_to_address,
154    commit_to_field,
155    commit_to_group,
156    contains,
157    get,
158    get_or_use,
159    hash_to_address,
160    hash_to_field,
161    hash_to_group,
162    hash_to_i8,
163    hash_to_i16,
164    hash_to_i32,
165    hash_to_i64,
166    hash_to_i128,
167    hash_to_u8,
168    hash_to_u16,
169    hash_to_u32,
170    hash_to_u64,
171    hash_to_u128,
172    hash_to_scalar,
173    Keccak256,
174    Keccak384,
175    Keccak512,
176    Mapping,
177    Pedersen64,
178    Pedersen128,
179    Poseidon2,
180    Poseidon4,
181    Poseidon8,
182    rand_address,
183    rand_bool,
184    rand_field,
185    rand_group,
186    rand_i8,
187    rand_i16,
188    rand_i32,
189    rand_i64,
190    rand_i128,
191    rand_scalar,
192    rand_u8,
193    rand_u16,
194    rand_u32,
195    rand_u64,
196    rand_u128,
197    remove,
198    set,
199    SHA3_256,
200    SHA3_384,
201    SHA3_512,
202    to_x_coordinate,
203    to_y_coordinate,
204    verify,
205    Await: "await",
206
207    // CheatCodes
208    CheatCode,
209    print_mapping,
210    set_block_height,
211
212    // types
213    address,
214    bool,
215    field,
216    group,
217    i8,
218    i16,
219    i32,
220    i64,
221    i128,
222    Future,
223    Fn,
224    record,
225    scalar,
226    signature,
227    string,
228    Struct: "struct",
229    u8,
230    u16,
231    u32,
232    u64,
233    u128,
234
235    // values
236    False: "false",
237    True: "true",
238
239    // annotations
240    should_fail,
241    test,
242
243    // annotation keys
244    private_key,
245
246    // general keywords
247    As: "as",
248    assert,
249    assert_eq,
250    assert_neq,
251    caller,
252    Const: "const",
253    constant,
254    decrement,
255    Else: "else",
256    For: "for",
257    function,
258    If: "if",
259    In: "in",
260    import,
261    increment,
262    inline,
263    input,
264    Let: "let",
265    leo,
266    main,
267    mapping,
268    Mut: "mut",
269    Return: "return",
270    script,
271    SelfLower: "self",
272    SelfUpper: "Self",
273    signer,
274    Star: "*",
275    transition,
276    Type: "type",
277
278    aleo,
279    public,
280    private,
281    owner,
282    _nonce,
283    program,
284    stub,
285    block,
286    height,
287    network,
288    id,
289}
290
291/// An interned string.
292///
293/// Represented as an index internally, with all operations based on that.
294/// A `Symbol` reserves the value `0`, so that `Option<Symbol>` only takes up 4 bytes.
295#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
296pub struct Symbol(
297    #[serde(deserialize_with = "Symbol::serde_to_symbol")]
298    #[serde(serialize_with = "Symbol::serde_from_symbol")]
299    NonZeroU32,
300);
301
302impl Default for Symbol {
303    fn default() -> Self {
304        Symbol(NonZeroU32::MIN)
305    }
306}
307
308impl Symbol {
309    /// Returns the corresponding `Symbol` for the given `index`.
310    pub const fn new(index: u32) -> Self {
311        let index = index.saturating_add(1);
312        Self(match NonZeroU32::new(index) {
313            None => unreachable!(),
314            Some(x) => x,
315        })
316    }
317
318    /// Maps a string to its interned representation.
319    pub fn intern(string: &str) -> Self {
320        with_session_globals(|session_globals| session_globals.symbol_interner.intern(string))
321    }
322
323    /// Convert to effectively a `&'static str` given the `SessionGlobals`.
324    pub fn as_str<R>(self, s: &SessionGlobals, with: impl FnOnce(&str) -> R) -> R {
325        s.symbol_interner.get(self, with)
326    }
327
328    /// Converts this symbol to the raw index.
329    pub const fn as_u32(self) -> u32 {
330        self.0.get() - 1
331    }
332
333    fn serde_to_symbol<'de, D: Deserializer<'de>>(de: D) -> Result<NonZeroU32, D::Error> {
334        Ok(Symbol::intern(<&str>::deserialize(de)?).0)
335    }
336
337    fn serde_from_symbol<S: Serializer>(index: &NonZeroU32, ser: S) -> Result<S::Ok, S::Error> {
338        with_session_globals(|sg| Self(*index).as_str(sg, |s| ser.serialize_str(s)))
339    }
340}
341
342impl fmt::Debug for Symbol {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        with_session_globals(|s| self.as_str(s, |s| fmt::Debug::fmt(s, f)))
345    }
346}
347
348impl fmt::Display for Symbol {
349    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350        with_session_globals(|s| self.as_str(s, |s| fmt::Display::fmt(s, f)))
351    }
352}
353
354/// All the globals for a compiler sessions.
355pub struct SessionGlobals {
356    /// The interner for `Symbol`s used in the compiler.
357    symbol_interner: Interner,
358    /// The source map used in the compiler.
359    pub source_map: SourceMap,
360}
361
362impl Default for SessionGlobals {
363    fn default() -> Self {
364        Self { symbol_interner: Interner::prefilled(), source_map: SourceMap::default() }
365    }
366}
367
368scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
369
370/// Creates the session globals and then runs the closure `f`.
371#[inline]
372pub fn create_session_if_not_set_then<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
373    if !SESSION_GLOBALS.is_set() {
374        let sg = SessionGlobals::default();
375        SESSION_GLOBALS.set(&sg, || SESSION_GLOBALS.with(f))
376    } else {
377        SESSION_GLOBALS.with(f)
378    }
379}
380
381/// Gives access to read or modify the session globals in `f`.
382#[inline]
383pub fn with_session_globals<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
384    SESSION_GLOBALS.with(f)
385}
386
387/// An interned string,
388/// either prefilled "at compile time" (`Static`),
389/// or created at runtime (`Owned`).
390#[derive(Eq)]
391enum InternedStr {
392    /// String is stored "at compile time", i.e. prefilled.
393    Static(&'static str),
394    /// String is constructed and stored during runtime.
395    Owned(Box<str>),
396}
397
398impl Borrow<str> for InternedStr {
399    fn borrow(&self) -> &str {
400        self.deref()
401    }
402}
403
404impl Deref for InternedStr {
405    type Target = str;
406
407    fn deref(&self) -> &Self::Target {
408        match self {
409            Self::Static(s) => s,
410            Self::Owned(s) => s,
411        }
412    }
413}
414
415impl PartialEq for InternedStr {
416    fn eq(&self, other: &InternedStr) -> bool {
417        self.deref() == other.deref()
418    }
419}
420
421impl Hash for InternedStr {
422    fn hash<H: Hasher>(&self, state: &mut H) {
423        self.deref().hash(state);
424    }
425}
426
427/// The inner interner.
428/// This construction is used to get interior mutability in `Interner`.
429struct InnerInterner {
430    // /// Arena used to allocate the strings, giving us `&'static str`s from it.
431    // arena: DroplessArena,
432    /// Registration of strings and symbol index allocation is done in this set.
433    set: IndexSet<InternedStr, FxBuildHasher>,
434}
435
436/// A symbol-to-string interner.
437struct Interner {
438    inner: RefCell<InnerInterner>,
439}
440
441impl Interner {
442    /// Returns an interner prefilled with commonly used strings in Leo.
443    fn prefilled() -> Self {
444        Self::prefill(PRE_DEFINED)
445    }
446
447    /// Returns an interner prefilled with `init`.
448    fn prefill(init: &[&'static str]) -> Self {
449        let inner = InnerInterner {
450            // arena: <_>::default(),
451            set: init.iter().copied().map(InternedStr::Static).collect(),
452        };
453        Self { inner: RefCell::new(inner) }
454    }
455
456    /// Interns `string`, returning a `Symbol` corresponding to it.
457    fn intern(&self, string: &str) -> Symbol {
458        let InnerInterner { set } = &mut *self.inner.borrow_mut();
459
460        if let Some(sym) = set.get_index_of(string) {
461            // Already interned, return that symbol.
462            return Symbol::new(sym as u32);
463        }
464
465        Symbol::new(set.insert_full(InternedStr::Owned(string.into())).0 as u32)
466    }
467
468    /// Returns the corresponding string for the given symbol.
469    fn get<R>(&self, symbol: Symbol, with: impl FnOnce(&str) -> R) -> R {
470        let set = &self.inner.borrow().set;
471        with(set.get_index(symbol.as_u32() as usize).unwrap())
472    }
473}