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    noupgrade,
243    custom,
244    admin,
245    key,
246
247    // annotation keys
248    private_key,
249
250    // general keywords
251    As: "as",
252    assert,
253    assert_eq,
254    assert_neq,
255    Async: "async",
256    caller,
257    Const: "const",
258    constant,
259    constructor,
260    decrement,
261    Else: "else",
262    For: "for",
263    function,
264    If: "if",
265    In: "in",
266    import,
267    increment,
268    inline,
269    input,
270    Let: "let",
271    leo,
272    main,
273    mapping,
274    Mut: "mut",
275    Return: "return",
276    script,
277    SelfLower: "self",
278    SelfUpper: "Self",
279    signer,
280    Star: "*",
281    transition,
282    Type: "type",
283
284    aleo,
285    public,
286    private,
287    owner,
288    _nonce,
289    program,
290    ProgramCore: "Program",
291    stub,
292    block,
293    height,
294    network,
295    id,
296    checksum,
297    edition,
298    program_owner,
299}
300
301/// An interned string.
302///
303/// Represented as an index internally, with all operations based on that.
304/// A `Symbol` reserves the value `0`, so that `Option<Symbol>` only takes up 4 bytes.
305#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
306pub struct Symbol(
307    #[serde(deserialize_with = "Symbol::serde_to_symbol")]
308    #[serde(serialize_with = "Symbol::serde_from_symbol")]
309    NonZeroU32,
310);
311
312impl Default for Symbol {
313    fn default() -> Self {
314        Symbol(NonZeroU32::MIN)
315    }
316}
317
318impl Symbol {
319    /// Returns the corresponding `Symbol` for the given `index`.
320    pub const fn new(index: u32) -> Self {
321        let index = index.saturating_add(1);
322        Self(match NonZeroU32::new(index) {
323            None => unreachable!(),
324            Some(x) => x,
325        })
326    }
327
328    /// Maps a string to its interned representation.
329    pub fn intern(string: &str) -> Self {
330        with_session_globals(|session_globals| session_globals.symbol_interner.intern(string))
331    }
332
333    /// Convert to effectively a `&'static str` given the `SessionGlobals`.
334    pub fn as_str<R>(self, s: &SessionGlobals, with: impl FnOnce(&str) -> R) -> R {
335        s.symbol_interner.get(self, with)
336    }
337
338    /// Converts this symbol to the raw index.
339    pub const fn as_u32(self) -> u32 {
340        self.0.get() - 1
341    }
342
343    fn serde_to_symbol<'de, D: Deserializer<'de>>(de: D) -> Result<NonZeroU32, D::Error> {
344        Ok(Symbol::intern(<&str>::deserialize(de)?).0)
345    }
346
347    fn serde_from_symbol<S: Serializer>(index: &NonZeroU32, ser: S) -> Result<S::Ok, S::Error> {
348        with_session_globals(|sg| Self(*index).as_str(sg, |s| ser.serialize_str(s)))
349    }
350}
351
352impl fmt::Debug for Symbol {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        with_session_globals(|s| self.as_str(s, |s| fmt::Debug::fmt(s, f)))
355    }
356}
357
358impl fmt::Display for Symbol {
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        with_session_globals(|s| self.as_str(s, |s| fmt::Display::fmt(s, f)))
361    }
362}
363
364/// All the globals for a compiler sessions.
365pub struct SessionGlobals {
366    /// The interner for `Symbol`s used in the compiler.
367    symbol_interner: Interner,
368    /// The source map used in the compiler.
369    pub source_map: SourceMap,
370}
371
372impl Default for SessionGlobals {
373    fn default() -> Self {
374        Self { symbol_interner: Interner::prefilled(), source_map: SourceMap::default() }
375    }
376}
377
378scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
379
380/// Creates the session globals and then runs the closure `f`.
381#[inline]
382pub fn create_session_if_not_set_then<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
383    if !SESSION_GLOBALS.is_set() {
384        let sg = SessionGlobals::default();
385        SESSION_GLOBALS.set(&sg, || SESSION_GLOBALS.with(f))
386    } else {
387        SESSION_GLOBALS.with(f)
388    }
389}
390
391/// Gives access to read or modify the session globals in `f`.
392#[inline]
393pub fn with_session_globals<R>(f: impl FnOnce(&SessionGlobals) -> R) -> R {
394    SESSION_GLOBALS.with(f)
395}
396
397/// An interned string,
398/// either prefilled "at compile time" (`Static`),
399/// or created at runtime (`Owned`).
400#[derive(Eq)]
401enum InternedStr {
402    /// String is stored "at compile time", i.e. prefilled.
403    Static(&'static str),
404    /// String is constructed and stored during runtime.
405    Owned(Box<str>),
406}
407
408impl Borrow<str> for InternedStr {
409    fn borrow(&self) -> &str {
410        self.deref()
411    }
412}
413
414impl Deref for InternedStr {
415    type Target = str;
416
417    fn deref(&self) -> &Self::Target {
418        match self {
419            Self::Static(s) => s,
420            Self::Owned(s) => s,
421        }
422    }
423}
424
425impl PartialEq for InternedStr {
426    fn eq(&self, other: &InternedStr) -> bool {
427        self.deref() == other.deref()
428    }
429}
430
431impl Hash for InternedStr {
432    fn hash<H: Hasher>(&self, state: &mut H) {
433        self.deref().hash(state);
434    }
435}
436
437/// The inner interner.
438/// This construction is used to get interior mutability in `Interner`.
439struct InnerInterner {
440    // /// Arena used to allocate the strings, giving us `&'static str`s from it.
441    // arena: DroplessArena,
442    /// Registration of strings and symbol index allocation is done in this set.
443    set: IndexSet<InternedStr, FxBuildHasher>,
444}
445
446/// A symbol-to-string interner.
447struct Interner {
448    inner: RefCell<InnerInterner>,
449}
450
451impl Interner {
452    /// Returns an interner prefilled with commonly used strings in Leo.
453    fn prefilled() -> Self {
454        Self::prefill(PRE_DEFINED)
455    }
456
457    /// Returns an interner prefilled with `init`.
458    fn prefill(init: &[&'static str]) -> Self {
459        let inner = InnerInterner {
460            // arena: <_>::default(),
461            set: init.iter().copied().map(InternedStr::Static).collect(),
462        };
463        Self { inner: RefCell::new(inner) }
464    }
465
466    /// Interns `string`, returning a `Symbol` corresponding to it.
467    fn intern(&self, string: &str) -> Symbol {
468        let InnerInterner { set } = &mut *self.inner.borrow_mut();
469
470        if let Some(sym) = set.get_index_of(string) {
471            // Already interned, return that symbol.
472            return Symbol::new(sym as u32);
473        }
474
475        Symbol::new(set.insert_full(InternedStr::Owned(string.into())).0 as u32)
476    }
477
478    /// Returns the corresponding string for the given symbol.
479    fn get<R>(&self, symbol: Symbol, with: impl FnOnce(&str) -> R) -> R {
480        let set = &self.inner.borrow().set;
481        with(set.get_index(symbol.as_u32() as usize).unwrap())
482    }
483}