1use crate::{SessionGlobals, Span};
2use solar_data_structures::{index::BaseIndex32, trustme};
3use solar_macros::symbols;
4use std::{cmp, fmt, hash, str};
5
6#[derive(Clone, Copy)]
8pub struct Ident {
9 pub name: Symbol,
11 pub span: Span,
13}
14
15impl Default for Ident {
16 #[inline]
17 fn default() -> Self {
18 Self::DUMMY
19 }
20}
21
22impl PartialEq for Ident {
23 #[inline]
24 fn eq(&self, rhs: &Self) -> bool {
25 self.name == rhs.name
26 }
27}
28
29impl Eq for Ident {}
30
31impl PartialOrd for Ident {
32 #[inline]
33 fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
34 Some(self.cmp(rhs))
35 }
36}
37
38impl Ord for Ident {
39 #[inline]
40 fn cmp(&self, rhs: &Self) -> cmp::Ordering {
41 self.name.cmp(&rhs.name)
42 }
43}
44
45impl hash::Hash for Ident {
46 #[inline]
47 fn hash<H: hash::Hasher>(&self, state: &mut H) {
48 self.name.hash(state);
49 }
50}
51
52impl fmt::Debug for Ident {
53 #[inline]
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 fmt::Display::fmt(self, f)
56 }
57}
58
59impl fmt::Display for Ident {
60 #[inline]
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 self.name.fmt(f)
63 }
64}
65
66impl Ident {
67 pub const DUMMY: Self = Self::new(Symbol::DUMMY, Span::DUMMY);
69
70 #[inline]
72 pub const fn new(name: Symbol, span: Span) -> Self {
73 Self { name, span }
74 }
75
76 #[inline]
78 pub const fn with_dummy_span(name: Symbol) -> Self {
79 Self::new(name, Span::DUMMY)
80 }
81
82 #[allow(clippy::should_implement_trait)]
84 pub fn from_str(string: &str) -> Self {
85 Self::with_dummy_span(Symbol::intern(string))
86 }
87
88 pub fn from_str_and_span(string: &str, span: Span) -> Self {
90 Self::new(Symbol::intern(string), span)
91 }
92
93 #[inline]
95 #[allow(clippy::inherent_to_string_shadow_display)]
96 pub fn to_string(&self) -> String {
97 self.as_str().to_string()
98 }
99
100 pub fn as_str(&self) -> &str {
105 self.name.as_str()
106 }
107
108 #[inline]
110 pub fn is_used_keyword(self) -> bool {
111 self.name.is_used_keyword()
112 }
113
114 #[inline]
116 pub fn is_unused_keyword(self) -> bool {
117 self.name.is_unused_keyword()
118 }
119
120 #[inline]
122 pub fn is_weak_keyword(self) -> bool {
123 self.name.is_weak_keyword()
124 }
125
126 #[inline]
128 pub fn is_yul_keyword(self) -> bool {
129 self.name.is_yul_keyword()
130 }
131
132 #[inline]
134 pub fn is_yul_evm_builtin(self) -> bool {
135 self.name.is_yul_builtin()
136 }
137
138 #[inline]
141 pub fn is_reserved(self, yul: bool) -> bool {
142 self.name.is_reserved(yul)
143 }
144
145 #[inline]
148 pub fn is_non_reserved(self, yul: bool) -> bool {
149 self.name.is_non_reserved(yul)
150 }
151
152 #[inline]
156 pub fn is_elementary_type(self) -> bool {
157 self.name.is_elementary_type()
158 }
159
160 #[inline]
162 pub fn is_bool_lit(self) -> bool {
163 self.name.is_bool_lit()
164 }
165
166 #[inline]
168 pub fn is_location_specifier(self) -> bool {
169 self.name.is_location_specifier()
170 }
171
172 #[inline]
174 pub fn is_mutability_specifier(self) -> bool {
175 self.name.is_mutability_specifier()
176 }
177
178 #[inline]
180 pub fn is_visibility_specifier(self) -> bool {
181 self.name.is_visibility_specifier()
182 }
183}
184
185#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
190pub struct Symbol(BaseIndex32);
191
192impl Default for Symbol {
193 #[inline]
194 fn default() -> Self {
195 Self::DUMMY
196 }
197}
198
199impl Symbol {
200 pub const DUMMY: Self = kw::Empty;
202
203 const fn new(n: u32) -> Self {
204 Self(BaseIndex32::new(n))
205 }
206
207 pub fn intern(string: &str) -> Self {
209 SessionGlobals::with(|g| g.symbol_interner.intern(string))
210 }
211
212 #[inline]
214 #[allow(clippy::inherent_to_string_shadow_display)]
215 pub fn to_string(&self) -> String {
216 self.as_str().to_string()
217 }
218
219 pub fn as_str(&self) -> &str {
228 SessionGlobals::with(|g| unsafe { trustme::decouple_lt(g.symbol_interner.get(*self)) })
229 }
230
231 #[inline(always)]
233 pub const fn as_u32(self) -> u32 {
234 self.0.get()
235 }
236
237 #[inline]
241 pub fn is_used_keyword(self) -> bool {
242 self < kw::After
243 }
244
245 #[inline]
247 pub fn is_unused_keyword(self) -> bool {
248 self >= kw::After && self <= kw::Var
249 }
250
251 #[inline]
253 pub fn is_weak_keyword(self) -> bool {
254 self >= kw::Leave && self <= kw::Builtin
255 }
256
257 #[inline]
259 pub fn is_yul_keyword(self) -> bool {
260 matches!(
262 self,
263 kw::Function
264 | kw::Let
265 | kw::If
266 | kw::Switch
267 | kw::Case
268 | kw::Default
269 | kw::For
270 | kw::Break
271 | kw::Continue
272 | kw::Leave
273 | kw::True
274 | kw::False
275 )
276 }
277
278 #[inline]
280 pub fn is_yul_builtin(self) -> bool {
281 (self >= kw::Add && self <= kw::Xor)
282 | matches!(self, kw::Address | kw::Byte | kw::Return | kw::Revert)
283 }
284
285 #[inline]
288 pub fn is_reserved(self, yul: bool) -> bool {
289 if yul {
290 self.is_yul_keyword() | self.is_yul_builtin()
291 } else {
292 self.is_used_keyword() | self.is_unused_keyword()
293 }
294 }
295
296 #[inline]
299 pub fn is_non_reserved(self, yul: bool) -> bool {
300 !self.is_reserved(yul)
301 }
302
303 #[inline]
307 pub fn is_elementary_type(self) -> bool {
308 self >= kw::Int && self <= kw::UFixed
309 }
310
311 #[inline]
313 pub fn is_bool_lit(self) -> bool {
314 self == kw::False || self == kw::True
315 }
316
317 #[inline]
319 pub fn is_location_specifier(self) -> bool {
320 matches!(self, kw::Calldata | kw::Memory | kw::Storage)
321 }
322
323 #[inline]
325 pub fn is_mutability_specifier(self) -> bool {
326 matches!(self, kw::Immutable | kw::Constant)
327 }
328
329 #[inline]
331 pub fn is_visibility_specifier(self) -> bool {
332 matches!(self, kw::Public | kw::Private | kw::Internal | kw::External)
333 }
334
335 #[inline]
337 pub const fn is_preinterned(self) -> bool {
338 self.as_u32() < PREINTERNED_SYMBOLS_COUNT
339 }
340}
341
342impl fmt::Debug for Symbol {
343 #[inline]
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 fmt::Debug::fmt(self.as_str(), f)
346 }
347}
348
349impl fmt::Display for Symbol {
350 #[inline]
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 fmt::Display::fmt(self.as_str(), f)
353 }
354}
355
356pub(crate) struct Interner(lasso::ThreadedRodeo<Symbol, solar_data_structures::map::FxBuildHasher>);
360
361impl Interner {
362 pub(crate) fn fresh() -> Self {
363 Self::prefill(PREINTERNED)
364 }
365
366 pub(crate) fn prefill(init: &[&'static str]) -> Self {
367 let capacity = if init.is_empty() {
368 Default::default()
369 } else {
370 let actual_string = init.len();
371 let strings = actual_string.next_power_of_two();
372 let actual_bytes = PREINTERNED_SYMBOLS_BYTES as usize;
373 let bytes = actual_bytes.next_power_of_two().max(4096);
374 trace!(strings, bytes, "prefill capacity");
375 lasso::Capacity::new(strings, std::num::NonZeroUsize::new(bytes).unwrap())
376 };
377 let rodeo = lasso::ThreadedRodeo::with_capacity_and_hasher(capacity, Default::default());
378 for &s in init {
379 rodeo.get_or_intern_static(s);
380 }
381 Self(rodeo)
382 }
383
384 #[inline]
385 fn intern(&self, string: &str) -> Symbol {
386 self.0.get_or_intern(string)
387 }
388
389 #[inline]
390 fn get(&self, symbol: Symbol) -> &str {
391 self.0.resolve(&symbol)
392 }
393}
394
395unsafe impl lasso::Key for Symbol {
396 #[inline]
397 fn into_usize(self) -> usize {
398 self.0.index()
399 }
400
401 #[inline]
402 fn try_from_usize(value: usize) -> Option<Self> {
403 BaseIndex32::try_from_usize(value).map(Self)
404 }
405}
406
407pub mod kw {
413 use crate::Symbol;
414
415 #[doc(inline)]
416 pub use super::kw_generated::*;
417
418 #[inline]
420 pub const fn boolean(b: bool) -> Symbol {
421 if b {
422 True
423 } else {
424 False
425 }
426 }
427
428 #[inline]
436 #[track_caller]
437 pub const fn int(n: u8) -> Symbol {
438 assert!(n <= 32);
439 Symbol::new(Int.as_u32() + n as u32)
440 }
441
442 #[inline]
450 #[track_caller]
451 pub const fn uint(n: u8) -> Symbol {
452 assert!(n <= 32);
453 Symbol::new(UInt.as_u32() + n as u32)
454 }
455
456 #[inline]
462 #[track_caller]
463 pub const fn fixed_bytes(n: u8) -> Symbol {
464 assert!(n > 0 && n <= 32);
465 Symbol::new(Bytes.as_u32() + n as u32)
466 }
467}
468
469pub mod sym {
475 use super::Symbol;
476
477 #[doc(inline)]
478 pub use super::sym_generated::*;
479
480 pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
484 if let Ok(idx @ 0..=9) = n.try_into() {
485 return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
486 }
487 Symbol::intern(itoa::Buffer::new().format(n))
488 }
489}
490
491symbols! {
493 Keywords {
497 Empty: "",
499
500 Abstract: "abstract",
501 Anonymous: "anonymous",
502 As: "as",
503 Assembly: "assembly",
504 Break: "break",
505 Calldata: "calldata",
506 Catch: "catch",
507 Constant: "constant",
508 Constructor: "constructor",
509 Continue: "continue",
510 Contract: "contract",
511 Delete: "delete",
512 Do: "do",
513 Else: "else",
514 Emit: "emit",
515 Enum: "enum",
516 Event: "event",
517 External: "external",
518 Fallback: "fallback",
519 For: "for",
520 Function: "function",
521 Hex: "hex",
522 If: "if",
523 Immutable: "immutable",
524 Import: "import",
525 Indexed: "indexed",
526 Interface: "interface",
527 Internal: "internal",
528 Is: "is",
529 Library: "library",
530 Mapping: "mapping",
531 Memory: "memory",
532 Modifier: "modifier",
533 New: "new",
534 Override: "override",
535 Payable: "payable",
536 Pragma: "pragma",
537 Private: "private",
538 Public: "public",
539 Pure: "pure",
540 Receive: "receive",
541 Return: "return",
542 Returns: "returns",
543 Storage: "storage",
544 Struct: "struct",
545 Throw: "throw",
546 Try: "try",
547 Type: "type",
548 Unchecked: "unchecked",
549 Unicode: "unicode",
550 Using: "using",
551 View: "view",
552 Virtual: "virtual",
553 While: "while",
554
555 Int: "int",
557 Int8: "int8",
558 Int16: "int16",
559 Int24: "int24",
560 Int32: "int32",
561 Int40: "int40",
562 Int48: "int48",
563 Int56: "int56",
564 Int64: "int64",
565 Int72: "int72",
566 Int80: "int80",
567 Int88: "int88",
568 Int96: "int96",
569 Int104: "int104",
570 Int112: "int112",
571 Int120: "int120",
572 Int128: "int128",
573 Int136: "int136",
574 Int144: "int144",
575 Int152: "int152",
576 Int160: "int160",
577 Int168: "int168",
578 Int176: "int176",
579 Int184: "int184",
580 Int192: "int192",
581 Int200: "int200",
582 Int208: "int208",
583 Int216: "int216",
584 Int224: "int224",
585 Int232: "int232",
586 Int240: "int240",
587 Int248: "int248",
588 Int256: "int256",
589 UInt: "uint",
590 UInt8: "uint8",
591 UInt16: "uint16",
592 UInt24: "uint24",
593 UInt32: "uint32",
594 UInt40: "uint40",
595 UInt48: "uint48",
596 UInt56: "uint56",
597 UInt64: "uint64",
598 UInt72: "uint72",
599 UInt80: "uint80",
600 UInt88: "uint88",
601 UInt96: "uint96",
602 UInt104: "uint104",
603 UInt112: "uint112",
604 UInt120: "uint120",
605 UInt128: "uint128",
606 UInt136: "uint136",
607 UInt144: "uint144",
608 UInt152: "uint152",
609 UInt160: "uint160",
610 UInt168: "uint168",
611 UInt176: "uint176",
612 UInt184: "uint184",
613 UInt192: "uint192",
614 UInt200: "uint200",
615 UInt208: "uint208",
616 UInt216: "uint216",
617 UInt224: "uint224",
618 UInt232: "uint232",
619 UInt240: "uint240",
620 UInt248: "uint248",
621 UInt256: "uint256",
622 Bytes: "bytes",
623 Bytes1: "bytes1",
624 Bytes2: "bytes2",
625 Bytes3: "bytes3",
626 Bytes4: "bytes4",
627 Bytes5: "bytes5",
628 Bytes6: "bytes6",
629 Bytes7: "bytes7",
630 Bytes8: "bytes8",
631 Bytes9: "bytes9",
632 Bytes10: "bytes10",
633 Bytes11: "bytes11",
634 Bytes12: "bytes12",
635 Bytes13: "bytes13",
636 Bytes14: "bytes14",
637 Bytes15: "bytes15",
638 Bytes16: "bytes16",
639 Bytes17: "bytes17",
640 Bytes18: "bytes18",
641 Bytes19: "bytes19",
642 Bytes20: "bytes20",
643 Bytes21: "bytes21",
644 Bytes22: "bytes22",
645 Bytes23: "bytes23",
646 Bytes24: "bytes24",
647 Bytes25: "bytes25",
648 Bytes26: "bytes26",
649 Bytes27: "bytes27",
650 Bytes28: "bytes28",
651 Bytes29: "bytes29",
652 Bytes30: "bytes30",
653 Bytes31: "bytes31",
654 Bytes32: "bytes32",
655 String: "string",
656 Address: "address",
657 Bool: "bool",
658 Fixed: "fixed",
659 UFixed: "ufixed",
660
661 Wei: "wei",
663 Gwei: "gwei",
664 Ether: "ether",
665 Seconds: "seconds",
666 Minutes: "minutes",
667 Hours: "hours",
668 Days: "days",
669 Weeks: "weeks",
670 Years: "years",
671
672 False: "false",
674 True: "true",
675
676 After: "after",
678 Alias: "alias",
679 Apply: "apply",
680 Auto: "auto",
681 Byte: "byte",
682 Case: "case",
683 CopyOf: "copyof",
684 Default: "default",
685 Define: "define",
686 Final: "final",
687 Implements: "implements",
688 In: "in",
689 Inline: "inline",
690 Let: "let",
691 Macro: "macro",
692 Match: "match",
693 Mutable: "mutable",
694 NullLiteral: "null",
695 Of: "of",
696 Partial: "partial",
697 Promise: "promise",
698 Reference: "reference",
699 Relocatable: "relocatable",
700 Sealed: "sealed",
701 Sizeof: "sizeof",
702 Static: "static",
703 Supports: "supports",
704 Switch: "switch",
705 Typedef: "typedef",
706 TypeOf: "typeof",
707 Var: "var",
708
709 Leave: "leave",
714 Revert: "revert",
715
716 Add: "add",
722 Addmod: "addmod",
723 And: "and",
724 Balance: "balance",
725 Basefee: "basefee",
726 Blobbasefee: "blobbasefee",
727 Blobhash: "blobhash",
728 Blockhash: "blockhash",
729 Call: "call",
730 Callcode: "callcode",
731 Calldatacopy: "calldatacopy",
732 Calldataload: "calldataload",
733 Calldatasize: "calldatasize",
734 Caller: "caller",
735 Callvalue: "callvalue",
736 Chainid: "chainid",
737 Coinbase: "coinbase",
738 Create: "create",
739 Create2: "create2",
740 Delegatecall: "delegatecall",
741 Difficulty: "difficulty",
742 Div: "div",
743 Eq: "eq",
744 Exp: "exp",
745 Extcodecopy: "extcodecopy",
746 Extcodehash: "extcodehash",
747 Extcodesize: "extcodesize",
748 Gas: "gas",
749 Gaslimit: "gaslimit",
750 Gasprice: "gasprice",
751 Gt: "gt",
752 Invalid: "invalid",
753 Iszero: "iszero",
754 Keccak256: "keccak256",
755 Log0: "log0",
756 Log1: "log1",
757 Log2: "log2",
758 Log3: "log3",
759 Log4: "log4",
760 Lt: "lt",
761 Mcopy: "mcopy",
762 Mload: "mload",
763 Mod: "mod",
764 Msize: "msize",
765 Mstore: "mstore",
766 Mstore8: "mstore8",
767 Mul: "mul",
768 Mulmod: "mulmod",
769 Not: "not",
770 Number: "number",
771 Or: "or",
772 Origin: "origin",
773 Pop: "pop",
774 Prevrandao: "prevrandao",
775 Returndatacopy: "returndatacopy",
776 Returndatasize: "returndatasize",
777 Sar: "sar",
778 Sdiv: "sdiv",
779 Selfbalance: "selfbalance",
780 Selfdestruct: "selfdestruct",
781 Sgt: "sgt",
782 Shl: "shl",
783 Shr: "shr",
784 Signextend: "signextend",
785 Sload: "sload",
786 Slt: "slt",
787 Smod: "smod",
788 Sstore: "sstore",
789 Staticcall: "staticcall",
790 Stop: "stop",
791 Sub: "sub",
792 Timestamp: "timestamp",
793 Tload: "tload",
794 Tstore: "tstore",
795 Xor: "xor",
796
797 Class: "class",
799 Instantiation: "instantiation",
800 Integer: "Integer",
801 Itself: "itself",
802 StaticAssert: "static_assert",
803 Builtin: "__builtin",
804 ForAll: "forall",
805 }
806
807 Symbols {
826 X,
827 __tmp_struct,
828 abi,
829 abicoder,
830 assert,
831 at,
832 block,
833 code,
834 codehash,
835 concat,
836 creationCode,
837 data,
838 decode,
839 ecrecover,
840 encode,
841 encodeCall,
842 encodePacked,
843 encodeWithSelector,
844 encodeWithSignature,
845 error,
846 experimental,
847 from,
848 gasleft,
849 global,
850 interfaceId,
851 layout,
852 length,
853 max,
854 min,
855 msg,
856 name,
857 object,
858 push,
859 require,
860 ripemd160,
861 runtimeCode,
862 selector,
863 send,
864 sender,
865 sha256,
866 sig,
867 solidity,
868 super_: "super",
869 this,
870 transfer,
871 transient,
872 tx,
873 underscore: "_",
874 unwrap,
875 value,
876 wrap,
877 x,
878 }
879}
880
881#[cfg(test)]
882mod tests {
883 use super::*;
884
885 #[test]
886 fn interner_tests() {
887 let i = Interner::prefill(&[]);
888 assert_eq!(i.intern("dog"), Symbol::new(0));
890 assert_eq!(i.intern("dog"), Symbol::new(0));
892 assert_eq!(i.intern("cat"), Symbol::new(1));
894 assert_eq!(i.intern("cat"), Symbol::new(1));
895 assert_eq!(i.intern("dog"), Symbol::new(0));
897 }
898
899 #[test]
900 fn defaults() {
901 assert_eq!(Symbol::DUMMY, Symbol::new(0));
902 assert_eq!(Symbol::DUMMY, Symbol::default());
903 assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
904 assert_eq!(Ident::DUMMY, Ident::default());
905
906 crate::enter(|| {
907 assert_eq!(Symbol::DUMMY.as_str(), "");
908 assert_eq!(Symbol::DUMMY.to_string(), "");
909 assert_eq!(Ident::DUMMY.as_str(), "");
910 assert_eq!(Ident::DUMMY.to_string(), "");
911 });
912 }
913}