use crate::{
StrKind,
ast::{BinOp, BinOpKind, UnOp, UnOpKind},
};
use solar_interface::{Ident, Span, Symbol, diagnostics::ErrorGuaranteed};
use std::{borrow::Cow, fmt, mem::MaybeUninit};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum CommentKind {
Line,
Block,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BinOpToken {
Plus,
Minus,
Star,
Slash,
Percent,
Caret,
And,
Or,
Shl,
Shr,
Sar,
}
impl fmt::Display for BinOpToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.to_str())
}
}
impl BinOpToken {
pub const fn to_str(self) -> &'static str {
match self {
Self::Plus => "+",
Self::Minus => "-",
Self::Star => "*",
Self::Slash => "/",
Self::Percent => "%",
Self::Caret => "^",
Self::And => "&",
Self::Or => "|",
Self::Shl => "<<",
Self::Shr => ">>",
Self::Sar => ">>>",
}
}
pub const fn to_str_with_eq(self) -> &'static str {
match self {
Self::Plus => "+=",
Self::Minus => "-=",
Self::Star => "*=",
Self::Slash => "/=",
Self::Percent => "%=",
Self::Caret => "^=",
Self::And => "&=",
Self::Or => "|=",
Self::Shl => "<<=",
Self::Shr => ">>=",
Self::Sar => ">>>=",
}
}
#[inline]
pub const fn as_binop(self) -> BinOpKind {
match self {
Self::Plus => BinOpKind::Add,
Self::Minus => BinOpKind::Sub,
Self::Star => BinOpKind::Mul,
Self::Slash => BinOpKind::Div,
Self::Percent => BinOpKind::Rem,
Self::Caret => BinOpKind::BitXor,
Self::And => BinOpKind::BitAnd,
Self::Or => BinOpKind::BitOr,
Self::Shl => BinOpKind::Shl,
Self::Shr => BinOpKind::Shr,
Self::Sar => BinOpKind::Sar,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Delimiter {
Parenthesis,
Brace,
Bracket,
}
impl Delimiter {
pub const fn to_open_str(self) -> &'static str {
match self {
Self::Parenthesis => "(",
Self::Brace => "{",
Self::Bracket => "[",
}
}
pub const fn to_close_str(self) -> &'static str {
match self {
Self::Parenthesis => ")",
Self::Brace => "}",
Self::Bracket => "]",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TokenLit {
pub symbol: Symbol,
pub kind: TokenLitKind,
}
impl fmt::Display for TokenLit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let &Self { kind, symbol } = self;
match kind {
TokenLitKind::Str => write!(f, "\"{symbol}\""),
TokenLitKind::UnicodeStr => write!(f, "unicode\"{symbol}\""),
TokenLitKind::HexStr => write!(f, "hex\"{symbol}\""),
TokenLitKind::Integer | TokenLitKind::Rational | TokenLitKind::Err(_) => {
write!(f, "{symbol}")
}
}
}
}
impl TokenLit {
#[inline]
pub const fn new(kind: TokenLitKind, symbol: Symbol) -> Self {
Self { kind, symbol }
}
pub const fn description(self) -> &'static str {
self.kind.description()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TokenLitKind {
Integer,
Rational,
Str,
UnicodeStr,
HexStr,
Err(ErrorGuaranteed),
}
impl From<StrKind> for TokenLitKind {
fn from(str_kind: StrKind) -> Self {
match str_kind {
StrKind::Str => Self::Str,
StrKind::Unicode => Self::UnicodeStr,
StrKind::Hex => Self::HexStr,
}
}
}
impl TokenLitKind {
pub const fn description(self) -> &'static str {
match self {
Self::Integer => "integer",
Self::Rational => "rational",
Self::Str => "string",
Self::UnicodeStr => "unicode string",
Self::HexStr => "hex string",
Self::Err(_) => "error",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenKind {
Eq,
Lt,
Le,
EqEq,
Ne,
Ge,
Gt,
AndAnd,
OrOr,
Not,
Tilde,
Walrus,
PlusPlus,
MinusMinus,
StarStar,
BinOp(BinOpToken),
BinOpEq(BinOpToken),
At,
Dot,
Comma,
Semi,
Colon,
Arrow,
FatArrow,
Question,
OpenDelim(Delimiter),
CloseDelim(Delimiter),
Literal(TokenLitKind, Symbol),
Ident(Symbol),
Comment(bool , CommentKind, Symbol),
Eof,
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.description())
}
}
impl TokenKind {
pub fn lit(kind: TokenLitKind, symbol: Symbol) -> Self {
Self::Literal(kind, symbol)
}
pub fn as_str(&self) -> &str {
match self {
Self::Eq => "=",
Self::Lt => "<",
Self::Le => "<=",
Self::EqEq => "==",
Self::Ne => "!=",
Self::Ge => ">=",
Self::Gt => ">",
Self::AndAnd => "&&",
Self::OrOr => "||",
Self::Not => "!",
Self::Tilde => "~",
Self::Walrus => ":=",
Self::PlusPlus => "++",
Self::MinusMinus => "--",
Self::StarStar => "**",
Self::BinOp(op) => op.to_str(),
Self::BinOpEq(op) => op.to_str_with_eq(),
Self::At => "@",
Self::Dot => ".",
Self::Comma => ",",
Self::Semi => ";",
Self::Colon => ":",
Self::Arrow => "->",
Self::FatArrow => "=>",
Self::Question => "?",
Self::OpenDelim(d) => d.to_open_str(),
Self::CloseDelim(d) => d.to_close_str(),
Self::Literal(.., symbol) | Self::Ident(.., symbol) | Self::Comment(.., symbol) => {
symbol.as_str()
}
Self::Eof => "<eof>",
}
}
pub fn description(&self) -> Cow<'_, str> {
match self {
Self::Literal(kind, _) => return format!("<{}>", kind.description()).into(),
Self::Ident(symbol) => return symbol.to_string().into(),
Self::Comment(false, CommentKind::Block, _) => "<block comment>",
Self::Comment(true, CommentKind::Block, _) => "<block doc-comment>",
Self::Comment(false, CommentKind::Line, _) => "<line comment>",
Self::Comment(true, CommentKind::Line, _) => "<line doc-comment>",
_ => self.as_str(),
}
.into()
}
pub const fn is_op(&self) -> bool {
use TokenKind::*;
match self {
Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | Walrus
| PlusPlus | MinusMinus | StarStar | BinOp(_) | BinOpEq(_) | At | Dot | Comma
| Colon | Arrow | FatArrow | Question => true,
OpenDelim(..) | CloseDelim(..) | Literal(..) | Comment(..) | Ident(..) | Semi | Eof => {
false
}
}
}
pub fn as_unop(&self, is_postfix: bool) -> Option<UnOpKind> {
let kind = if is_postfix {
match self {
Self::PlusPlus => UnOpKind::PostInc,
Self::MinusMinus => UnOpKind::PostDec,
_ => return None,
}
} else {
match self {
Self::PlusPlus => UnOpKind::PreInc,
Self::MinusMinus => UnOpKind::PreDec,
Self::Not => UnOpKind::Not,
Self::Tilde => UnOpKind::BitNot,
Self::BinOp(BinOpToken::Minus) => UnOpKind::Neg,
_ => return None,
}
};
debug_assert_eq!(kind.is_postfix(), is_postfix);
Some(kind)
}
#[inline]
pub fn as_binop(&self) -> Option<BinOpKind> {
match self {
Self::Eq => Some(BinOpKind::Eq),
Self::Lt => Some(BinOpKind::Lt),
Self::Le => Some(BinOpKind::Le),
Self::EqEq => Some(BinOpKind::Eq),
Self::Ne => Some(BinOpKind::Ne),
Self::Ge => Some(BinOpKind::Ge),
Self::Gt => Some(BinOpKind::Gt),
Self::AndAnd => Some(BinOpKind::And),
Self::OrOr => Some(BinOpKind::Or),
Self::StarStar => Some(BinOpKind::Pow),
Self::BinOp(op) => Some(op.as_binop()),
_ => None,
}
}
#[inline]
pub fn as_binop_eq(&self) -> Option<BinOpKind> {
match self {
Self::BinOpEq(op) => Some(op.as_binop()),
_ => None,
}
}
#[inline]
pub const fn is_comment(&self) -> bool {
matches!(self, Self::Comment(false, ..))
}
#[inline]
pub const fn is_comment_or_doc(&self) -> bool {
matches!(self, Self::Comment(..))
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Token {
_kind: MaybeUninit<u64>,
_span: u64,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct TokenRepr {
pub kind: TokenKind,
pub span: Span,
}
const _: () = {
assert!(size_of::<Token>() == size_of::<TokenRepr>());
assert!(align_of::<Token>() >= align_of::<TokenRepr>());
assert!(std::mem::offset_of!(Token, _kind) == std::mem::offset_of!(TokenRepr, kind));
assert!(std::mem::offset_of!(Token, _span) == std::mem::offset_of!(TokenRepr, span));
};
impl std::ops::Deref for Token {
type Target = TokenRepr;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute(self) }
}
}
impl std::ops::DerefMut for Token {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::mem::transmute(self) }
}
}
impl fmt::Debug for Token {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl fmt::Debug for TokenRepr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Token").field("kind", &self.kind).field("span", &self.span).finish()
}
}
impl PartialEq for Token {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl Eq for Token {}
impl From<Ident> for Token {
#[inline]
fn from(ident: Ident) -> Self {
Self::from_ast_ident(ident)
}
}
impl Token {
pub const EOF: Self = Self::new(TokenKind::Eof, Span::DUMMY);
pub const DUMMY: Self = Self::new(TokenKind::Question, Span::DUMMY);
#[inline]
pub const fn new(kind: TokenKind, span: Span) -> Self {
unsafe { std::mem::transmute(TokenRepr { kind, span }) }
}
#[inline]
pub fn from_ast_ident(ident: Ident) -> Self {
Self::new(TokenKind::Ident(ident.name), ident.span)
}
#[inline]
pub fn ident(&self) -> Option<Ident> {
match self.kind {
TokenKind::Ident(ident) => Some(Ident::new(ident, self.span)),
_ => None,
}
}
#[inline]
pub fn lit(&self) -> Option<TokenLit> {
match self.kind {
TokenKind::Literal(kind, symbol) => Some(TokenLit::new(kind, symbol)),
_ => None,
}
}
#[inline]
pub fn lit_kind(&self) -> Option<TokenLitKind> {
match self.kind {
TokenKind::Literal(kind, _) => Some(kind),
_ => None,
}
}
#[inline]
pub fn comment(&self) -> Option<(bool, CommentKind, Symbol)> {
match self.kind {
TokenKind::Comment(is_doc, kind, symbol) => Some((is_doc, kind, symbol)),
_ => None,
}
}
#[inline]
pub fn is_op(&self) -> bool {
self.kind.is_op()
}
#[inline]
pub fn as_unop(&self, is_postfix: bool) -> Option<UnOp> {
self.kind.as_unop(is_postfix).map(|kind| UnOp { span: self.span, kind })
}
#[inline]
pub fn as_binop(&self) -> Option<BinOp> {
self.kind.as_binop().map(|kind| BinOp { span: self.span, kind })
}
#[inline]
pub fn as_binop_eq(&self) -> Option<BinOp> {
self.kind.as_binop_eq().map(|kind| BinOp { span: self.span, kind })
}
#[inline]
pub fn is_ident(&self) -> bool {
matches!(self.kind, TokenKind::Ident(_))
}
#[inline]
pub fn is_lit(&self) -> bool {
matches!(self.kind, TokenKind::Literal(..)) || self.is_bool_lit()
}
#[inline]
pub fn is_keyword(&self, kw: Symbol) -> bool {
self.is_ident_where(|id| id.name == kw)
}
#[inline]
pub fn is_keyword_any(&self, kws: &[Symbol]) -> bool {
self.is_ident_where(|id| kws.contains(&id.name))
}
#[inline]
pub fn is_used_keyword(&self) -> bool {
self.is_ident_where(Ident::is_used_keyword)
}
#[inline]
pub fn is_unused_keyword(&self) -> bool {
self.is_ident_where(Ident::is_unused_keyword)
}
#[inline]
pub fn is_reserved_ident(&self, yul: bool) -> bool {
self.is_ident_where(|i| i.is_reserved(yul))
}
#[inline]
pub fn is_non_reserved_ident(&self, yul: bool) -> bool {
self.is_ident_where(|i| i.is_non_reserved(yul))
}
#[inline]
pub fn is_elementary_type(&self) -> bool {
self.is_ident_where(Ident::is_elementary_type)
}
#[inline]
pub fn is_bool_lit(&self) -> bool {
self.is_ident_where(|id| id.name.is_bool_lit())
}
#[inline]
pub fn is_numeric_lit(&self) -> bool {
matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer | TokenLitKind::Rational, _))
}
#[inline]
pub fn is_integer_lit(&self) -> bool {
matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer, _))
}
#[inline]
pub fn is_rational_lit(&self) -> bool {
matches!(self.kind, TokenKind::Literal(TokenLitKind::Rational, _))
}
#[inline]
pub fn is_str_lit(&self) -> bool {
matches!(self.kind, TokenKind::Literal(TokenLitKind::Str, _))
}
#[inline]
pub fn is_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
self.ident().is_some_and(pred)
}
#[inline]
pub fn is_eof(&self) -> bool {
matches!(self.kind, TokenKind::Eof)
}
#[inline]
pub fn is_open_delim(&self, d: Delimiter) -> bool {
self.kind == TokenKind::OpenDelim(d)
}
#[inline]
pub fn is_close_delim(&self, d: Delimiter) -> bool {
self.kind == TokenKind::CloseDelim(d)
}
#[inline]
pub fn is_comment(&self) -> bool {
self.kind.is_comment()
}
#[inline]
pub fn is_comment_or_doc(&self) -> bool {
self.kind.is_comment_or_doc()
}
#[inline]
pub fn is_location_specifier(&self) -> bool {
self.is_ident_where(Ident::is_location_specifier)
}
#[inline]
pub fn is_mutability_specifier(&self) -> bool {
self.is_ident_where(Ident::is_mutability_specifier)
}
#[inline]
pub fn is_visibility_specifier(&self) -> bool {
self.is_ident_where(Ident::is_visibility_specifier)
}
pub fn full_description(&self) -> impl fmt::Display + '_ {
if let Some(description) = self.description() {
format!("{description} `{}`", self.kind)
} else {
format!("`{}`", self.kind)
}
}
pub fn as_str(&self) -> &str {
self.kind.as_str()
}
#[inline]
pub fn description(self) -> Option<TokenDescription> {
TokenDescription::from_token(self)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenDescription {
Keyword,
ReservedKeyword,
YulKeyword,
YulEvmBuiltin,
}
impl fmt::Display for TokenDescription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.to_str())
}
}
impl TokenDescription {
pub fn from_token(token: Token) -> Option<Self> {
match token.kind {
_ if token.is_used_keyword() => Some(Self::Keyword),
_ if token.is_unused_keyword() => Some(Self::ReservedKeyword),
_ if token.is_ident_where(|id| id.is_yul_keyword()) => Some(Self::YulKeyword),
_ if token.is_ident_where(|id| id.is_yul_builtin()) => Some(Self::YulEvmBuiltin),
_ => None,
}
}
pub const fn to_str(self) -> &'static str {
match self {
Self::Keyword => "keyword",
Self::ReservedKeyword => "reserved keyword",
Self::YulKeyword => "Yul keyword",
Self::YulEvmBuiltin => "Yul EVM builtin keyword",
}
}
}