1use crate::{
4 DocComment, 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, DocComment)> {
571 match self.kind {
572 TokenKind::Comment(is_doc, kind, symbol) => {
573 Some((is_doc, DocComment { span: self.span, kind, symbol }))
574 }
575 _ => None,
576 }
577 }
578
579 #[inline]
583 pub fn doc(&self) -> Option<DocComment> {
584 match self.kind {
585 TokenKind::Comment(_, kind, symbol) => {
586 Some(DocComment { span: self.span, kind, symbol })
587 }
588 _ => None,
589 }
590 }
591
592 #[inline]
594 pub fn is_op(&self) -> bool {
595 self.kind.is_op()
596 }
597
598 #[inline]
600 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOp> {
601 self.kind.as_unop(is_postfix).map(|kind| UnOp { span: self.span, kind })
602 }
603
604 #[inline]
606 pub fn as_binop(&self) -> Option<BinOp> {
607 self.kind.as_binop().map(|kind| BinOp { span: self.span, kind })
608 }
609
610 #[inline]
612 pub fn as_binop_eq(&self) -> Option<BinOp> {
613 self.kind.as_binop_eq().map(|kind| BinOp { span: self.span, kind })
614 }
615
616 #[inline]
618 pub fn is_ident(&self) -> bool {
619 matches!(self.kind, TokenKind::Ident(_))
620 }
621
622 #[inline]
624 pub fn is_lit(&self) -> bool {
625 matches!(self.kind, TokenKind::Literal(..)) || self.is_bool_lit()
626 }
627
628 #[inline]
630 pub fn is_keyword(&self, kw: Symbol) -> bool {
631 self.is_ident_where(|id| id.name == kw)
632 }
633
634 #[inline]
636 pub fn is_keyword_any(&self, kws: &[Symbol]) -> bool {
637 self.is_ident_where(|id| kws.contains(&id.name))
638 }
639
640 #[inline]
642 pub fn is_used_keyword(&self) -> bool {
643 self.is_ident_where(Ident::is_used_keyword)
644 }
645
646 #[inline]
648 pub fn is_unused_keyword(&self) -> bool {
649 self.is_ident_where(Ident::is_unused_keyword)
650 }
651
652 #[inline]
654 pub fn is_reserved_ident(&self, yul: bool) -> bool {
655 self.is_ident_where(|i| i.is_reserved(yul))
656 }
657
658 #[inline]
660 pub fn is_non_reserved_ident(&self, yul: bool) -> bool {
661 self.is_ident_where(|i| i.is_non_reserved(yul))
662 }
663
664 #[inline]
668 pub fn is_elementary_type(&self) -> bool {
669 self.is_ident_where(Ident::is_elementary_type)
670 }
671
672 #[inline]
674 pub fn is_bool_lit(&self) -> bool {
675 self.is_ident_where(|id| id.name.is_bool_lit())
676 }
677
678 #[inline]
680 pub fn is_numeric_lit(&self) -> bool {
681 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer | TokenLitKind::Rational, _))
682 }
683
684 #[inline]
686 pub fn is_integer_lit(&self) -> bool {
687 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer, _))
688 }
689
690 #[inline]
692 pub fn is_rational_lit(&self) -> bool {
693 matches!(self.kind, TokenKind::Literal(TokenLitKind::Rational, _))
694 }
695
696 #[inline]
698 pub fn is_str_lit(&self) -> bool {
699 matches!(self.kind, TokenKind::Literal(TokenLitKind::Str, _))
700 }
701
702 #[inline]
704 pub fn is_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
705 self.ident().is_some_and(pred)
706 }
707
708 #[inline]
710 pub fn is_eof(&self) -> bool {
711 matches!(self.kind, TokenKind::Eof)
712 }
713
714 #[inline]
716 pub fn is_open_delim(&self, d: Delimiter) -> bool {
717 self.kind == TokenKind::OpenDelim(d)
718 }
719
720 #[inline]
722 pub fn is_close_delim(&self, d: Delimiter) -> bool {
723 self.kind == TokenKind::CloseDelim(d)
724 }
725
726 #[inline]
728 pub fn is_comment(&self) -> bool {
729 self.kind.is_comment()
730 }
731
732 #[inline]
734 pub fn is_comment_or_doc(&self) -> bool {
735 self.kind.is_comment_or_doc()
736 }
737
738 #[inline]
740 pub fn is_location_specifier(&self) -> bool {
741 self.is_ident_where(Ident::is_location_specifier)
742 }
743
744 #[inline]
746 pub fn is_mutability_specifier(&self) -> bool {
747 self.is_ident_where(Ident::is_mutability_specifier)
748 }
749
750 #[inline]
752 pub fn is_visibility_specifier(&self) -> bool {
753 self.is_ident_where(Ident::is_visibility_specifier)
754 }
755
756 pub fn full_description(&self) -> impl fmt::Display + '_ {
758 if let Some(description) = self.description() {
760 format!("{description} `{}`", self.kind)
761 } else {
762 format!("`{}`", self.kind)
763 }
764 }
765
766 pub fn as_str(&self) -> &str {
768 self.kind.as_str()
769 }
770
771 #[inline]
773 pub fn description(self) -> Option<TokenDescription> {
774 TokenDescription::from_token(self)
775 }
776}
777
778#[derive(Clone, Copy, Debug, PartialEq, Eq)]
783pub enum TokenDescription {
784 Keyword,
786 ReservedKeyword,
788 YulKeyword,
790 YulEvmBuiltin,
792}
793
794impl fmt::Display for TokenDescription {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 f.write_str(self.to_str())
797 }
798}
799
800impl TokenDescription {
801 pub fn from_token(token: Token) -> Option<Self> {
803 match token.kind {
804 _ if token.is_used_keyword() => Some(Self::Keyword),
805 _ if token.is_unused_keyword() => Some(Self::ReservedKeyword),
806 _ if token.is_ident_where(|id| id.is_yul_keyword()) => Some(Self::YulKeyword),
807 _ if token.is_ident_where(|id| id.is_yul_evm_builtin()) => Some(Self::YulEvmBuiltin),
808 _ => None,
809 }
810 }
811
812 pub const fn to_str(self) -> &'static str {
814 match self {
815 Self::Keyword => "keyword",
816 Self::ReservedKeyword => "reserved keyword",
817 Self::YulKeyword => "Yul keyword",
818 Self::YulEvmBuiltin => "Yul EVM builtin keyword",
819 }
820 }
821}