1use crate::{
4 ast::{BinOp, BinOpKind, UnOp, UnOpKind},
5 DocComment,
6};
7use solar_interface::{diagnostics::ErrorGuaranteed, Ident, Span, Symbol};
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::Pow,
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
120#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123pub struct TokenLit {
124 pub symbol: Symbol,
126 pub kind: TokenLitKind,
128}
129
130impl fmt::Display for TokenLit {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 let &Self { kind, symbol } = self;
133 match kind {
134 TokenLitKind::Str => write!(f, "\"{symbol}\""),
135 TokenLitKind::UnicodeStr => write!(f, "unicode\"{symbol}\""),
136 TokenLitKind::HexStr => write!(f, "hex\"{symbol}\""),
137 TokenLitKind::Integer | TokenLitKind::Rational | TokenLitKind::Err(_) => {
138 write!(f, "{symbol}")
139 }
140 }
141 }
142}
143
144impl TokenLit {
145 #[inline]
147 pub const fn new(kind: TokenLitKind, symbol: Symbol) -> Self {
148 Self { kind, symbol }
149 }
150
151 pub const fn description(self) -> &'static str {
153 self.kind.description()
154 }
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
159pub enum TokenLitKind {
160 Integer,
162 Rational,
164 Str,
166 UnicodeStr,
168 HexStr,
170 Err(ErrorGuaranteed),
172}
173
174impl TokenLitKind {
175 pub const fn description(self) -> &'static str {
177 match self {
178 Self::Integer => "integer",
179 Self::Rational => "rational",
180 Self::Str => "string",
181 Self::UnicodeStr => "unicode string",
182 Self::HexStr => "hex string",
183 Self::Err(_) => "error",
184 }
185 }
186}
187
188#[derive(Clone, Copy, Debug, PartialEq, Eq)]
190pub enum TokenKind {
191 Eq,
194 Lt,
196 Le,
198 EqEq,
200 Ne,
202 Ge,
204 Gt,
206 AndAnd,
208 OrOr,
210 Not,
212 Tilde,
214 Walrus,
216 PlusPlus,
218 MinusMinus,
220 StarStar,
222 BinOp(BinOpToken),
224 BinOpEq(BinOpToken),
226
227 At,
230 Dot,
232 Comma,
234 Semi,
236 Colon,
238 Arrow,
240 FatArrow,
242 Question,
244 OpenDelim(Delimiter),
246 CloseDelim(Delimiter),
248
249 Literal(TokenLitKind, Symbol),
254
255 Ident(Symbol),
257
258 Comment(bool , CommentKind, Symbol),
262
263 Eof,
265}
266
267impl fmt::Display for TokenKind {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 f.write_str(&self.description())
270 }
271}
272
273impl TokenKind {
274 pub fn lit(kind: TokenLitKind, symbol: Symbol) -> Self {
276 Self::Literal(kind, symbol)
277 }
278
279 pub fn as_str(&self) -> &str {
281 match self {
282 Self::Eq => "=",
283 Self::Lt => "<",
284 Self::Le => "<=",
285 Self::EqEq => "==",
286 Self::Ne => "!=",
287 Self::Ge => ">=",
288 Self::Gt => ">",
289 Self::AndAnd => "&&",
290 Self::OrOr => "||",
291 Self::Not => "!",
292 Self::Tilde => "~",
293 Self::Walrus => ":=",
294 Self::PlusPlus => "++",
295 Self::MinusMinus => "--",
296 Self::StarStar => "**",
297 Self::BinOp(op) => op.to_str(),
298 Self::BinOpEq(op) => op.to_str_with_eq(),
299
300 Self::At => "@",
301 Self::Dot => ".",
302 Self::Comma => ",",
303 Self::Semi => ";",
304 Self::Colon => ":",
305 Self::Arrow => "->",
306 Self::FatArrow => "=>",
307 Self::Question => "?",
308 Self::OpenDelim(Delimiter::Parenthesis) => "(",
309 Self::CloseDelim(Delimiter::Parenthesis) => ")",
310 Self::OpenDelim(Delimiter::Brace) => "{",
311 Self::CloseDelim(Delimiter::Brace) => "}",
312 Self::OpenDelim(Delimiter::Bracket) => "[",
313 Self::CloseDelim(Delimiter::Bracket) => "]",
314
315 Self::Literal(.., symbol) | Self::Ident(.., symbol) | Self::Comment(.., symbol) => {
316 symbol.as_str()
317 }
318
319 Self::Eof => "<eof>",
320 }
321 }
322
323 pub fn description(&self) -> Cow<'_, str> {
325 match self {
326 Self::Literal(kind, _) => return format!("<{}>", kind.description()).into(),
327 Self::Ident(symbol) => return symbol.to_string().into(),
328 Self::Comment(false, CommentKind::Block, _) => "<block comment>",
329 Self::Comment(true, CommentKind::Block, _) => "<block doc-comment>",
330 Self::Comment(false, CommentKind::Line, _) => "<line comment>",
331 Self::Comment(true, CommentKind::Line, _) => "<line doc-comment>",
332 _ => self.as_str(),
333 }
334 .into()
335 }
336
337 pub const fn is_op(&self) -> bool {
339 use TokenKind::*;
340 match self {
341 Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | Walrus
342 | PlusPlus | MinusMinus | StarStar | BinOp(_) | BinOpEq(_) | At | Dot | Comma
343 | Colon | Arrow | FatArrow | Question => true,
344
345 OpenDelim(..) | CloseDelim(..) | Literal(..) | Comment(..) | Ident(..) | Semi | Eof => {
346 false
347 }
348 }
349 }
350
351 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOpKind> {
353 let kind = if is_postfix {
354 match self {
355 Self::PlusPlus => UnOpKind::PostInc,
356 Self::MinusMinus => UnOpKind::PostDec,
357 _ => return None,
358 }
359 } else {
360 match self {
361 Self::PlusPlus => UnOpKind::PreInc,
362 Self::MinusMinus => UnOpKind::PreDec,
363 Self::Not => UnOpKind::Not,
364 Self::Tilde => UnOpKind::BitNot,
365 Self::BinOp(BinOpToken::Minus) => UnOpKind::Neg,
366 _ => return None,
367 }
368 };
369 debug_assert_eq!(kind.is_postfix(), is_postfix);
370 Some(kind)
371 }
372
373 #[inline]
375 pub fn as_binop(&self) -> Option<BinOpKind> {
376 match self {
377 Self::Eq => Some(BinOpKind::Eq),
378 Self::Lt => Some(BinOpKind::Lt),
379 Self::Le => Some(BinOpKind::Le),
380 Self::EqEq => Some(BinOpKind::Eq),
381 Self::Ne => Some(BinOpKind::Ne),
382 Self::Ge => Some(BinOpKind::Ge),
383 Self::Gt => Some(BinOpKind::Gt),
384 Self::AndAnd => Some(BinOpKind::And),
385 Self::OrOr => Some(BinOpKind::Or),
386 Self::StarStar => Some(BinOpKind::Pow),
387 Self::BinOp(op) => Some(op.as_binop()),
388 _ => None,
389 }
390 }
391
392 #[inline]
394 pub fn as_binop_eq(&self) -> Option<BinOpKind> {
395 match self {
396 Self::BinOpEq(op) => Some(op.as_binop()),
397 _ => None,
398 }
399 }
400
401 #[inline]
403 pub const fn is_comment(&self) -> bool {
404 matches!(self, Self::Comment(false, ..))
405 }
406
407 #[inline]
409 pub const fn is_comment_or_doc(&self) -> bool {
410 matches!(self, Self::Comment(..))
411 }
412
413 pub const fn glue(self, other: Self) -> Option<Self> {
415 use BinOpToken::*;
416 use TokenKind::*;
417 Some(match self {
418 Eq => match other {
419 Eq => EqEq,
420 Gt => FatArrow,
421 _ => return None,
422 },
423 Lt => match other {
424 Eq => Le,
425 Lt => BinOp(Shl),
426 Le => BinOpEq(Shl),
427 _ => return None,
428 },
429 Gt => match other {
430 Eq => Ge,
431 Gt => BinOp(Shr),
432 Ge => BinOpEq(Shr),
433 BinOp(Shr) => BinOp(Sar),
434 BinOpEq(Shr) => BinOpEq(Sar),
435 _ => return None,
436 },
437 Not => match other {
438 Eq => Ne,
439 _ => return None,
440 },
441 Colon => match other {
442 Eq => Walrus,
443 _ => return None,
444 },
445 BinOp(op) => match (op, other) {
446 (op, Eq) => BinOpEq(op),
447 (And, BinOp(And)) => AndAnd,
448 (Or, BinOp(Or)) => OrOr,
449 (Minus, Gt) => Arrow,
450 (Shr, Gt) => BinOp(Sar),
451 (Shr, Ge) => BinOpEq(Sar),
452 (Plus, BinOp(Plus)) => PlusPlus,
453 (Minus, BinOp(Minus)) => MinusMinus,
454 (Star, BinOp(Star)) => StarStar,
455 _ => return None,
456 },
457
458 Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | Walrus | PlusPlus | MinusMinus
459 | StarStar | BinOpEq(_) | At | Dot | Comma | Semi | Arrow | FatArrow | Question
460 | OpenDelim(_) | CloseDelim(_) | Literal(..) | Ident(_) | Comment(..) | Eof => {
461 return None
462 }
463 })
464 }
465}
466
467#[derive(Clone, Copy, Debug, PartialEq, Eq)]
469pub struct Token {
470 pub kind: TokenKind,
472 pub span: Span,
474}
475
476impl From<Ident> for Token {
477 #[inline]
478 fn from(ident: Ident) -> Self {
479 Self::from_ast_ident(ident)
480 }
481}
482
483impl Token {
484 pub const EOF: Self = Self::new(TokenKind::Eof, Span::DUMMY);
486
487 pub const DUMMY: Self = Self::new(TokenKind::Question, Span::DUMMY);
489
490 #[inline]
492 pub const fn new(kind: TokenKind, span: Span) -> Self {
493 Self { kind, span }
494 }
495
496 #[inline]
498 pub fn from_ast_ident(ident: Ident) -> Self {
499 Self::new(TokenKind::Ident(ident.name), ident.span)
500 }
501
502 #[inline]
504 pub const fn ident(&self) -> Option<Ident> {
505 match self.kind {
506 TokenKind::Ident(ident) => Some(Ident::new(ident, self.span)),
507 _ => None,
508 }
509 }
510
511 #[inline]
513 pub const fn lit(&self) -> Option<TokenLit> {
514 match self.kind {
515 TokenKind::Literal(kind, symbol) => Some(TokenLit::new(kind, symbol)),
516 _ => None,
517 }
518 }
519
520 #[inline]
522 pub const fn lit_kind(&self) -> Option<TokenLitKind> {
523 match self.kind {
524 TokenKind::Literal(kind, _) => Some(kind),
525 _ => None,
526 }
527 }
528
529 #[inline]
531 pub const fn comment(&self) -> Option<(bool, DocComment)> {
532 match self.kind {
533 TokenKind::Comment(is_doc, kind, symbol) => {
534 Some((is_doc, DocComment { span: self.span, kind, symbol }))
535 }
536 _ => None,
537 }
538 }
539
540 #[inline]
544 pub const fn doc(&self) -> Option<DocComment> {
545 match self.kind {
546 TokenKind::Comment(_, kind, symbol) => {
547 Some(DocComment { span: self.span, kind, symbol })
548 }
549 _ => None,
550 }
551 }
552
553 #[inline]
555 pub const fn is_op(&self) -> bool {
556 self.kind.is_op()
557 }
558
559 #[inline]
561 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOp> {
562 self.kind.as_unop(is_postfix).map(|kind| UnOp { span: self.span, kind })
563 }
564
565 #[inline]
567 pub fn as_binop(&self) -> Option<BinOp> {
568 self.kind.as_binop().map(|kind| BinOp { span: self.span, kind })
569 }
570
571 #[inline]
573 pub fn as_binop_eq(&self) -> Option<BinOp> {
574 self.kind.as_binop_eq().map(|kind| BinOp { span: self.span, kind })
575 }
576
577 #[inline]
579 pub const fn is_ident(&self) -> bool {
580 matches!(self.kind, TokenKind::Ident(_))
581 }
582
583 #[inline]
585 pub fn is_lit(&self) -> bool {
586 matches!(self.kind, TokenKind::Literal(..)) || self.is_bool_lit()
587 }
588
589 #[inline]
591 pub fn is_keyword(&self, kw: Symbol) -> bool {
592 self.is_ident_where(|id| id.name == kw)
593 }
594
595 #[inline]
597 pub fn is_keyword_any(&self, kws: &[Symbol]) -> bool {
598 self.is_ident_where(|id| kws.contains(&id.name))
599 }
600
601 #[inline]
603 pub fn is_used_keyword(&self) -> bool {
604 self.is_ident_where(Ident::is_used_keyword)
605 }
606
607 #[inline]
609 pub fn is_unused_keyword(&self) -> bool {
610 self.is_ident_where(Ident::is_unused_keyword)
611 }
612
613 #[inline]
615 pub fn is_reserved_ident(&self, yul: bool) -> bool {
616 self.is_ident_where(|i| i.is_reserved(yul))
617 }
618
619 #[inline]
621 pub fn is_non_reserved_ident(&self, yul: bool) -> bool {
622 self.is_ident_where(|i| i.is_non_reserved(yul))
623 }
624
625 #[inline]
629 pub fn is_elementary_type(&self) -> bool {
630 self.is_ident_where(Ident::is_elementary_type)
631 }
632
633 #[inline]
635 pub fn is_bool_lit(&self) -> bool {
636 self.is_ident_where(|id| id.name.is_bool_lit())
637 }
638
639 #[inline]
641 pub fn is_numeric_lit(&self) -> bool {
642 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer | TokenLitKind::Rational, _))
643 }
644
645 #[inline]
647 pub fn is_integer_lit(&self) -> bool {
648 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer, _))
649 }
650
651 #[inline]
653 pub fn is_rational_lit(&self) -> bool {
654 matches!(self.kind, TokenKind::Literal(TokenLitKind::Rational, _))
655 }
656
657 #[inline]
659 pub fn is_str_lit(&self) -> bool {
660 matches!(self.kind, TokenKind::Literal(TokenLitKind::Str, _))
661 }
662
663 #[inline]
665 pub fn is_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
666 self.ident().map(pred).unwrap_or(false)
667 }
668
669 #[inline]
671 pub const fn is_eof(&self) -> bool {
672 matches!(self.kind, TokenKind::Eof)
673 }
674
675 #[inline]
677 pub fn is_open_delim(&self, d: Delimiter) -> bool {
678 self.kind == TokenKind::OpenDelim(d)
679 }
680
681 #[inline]
683 pub fn is_close_delim(&self, d: Delimiter) -> bool {
684 self.kind == TokenKind::CloseDelim(d)
685 }
686
687 #[inline]
689 pub const fn is_comment(&self) -> bool {
690 self.kind.is_comment()
691 }
692
693 #[inline]
695 pub const fn is_comment_or_doc(&self) -> bool {
696 self.kind.is_comment_or_doc()
697 }
698
699 #[inline]
701 pub fn is_location_specifier(&self) -> bool {
702 self.is_ident_where(Ident::is_location_specifier)
703 }
704
705 #[inline]
707 pub fn is_mutability_specifier(&self) -> bool {
708 self.is_ident_where(Ident::is_mutability_specifier)
709 }
710
711 #[inline]
713 pub fn is_visibility_specifier(&self) -> bool {
714 self.is_ident_where(Ident::is_visibility_specifier)
715 }
716
717 pub fn full_description(&self) -> impl fmt::Display + '_ {
719 if let Some(description) = self.description() {
721 format!("{description} `{}`", self.kind)
722 } else {
723 format!("`{}`", self.kind)
724 }
725 }
726
727 pub fn as_str(&self) -> &str {
729 self.kind.as_str()
730 }
731
732 #[inline]
734 pub fn description(self) -> Option<TokenDescription> {
735 TokenDescription::from_token(self)
736 }
737
738 pub fn glue(self, other: Self) -> Option<Self> {
740 self.kind.glue(other.kind).map(|kind| Self::new(kind, self.span.to(other.span)))
741 }
742}
743
744#[derive(Clone, Copy, Debug, PartialEq, Eq)]
749pub enum TokenDescription {
750 Keyword,
752 ReservedKeyword,
754 YulKeyword,
756 YulEvmBuiltin,
758}
759
760impl fmt::Display for TokenDescription {
761 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
762 f.write_str(self.to_str())
763 }
764}
765
766impl TokenDescription {
767 pub fn from_token(token: Token) -> Option<Self> {
769 match token.kind {
770 _ if token.is_used_keyword() => Some(Self::Keyword),
771 _ if token.is_unused_keyword() => Some(Self::ReservedKeyword),
772 _ if token.is_ident_where(|id| id.is_yul_keyword()) => Some(Self::YulKeyword),
773 _ if token.is_ident_where(|id| id.is_yul_evm_builtin()) => Some(Self::YulEvmBuiltin),
774 _ => None,
775 }
776 }
777
778 pub const fn to_str(self) -> &'static str {
780 match self {
781 Self::Keyword => "keyword",
782 Self::ReservedKeyword => "reserved keyword",
783 Self::YulKeyword => "Yul keyword",
784 Self::YulEvmBuiltin => "Yul EVM builtin keyword",
785 }
786 }
787}