use alloy_primitives::{Address, U256};
use solar_interface::{ByteSymbol, Span, Symbol, diagnostics::ErrorGuaranteed, kw};
use std::fmt;
#[derive(Clone, Copy, Debug)]
pub struct Lit<'ast> {
pub span: Span,
pub symbol: Symbol,
pub kind: LitKind<'ast>,
}
impl fmt::Display for Lit<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { ref kind, symbol, span: _ } = *self;
match kind {
LitKind::Str(s, ..) => write!(f, "{}\"{symbol}\"", s.prefix()),
LitKind::Number(_)
| LitKind::Rational(_)
| LitKind::Err(_)
| LitKind::Address(_)
| LitKind::Bool(_) => write!(f, "{symbol}"),
}
}
}
impl Lit<'_> {
pub fn first_span(&self) -> Span {
if let LitKind::Str(kind, _, extra) = &self.kind
&& !extra.is_empty()
{
let str_len = kind.prefix().len() + 1 + self.symbol.as_str().len() + 1;
return self.span.with_hi(self.span.lo() + str_len as u32);
}
self.span
}
pub fn literals(&self) -> impl Iterator<Item = (Span, Symbol)> + '_ {
let extra = if let LitKind::Str(_, _, extra) = self.kind { extra } else { &[] };
std::iter::once((self.first_span(), self.symbol)).chain(extra.iter().copied())
}
pub fn copy_without_data<'a>(&self) -> Lit<'a> {
if let LitKind::Str(str_kind, byte_symbol, items) = self.kind
&& !items.is_empty()
{
return Lit {
span: self.span,
symbol: self.symbol,
kind: LitKind::Str(str_kind, byte_symbol, &[]),
};
}
unsafe { std::mem::transmute::<Lit<'_>, Lit<'a>>(*self) }
}
}
#[derive(Clone, Copy)]
pub enum LitKind<'ast> {
Str(StrKind, ByteSymbol, &'ast [(Span, Symbol)]),
Number(U256),
Rational(num_rational::Ratio<U256>),
Address(Address),
Bool(bool),
Err(ErrorGuaranteed),
}
impl fmt::Debug for LitKind<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Str(kind, value, extra) => {
write!(f, "{kind:?}(")?;
let value = value.as_byte_str();
if let Ok(utf8) = std::str::from_utf8(value) {
write!(f, "{utf8:?}")?;
} else {
f.write_str(&alloy_primitives::hex::encode_prefixed(value))?;
}
if !extra.is_empty() {
write!(f, ", {extra:?}")?;
}
f.write_str(")")
}
Self::Number(value) => write!(f, "Number({value:?})"),
Self::Rational(value) => write!(f, "Rational({value:?})"),
Self::Address(value) => write!(f, "Address({value:?})"),
Self::Bool(value) => write!(f, "Bool({value:?})"),
Self::Err(_) => write!(f, "Err"),
}
}
}
impl LitKind<'_> {
pub fn description(&self) -> &'static str {
match self {
Self::Str(kind, ..) => kind.description(),
Self::Number(_) => "number",
Self::Rational(_) => "rational",
Self::Address(_) => "address",
Self::Bool(_) => "boolean",
Self::Err(_) => "<error>",
}
}
}
#[derive(Clone, Debug)]
pub struct StrLit {
pub span: Span,
pub value: Symbol,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum StrKind {
Str,
Unicode,
Hex,
}
impl StrKind {
pub fn description(self) -> &'static str {
match self {
Self::Str => "string",
Self::Unicode => "unicode string",
Self::Hex => "hex string",
}
}
#[doc(alias = "to_str")]
pub fn prefix(self) -> &'static str {
match self {
Self::Str => "",
Self::Unicode => "unicode",
Self::Hex => "hex",
}
}
#[doc(alias = "to_symbol")]
pub fn prefix_symbol(self) -> Symbol {
match self {
Self::Str => kw::Empty,
Self::Unicode => kw::Unicode,
Self::Hex => kw::Hex,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SubDenomination {
Ether(EtherSubDenomination),
Time(TimeSubDenomination),
}
impl fmt::Display for SubDenomination {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ether(sub_denomination) => sub_denomination.fmt(f),
Self::Time(sub_denomination) => sub_denomination.fmt(f),
}
}
}
impl SubDenomination {
pub const fn to_str(self) -> &'static str {
match self {
Self::Ether(sub_denomination) => sub_denomination.to_str(),
Self::Time(sub_denomination) => sub_denomination.to_str(),
}
}
pub const fn to_symbol(self) -> Symbol {
match self {
Self::Ether(sub_denomination) => sub_denomination.to_symbol(),
Self::Time(sub_denomination) => sub_denomination.to_symbol(),
}
}
pub const fn value(self) -> u64 {
match self {
Self::Ether(sub_denomination) => sub_denomination.wei(),
Self::Time(sub_denomination) => sub_denomination.seconds(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EtherSubDenomination {
Wei,
Gwei,
Ether,
}
impl fmt::Display for EtherSubDenomination {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.to_str())
}
}
impl EtherSubDenomination {
pub const fn to_str(self) -> &'static str {
match self {
Self::Wei => "wei",
Self::Gwei => "gwei",
Self::Ether => "ether",
}
}
pub const fn to_symbol(self) -> Symbol {
match self {
Self::Wei => kw::Wei,
Self::Gwei => kw::Gwei,
Self::Ether => kw::Ether,
}
}
pub const fn wei(self) -> u64 {
match self {
Self::Wei => 1,
Self::Gwei => 1_000_000_000,
Self::Ether => 1_000_000_000_000_000_000,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TimeSubDenomination {
Seconds,
Minutes,
Hours,
Days,
Weeks,
Years,
}
impl fmt::Display for TimeSubDenomination {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.to_str())
}
}
impl TimeSubDenomination {
pub const fn to_str(self) -> &'static str {
match self {
Self::Seconds => "seconds",
Self::Minutes => "minutes",
Self::Hours => "hours",
Self::Days => "days",
Self::Weeks => "weeks",
Self::Years => "years",
}
}
pub const fn to_symbol(self) -> Symbol {
match self {
Self::Seconds => kw::Seconds,
Self::Minutes => kw::Minutes,
Self::Hours => kw::Hours,
Self::Days => kw::Days,
Self::Weeks => kw::Weeks,
Self::Years => kw::Years,
}
}
pub const fn seconds(self) -> u64 {
match self {
Self::Seconds => 1,
Self::Minutes => 60,
Self::Hours => 3_600,
Self::Days => 86_400,
Self::Weeks => 604_800,
Self::Years => 31_536_000,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Base {
Binary = 2,
Octal = 8,
Decimal = 10,
Hexadecimal = 16,
}
impl fmt::Display for Base {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_str().fmt(f)
}
}
impl Base {
pub fn to_str(self) -> &'static str {
match self {
Self::Binary => "binary",
Self::Octal => "octal",
Self::Decimal => "decimal",
Self::Hexadecimal => "hexadecimal",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use solar_interface::{BytePos, enter};
fn bs(s: &[u8]) -> ByteSymbol {
ByteSymbol::intern(s)
}
#[test]
fn literal_fmt() {
enter(|| {
let lit = LitKind::Str(StrKind::Str, bs(b"hello world"), &[]);
assert_eq!(lit.description(), "string");
assert_eq!(format!("{lit:?}"), "Str(\"hello world\")");
let lit = LitKind::Str(StrKind::Str, bs(b"hello\0world"), &[]);
assert_eq!(lit.description(), "string");
assert_eq!(format!("{lit:?}"), "Str(\"hello\\0world\")");
let lit = LitKind::Str(StrKind::Str, bs(&[255u8][..]), &[]);
assert_eq!(lit.description(), "string");
assert_eq!(format!("{lit:?}"), "Str(0xff)");
let extra = [(Span::new(BytePos(69), BytePos(420)), Symbol::intern("world"))];
let lit = LitKind::Str(StrKind::Str, bs(b"hello world"), &extra);
assert_eq!(lit.description(), "string");
assert_eq!(format!("{lit:?}"), "Str(\"hello world\", [(Span(69..420), \"world\")])");
})
}
}