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};
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 pub const fn glue(self, other: Self) -> Option<Self> {
445 use BinOpToken::*;
446 use TokenKind::*;
447 Some(match self {
448 Eq => match other {
449 Eq => EqEq,
450 Gt => FatArrow,
451 _ => return None,
452 },
453 Lt => match other {
454 Eq => Le,
455 Lt => BinOp(Shl),
456 Le => BinOpEq(Shl),
457 _ => return None,
458 },
459 Gt => match other {
460 Eq => Ge,
461 Gt => BinOp(Shr),
462 Ge => BinOpEq(Shr),
463 BinOp(Shr) => BinOp(Sar),
464 BinOpEq(Shr) => BinOpEq(Sar),
465 _ => return None,
466 },
467 Not => match other {
468 Eq => Ne,
469 _ => return None,
470 },
471 Colon => match other {
472 Eq => Walrus,
473 _ => return None,
474 },
475 BinOp(op) => match (op, other) {
476 (op, Eq) => BinOpEq(op),
477 (And, BinOp(And)) => AndAnd,
478 (Or, BinOp(Or)) => OrOr,
479 (Minus, Gt) => Arrow,
480 (Shr, Gt) => BinOp(Sar),
481 (Shr, Ge) => BinOpEq(Sar),
482 (Plus, BinOp(Plus)) => PlusPlus,
483 (Minus, BinOp(Minus)) => MinusMinus,
484 (Star, BinOp(Star)) => StarStar,
485 _ => return None,
486 },
487
488 Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | Walrus | PlusPlus | MinusMinus
489 | StarStar | BinOpEq(_) | At | Dot | Comma | Semi | Arrow | FatArrow | Question
490 | OpenDelim(_) | CloseDelim(_) | Literal(..) | Ident(_) | Comment(..) | Eof => {
491 return None;
492 }
493 })
494 }
495}
496
497#[derive(Clone, Copy, Debug, PartialEq, Eq)]
499pub struct Token {
500 pub kind: TokenKind,
502 pub span: Span,
504}
505
506impl From<Ident> for Token {
507 #[inline]
508 fn from(ident: Ident) -> Self {
509 Self::from_ast_ident(ident)
510 }
511}
512
513impl Token {
514 pub const EOF: Self = Self::new(TokenKind::Eof, Span::DUMMY);
516
517 pub const DUMMY: Self = Self::new(TokenKind::Question, Span::DUMMY);
519
520 #[inline]
522 pub const fn new(kind: TokenKind, span: Span) -> Self {
523 Self { kind, span }
524 }
525
526 #[inline]
528 pub fn from_ast_ident(ident: Ident) -> Self {
529 Self::new(TokenKind::Ident(ident.name), ident.span)
530 }
531
532 #[inline]
534 pub const fn ident(&self) -> Option<Ident> {
535 match self.kind {
536 TokenKind::Ident(ident) => Some(Ident::new(ident, self.span)),
537 _ => None,
538 }
539 }
540
541 #[inline]
543 pub const fn lit(&self) -> Option<TokenLit> {
544 match self.kind {
545 TokenKind::Literal(kind, symbol) => Some(TokenLit::new(kind, symbol)),
546 _ => None,
547 }
548 }
549
550 #[inline]
552 pub const fn lit_kind(&self) -> Option<TokenLitKind> {
553 match self.kind {
554 TokenKind::Literal(kind, _) => Some(kind),
555 _ => None,
556 }
557 }
558
559 #[inline]
561 pub const fn comment(&self) -> Option<(bool, DocComment)> {
562 match self.kind {
563 TokenKind::Comment(is_doc, kind, symbol) => {
564 Some((is_doc, DocComment { span: self.span, kind, symbol }))
565 }
566 _ => None,
567 }
568 }
569
570 #[inline]
574 pub const fn doc(&self) -> Option<DocComment> {
575 match self.kind {
576 TokenKind::Comment(_, kind, symbol) => {
577 Some(DocComment { span: self.span, kind, symbol })
578 }
579 _ => None,
580 }
581 }
582
583 #[inline]
585 pub const fn is_op(&self) -> bool {
586 self.kind.is_op()
587 }
588
589 #[inline]
591 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOp> {
592 self.kind.as_unop(is_postfix).map(|kind| UnOp { span: self.span, kind })
593 }
594
595 #[inline]
597 pub fn as_binop(&self) -> Option<BinOp> {
598 self.kind.as_binop().map(|kind| BinOp { span: self.span, kind })
599 }
600
601 #[inline]
603 pub fn as_binop_eq(&self) -> Option<BinOp> {
604 self.kind.as_binop_eq().map(|kind| BinOp { span: self.span, kind })
605 }
606
607 #[inline]
609 pub const fn is_ident(&self) -> bool {
610 matches!(self.kind, TokenKind::Ident(_))
611 }
612
613 #[inline]
615 pub fn is_lit(&self) -> bool {
616 matches!(self.kind, TokenKind::Literal(..)) || self.is_bool_lit()
617 }
618
619 #[inline]
621 pub fn is_keyword(&self, kw: Symbol) -> bool {
622 self.is_ident_where(|id| id.name == kw)
623 }
624
625 #[inline]
627 pub fn is_keyword_any(&self, kws: &[Symbol]) -> bool {
628 self.is_ident_where(|id| kws.contains(&id.name))
629 }
630
631 #[inline]
633 pub fn is_used_keyword(&self) -> bool {
634 self.is_ident_where(Ident::is_used_keyword)
635 }
636
637 #[inline]
639 pub fn is_unused_keyword(&self) -> bool {
640 self.is_ident_where(Ident::is_unused_keyword)
641 }
642
643 #[inline]
645 pub fn is_reserved_ident(&self, yul: bool) -> bool {
646 self.is_ident_where(|i| i.is_reserved(yul))
647 }
648
649 #[inline]
651 pub fn is_non_reserved_ident(&self, yul: bool) -> bool {
652 self.is_ident_where(|i| i.is_non_reserved(yul))
653 }
654
655 #[inline]
659 pub fn is_elementary_type(&self) -> bool {
660 self.is_ident_where(Ident::is_elementary_type)
661 }
662
663 #[inline]
665 pub fn is_bool_lit(&self) -> bool {
666 self.is_ident_where(|id| id.name.is_bool_lit())
667 }
668
669 #[inline]
671 pub fn is_numeric_lit(&self) -> bool {
672 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer | TokenLitKind::Rational, _))
673 }
674
675 #[inline]
677 pub fn is_integer_lit(&self) -> bool {
678 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer, _))
679 }
680
681 #[inline]
683 pub fn is_rational_lit(&self) -> bool {
684 matches!(self.kind, TokenKind::Literal(TokenLitKind::Rational, _))
685 }
686
687 #[inline]
689 pub fn is_str_lit(&self) -> bool {
690 matches!(self.kind, TokenKind::Literal(TokenLitKind::Str, _))
691 }
692
693 #[inline]
695 pub fn is_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
696 self.ident().map(pred).unwrap_or(false)
697 }
698
699 #[inline]
701 pub const fn is_eof(&self) -> bool {
702 matches!(self.kind, TokenKind::Eof)
703 }
704
705 #[inline]
707 pub fn is_open_delim(&self, d: Delimiter) -> bool {
708 self.kind == TokenKind::OpenDelim(d)
709 }
710
711 #[inline]
713 pub fn is_close_delim(&self, d: Delimiter) -> bool {
714 self.kind == TokenKind::CloseDelim(d)
715 }
716
717 #[inline]
719 pub const fn is_comment(&self) -> bool {
720 self.kind.is_comment()
721 }
722
723 #[inline]
725 pub const fn is_comment_or_doc(&self) -> bool {
726 self.kind.is_comment_or_doc()
727 }
728
729 #[inline]
731 pub fn is_location_specifier(&self) -> bool {
732 self.is_ident_where(Ident::is_location_specifier)
733 }
734
735 #[inline]
737 pub fn is_mutability_specifier(&self) -> bool {
738 self.is_ident_where(Ident::is_mutability_specifier)
739 }
740
741 #[inline]
743 pub fn is_visibility_specifier(&self) -> bool {
744 self.is_ident_where(Ident::is_visibility_specifier)
745 }
746
747 pub fn full_description(&self) -> impl fmt::Display + '_ {
749 if let Some(description) = self.description() {
751 format!("{description} `{}`", self.kind)
752 } else {
753 format!("`{}`", self.kind)
754 }
755 }
756
757 pub fn as_str(&self) -> &str {
759 self.kind.as_str()
760 }
761
762 #[inline]
764 pub fn description(self) -> Option<TokenDescription> {
765 TokenDescription::from_token(self)
766 }
767
768 pub fn glue(self, other: Self) -> Option<Self> {
770 self.kind.glue(other.kind).map(|kind| Self::new(kind, self.span.to(other.span)))
771 }
772}
773
774#[derive(Clone, Copy, Debug, PartialEq, Eq)]
779pub enum TokenDescription {
780 Keyword,
782 ReservedKeyword,
784 YulKeyword,
786 YulEvmBuiltin,
788}
789
790impl fmt::Display for TokenDescription {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 f.write_str(self.to_str())
793 }
794}
795
796impl TokenDescription {
797 pub fn from_token(token: Token) -> Option<Self> {
799 match token.kind {
800 _ if token.is_used_keyword() => Some(Self::Keyword),
801 _ if token.is_unused_keyword() => Some(Self::ReservedKeyword),
802 _ if token.is_ident_where(|id| id.is_yul_keyword()) => Some(Self::YulKeyword),
803 _ if token.is_ident_where(|id| id.is_yul_evm_builtin()) => Some(Self::YulEvmBuiltin),
804 _ => None,
805 }
806 }
807
808 pub const fn to_str(self) -> &'static str {
810 match self {
811 Self::Keyword => "keyword",
812 Self::ReservedKeyword => "reserved keyword",
813 Self::YulKeyword => "Yul keyword",
814 Self::YulEvmBuiltin => "Yul EVM builtin keyword",
815 }
816 }
817}