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
444#[derive(Clone, Copy, Debug, PartialEq, Eq)]
446pub struct Token {
447 pub kind: TokenKind,
449 pub span: Span,
451}
452
453impl From<Ident> for Token {
454 #[inline]
455 fn from(ident: Ident) -> Self {
456 Self::from_ast_ident(ident)
457 }
458}
459
460impl Token {
461 pub const EOF: Self = Self::new(TokenKind::Eof, Span::DUMMY);
463
464 pub const DUMMY: Self = Self::new(TokenKind::Question, Span::DUMMY);
466
467 #[inline]
469 pub const fn new(kind: TokenKind, span: Span) -> Self {
470 Self { kind, span }
471 }
472
473 #[inline]
475 pub fn from_ast_ident(ident: Ident) -> Self {
476 Self::new(TokenKind::Ident(ident.name), ident.span)
477 }
478
479 #[inline]
481 pub const fn ident(&self) -> Option<Ident> {
482 match self.kind {
483 TokenKind::Ident(ident) => Some(Ident::new(ident, self.span)),
484 _ => None,
485 }
486 }
487
488 #[inline]
490 pub const fn lit(&self) -> Option<TokenLit> {
491 match self.kind {
492 TokenKind::Literal(kind, symbol) => Some(TokenLit::new(kind, symbol)),
493 _ => None,
494 }
495 }
496
497 #[inline]
499 pub const fn lit_kind(&self) -> Option<TokenLitKind> {
500 match self.kind {
501 TokenKind::Literal(kind, _) => Some(kind),
502 _ => None,
503 }
504 }
505
506 #[inline]
508 pub const fn comment(&self) -> Option<(bool, DocComment)> {
509 match self.kind {
510 TokenKind::Comment(is_doc, kind, symbol) => {
511 Some((is_doc, DocComment { span: self.span, kind, symbol }))
512 }
513 _ => None,
514 }
515 }
516
517 #[inline]
521 pub const fn doc(&self) -> Option<DocComment> {
522 match self.kind {
523 TokenKind::Comment(_, kind, symbol) => {
524 Some(DocComment { span: self.span, kind, symbol })
525 }
526 _ => None,
527 }
528 }
529
530 #[inline]
532 pub const fn is_op(&self) -> bool {
533 self.kind.is_op()
534 }
535
536 #[inline]
538 pub fn as_unop(&self, is_postfix: bool) -> Option<UnOp> {
539 self.kind.as_unop(is_postfix).map(|kind| UnOp { span: self.span, kind })
540 }
541
542 #[inline]
544 pub fn as_binop(&self) -> Option<BinOp> {
545 self.kind.as_binop().map(|kind| BinOp { span: self.span, kind })
546 }
547
548 #[inline]
550 pub fn as_binop_eq(&self) -> Option<BinOp> {
551 self.kind.as_binop_eq().map(|kind| BinOp { span: self.span, kind })
552 }
553
554 #[inline]
556 pub const fn is_ident(&self) -> bool {
557 matches!(self.kind, TokenKind::Ident(_))
558 }
559
560 #[inline]
562 pub fn is_lit(&self) -> bool {
563 matches!(self.kind, TokenKind::Literal(..)) || self.is_bool_lit()
564 }
565
566 #[inline]
568 pub fn is_keyword(&self, kw: Symbol) -> bool {
569 self.is_ident_where(|id| id.name == kw)
570 }
571
572 #[inline]
574 pub fn is_keyword_any(&self, kws: &[Symbol]) -> bool {
575 self.is_ident_where(|id| kws.contains(&id.name))
576 }
577
578 #[inline]
580 pub fn is_used_keyword(&self) -> bool {
581 self.is_ident_where(Ident::is_used_keyword)
582 }
583
584 #[inline]
586 pub fn is_unused_keyword(&self) -> bool {
587 self.is_ident_where(Ident::is_unused_keyword)
588 }
589
590 #[inline]
592 pub fn is_reserved_ident(&self, yul: bool) -> bool {
593 self.is_ident_where(|i| i.is_reserved(yul))
594 }
595
596 #[inline]
598 pub fn is_non_reserved_ident(&self, yul: bool) -> bool {
599 self.is_ident_where(|i| i.is_non_reserved(yul))
600 }
601
602 #[inline]
606 pub fn is_elementary_type(&self) -> bool {
607 self.is_ident_where(Ident::is_elementary_type)
608 }
609
610 #[inline]
612 pub fn is_bool_lit(&self) -> bool {
613 self.is_ident_where(|id| id.name.is_bool_lit())
614 }
615
616 #[inline]
618 pub fn is_numeric_lit(&self) -> bool {
619 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer | TokenLitKind::Rational, _))
620 }
621
622 #[inline]
624 pub fn is_integer_lit(&self) -> bool {
625 matches!(self.kind, TokenKind::Literal(TokenLitKind::Integer, _))
626 }
627
628 #[inline]
630 pub fn is_rational_lit(&self) -> bool {
631 matches!(self.kind, TokenKind::Literal(TokenLitKind::Rational, _))
632 }
633
634 #[inline]
636 pub fn is_str_lit(&self) -> bool {
637 matches!(self.kind, TokenKind::Literal(TokenLitKind::Str, _))
638 }
639
640 #[inline]
642 pub fn is_ident_where(&self, pred: impl FnOnce(Ident) -> bool) -> bool {
643 self.ident().map(pred).unwrap_or(false)
644 }
645
646 #[inline]
648 pub const fn is_eof(&self) -> bool {
649 matches!(self.kind, TokenKind::Eof)
650 }
651
652 #[inline]
654 pub fn is_open_delim(&self, d: Delimiter) -> bool {
655 self.kind == TokenKind::OpenDelim(d)
656 }
657
658 #[inline]
660 pub fn is_close_delim(&self, d: Delimiter) -> bool {
661 self.kind == TokenKind::CloseDelim(d)
662 }
663
664 #[inline]
666 pub const fn is_comment(&self) -> bool {
667 self.kind.is_comment()
668 }
669
670 #[inline]
672 pub const fn is_comment_or_doc(&self) -> bool {
673 self.kind.is_comment_or_doc()
674 }
675
676 #[inline]
678 pub fn is_location_specifier(&self) -> bool {
679 self.is_ident_where(Ident::is_location_specifier)
680 }
681
682 #[inline]
684 pub fn is_mutability_specifier(&self) -> bool {
685 self.is_ident_where(Ident::is_mutability_specifier)
686 }
687
688 #[inline]
690 pub fn is_visibility_specifier(&self) -> bool {
691 self.is_ident_where(Ident::is_visibility_specifier)
692 }
693
694 pub fn full_description(&self) -> impl fmt::Display + '_ {
696 if let Some(description) = self.description() {
698 format!("{description} `{}`", self.kind)
699 } else {
700 format!("`{}`", self.kind)
701 }
702 }
703
704 pub fn as_str(&self) -> &str {
706 self.kind.as_str()
707 }
708
709 #[inline]
711 pub fn description(self) -> Option<TokenDescription> {
712 TokenDescription::from_token(self)
713 }
714}
715
716#[derive(Clone, Copy, Debug, PartialEq, Eq)]
721pub enum TokenDescription {
722 Keyword,
724 ReservedKeyword,
726 YulKeyword,
728 YulEvmBuiltin,
730}
731
732impl fmt::Display for TokenDescription {
733 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
734 f.write_str(self.to_str())
735 }
736}
737
738impl TokenDescription {
739 pub fn from_token(token: Token) -> Option<Self> {
741 match token.kind {
742 _ if token.is_used_keyword() => Some(Self::Keyword),
743 _ if token.is_unused_keyword() => Some(Self::ReservedKeyword),
744 _ if token.is_ident_where(|id| id.is_yul_keyword()) => Some(Self::YulKeyword),
745 _ if token.is_ident_where(|id| id.is_yul_evm_builtin()) => Some(Self::YulEvmBuiltin),
746 _ => None,
747 }
748 }
749
750 pub const fn to_str(self) -> &'static str {
752 match self {
753 Self::Keyword => "keyword",
754 Self::ReservedKeyword => "reserved keyword",
755 Self::YulKeyword => "Yul keyword",
756 Self::YulEvmBuiltin => "Yul EVM builtin keyword",
757 }
758 }
759}