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