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