1use crate::{
4 StrKind,
5 ast::{BinOp, BinOpKind, UnOp, UnOpKind},
6};
7use solar_interface::{Ident, Span, Symbol, diagnostics::ErrorGuaranteed};
8use std::{borrow::Cow, fmt, mem::MaybeUninit};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub enum CommentKind {
13 Line,
15 Block,
17}
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23pub enum BinOpToken {
24 Plus,
26 Minus,
28 Star,
30 Slash,
32 Percent,
34 Caret,
36 And,
38 Or,
40 Shl,
42 Shr,
44 Sar,
46}
47
48impl fmt::Display for BinOpToken {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.write_str(self.to_str())
51 }
52}
53
54impl BinOpToken {
55 pub const fn to_str(self) -> &'static str {
57 match self {
58 Self::Plus => "+",
59 Self::Minus => "-",
60 Self::Star => "*",
61 Self::Slash => "/",
62 Self::Percent => "%",
63 Self::Caret => "^",
64 Self::And => "&",
65 Self::Or => "|",
66 Self::Shl => "<<",
67 Self::Shr => ">>",
68 Self::Sar => ">>>",
69 }
70 }
71
72 pub const fn to_str_with_eq(self) -> &'static str {
75 match self {
76 Self::Plus => "+=",
77 Self::Minus => "-=",
78 Self::Star => "*=",
79 Self::Slash => "/=",
80 Self::Percent => "%=",
81 Self::Caret => "^=",
82 Self::And => "&=",
83 Self::Or => "|=",
84 Self::Shl => "<<=",
85 Self::Shr => ">>=",
86 Self::Sar => ">>>=",
87 }
88 }
89
90 #[inline]
92 pub const fn as_binop(self) -> BinOpKind {
93 match self {
94 Self::Plus => BinOpKind::Add,
95 Self::Minus => BinOpKind::Sub,
96 Self::Star => BinOpKind::Mul,
97 Self::Slash => BinOpKind::Div,
98 Self::Percent => BinOpKind::Rem,
99 Self::Caret => BinOpKind::BitXor,
100 Self::And => BinOpKind::BitAnd,
101 Self::Or => BinOpKind::BitOr,
102 Self::Shl => BinOpKind::Shl,
103 Self::Shr => BinOpKind::Shr,
104 Self::Sar => BinOpKind::Sar,
105 }
106 }
107}
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
111pub enum Delimiter {
112 Parenthesis,
114 Brace,
116 Bracket,
118}
119
120impl Delimiter {
121 pub const fn to_open_str(self) -> &'static str {
123 match self {
124 Self::Parenthesis => "(",
125 Self::Brace => "{",
126 Self::Bracket => "[",
127 }
128 }
129
130 pub const fn to_close_str(self) -> &'static str {
132 match self {
133 Self::Parenthesis => ")",
134 Self::Brace => "}",
135 Self::Bracket => "]",
136 }
137 }
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
143pub struct TokenLit {
144 pub symbol: Symbol,
146 pub kind: TokenLitKind,
148}
149
150impl fmt::Display for TokenLit {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 let &Self { kind, symbol } = self;
153 match kind {
154 TokenLitKind::Str => write!(f, "\"{symbol}\""),
155 TokenLitKind::UnicodeStr => write!(f, "unicode\"{symbol}\""),
156 TokenLitKind::HexStr => write!(f, "hex\"{symbol}\""),
157 TokenLitKind::Integer | TokenLitKind::Rational | TokenLitKind::Err(_) => {
158 write!(f, "{symbol}")
159 }
160 }
161 }
162}
163
164impl TokenLit {
165 #[inline]
167 pub const fn new(kind: TokenLitKind, symbol: Symbol) -> Self {
168 Self { kind, symbol }
169 }
170
171 pub const fn description(self) -> &'static str {
173 self.kind.description()
174 }
175}
176
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
179pub enum TokenLitKind {
180 Integer,
182 Rational,
184 Str,
186 UnicodeStr,
188 HexStr,
190 Err(ErrorGuaranteed),
192}
193
194impl From<StrKind> for TokenLitKind {
195 fn from(str_kind: StrKind) -> Self {
196 match str_kind {
197 StrKind::Str => Self::Str,
198 StrKind::Unicode => Self::UnicodeStr,
199 StrKind::Hex => Self::HexStr,
200 }
201 }
202}
203
204impl TokenLitKind {
205 pub const fn description(self) -> &'static str {
207 match self {
208 Self::Integer => "integer",
209 Self::Rational => "rational",
210 Self::Str => "string",
211 Self::UnicodeStr => "unicode string",
212 Self::HexStr => "hex string",
213 Self::Err(_) => "error",
214 }
215 }
216}
217
218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
220pub enum TokenKind {
221 Eq,
224 Lt,
226 Le,
228 EqEq,
230 Ne,
232 Ge,
234 Gt,
236 AndAnd,
238 OrOr,
240 Not,
242 Tilde,
244 Walrus,
246 PlusPlus,
248 MinusMinus,
250 StarStar,
252 BinOp(BinOpToken),
254 BinOpEq(BinOpToken),
256
257 At,
260 Dot,
262 Comma,
264 Semi,
266 Colon,
268 Arrow,
270 FatArrow,
272 Question,
274 OpenDelim(Delimiter),
276 CloseDelim(Delimiter),
278
279 Literal(TokenLitKind, Symbol),
287
288 Ident(Symbol),
290
291 Comment(bool , CommentKind, Symbol),
296
297 Eof,
299}
300
301impl fmt::Display for TokenKind {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 f.write_str(&self.description())
304 }
305}
306
307impl TokenKind {
308 pub fn lit(kind: TokenLitKind, symbol: Symbol) -> Self {
310 Self::Literal(kind, symbol)
311 }
312
313 pub fn as_str(&self) -> &str {
315 match self {
316 Self::Eq => "=",
317 Self::Lt => "<",
318 Self::Le => "<=",
319 Self::EqEq => "==",
320 Self::Ne => "!=",
321 Self::Ge => ">=",
322 Self::Gt => ">",
323 Self::AndAnd => "&&",
324 Self::OrOr => "||",
325 Self::Not => "!",
326 Self::Tilde => "~",
327 Self::Walrus => ":=",
328 Self::PlusPlus => "++",
329 Self::MinusMinus => "--",
330 Self::StarStar => "**",
331 Self::BinOp(op) => op.to_str(),
332 Self::BinOpEq(op) => op.to_str_with_eq(),
333
334 Self::At => "@",
335 Self::Dot => ".",
336 Self::Comma => ",",
337 Self::Semi => ";",
338 Self::Colon => ":",
339 Self::Arrow => "->",
340 Self::FatArrow => "=>",
341 Self::Question => "?",
342 Self::OpenDelim(d) => d.to_open_str(),
343 Self::CloseDelim(d) => d.to_close_str(),
344
345 Self::Literal(.., symbol) | Self::Ident(.., symbol) | Self::Comment(.., symbol) => {
346 symbol.as_str()
347 }
348
349 Self::Eof => "<eof>",
350 }
351 }
352
353 pub fn description(&self) -> Cow<'_, str> {
355 match self {
356 Self::Literal(kind, _) => return format!("<{}>", kind.description()).into(),
357 Self::Ident(symbol) => return symbol.to_string().into(),
358 Self::Comment(false, CommentKind::Block, _) => "<block comment>",
359 Self::Comment(true, CommentKind::Block, _) => "<block doc-comment>",
360 Self::Comment(false, CommentKind::Line, _) => "<line comment>",
361 Self::Comment(true, CommentKind::Line, _) => "<line doc-comment>",
362 _ => self.as_str(),
363 }
364 .into()
365 }
366
367 pub const fn is_op(&self) -> bool {
369 use TokenKind::*;
370 match self {
371 Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | Walrus
372 | PlusPlus | MinusMinus | StarStar | BinOp(_) | BinOpEq(_) | At | Dot | Comma
373 | Colon | Arrow | FatArrow | Question => true,
374
375 OpenDelim(..) | CloseDelim(..) | Literal(..) | Comment(..) | Ident(..) | Semi | Eof => {
376 false
377 }
378 }
379 }
380
381 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOpKind> {
383 let kind = if is_postfix {
384 match self {
385 Self::PlusPlus => UnOpKind::PostInc,
386 Self::MinusMinus => UnOpKind::PostDec,
387 _ => return None,
388 }
389 } else {
390 match self {
391 Self::PlusPlus => UnOpKind::PreInc,
392 Self::MinusMinus => UnOpKind::PreDec,
393 Self::Not => UnOpKind::Not,
394 Self::Tilde => UnOpKind::BitNot,
395 Self::BinOp(BinOpToken::Minus) => UnOpKind::Neg,
396 _ => return None,
397 }
398 };
399 debug_assert_eq!(kind.is_postfix(), is_postfix);
400 Some(kind)
401 }
402
403 #[inline]
405 pub fn as_binop(&self) -> Option<BinOpKind> {
406 match self {
407 Self::Eq => Some(BinOpKind::Eq),
408 Self::Lt => Some(BinOpKind::Lt),
409 Self::Le => Some(BinOpKind::Le),
410 Self::EqEq => Some(BinOpKind::Eq),
411 Self::Ne => Some(BinOpKind::Ne),
412 Self::Ge => Some(BinOpKind::Ge),
413 Self::Gt => Some(BinOpKind::Gt),
414 Self::AndAnd => Some(BinOpKind::And),
415 Self::OrOr => Some(BinOpKind::Or),
416 Self::StarStar => Some(BinOpKind::Pow),
417 Self::BinOp(op) => Some(op.as_binop()),
418 _ => None,
419 }
420 }
421
422 #[inline]
424 pub fn as_binop_eq(&self) -> Option<BinOpKind> {
425 match self {
426 Self::BinOpEq(op) => Some(op.as_binop()),
427 _ => None,
428 }
429 }
430
431 #[inline]
433 pub const fn is_comment(&self) -> bool {
434 matches!(self, Self::Comment(false, ..))
435 }
436
437 #[inline]
439 pub const fn is_comment_or_doc(&self) -> bool {
440 matches!(self, Self::Comment(..))
441 }
442}
443
444#[derive(Clone, Copy)]
449#[repr(C)]
450pub struct Token {
451 _kind: MaybeUninit<u64>,
452 _span: u64,
453}
454
455#[derive(Clone, Copy, PartialEq, Eq)]
459pub struct TokenRepr {
460 pub kind: TokenKind,
462 pub span: Span,
464}
465
466const _: () = {
467 assert!(size_of::<Token>() == size_of::<TokenRepr>());
468 assert!(align_of::<Token>() >= align_of::<TokenRepr>());
469 assert!(std::mem::offset_of!(Token, _kind) == std::mem::offset_of!(TokenRepr, kind));
470 assert!(std::mem::offset_of!(Token, _span) == std::mem::offset_of!(TokenRepr, span));
471};
472
473impl std::ops::Deref for Token {
474 type Target = TokenRepr;
475
476 #[inline(always)]
477 fn deref(&self) -> &Self::Target {
478 unsafe { std::mem::transmute(self) }
480 }
481}
482
483impl std::ops::DerefMut for Token {
484 #[inline(always)]
485 fn deref_mut(&mut self) -> &mut Self::Target {
486 unsafe { std::mem::transmute(self) }
488 }
489}
490
491impl fmt::Debug for Token {
492 #[inline]
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 fmt::Debug::fmt(&**self, f)
495 }
496}
497
498impl fmt::Debug for TokenRepr {
499 #[inline]
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 f.debug_struct("Token").field("kind", &self.kind).field("span", &self.span).finish()
502 }
503}
504
505impl PartialEq for Token {
506 #[inline]
507 fn eq(&self, other: &Self) -> bool {
508 **self == **other
509 }
510}
511
512impl Eq for Token {}
513
514impl From<Ident> for Token {
515 #[inline]
516 fn from(ident: Ident) -> Self {
517 Self::from_ast_ident(ident)
518 }
519}
520
521impl Token {
522 pub const EOF: Self = Self::new(TokenKind::Eof, Span::DUMMY);
524
525 pub const DUMMY: Self = Self::new(TokenKind::Question, Span::DUMMY);
527
528 #[inline]
530 pub const fn new(kind: TokenKind, span: Span) -> Self {
531 unsafe { std::mem::transmute(TokenRepr { kind, span }) }
533 }
534
535 #[inline]
537 pub fn from_ast_ident(ident: Ident) -> Self {
538 Self::new(TokenKind::Ident(ident.name), ident.span)
539 }
540
541 #[inline]
543 pub fn ident(&self) -> Option<Ident> {
544 match self.kind {
545 TokenKind::Ident(ident) => Some(Ident::new(ident, self.span)),
546 _ => None,
547 }
548 }
549
550 #[inline]
552 pub fn lit(&self) -> Option<TokenLit> {
553 match self.kind {
554 TokenKind::Literal(kind, symbol) => Some(TokenLit::new(kind, symbol)),
555 _ => None,
556 }
557 }
558
559 #[inline]
561 pub fn lit_kind(&self) -> Option<TokenLitKind> {
562 match self.kind {
563 TokenKind::Literal(kind, _) => Some(kind),
564 _ => None,
565 }
566 }
567
568 #[inline]
570 pub fn comment(&self) -> Option<(bool, CommentKind, Symbol)> {
571 match self.kind {
572 TokenKind::Comment(is_doc, kind, symbol) => Some((is_doc, kind, symbol)),
573 _ => None,
574 }
575 }
576
577 #[inline]
579 pub fn is_op(&self) -> bool {
580 self.kind.is_op()
581 }
582
583 #[inline]
585 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOp> {
586 self.kind.as_unop(is_postfix).map(|kind| UnOp { span: self.span, kind })
587 }
588
589 #[inline]
591 pub fn as_binop(&self) -> Option<BinOp> {
592 self.kind.as_binop().map(|kind| BinOp { span: self.span, kind })
593 }
594
595 #[inline]
597 pub fn as_binop_eq(&self) -> Option<BinOp> {
598 self.kind.as_binop_eq().map(|kind| BinOp { span: self.span, kind })
599 }
600
601 #[inline]
603 pub fn is_ident(&self) -> bool {
604 matches!(self.kind, TokenKind::Ident(_))
605 }
606
607 #[inline]
609 pub fn is_lit(&self) -> bool {
610 matches!(self.kind, TokenKind::Literal(..)) || self.is_bool_lit()
611 }
612
613 #[inline]
615 pub fn is_keyword(&self, kw: Symbol) -> bool {
616 self.is_ident_where(|id| id.name == kw)
617 }
618
619 #[inline]
621 pub fn is_keyword_any(&self, kws: &[Symbol]) -> bool {
622 self.is_ident_where(|id| kws.contains(&id.name))
623 }
624
625 #[inline]
627 pub fn is_used_keyword(&self) -> bool {
628 self.is_ident_where(Ident::is_used_keyword)
629 }
630
631 #[inline]
633 pub fn is_unused_keyword(&self) -> bool {
634 self.is_ident_where(Ident::is_unused_keyword)
635 }
636
637 #[inline]
639 pub fn is_reserved_ident(&self, yul: bool) -> bool {
640 self.is_ident_where(|i| i.is_reserved(yul))
641 }
642
643 #[inline]
645 pub fn is_non_reserved_ident(&self, yul: bool) -> bool {
646 self.is_ident_where(|i| i.is_non_reserved(yul))
647 }
648
649 #[inline]
653 pub fn is_elementary_type(&self) -> bool {
654 self.is_ident_where(Ident::is_elementary_type)
655 }
656
657 #[inline]
659 pub fn is_bool_lit(&self) -> bool {
660 self.is_ident_where(|id| id.name.is_bool_lit())
661 }
662
663 #[inline]
665 pub fn is_numeric_lit(&self) -> bool {
666 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer | TokenLitKind::Rational, _))
667 }
668
669 #[inline]
671 pub fn is_integer_lit(&self) -> bool {
672 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer, _))
673 }
674
675 #[inline]
677 pub fn is_rational_lit(&self) -> bool {
678 matches!(self.kind, TokenKind::Literal(TokenLitKind::Rational, _))
679 }
680
681 #[inline]
683 pub fn is_str_lit(&self) -> bool {
684 matches!(self.kind, TokenKind::Literal(TokenLitKind::Str, _))
685 }
686
687 #[inline]
689 pub fn is_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
690 self.ident().is_some_and(pred)
691 }
692
693 #[inline]
695 pub fn is_eof(&self) -> bool {
696 matches!(self.kind, TokenKind::Eof)
697 }
698
699 #[inline]
701 pub fn is_open_delim(&self, d: Delimiter) -> bool {
702 self.kind == TokenKind::OpenDelim(d)
703 }
704
705 #[inline]
707 pub fn is_close_delim(&self, d: Delimiter) -> bool {
708 self.kind == TokenKind::CloseDelim(d)
709 }
710
711 #[inline]
713 pub fn is_comment(&self) -> bool {
714 self.kind.is_comment()
715 }
716
717 #[inline]
719 pub fn is_comment_or_doc(&self) -> bool {
720 self.kind.is_comment_or_doc()
721 }
722
723 #[inline]
725 pub fn is_location_specifier(&self) -> bool {
726 self.is_ident_where(Ident::is_location_specifier)
727 }
728
729 #[inline]
731 pub fn is_mutability_specifier(&self) -> bool {
732 self.is_ident_where(Ident::is_mutability_specifier)
733 }
734
735 #[inline]
737 pub fn is_visibility_specifier(&self) -> bool {
738 self.is_ident_where(Ident::is_visibility_specifier)
739 }
740
741 pub fn full_description(&self) -> impl fmt::Display + '_ {
743 if let Some(description) = self.description() {
745 format!("{description} `{}`", self.kind)
746 } else {
747 format!("`{}`", self.kind)
748 }
749 }
750
751 pub fn as_str(&self) -> &str {
753 self.kind.as_str()
754 }
755
756 #[inline]
758 pub fn description(self) -> Option<TokenDescription> {
759 TokenDescription::from_token(self)
760 }
761}
762
763#[derive(Clone, Copy, Debug, PartialEq, Eq)]
768pub enum TokenDescription {
769 Keyword,
771 ReservedKeyword,
773 YulKeyword,
775 YulEvmBuiltin,
777}
778
779impl fmt::Display for TokenDescription {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 f.write_str(self.to_str())
782 }
783}
784
785impl TokenDescription {
786 pub fn from_token(token: Token) -> Option<Self> {
788 match token.kind {
789 _ if token.is_used_keyword() => Some(Self::Keyword),
790 _ if token.is_unused_keyword() => Some(Self::ReservedKeyword),
791 _ if token.is_ident_where(|id| id.is_yul_keyword()) => Some(Self::YulKeyword),
792 _ if token.is_ident_where(|id| id.is_yul_builtin()) => Some(Self::YulEvmBuiltin),
793 _ => None,
794 }
795 }
796
797 pub const fn to_str(self) -> &'static str {
799 match self {
800 Self::Keyword => "keyword",
801 Self::ReservedKeyword => "reserved keyword",
802 Self::YulKeyword => "Yul keyword",
803 Self::YulEvmBuiltin => "Yul EVM builtin keyword",
804 }
805 }
806}