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