1#[repr(u8)]
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub enum TokenKind {
9 FloatLiteral,
14
15 FloatLiteralSimple,
17
18 FloatLiteralLeadingDot,
20
21 HexIntLiteral,
23
24 BinIntLiteral,
26
27 OctIntLiteralNew,
29
30 OctIntLiteral,
32
33 IntLiteral,
35
36 SingleQuotedString,
38
39 DoubleQuotedString,
41
42 BacktickString,
44
45 Variable,
50
51 Dollar,
53
54 Identifier,
60
61 Plus,
66 Minus,
68 Star,
70 Slash,
72 Percent,
74 StarStar,
76 Dot,
78
79 Equals,
85 PlusEquals,
87 MinusEquals,
89 StarEquals,
91 SlashEquals,
93 PercentEquals,
95 StarStarEquals,
97 DotEquals,
99 AmpersandEquals,
101 PipeEquals,
103 CaretEquals,
105 ShiftLeftEquals,
107 ShiftRightEquals,
109 CoalesceEquals,
111
112 EqualsEquals,
117 BangEquals,
119 EqualsEqualsEquals,
121 BangEqualsEquals,
123 LessThan,
125 GreaterThan,
127 LessThanEquals,
129 GreaterThanEquals,
131 Spaceship,
133
134 AmpersandAmpersand,
139 PipePipe,
141 Bang,
143
144 Ampersand,
146 Pipe,
148 Caret,
150 Tilde,
152 ShiftLeft,
154 ShiftRight,
156
157 PlusPlus,
162 MinusMinus,
164
165 Question,
170 QuestionQuestion,
172 Colon,
174
175 FatArrow,
177
178 PipeArrow,
180
181 LeftParen,
186 RightParen,
188 LeftBracket,
190 RightBracket,
192 LeftBrace,
194 RightBrace,
196 Semicolon,
198 Comma,
200
201 DoubleColon,
203
204 Arrow,
206
207 NullsafeArrow,
209
210 Backslash,
212
213 At,
215
216 HashBracket,
218
219 Ellipsis,
221
222 If,
227 Else,
229 ElseIf,
231 While,
233 Do,
235 For,
237 Foreach,
239 As,
241 Function,
243 Return,
245 Echo,
247 Print,
249 True,
251 False,
253 Null,
255 And,
257 Or,
259 Xor,
261 Break,
263 Continue,
265 Switch,
267 Case,
269 Default,
271 EndIf,
273 EndWhile,
275 EndFor,
277 EndForeach,
279 Throw,
281 Try,
283 Catch,
285 Finally,
287 Instanceof,
289 Array,
291 List,
293 Goto,
295 Declare,
297 Unset,
299 Global,
301 EndDeclare,
303 EndSwitch,
305 Isset,
307 Empty,
309 Include,
311 IncludeOnce,
313 Require,
315 RequireOnce,
317 Eval,
319 Exit,
321 Die,
323 Clone,
325
326 New,
329 Class,
331 Abstract,
333 Final,
335 Interface,
337 Trait,
339 Extends,
341 Implements,
343 Public,
345 Protected,
347 Private,
349 Static,
351 Const,
353 Fn_,
355 Match_,
357 Namespace,
359 Use,
361 Readonly,
363 Enum_,
365 Yield_,
367 Self_,
369 Parent_,
371
372 MagicClass,
377 MagicDir,
379 MagicFile,
381 MagicFunction,
383 MagicLine,
385 MagicMethod,
387 MagicNamespace,
389 MagicTrait,
391 MagicProperty,
393 HaltCompiler,
395
396 OpenTag,
401
402 CloseTag,
404
405 InlineHtml,
407
408 Heredoc,
413 Nowdoc,
415
416 InvalidNumericLiteral,
422
423 LineComment,
428 HashComment,
430 BlockComment,
432 DocComment,
434
435 Eof,
440}
441
442impl TokenKind {
443 #[inline]
445 pub fn is_comment(self) -> bool {
446 matches!(
447 self,
448 TokenKind::LineComment
449 | TokenKind::HashComment
450 | TokenKind::BlockComment
451 | TokenKind::DocComment
452 )
453 }
454}
455
456impl TokenKind {
457 #[inline(always)]
459 pub fn is_assignment_op(self) -> bool {
460 (self as u8).wrapping_sub(TokenKind::Equals as u8)
463 <= (TokenKind::CoalesceEquals as u8 - TokenKind::Equals as u8)
464 }
465}
466
467pub fn resolve_keyword(text: &str) -> Option<TokenKind> {
470 let t = text;
473 match text.len() {
474 2 => {
475 if t.eq_ignore_ascii_case("if") {
476 return Some(TokenKind::If);
477 }
478 if t.eq_ignore_ascii_case("do") {
479 return Some(TokenKind::Do);
480 }
481 if t.eq_ignore_ascii_case("or") {
482 return Some(TokenKind::Or);
483 }
484 if t.eq_ignore_ascii_case("as") {
485 return Some(TokenKind::As);
486 }
487 if t.eq_ignore_ascii_case("fn") {
488 return Some(TokenKind::Fn_);
489 }
490 }
491 3 => {
492 if t.eq_ignore_ascii_case("for") {
493 return Some(TokenKind::For);
494 }
495 if t.eq_ignore_ascii_case("xor") {
496 return Some(TokenKind::Xor);
497 }
498 if t.eq_ignore_ascii_case("and") {
499 return Some(TokenKind::And);
500 }
501 if t.eq_ignore_ascii_case("new") {
502 return Some(TokenKind::New);
503 }
504 if t.eq_ignore_ascii_case("use") {
505 return Some(TokenKind::Use);
506 }
507 if t.eq_ignore_ascii_case("try") {
508 return Some(TokenKind::Try);
509 }
510 if t.eq_ignore_ascii_case("die") {
511 return Some(TokenKind::Die);
512 }
513 }
514 4 => {
515 if t.eq_ignore_ascii_case("else") {
516 return Some(TokenKind::Else);
517 }
518 if t.eq_ignore_ascii_case("echo") {
519 return Some(TokenKind::Echo);
520 }
521 if t.eq_ignore_ascii_case("true") {
522 return Some(TokenKind::True);
523 }
524 if t.eq_ignore_ascii_case("null") {
525 return Some(TokenKind::Null);
526 }
527 if t.eq_ignore_ascii_case("list") {
528 return Some(TokenKind::List);
529 }
530 if t.eq_ignore_ascii_case("goto") {
531 return Some(TokenKind::Goto);
532 }
533 if t.eq_ignore_ascii_case("case") {
534 return Some(TokenKind::Case);
535 }
536 if t.eq_ignore_ascii_case("self") {
537 return Some(TokenKind::Self_);
538 }
539 if t.eq_ignore_ascii_case("enum") {
540 return Some(TokenKind::Enum_);
541 }
542 if t.eq_ignore_ascii_case("eval") {
543 return Some(TokenKind::Eval);
544 }
545 if t.eq_ignore_ascii_case("exit") {
546 return Some(TokenKind::Exit);
547 }
548 }
549 5 => {
550 if t.eq_ignore_ascii_case("while") {
551 return Some(TokenKind::While);
552 }
553 if t.eq_ignore_ascii_case("false") {
554 return Some(TokenKind::False);
555 }
556 if t.eq_ignore_ascii_case("array") {
557 return Some(TokenKind::Array);
558 }
559 if t.eq_ignore_ascii_case("unset") {
560 return Some(TokenKind::Unset);
561 }
562 if t.eq_ignore_ascii_case("isset") {
563 return Some(TokenKind::Isset);
564 }
565 if t.eq_ignore_ascii_case("empty") {
566 return Some(TokenKind::Empty);
567 }
568 if t.eq_ignore_ascii_case("print") {
569 return Some(TokenKind::Print);
570 }
571 if t.eq_ignore_ascii_case("throw") {
572 return Some(TokenKind::Throw);
573 }
574 if t.eq_ignore_ascii_case("catch") {
575 return Some(TokenKind::Catch);
576 }
577 if t.eq_ignore_ascii_case("break") {
578 return Some(TokenKind::Break);
579 }
580 if t.eq_ignore_ascii_case("yield") {
581 return Some(TokenKind::Yield_);
582 }
583 if t.eq_ignore_ascii_case("class") {
584 return Some(TokenKind::Class);
585 }
586 if t.eq_ignore_ascii_case("const") {
587 return Some(TokenKind::Const);
588 }
589 if t.eq_ignore_ascii_case("final") {
590 return Some(TokenKind::Final);
591 }
592 if t.eq_ignore_ascii_case("match") {
593 return Some(TokenKind::Match_);
594 }
595 if t.eq_ignore_ascii_case("trait") {
596 return Some(TokenKind::Trait);
597 }
598 if t.eq_ignore_ascii_case("clone") {
599 return Some(TokenKind::Clone);
600 }
601 if t.eq_ignore_ascii_case("endif") {
602 return Some(TokenKind::EndIf);
603 }
604 }
605 6 => {
606 if t.eq_ignore_ascii_case("elseif") {
607 return Some(TokenKind::ElseIf);
608 }
609 if t.eq_ignore_ascii_case("return") {
610 return Some(TokenKind::Return);
611 }
612 if t.eq_ignore_ascii_case("switch") {
613 return Some(TokenKind::Switch);
614 }
615 if t.eq_ignore_ascii_case("global") {
616 return Some(TokenKind::Global);
617 }
618 if t.eq_ignore_ascii_case("static") {
619 return Some(TokenKind::Static);
620 }
621 if t.eq_ignore_ascii_case("public") {
622 return Some(TokenKind::Public);
623 }
624 if t.eq_ignore_ascii_case("parent") {
625 return Some(TokenKind::Parent_);
626 }
627 if t.eq_ignore_ascii_case("endfor") {
628 return Some(TokenKind::EndFor);
629 }
630 }
631 7 => {
632 if t.eq_ignore_ascii_case("foreach") {
633 return Some(TokenKind::Foreach);
634 }
635 if t.eq_ignore_ascii_case("default") {
636 return Some(TokenKind::Default);
637 }
638 if t.eq_ignore_ascii_case("finally") {
639 return Some(TokenKind::Finally);
640 }
641 if t.eq_ignore_ascii_case("include") {
642 return Some(TokenKind::Include);
643 }
644 if t.eq_ignore_ascii_case("declare") {
645 return Some(TokenKind::Declare);
646 }
647 if t.eq_ignore_ascii_case("extends") {
648 return Some(TokenKind::Extends);
649 }
650 if t.eq_ignore_ascii_case("require") {
651 return Some(TokenKind::Require);
652 }
653 if t.eq_ignore_ascii_case("private") {
654 return Some(TokenKind::Private);
655 }
656 if t.eq_ignore_ascii_case("__dir__") {
657 return Some(TokenKind::MagicDir);
658 }
659 }
660 8 => {
661 if t.eq_ignore_ascii_case("function") {
662 return Some(TokenKind::Function);
663 }
664 if t.eq_ignore_ascii_case("abstract") {
665 return Some(TokenKind::Abstract);
666 }
667 if t.eq_ignore_ascii_case("readonly") {
668 return Some(TokenKind::Readonly);
669 }
670 if t.eq_ignore_ascii_case("continue") {
671 return Some(TokenKind::Continue);
672 }
673 if t.eq_ignore_ascii_case("endwhile") {
674 return Some(TokenKind::EndWhile);
675 }
676 if t.eq_ignore_ascii_case("__file__") {
677 return Some(TokenKind::MagicFile);
678 }
679 if t.eq_ignore_ascii_case("__line__") {
680 return Some(TokenKind::MagicLine);
681 }
682 }
683 9 => {
684 if t.eq_ignore_ascii_case("namespace") {
685 return Some(TokenKind::Namespace);
686 }
687 if t.eq_ignore_ascii_case("interface") {
688 return Some(TokenKind::Interface);
689 }
690 if t.eq_ignore_ascii_case("protected") {
691 return Some(TokenKind::Protected);
692 }
693 if t.eq_ignore_ascii_case("endswitch") {
694 return Some(TokenKind::EndSwitch);
695 }
696 if t.eq_ignore_ascii_case("__class__") {
697 return Some(TokenKind::MagicClass);
698 }
699 if t.eq_ignore_ascii_case("__trait__") {
700 return Some(TokenKind::MagicTrait);
701 }
702 }
703 10 => {
704 if t.eq_ignore_ascii_case("implements") {
705 return Some(TokenKind::Implements);
706 }
707 if t.eq_ignore_ascii_case("instanceof") {
708 return Some(TokenKind::Instanceof);
709 }
710 if t.eq_ignore_ascii_case("endforeach") {
711 return Some(TokenKind::EndForeach);
712 }
713 if t.eq_ignore_ascii_case("enddeclare") {
714 return Some(TokenKind::EndDeclare);
715 }
716 if t.eq_ignore_ascii_case("__method__") {
717 return Some(TokenKind::MagicMethod);
718 }
719 }
720 12 => {
721 if t.eq_ignore_ascii_case("include_once") {
722 return Some(TokenKind::IncludeOnce);
723 }
724 if t.eq_ignore_ascii_case("require_once") {
725 return Some(TokenKind::RequireOnce);
726 }
727 if t.eq_ignore_ascii_case("__function__") {
728 return Some(TokenKind::MagicFunction);
729 }
730 if t.eq_ignore_ascii_case("__property__") {
731 return Some(TokenKind::MagicProperty);
732 }
733 }
734 13 if t.eq_ignore_ascii_case("__namespace__") => {
735 return Some(TokenKind::MagicNamespace);
736 }
737 15 if t.eq_ignore_ascii_case("__halt_compiler") => {
738 return Some(TokenKind::HaltCompiler);
739 }
740 _ => {}
741 }
742 None
743}
744
745impl TokenKind {
746 pub fn description(self) -> &'static str {
749 match self {
750 TokenKind::IntLiteral => "integer",
751 TokenKind::HexIntLiteral => "hex integer",
752 TokenKind::BinIntLiteral => "binary integer",
753 TokenKind::OctIntLiteral | TokenKind::OctIntLiteralNew => "octal integer",
754 TokenKind::FloatLiteral
755 | TokenKind::FloatLiteralSimple
756 | TokenKind::FloatLiteralLeadingDot => "float",
757 TokenKind::SingleQuotedString | TokenKind::DoubleQuotedString => "string",
758 TokenKind::BacktickString => "backtick string",
759 TokenKind::Variable => "variable",
760 TokenKind::Dollar => "'$'",
761 TokenKind::Identifier => "identifier",
762 TokenKind::Plus => "'+'",
763 TokenKind::Minus => "'-'",
764 TokenKind::Star => "'*'",
765 TokenKind::Slash => "'/'",
766 TokenKind::Percent => "'%'",
767 TokenKind::StarStar => "'**'",
768 TokenKind::Dot => "'.'",
769 TokenKind::Equals => "'='",
770 TokenKind::PlusEquals => "'+='",
771 TokenKind::MinusEquals => "'-='",
772 TokenKind::StarEquals => "'*='",
773 TokenKind::SlashEquals => "'/='",
774 TokenKind::PercentEquals => "'%='",
775 TokenKind::StarStarEquals => "'**='",
776 TokenKind::DotEquals => "'.='",
777 TokenKind::AmpersandEquals => "'&='",
778 TokenKind::PipeEquals => "'|='",
779 TokenKind::CaretEquals => "'^='",
780 TokenKind::ShiftLeftEquals => "'<<='",
781 TokenKind::ShiftRightEquals => "'>>='",
782 TokenKind::CoalesceEquals => "'??='",
783 TokenKind::EqualsEquals => "'=='",
784 TokenKind::BangEquals => "'!='",
785 TokenKind::EqualsEqualsEquals => "'==='",
786 TokenKind::BangEqualsEquals => "'!=='",
787 TokenKind::LessThan => "'<'",
788 TokenKind::GreaterThan => "'>'",
789 TokenKind::LessThanEquals => "'<='",
790 TokenKind::GreaterThanEquals => "'>='",
791 TokenKind::Spaceship => "'<=>'",
792 TokenKind::AmpersandAmpersand => "'&&'",
793 TokenKind::PipePipe => "'||'",
794 TokenKind::Bang => "'!'",
795 TokenKind::Ampersand => "'&'",
796 TokenKind::Pipe => "'|'",
797 TokenKind::Caret => "'^'",
798 TokenKind::Tilde => "'~'",
799 TokenKind::ShiftLeft => "'<<'",
800 TokenKind::ShiftRight => "'>>'",
801 TokenKind::PlusPlus => "'++'",
802 TokenKind::MinusMinus => "'--'",
803 TokenKind::Question => "'?'",
804 TokenKind::QuestionQuestion => "'??'",
805 TokenKind::Colon => "':'",
806 TokenKind::FatArrow => "'=>'",
807 TokenKind::PipeArrow => "'|>'",
808 TokenKind::LeftParen => "'('",
809 TokenKind::RightParen => "')'",
810 TokenKind::LeftBracket => "'['",
811 TokenKind::RightBracket => "']'",
812 TokenKind::LeftBrace => "'{'",
813 TokenKind::RightBrace => "'}'",
814 TokenKind::Semicolon => "';'",
815 TokenKind::Comma => "','",
816 TokenKind::DoubleColon => "'::'",
817 TokenKind::Arrow => "'->'",
818 TokenKind::NullsafeArrow => "'?->'",
819 TokenKind::Backslash => "'\\'",
820 TokenKind::At => "'@'",
821 TokenKind::HashBracket => "'#['",
822 TokenKind::Ellipsis => "'...'",
823 TokenKind::If => "'if'",
824 TokenKind::Else => "'else'",
825 TokenKind::ElseIf => "'elseif'",
826 TokenKind::While => "'while'",
827 TokenKind::Do => "'do'",
828 TokenKind::For => "'for'",
829 TokenKind::Foreach => "'foreach'",
830 TokenKind::As => "'as'",
831 TokenKind::Function => "'function'",
832 TokenKind::Return => "'return'",
833 TokenKind::Echo => "'echo'",
834 TokenKind::Print => "'print'",
835 TokenKind::True => "'true'",
836 TokenKind::False => "'false'",
837 TokenKind::Null => "'null'",
838 TokenKind::And => "'and'",
839 TokenKind::Or => "'or'",
840 TokenKind::Xor => "'xor'",
841 TokenKind::Break => "'break'",
842 TokenKind::Continue => "'continue'",
843 TokenKind::Switch => "'switch'",
844 TokenKind::Case => "'case'",
845 TokenKind::Default => "'default'",
846 TokenKind::EndIf => "'endif'",
847 TokenKind::EndWhile => "'endwhile'",
848 TokenKind::EndFor => "'endfor'",
849 TokenKind::EndForeach => "'endforeach'",
850 TokenKind::Throw => "'throw'",
851 TokenKind::Try => "'try'",
852 TokenKind::Catch => "'catch'",
853 TokenKind::Finally => "'finally'",
854 TokenKind::Instanceof => "'instanceof'",
855 TokenKind::Array => "'array'",
856 TokenKind::List => "'list'",
857 TokenKind::Goto => "'goto'",
858 TokenKind::Declare => "'declare'",
859 TokenKind::Unset => "'unset'",
860 TokenKind::Global => "'global'",
861 TokenKind::EndDeclare => "'enddeclare'",
862 TokenKind::EndSwitch => "'endswitch'",
863 TokenKind::Isset => "'isset'",
864 TokenKind::Empty => "'empty'",
865 TokenKind::Include => "'include'",
866 TokenKind::IncludeOnce => "'include_once'",
867 TokenKind::Require => "'require'",
868 TokenKind::RequireOnce => "'require_once'",
869 TokenKind::Eval => "'eval'",
870 TokenKind::Exit => "'exit'",
871 TokenKind::Die => "'die'",
872 TokenKind::Clone => "'clone'",
873 TokenKind::New => "'new'",
874 TokenKind::Class => "'class'",
875 TokenKind::Abstract => "'abstract'",
876 TokenKind::Final => "'final'",
877 TokenKind::Interface => "'interface'",
878 TokenKind::Trait => "'trait'",
879 TokenKind::Extends => "'extends'",
880 TokenKind::Implements => "'implements'",
881 TokenKind::Public => "'public'",
882 TokenKind::Protected => "'protected'",
883 TokenKind::Private => "'private'",
884 TokenKind::Static => "'static'",
885 TokenKind::Const => "'const'",
886 TokenKind::Fn_ => "'fn'",
887 TokenKind::Match_ => "'match'",
888 TokenKind::Namespace => "'namespace'",
889 TokenKind::Use => "'use'",
890 TokenKind::Readonly => "'readonly'",
891 TokenKind::Enum_ => "'enum'",
892 TokenKind::Yield_ => "'yield'",
893 TokenKind::Self_ => "'self'",
894 TokenKind::Parent_ => "'parent'",
895 TokenKind::MagicClass => "'__CLASS__'",
896 TokenKind::MagicDir => "'__DIR__'",
897 TokenKind::MagicFile => "'__FILE__'",
898 TokenKind::MagicFunction => "'__FUNCTION__'",
899 TokenKind::MagicLine => "'__LINE__'",
900 TokenKind::MagicMethod => "'__METHOD__'",
901 TokenKind::MagicNamespace => "'__NAMESPACE__'",
902 TokenKind::MagicTrait => "'__TRAIT__'",
903 TokenKind::MagicProperty => "'__PROPERTY__'",
904 TokenKind::HaltCompiler => "'__halt_compiler'",
905 TokenKind::OpenTag => "'<?php'",
906 TokenKind::CloseTag => "'?>'",
907 TokenKind::InlineHtml => "inline HTML",
908 TokenKind::Heredoc => "heredoc",
909 TokenKind::Nowdoc => "nowdoc",
910 TokenKind::InvalidNumericLiteral => "invalid numeric literal",
911 TokenKind::LineComment => "line comment",
912 TokenKind::HashComment => "hash comment",
913 TokenKind::BlockComment => "block comment",
914 TokenKind::DocComment => "doc comment",
915 TokenKind::Eof => "end of file",
916 }
917 }
918}
919
920impl std::fmt::Display for TokenKind {
921 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
922 f.write_str(self.description())
923 }
924}
925
926#[cfg(test)]
927mod tests {
928 use super::*;
929
930 #[test]
931 fn test_resolve_keyword() {
932 assert_eq!(resolve_keyword("if"), Some(TokenKind::If));
933 assert_eq!(resolve_keyword("IF"), Some(TokenKind::If));
934 assert_eq!(resolve_keyword("If"), Some(TokenKind::If));
935 assert_eq!(resolve_keyword("function"), Some(TokenKind::Function));
936 assert_eq!(resolve_keyword("myFunc"), None);
937 assert_eq!(resolve_keyword("true"), Some(TokenKind::True));
938 assert_eq!(resolve_keyword("TRUE"), Some(TokenKind::True));
939 assert_eq!(resolve_keyword("null"), Some(TokenKind::Null));
940 }
941
942 #[test]
943 fn test_is_assignment_op() {
944 assert!(TokenKind::Equals.is_assignment_op());
945 assert!(TokenKind::PlusEquals.is_assignment_op());
946 assert!(TokenKind::DotEquals.is_assignment_op());
947 assert!(!TokenKind::Plus.is_assignment_op());
948 assert!(!TokenKind::EqualsEquals.is_assignment_op());
949 }
950}