use crate::{Session, SessionGlobals, Span};
use solar_data_structures::{index::BaseIndex32, trustme};
use solar_macros::symbols;
use std::{cmp, fmt, hash, str};
#[derive(Clone, Copy)]
pub struct Ident {
pub name: Symbol,
pub span: Span,
}
impl Default for Ident {
#[inline]
fn default() -> Self {
Self::DUMMY
}
}
impl PartialEq for Ident {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
self.name == rhs.name
}
}
impl Eq for Ident {}
impl PartialOrd for Ident {
#[inline]
fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(rhs))
}
}
impl Ord for Ident {
#[inline]
fn cmp(&self, rhs: &Self) -> cmp::Ordering {
self.name.cmp(&rhs.name)
}
}
impl hash::Hash for Ident {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl fmt::Debug for Ident {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Ident {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.name.fmt(f)
}
}
impl Ident {
pub const DUMMY: Self = Self::new(Symbol::DUMMY, Span::DUMMY);
#[inline]
pub const fn new(name: Symbol, span: Span) -> Self {
Self { name, span }
}
#[inline]
pub const fn with_dummy_span(name: Symbol) -> Self {
Self::new(name, Span::DUMMY)
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(string: &str) -> Self {
Self::with_dummy_span(Symbol::intern(string))
}
pub fn from_str_and_span(string: &str, span: Span) -> Self {
Self::new(Symbol::intern(string), span)
}
#[inline]
#[allow(clippy::inherent_to_string_shadow_display)]
#[cfg_attr(debug_assertions, track_caller)]
pub fn to_string(&self) -> String {
self.as_str().to_string()
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn as_str(&self) -> &str {
self.name.as_str()
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn as_str_in(self, session: &Session) -> &str {
self.name.as_str_in(session)
}
#[inline]
pub fn is_used_keyword(self) -> bool {
self.name.is_used_keyword()
}
#[inline]
pub fn is_unused_keyword(self) -> bool {
self.name.is_unused_keyword()
}
#[inline]
pub fn is_weak_keyword(self) -> bool {
self.name.is_weak_keyword()
}
#[inline]
pub fn is_yul_keyword(self) -> bool {
self.name.is_yul_keyword()
}
#[inline]
pub fn is_yul_evm_builtin(self) -> bool {
self.name.is_yul_builtin()
}
#[inline]
pub fn is_reserved(self, yul: bool) -> bool {
self.name.is_reserved(yul)
}
#[inline]
pub fn is_non_reserved(self, yul: bool) -> bool {
self.name.is_non_reserved(yul)
}
#[inline]
pub fn is_elementary_type(self) -> bool {
self.name.is_elementary_type()
}
#[inline]
pub fn is_bool_lit(self) -> bool {
self.name.is_bool_lit()
}
#[inline]
pub fn is_location_specifier(self) -> bool {
self.name.is_location_specifier()
}
#[inline]
pub fn is_mutability_specifier(self) -> bool {
self.name.is_mutability_specifier()
}
#[inline]
pub fn is_visibility_specifier(self) -> bool {
self.name.is_visibility_specifier()
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(BaseIndex32);
impl Default for Symbol {
#[inline]
fn default() -> Self {
Self::DUMMY
}
}
impl Symbol {
pub const DUMMY: Self = kw::Empty;
const fn new(n: u32) -> Self {
Self(BaseIndex32::new(n))
}
pub fn intern(string: &str) -> Self {
SessionGlobals::with(|g| g.symbol_interner.intern(string))
}
#[inline]
pub fn intern_in(string: &str, session: &Session) -> Self {
session.intern(string)
}
#[inline]
#[allow(clippy::inherent_to_string_shadow_display)]
pub fn to_string(&self) -> String {
self.as_str().to_string()
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn as_str(&self) -> &str {
SessionGlobals::with(|g| unsafe { trustme::decouple_lt(g.symbol_interner.get(*self)) })
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn as_str_in(self, session: &Session) -> &str {
session.resolve_symbol(self)
}
#[inline(always)]
pub const fn as_u32(self) -> u32 {
self.0.get()
}
#[inline]
pub fn is_used_keyword(self) -> bool {
self < kw::After
}
#[inline]
pub fn is_unused_keyword(self) -> bool {
self >= kw::After && self <= kw::Var
}
#[inline]
pub fn is_weak_keyword(self) -> bool {
self >= kw::Leave && self <= kw::Builtin
}
#[inline]
pub fn is_yul_keyword(self) -> bool {
matches!(
self,
kw::Function
| kw::Let
| kw::If
| kw::Switch
| kw::Case
| kw::Default
| kw::For
| kw::Break
| kw::Continue
| kw::Leave
| kw::True
| kw::False
)
}
#[inline]
pub fn is_yul_builtin(self) -> bool {
(self >= kw::Add && self <= kw::Xor)
| matches!(self, kw::Address | kw::Byte | kw::Return | kw::Revert)
}
#[inline]
pub fn is_reserved(self, yul: bool) -> bool {
if yul {
self.is_yul_keyword() | self.is_yul_builtin()
} else {
self.is_used_keyword() | self.is_unused_keyword()
}
}
#[inline]
pub fn is_non_reserved(self, yul: bool) -> bool {
!self.is_reserved(yul)
}
#[inline]
pub fn is_elementary_type(self) -> bool {
self >= kw::Int && self <= kw::UFixed
}
#[inline]
pub fn is_bool_lit(self) -> bool {
self == kw::False || self == kw::True
}
#[inline]
pub fn is_location_specifier(self) -> bool {
matches!(self, kw::Calldata | kw::Memory | kw::Storage)
}
#[inline]
pub fn is_mutability_specifier(self) -> bool {
matches!(self, kw::Immutable | kw::Constant)
}
#[inline]
pub fn is_visibility_specifier(self) -> bool {
matches!(self, kw::Public | kw::Private | kw::Internal | kw::External)
}
#[inline]
pub const fn is_preinterned(self) -> bool {
self.as_u32() < PREINTERNED_SYMBOLS_COUNT
}
}
impl fmt::Debug for Symbol {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl fmt::Display for Symbol {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ByteSymbol(BaseIndex32);
impl ByteSymbol {
pub fn intern(byte_str: &[u8]) -> Self {
SessionGlobals::with(|g| g.symbol_interner.intern_byte_str(byte_str))
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn as_byte_str(&self) -> &[u8] {
SessionGlobals::with(|g| unsafe {
trustme::decouple_lt(g.symbol_interner.get_byte_str(*self))
})
}
#[cfg_attr(debug_assertions, track_caller)]
pub fn as_byte_str_in(self, session: &Session) -> &[u8] {
session.resolve_byte_str(self)
}
#[inline(always)]
pub fn as_u32(self) -> u32 {
self.0.get()
}
}
impl fmt::Debug for ByteSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_byte_str(), f)
}
}
pub(crate) struct Interner {
inner: inturn::BytesInterner<ByteSymbol, solar_data_structures::map::FxBuildHasher>,
}
impl Interner {
pub(crate) fn fresh() -> Self {
Self::prefill(PREINTERNED)
}
pub(crate) fn prefill(init: &[&'static str]) -> Self {
let mut inner =
inturn::BytesInterner::with_capacity_and_hasher(init.len() * 4, Default::default());
inner.intern_many_mut_static(init.iter().map(|s| s.as_bytes()));
Self { inner }
}
#[inline]
pub(crate) fn intern(&self, string: &str) -> Symbol {
Symbol(self.inner.intern(string.as_bytes()).0)
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub(crate) fn get(&self, symbol: Symbol) -> &str {
let s = self.inner.resolve(ByteSymbol(symbol.0));
unsafe { std::str::from_utf8_unchecked(s) }
}
#[inline]
pub(crate) fn intern_byte_str(&self, byte_str: &[u8]) -> ByteSymbol {
self.inner.intern(byte_str)
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub(crate) fn get_byte_str(&self, symbol: ByteSymbol) -> &[u8] {
self.inner.resolve(symbol)
}
fn trace_stats(&mut self) {
if enabled!(tracing::Level::TRACE) {
self.trace_stats_impl();
}
}
#[inline(never)]
fn trace_stats_impl(&mut self) {
let mut lengths = self.inner.iter().map(|(_, s)| s.len()).collect::<Vec<_>>();
lengths.sort_unstable();
let len = lengths.len();
assert!(len > 0);
let bytes = lengths.iter().copied().sum::<usize>();
trace!(
preinterned=PREINTERNED_SYMBOLS_COUNT,
len,
bytes,
max=lengths.last().copied().unwrap_or(0),
mean=%format!("{:.2}", (bytes as f64 / len as f64)),
median=%format!("{:.2}", if len.is_multiple_of(2) {
(lengths[len / 2 - 1] + lengths[len / 2]) as f64 / 2.0
} else {
lengths[len / 2] as f64
}),
"Interner stats",
);
}
}
impl inturn::InternerSymbol for ByteSymbol {
#[inline]
fn try_from_usize(n: usize) -> Option<Self> {
BaseIndex32::try_from_usize(n).map(Self)
}
#[inline]
fn to_usize(self) -> usize {
self.0.index()
}
}
impl Drop for Interner {
fn drop(&mut self) {
self.trace_stats();
}
}
pub mod kw {
use crate::Symbol;
#[doc(inline)]
pub use super::kw_generated::*;
#[inline]
pub const fn boolean(b: bool) -> Symbol {
if b { True } else { False }
}
#[inline]
#[track_caller]
pub const fn int(n: u8) -> Symbol {
assert!(n <= 32);
Symbol::new(Int.as_u32() + n as u32)
}
#[inline]
#[track_caller]
pub const fn uint(n: u8) -> Symbol {
assert!(n <= 32);
Symbol::new(UInt.as_u32() + n as u32)
}
#[inline]
#[track_caller]
pub const fn fixed_bytes(n: u8) -> Symbol {
assert!(n > 0 && n <= 32);
Symbol::new(Bytes.as_u32() + n as u32)
}
}
pub mod sym {
use super::Symbol;
#[doc(inline)]
pub use super::sym_generated::*;
pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
if let Ok(idx @ 0..=9) = n.try_into() {
return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
}
Symbol::intern(itoa::Buffer::new().format(n))
}
}
symbols! {
Keywords {
Empty: "",
Abstract: "abstract",
Anonymous: "anonymous",
As: "as",
Assembly: "assembly",
Break: "break",
Calldata: "calldata",
Catch: "catch",
Constant: "constant",
Constructor: "constructor",
Continue: "continue",
Contract: "contract",
Delete: "delete",
Do: "do",
Else: "else",
Emit: "emit",
Enum: "enum",
Event: "event",
External: "external",
Fallback: "fallback",
For: "for",
Function: "function",
Hex: "hex",
If: "if",
Immutable: "immutable",
Import: "import",
Indexed: "indexed",
Interface: "interface",
Internal: "internal",
Is: "is",
Library: "library",
Mapping: "mapping",
Memory: "memory",
Modifier: "modifier",
New: "new",
Override: "override",
Payable: "payable",
Pragma: "pragma",
Private: "private",
Public: "public",
Pure: "pure",
Receive: "receive",
Return: "return",
Returns: "returns",
Storage: "storage",
Struct: "struct",
Throw: "throw",
Try: "try",
Type: "type",
Unchecked: "unchecked",
Unicode: "unicode",
Using: "using",
View: "view",
Virtual: "virtual",
While: "while",
Int: "int",
Int8: "int8",
Int16: "int16",
Int24: "int24",
Int32: "int32",
Int40: "int40",
Int48: "int48",
Int56: "int56",
Int64: "int64",
Int72: "int72",
Int80: "int80",
Int88: "int88",
Int96: "int96",
Int104: "int104",
Int112: "int112",
Int120: "int120",
Int128: "int128",
Int136: "int136",
Int144: "int144",
Int152: "int152",
Int160: "int160",
Int168: "int168",
Int176: "int176",
Int184: "int184",
Int192: "int192",
Int200: "int200",
Int208: "int208",
Int216: "int216",
Int224: "int224",
Int232: "int232",
Int240: "int240",
Int248: "int248",
Int256: "int256",
UInt: "uint",
UInt8: "uint8",
UInt16: "uint16",
UInt24: "uint24",
UInt32: "uint32",
UInt40: "uint40",
UInt48: "uint48",
UInt56: "uint56",
UInt64: "uint64",
UInt72: "uint72",
UInt80: "uint80",
UInt88: "uint88",
UInt96: "uint96",
UInt104: "uint104",
UInt112: "uint112",
UInt120: "uint120",
UInt128: "uint128",
UInt136: "uint136",
UInt144: "uint144",
UInt152: "uint152",
UInt160: "uint160",
UInt168: "uint168",
UInt176: "uint176",
UInt184: "uint184",
UInt192: "uint192",
UInt200: "uint200",
UInt208: "uint208",
UInt216: "uint216",
UInt224: "uint224",
UInt232: "uint232",
UInt240: "uint240",
UInt248: "uint248",
UInt256: "uint256",
Bytes: "bytes",
Bytes1: "bytes1",
Bytes2: "bytes2",
Bytes3: "bytes3",
Bytes4: "bytes4",
Bytes5: "bytes5",
Bytes6: "bytes6",
Bytes7: "bytes7",
Bytes8: "bytes8",
Bytes9: "bytes9",
Bytes10: "bytes10",
Bytes11: "bytes11",
Bytes12: "bytes12",
Bytes13: "bytes13",
Bytes14: "bytes14",
Bytes15: "bytes15",
Bytes16: "bytes16",
Bytes17: "bytes17",
Bytes18: "bytes18",
Bytes19: "bytes19",
Bytes20: "bytes20",
Bytes21: "bytes21",
Bytes22: "bytes22",
Bytes23: "bytes23",
Bytes24: "bytes24",
Bytes25: "bytes25",
Bytes26: "bytes26",
Bytes27: "bytes27",
Bytes28: "bytes28",
Bytes29: "bytes29",
Bytes30: "bytes30",
Bytes31: "bytes31",
Bytes32: "bytes32",
String: "string",
Address: "address",
Bool: "bool",
Fixed: "fixed",
UFixed: "ufixed",
Wei: "wei",
Gwei: "gwei",
Ether: "ether",
Seconds: "seconds",
Minutes: "minutes",
Hours: "hours",
Days: "days",
Weeks: "weeks",
Years: "years",
False: "false",
True: "true",
After: "after",
Alias: "alias",
Apply: "apply",
Auto: "auto",
Byte: "byte",
Case: "case",
CopyOf: "copyof",
Default: "default",
Define: "define",
Final: "final",
Implements: "implements",
In: "in",
Inline: "inline",
Let: "let",
Macro: "macro",
Match: "match",
Mutable: "mutable",
NullLiteral: "null",
Of: "of",
Partial: "partial",
Promise: "promise",
Reference: "reference",
Relocatable: "relocatable",
Sealed: "sealed",
Sizeof: "sizeof",
Static: "static",
Supports: "supports",
Switch: "switch",
Typedef: "typedef",
TypeOf: "typeof",
Var: "var",
Leave: "leave",
Revert: "revert",
Add: "add",
Addmod: "addmod",
And: "and",
Balance: "balance",
Basefee: "basefee",
Blobbasefee: "blobbasefee",
Blobhash: "blobhash",
Blockhash: "blockhash",
Call: "call",
Callcode: "callcode",
Calldatacopy: "calldatacopy",
Calldataload: "calldataload",
Calldatasize: "calldatasize",
Caller: "caller",
Callvalue: "callvalue",
Chainid: "chainid",
Coinbase: "coinbase",
Create: "create",
Create2: "create2",
Delegatecall: "delegatecall",
Difficulty: "difficulty",
Div: "div",
Eq: "eq",
Exp: "exp",
Extcodecopy: "extcodecopy",
Extcodehash: "extcodehash",
Extcodesize: "extcodesize",
Gas: "gas",
Gaslimit: "gaslimit",
Gasprice: "gasprice",
Gt: "gt",
Invalid: "invalid",
Iszero: "iszero",
Keccak256: "keccak256",
Log0: "log0",
Log1: "log1",
Log2: "log2",
Log3: "log3",
Log4: "log4",
Lt: "lt",
Mcopy: "mcopy",
Mload: "mload",
Mod: "mod",
Msize: "msize",
Mstore: "mstore",
Mstore8: "mstore8",
Mul: "mul",
Mulmod: "mulmod",
Not: "not",
Number: "number",
Or: "or",
Origin: "origin",
Pop: "pop",
Prevrandao: "prevrandao",
Returndatacopy: "returndatacopy",
Returndatasize: "returndatasize",
Sar: "sar",
Sdiv: "sdiv",
Selfbalance: "selfbalance",
Selfdestruct: "selfdestruct",
Sgt: "sgt",
Shl: "shl",
Shr: "shr",
Signextend: "signextend",
Sload: "sload",
Slt: "slt",
Smod: "smod",
Sstore: "sstore",
Staticcall: "staticcall",
Stop: "stop",
Sub: "sub",
Timestamp: "timestamp",
Tload: "tload",
Tstore: "tstore",
Xor: "xor",
Class: "class",
Instantiation: "instantiation",
Integer: "Integer",
Itself: "itself",
StaticAssert: "static_assert",
Builtin: "__builtin",
ForAll: "forall",
}
Symbols {
X,
__tmp_struct,
abi,
abicoder,
assert,
at,
block,
code,
codehash,
concat,
creationCode,
data,
decode,
ecrecover,
encode,
encodeCall,
encodePacked,
encodeWithSelector,
encodeWithSignature,
error,
experimental,
from,
gasleft,
global,
interfaceId,
layout,
length,
max,
memory_dash_safe: "memory-safe",
min,
msg,
name,
object,
push,
require,
ripemd160,
runtimeCode,
selector,
send,
sender,
sha256,
sig,
solidity,
super_: "super",
this,
transfer,
transient,
tx,
underscore: "_",
unwrap,
value,
wrap,
x,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interner_tests() {
let i = Interner::prefill(&[]);
assert_eq!(i.intern("dog"), Symbol::new(0));
assert_eq!(i.intern("dog"), Symbol::new(0));
assert_eq!(i.intern("cat"), Symbol::new(1));
assert_eq!(i.intern("cat"), Symbol::new(1));
assert_eq!(i.intern("dog"), Symbol::new(0));
}
#[test]
fn defaults() {
assert_eq!(Symbol::DUMMY, Symbol::new(0));
assert_eq!(Symbol::DUMMY, Symbol::default());
assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
assert_eq!(Ident::DUMMY, Ident::default());
crate::enter(|| {
assert_eq!(Symbol::DUMMY.as_str(), "");
assert_eq!(Symbol::DUMMY.to_string(), "");
assert_eq!(Ident::DUMMY.as_str(), "");
assert_eq!(Ident::DUMMY.to_string(), "");
});
}
}