1use std::fmt;
9
10use bitflags::bitflags;
11
12use crate::str::{Quote, TripleQuotes};
13use crate::str_prefix::{
14 AnyStringPrefix, ByteStringPrefix, FStringPrefix, StringLiteralPrefix, TStringPrefix,
15};
16use crate::{AnyStringFlags, BoolOp, Operator, StringFlags, UnaryOp};
17use ruff_text_size::{Ranged, TextRange};
18
19mod parentheses;
20mod tokens;
21
22pub use parentheses::{parentheses_iterator, parenthesized_range};
23pub use tokens::{TokenAt, TokenIterWithContext, Tokens};
24
25#[derive(Clone, Copy, PartialEq, Eq)]
26#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
27pub struct Token {
28 kind: TokenKind,
30 range: TextRange,
32 flags: TokenFlags,
34}
35
36impl Token {
37 pub fn new(kind: TokenKind, range: TextRange, flags: TokenFlags) -> Token {
38 Self { kind, range, flags }
39 }
40
41 #[inline]
43 pub const fn kind(&self) -> TokenKind {
44 self.kind
45 }
46
47 #[inline]
49 pub const fn as_tuple(&self) -> (TokenKind, TextRange) {
50 (self.kind, self.range)
51 }
52
53 pub fn is_triple_quoted_string(self) -> bool {
59 self.unwrap_string_flags().is_triple_quoted()
60 }
61
62 pub fn string_quote_style(self) -> Quote {
68 self.unwrap_string_flags().quote_style()
69 }
70
71 pub fn unwrap_string_flags(self) -> AnyStringFlags {
77 self.string_flags()
78 .unwrap_or_else(|| panic!("token to be a string"))
79 }
80
81 pub fn string_flags(self) -> Option<AnyStringFlags> {
83 if self.is_any_string() {
84 Some(self.flags.as_any_string_flags())
85 } else {
86 None
87 }
88 }
89
90 const fn is_any_string(self) -> bool {
93 matches!(
94 self.kind,
95 TokenKind::String
96 | TokenKind::FStringStart
97 | TokenKind::FStringMiddle
98 | TokenKind::FStringEnd
99 | TokenKind::TStringStart
100 | TokenKind::TStringMiddle
101 | TokenKind::TStringEnd
102 )
103 }
104}
105
106impl Ranged for Token {
107 fn range(&self) -> TextRange {
108 self.range
109 }
110}
111
112impl fmt::Debug for Token {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "{:?} {:?}", self.kind, self.range)?;
115 if !self.flags.is_empty() {
116 f.write_str(" (flags = ")?;
117 let mut first = true;
118 for (name, _) in self.flags.iter_names() {
119 if first {
120 first = false;
121 } else {
122 f.write_str(" | ")?;
123 }
124 f.write_str(name)?;
125 }
126 f.write_str(")")?;
127 }
128 Ok(())
129 }
130}
131
132#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
134#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
135pub enum TokenKind {
136 Identifier,
141 Int,
143 Float,
145 Complex,
147 String,
149 FStringStart,
152 FStringMiddle,
155 FStringEnd,
157 TStringStart,
160 TStringMiddle,
163 TStringEnd,
165 IpyEscapeCommand,
167 Comment,
169 Newline,
171 NonLogicalNewline,
174 Indent,
176 Dedent,
178 EndOfFile,
179 Question,
181 Exclamation,
183 Lpar,
185 Rpar,
187 Lsqb,
189 Rsqb,
191 Colon,
193 Comma,
195 Semi,
197 Plus,
199 Minus,
201 Star,
203 Slash,
205 Vbar,
207 Amper,
209 Less,
211 Greater,
213 Equal,
215 Dot,
217 Percent,
219 Lbrace,
221 Rbrace,
223 EqEqual,
225 NotEqual,
227 LessEqual,
229 GreaterEqual,
231 Tilde,
233 CircumFlex,
235 LeftShift,
237 RightShift,
239 DoubleStar,
241 DoubleStarEqual,
243 PlusEqual,
245 MinusEqual,
247 StarEqual,
249 SlashEqual,
251 PercentEqual,
253 AmperEqual,
255 VbarEqual,
257 CircumflexEqual,
259 LeftShiftEqual,
261 RightShiftEqual,
263 DoubleSlash,
265 DoubleSlashEqual,
267 ColonEqual,
269 At,
271 AtEqual,
273 Rarrow,
275 Ellipsis,
277
278 And,
283 As,
284 Assert,
285 Async,
286 Await,
287 Break,
288 Class,
289 Continue,
290 Def,
291 Del,
292 Elif,
293 Else,
294 Except,
295 False,
296 Finally,
297 For,
298 From,
299 Global,
300 If,
301 Import,
302 In,
303 Is,
304 Lambda,
305 None,
306 Nonlocal,
307 Not,
308 Or,
309 Pass,
310 Raise,
311 Return,
312 True,
313 Try,
314 While,
315 With,
316 Yield,
317
318 Case,
320 Lazy,
321 Match,
322 Type,
323
324 Unknown,
325}
326
327impl TokenKind {
328 #[inline]
330 pub const fn is_eof(self) -> bool {
331 matches!(self, TokenKind::EndOfFile)
332 }
333
334 #[inline]
336 pub const fn is_dot(self) -> bool {
337 matches!(self, TokenKind::Dot)
338 }
339
340 #[inline]
342 pub const fn is_lbrace(self) -> bool {
343 matches!(self, TokenKind::Lbrace)
344 }
345
346 #[inline]
348 pub const fn is_any_newline(self) -> bool {
349 matches!(self, TokenKind::Newline | TokenKind::NonLogicalNewline)
350 }
351
352 #[inline]
359 pub fn is_keyword(self) -> bool {
360 TokenKind::And <= self && self <= TokenKind::Type
361 }
362
363 #[inline]
370 pub fn is_soft_keyword(self) -> bool {
371 TokenKind::Case <= self && self <= TokenKind::Type
372 }
373
374 #[inline]
381 pub fn is_non_soft_keyword(self) -> bool {
382 TokenKind::And <= self && self <= TokenKind::Yield
383 }
384
385 #[inline]
386 pub const fn is_operator(self) -> bool {
387 matches!(
388 self,
389 TokenKind::Lpar
390 | TokenKind::Rpar
391 | TokenKind::Lsqb
392 | TokenKind::Rsqb
393 | TokenKind::Comma
394 | TokenKind::Semi
395 | TokenKind::Plus
396 | TokenKind::Minus
397 | TokenKind::Star
398 | TokenKind::Slash
399 | TokenKind::Vbar
400 | TokenKind::Amper
401 | TokenKind::Less
402 | TokenKind::Greater
403 | TokenKind::Equal
404 | TokenKind::Dot
405 | TokenKind::Percent
406 | TokenKind::Lbrace
407 | TokenKind::Rbrace
408 | TokenKind::EqEqual
409 | TokenKind::NotEqual
410 | TokenKind::LessEqual
411 | TokenKind::GreaterEqual
412 | TokenKind::Tilde
413 | TokenKind::CircumFlex
414 | TokenKind::LeftShift
415 | TokenKind::RightShift
416 | TokenKind::DoubleStar
417 | TokenKind::PlusEqual
418 | TokenKind::MinusEqual
419 | TokenKind::StarEqual
420 | TokenKind::SlashEqual
421 | TokenKind::PercentEqual
422 | TokenKind::AmperEqual
423 | TokenKind::VbarEqual
424 | TokenKind::CircumflexEqual
425 | TokenKind::LeftShiftEqual
426 | TokenKind::RightShiftEqual
427 | TokenKind::DoubleStarEqual
428 | TokenKind::DoubleSlash
429 | TokenKind::DoubleSlashEqual
430 | TokenKind::At
431 | TokenKind::AtEqual
432 | TokenKind::Rarrow
433 | TokenKind::Ellipsis
434 | TokenKind::ColonEqual
435 | TokenKind::Colon
436 | TokenKind::And
437 | TokenKind::Or
438 | TokenKind::Not
439 | TokenKind::In
440 | TokenKind::Is
441 )
442 }
443
444 #[inline]
446 pub const fn is_singleton(self) -> bool {
447 matches!(self, TokenKind::False | TokenKind::True | TokenKind::None)
448 }
449
450 #[inline]
452 pub const fn is_trivia(&self) -> bool {
453 matches!(self, TokenKind::Comment | TokenKind::NonLogicalNewline)
454 }
455
456 #[inline]
458 pub const fn is_comment(&self) -> bool {
459 matches!(self, TokenKind::Comment)
460 }
461
462 #[inline]
463 pub const fn is_arithmetic(self) -> bool {
464 matches!(
465 self,
466 TokenKind::DoubleStar
467 | TokenKind::Star
468 | TokenKind::Plus
469 | TokenKind::Minus
470 | TokenKind::Slash
471 | TokenKind::DoubleSlash
472 | TokenKind::At
473 )
474 }
475
476 #[inline]
477 pub const fn is_bitwise_or_shift(self) -> bool {
478 matches!(
479 self,
480 TokenKind::LeftShift
481 | TokenKind::LeftShiftEqual
482 | TokenKind::RightShift
483 | TokenKind::RightShiftEqual
484 | TokenKind::Amper
485 | TokenKind::AmperEqual
486 | TokenKind::Vbar
487 | TokenKind::VbarEqual
488 | TokenKind::CircumFlex
489 | TokenKind::CircumflexEqual
490 | TokenKind::Tilde
491 )
492 }
493
494 #[inline]
496 pub const fn is_unary_arithmetic_operator(self) -> bool {
497 matches!(self, TokenKind::Plus | TokenKind::Minus)
498 }
499
500 #[inline]
501 pub const fn is_interpolated_string_end(self) -> bool {
502 matches!(self, TokenKind::FStringEnd | TokenKind::TStringEnd)
503 }
504
505 #[inline]
512 pub const fn as_unary_arithmetic_operator(self) -> Option<UnaryOp> {
513 Some(match self {
514 TokenKind::Plus => UnaryOp::UAdd,
515 TokenKind::Minus => UnaryOp::USub,
516 _ => return None,
517 })
518 }
519
520 #[inline]
527 pub const fn as_unary_operator(self) -> Option<UnaryOp> {
528 Some(match self {
529 TokenKind::Plus => UnaryOp::UAdd,
530 TokenKind::Minus => UnaryOp::USub,
531 TokenKind::Tilde => UnaryOp::Invert,
532 TokenKind::Not => UnaryOp::Not,
533 _ => return None,
534 })
535 }
536
537 #[inline]
540 pub const fn as_bool_operator(self) -> Option<BoolOp> {
541 Some(match self {
542 TokenKind::And => BoolOp::And,
543 TokenKind::Or => BoolOp::Or,
544 _ => return None,
545 })
546 }
547
548 pub const fn as_binary_operator(self) -> Option<Operator> {
555 Some(match self {
556 TokenKind::Plus => Operator::Add,
557 TokenKind::Minus => Operator::Sub,
558 TokenKind::Star => Operator::Mult,
559 TokenKind::At => Operator::MatMult,
560 TokenKind::DoubleStar => Operator::Pow,
561 TokenKind::Slash => Operator::Div,
562 TokenKind::DoubleSlash => Operator::FloorDiv,
563 TokenKind::Percent => Operator::Mod,
564 TokenKind::Amper => Operator::BitAnd,
565 TokenKind::Vbar => Operator::BitOr,
566 TokenKind::CircumFlex => Operator::BitXor,
567 TokenKind::LeftShift => Operator::LShift,
568 TokenKind::RightShift => Operator::RShift,
569 _ => return None,
570 })
571 }
572
573 #[inline]
576 pub const fn as_augmented_assign_operator(self) -> Option<Operator> {
577 Some(match self {
578 TokenKind::PlusEqual => Operator::Add,
579 TokenKind::MinusEqual => Operator::Sub,
580 TokenKind::StarEqual => Operator::Mult,
581 TokenKind::AtEqual => Operator::MatMult,
582 TokenKind::DoubleStarEqual => Operator::Pow,
583 TokenKind::SlashEqual => Operator::Div,
584 TokenKind::DoubleSlashEqual => Operator::FloorDiv,
585 TokenKind::PercentEqual => Operator::Mod,
586 TokenKind::AmperEqual => Operator::BitAnd,
587 TokenKind::VbarEqual => Operator::BitOr,
588 TokenKind::CircumflexEqual => Operator::BitXor,
589 TokenKind::LeftShiftEqual => Operator::LShift,
590 TokenKind::RightShiftEqual => Operator::RShift,
591 _ => return None,
592 })
593 }
594}
595
596impl From<BoolOp> for TokenKind {
597 #[inline]
598 fn from(op: BoolOp) -> Self {
599 match op {
600 BoolOp::And => TokenKind::And,
601 BoolOp::Or => TokenKind::Or,
602 }
603 }
604}
605
606impl From<UnaryOp> for TokenKind {
607 #[inline]
608 fn from(op: UnaryOp) -> Self {
609 match op {
610 UnaryOp::Invert => TokenKind::Tilde,
611 UnaryOp::Not => TokenKind::Not,
612 UnaryOp::UAdd => TokenKind::Plus,
613 UnaryOp::USub => TokenKind::Minus,
614 }
615 }
616}
617
618impl From<Operator> for TokenKind {
619 #[inline]
620 fn from(op: Operator) -> Self {
621 match op {
622 Operator::Add => TokenKind::Plus,
623 Operator::Sub => TokenKind::Minus,
624 Operator::Mult => TokenKind::Star,
625 Operator::MatMult => TokenKind::At,
626 Operator::Div => TokenKind::Slash,
627 Operator::Mod => TokenKind::Percent,
628 Operator::Pow => TokenKind::DoubleStar,
629 Operator::LShift => TokenKind::LeftShift,
630 Operator::RShift => TokenKind::RightShift,
631 Operator::BitOr => TokenKind::Vbar,
632 Operator::BitXor => TokenKind::CircumFlex,
633 Operator::BitAnd => TokenKind::Amper,
634 Operator::FloorDiv => TokenKind::DoubleSlash,
635 }
636 }
637}
638
639impl fmt::Display for TokenKind {
640 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
641 let value = match self {
642 TokenKind::Unknown => "Unknown",
643 TokenKind::Newline => "newline",
644 TokenKind::NonLogicalNewline => "NonLogicalNewline",
645 TokenKind::Indent => "indent",
646 TokenKind::Dedent => "dedent",
647 TokenKind::EndOfFile => "end of file",
648 TokenKind::Identifier => "identifier",
649 TokenKind::Int => "int",
650 TokenKind::Float => "float",
651 TokenKind::Complex => "complex",
652 TokenKind::String => "string",
653 TokenKind::FStringStart => "FStringStart",
654 TokenKind::FStringMiddle => "FStringMiddle",
655 TokenKind::FStringEnd => "FStringEnd",
656 TokenKind::TStringStart => "TStringStart",
657 TokenKind::TStringMiddle => "TStringMiddle",
658 TokenKind::TStringEnd => "TStringEnd",
659 TokenKind::IpyEscapeCommand => "IPython escape command",
660 TokenKind::Comment => "comment",
661 TokenKind::Question => "`?`",
662 TokenKind::Exclamation => "`!`",
663 TokenKind::Lpar => "`(`",
664 TokenKind::Rpar => "`)`",
665 TokenKind::Lsqb => "`[`",
666 TokenKind::Rsqb => "`]`",
667 TokenKind::Lbrace => "`{`",
668 TokenKind::Rbrace => "`}`",
669 TokenKind::Equal => "`=`",
670 TokenKind::ColonEqual => "`:=`",
671 TokenKind::Dot => "`.`",
672 TokenKind::Colon => "`:`",
673 TokenKind::Semi => "`;`",
674 TokenKind::Comma => "`,`",
675 TokenKind::Rarrow => "`->`",
676 TokenKind::Plus => "`+`",
677 TokenKind::Minus => "`-`",
678 TokenKind::Star => "`*`",
679 TokenKind::DoubleStar => "`**`",
680 TokenKind::Slash => "`/`",
681 TokenKind::DoubleSlash => "`//`",
682 TokenKind::Percent => "`%`",
683 TokenKind::Vbar => "`|`",
684 TokenKind::Amper => "`&`",
685 TokenKind::CircumFlex => "`^`",
686 TokenKind::LeftShift => "`<<`",
687 TokenKind::RightShift => "`>>`",
688 TokenKind::Tilde => "`~`",
689 TokenKind::At => "`@`",
690 TokenKind::Less => "`<`",
691 TokenKind::Greater => "`>`",
692 TokenKind::EqEqual => "`==`",
693 TokenKind::NotEqual => "`!=`",
694 TokenKind::LessEqual => "`<=`",
695 TokenKind::GreaterEqual => "`>=`",
696 TokenKind::PlusEqual => "`+=`",
697 TokenKind::MinusEqual => "`-=`",
698 TokenKind::StarEqual => "`*=`",
699 TokenKind::DoubleStarEqual => "`**=`",
700 TokenKind::SlashEqual => "`/=`",
701 TokenKind::DoubleSlashEqual => "`//=`",
702 TokenKind::PercentEqual => "`%=`",
703 TokenKind::VbarEqual => "`|=`",
704 TokenKind::AmperEqual => "`&=`",
705 TokenKind::CircumflexEqual => "`^=`",
706 TokenKind::LeftShiftEqual => "`<<=`",
707 TokenKind::RightShiftEqual => "`>>=`",
708 TokenKind::AtEqual => "`@=`",
709 TokenKind::Ellipsis => "`...`",
710 TokenKind::False => "`False`",
711 TokenKind::None => "`None`",
712 TokenKind::True => "`True`",
713 TokenKind::And => "`and`",
714 TokenKind::As => "`as`",
715 TokenKind::Assert => "`assert`",
716 TokenKind::Async => "`async`",
717 TokenKind::Await => "`await`",
718 TokenKind::Break => "`break`",
719 TokenKind::Class => "`class`",
720 TokenKind::Continue => "`continue`",
721 TokenKind::Def => "`def`",
722 TokenKind::Del => "`del`",
723 TokenKind::Elif => "`elif`",
724 TokenKind::Else => "`else`",
725 TokenKind::Except => "`except`",
726 TokenKind::Finally => "`finally`",
727 TokenKind::For => "`for`",
728 TokenKind::From => "`from`",
729 TokenKind::Global => "`global`",
730 TokenKind::If => "`if`",
731 TokenKind::Import => "`import`",
732 TokenKind::In => "`in`",
733 TokenKind::Is => "`is`",
734 TokenKind::Lambda => "`lambda`",
735 TokenKind::Nonlocal => "`nonlocal`",
736 TokenKind::Not => "`not`",
737 TokenKind::Or => "`or`",
738 TokenKind::Pass => "`pass`",
739 TokenKind::Raise => "`raise`",
740 TokenKind::Return => "`return`",
741 TokenKind::Try => "`try`",
742 TokenKind::While => "`while`",
743 TokenKind::Lazy => "`lazy`",
744 TokenKind::Match => "`match`",
745 TokenKind::Type => "`type`",
746 TokenKind::Case => "`case`",
747 TokenKind::With => "`with`",
748 TokenKind::Yield => "`yield`",
749 };
750 f.write_str(value)
751 }
752}
753
754bitflags! {
755 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
756 pub struct TokenFlags: u16 {
757 const DOUBLE_QUOTES = 1 << 0;
759 const TRIPLE_QUOTED_STRING = 1 << 1;
762
763 const UNICODE_STRING = 1 << 2;
765 const BYTE_STRING = 1 << 3;
767 const F_STRING = 1 << 4;
769 const T_STRING = 1 << 5;
771 const RAW_STRING_LOWERCASE = 1 << 6;
773 const RAW_STRING_UPPERCASE = 1 << 7;
775 const UNCLOSED_STRING = 1 << 8;
777 const NON_ASCII_IDENTIFIER = 1 << 9;
779
780 const RAW_STRING = Self::RAW_STRING_LOWERCASE.bits() | Self::RAW_STRING_UPPERCASE.bits();
782
783 }
784}
785
786#[cfg(feature = "get-size")]
787impl get_size2::GetSize for TokenFlags {}
788
789impl StringFlags for TokenFlags {
790 fn quote_style(self) -> Quote {
791 if self.intersects(TokenFlags::DOUBLE_QUOTES) {
792 Quote::Double
793 } else {
794 Quote::Single
795 }
796 }
797
798 fn triple_quotes(self) -> TripleQuotes {
799 if self.intersects(TokenFlags::TRIPLE_QUOTED_STRING) {
800 TripleQuotes::Yes
801 } else {
802 TripleQuotes::No
803 }
804 }
805
806 fn prefix(self) -> AnyStringPrefix {
807 if self.intersects(TokenFlags::F_STRING) {
808 if self.intersects(TokenFlags::RAW_STRING_LOWERCASE) {
809 AnyStringPrefix::Format(FStringPrefix::Raw { uppercase_r: false })
810 } else if self.intersects(TokenFlags::RAW_STRING_UPPERCASE) {
811 AnyStringPrefix::Format(FStringPrefix::Raw { uppercase_r: true })
812 } else {
813 AnyStringPrefix::Format(FStringPrefix::Regular)
814 }
815 } else if self.intersects(TokenFlags::T_STRING) {
816 if self.intersects(TokenFlags::RAW_STRING_LOWERCASE) {
817 AnyStringPrefix::Template(TStringPrefix::Raw { uppercase_r: false })
818 } else if self.intersects(TokenFlags::RAW_STRING_UPPERCASE) {
819 AnyStringPrefix::Template(TStringPrefix::Raw { uppercase_r: true })
820 } else {
821 AnyStringPrefix::Template(TStringPrefix::Regular)
822 }
823 } else if self.intersects(TokenFlags::BYTE_STRING) {
824 if self.intersects(TokenFlags::RAW_STRING_LOWERCASE) {
825 AnyStringPrefix::Bytes(ByteStringPrefix::Raw { uppercase_r: false })
826 } else if self.intersects(TokenFlags::RAW_STRING_UPPERCASE) {
827 AnyStringPrefix::Bytes(ByteStringPrefix::Raw { uppercase_r: true })
828 } else {
829 AnyStringPrefix::Bytes(ByteStringPrefix::Regular)
830 }
831 } else if self.intersects(TokenFlags::RAW_STRING_LOWERCASE) {
832 AnyStringPrefix::Regular(StringLiteralPrefix::Raw { uppercase: false })
833 } else if self.intersects(TokenFlags::RAW_STRING_UPPERCASE) {
834 AnyStringPrefix::Regular(StringLiteralPrefix::Raw { uppercase: true })
835 } else if self.intersects(TokenFlags::UNICODE_STRING) {
836 AnyStringPrefix::Regular(StringLiteralPrefix::Unicode)
837 } else {
838 AnyStringPrefix::Regular(StringLiteralPrefix::Empty)
839 }
840 }
841
842 fn is_unclosed(self) -> bool {
843 self.intersects(TokenFlags::UNCLOSED_STRING)
844 }
845}
846
847impl TokenFlags {
848 pub const fn is_f_string(self) -> bool {
850 self.intersects(TokenFlags::F_STRING)
851 }
852
853 pub const fn is_t_string(self) -> bool {
855 self.intersects(TokenFlags::T_STRING)
856 }
857
858 pub const fn is_interpolated_string(self) -> bool {
860 self.intersects(TokenFlags::T_STRING.union(TokenFlags::F_STRING))
861 }
862
863 pub fn is_triple_quoted_interpolated_string(self) -> bool {
865 self.intersects(TokenFlags::TRIPLE_QUOTED_STRING) && self.is_interpolated_string()
866 }
867
868 pub const fn is_raw_string(self) -> bool {
870 self.intersects(TokenFlags::RAW_STRING)
871 }
872
873 #[inline]
875 pub const fn is_non_ascii_identifier(self) -> bool {
876 self.intersects(TokenFlags::NON_ASCII_IDENTIFIER)
877 }
878}