1use serde::Serialize;
2use strum::Display;
3
4use mago_span::Span;
5
6use crate::T;
7
8#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
9#[serde(tag = "type", content = "value")]
10pub enum DocumentKind {
11 Heredoc,
12 Nowdoc,
13}
14
15#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
16#[serde(tag = "type", content = "value")]
17pub enum Associativity {
18 NonAssociative,
19 Left,
20 Right,
21}
22
23#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
24#[serde(tag = "type", content = "value")]
25pub enum Precedence {
26 Lowest,
27 Print,
28 YieldFrom,
29 Yield,
30 KeyOr,
31 KeyXor,
32 KeyAnd,
33 Assignment,
34 ElvisOrConditional,
35 NullCoalesce,
36 Or,
37 And,
38 BitwiseOr,
39 BitwiseXor,
40 BitwiseAnd,
41 Equality,
42 Comparison,
43 Pipe,
50 Concat,
51 BitShift,
52 AddSub,
53 MulDivMod,
54 Unary,
55 Instanceof,
56 ErrorControl,
57 Pow,
58 Clone,
59 IncDec,
60 Reference,
61 CallDim,
62 New,
63 ArrayDim,
64 ObjectAccess,
65 Highest,
66}
67
68pub trait GetPrecedence {
69 fn precedence(&self) -> Precedence;
70}
71
72#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
73#[serde(tag = "type", content = "value")]
74pub enum TokenKind {
75 Whitespace, Eval, Die, Self_, Parent, Backtick, DocumentStart(DocumentKind), DocumentEnd, From, Print, Dollar, HaltCompiler, Readonly, Global, Abstract, Ampersand, AmpersandEqual, AmpersandAmpersand, AmpersandAmpersandEqual, Array, ArrayCast, MinusGreaterThan, QuestionMinusGreaterThan, At, As, Asterisk, HashLeftBracket, Bang, BangEqual, LessThanGreaterThan, BangEqualEqual, LessThanEqualGreaterThan, BoolCast, BooleanCast, And, Or, Break, Callable, Caret, CaretEqual, Case, Catch, Class, ClassConstant, TraitConstant, FunctionConstant, MethodConstant, LineConstant, FileConstant, Clone, MinusEqual, CloseTag, QuestionQuestion, QuestionQuestionEqual, AsteriskEqual, Colon, Comma, SingleLineComment, HashComment, MultiLineComment, DocBlockComment, Const, PartialLiteralString, LiteralString, Continue, Declare, MinusMinus, Default, DirConstant, SlashEqual, Do, DollarLeftBrace, Dot, DotEqual, EqualGreaterThan, DoubleCast, RealCast, FloatCast, ColonColon, EqualEqual, DoubleQuote, Else, Echo, DotDotDot, ElseIf, Empty, EndDeclare, EndFor, EndForeach, EndIf, EndSwitch, EndWhile, Enum, Equal, Extends, False, Final, Finally, LiteralFloat, Fn, For, Foreach, FullyQualifiedIdentifier, Function, Goto, GreaterThan, GreaterThanEqual, Identifier, If, Implements, Include, IncludeOnce, PlusPlus, InlineText, InlineShebang, Instanceof, Insteadof, Exit, Unset, Isset, List, LiteralInteger, IntCast, IntegerCast, Interface, LeftBrace, LeftBracket, LeftParenthesis, LeftShift, LeftShiftEqual, RightShift, RightShiftEqual, LessThan, LessThanEqual, Match, Minus, Namespace, NamespaceSeparator, NamespaceConstant, New, Null, ObjectCast, UnsetCast, OpenTag, EchoTag, ShortOpenTag, Percent, PercentEqual, Pipe, PipeEqual, Plus, PlusEqual, AsteriskAsterisk, AsteriskAsteriskEqual, Private, PrivateSet, Protected, ProtectedSet, Public, PublicSet, QualifiedIdentifier, Question, Require, RequireOnce, Return, RightBrace, RightBracket, RightParenthesis, Semicolon, Slash, Static, StringCast, BinaryCast, VoidCast, StringPart, Switch, Throw, Trait, EqualEqualEqual, True, Try, Use, Var, Variable, Yield, While, Tilde, PipePipe, Xor, PipeGreaterThan, }
266
267#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
268pub struct Token<'arena> {
269 pub kind: TokenKind,
270 pub value: &'arena str,
271 pub span: Span,
272}
273
274impl Precedence {
275 #[inline]
276 pub const fn infix(kind: &TokenKind) -> Precedence {
277 match kind {
278 T!["**"] => Precedence::Pow,
279 T!["instanceof"] => Precedence::Instanceof,
280 T!["*" | "/" | "%"] => Precedence::MulDivMod,
281 T!["+" | "-"] => Precedence::AddSub,
282 T!["<<"] | T![">>"] => Precedence::BitShift,
283 T!["."] => Precedence::Concat,
284 T!["<" | "<=" | ">" | ">="] => Precedence::Comparison,
285 T!["==" | "!=" | "===" | "!==" | "<>" | "<=>"] => Precedence::Equality,
286 T!["&"] => Precedence::BitwiseAnd,
287 T!["^"] => Precedence::BitwiseXor,
288 T!["|"] => Precedence::BitwiseOr,
289 T!["&&"] => Precedence::And,
290 T!["||"] => Precedence::Or,
291 T!["??"] => Precedence::NullCoalesce,
292 T!["?"] => Precedence::ElvisOrConditional,
293 T!["="
294 | "+="
295 | "-="
296 | "*="
297 | "**="
298 | "/="
299 | ".="
300 | "&&="
301 | "??="
302 | "%="
303 | "&="
304 | "|="
305 | "^="
306 | "<<="
307 | ">>="] => Precedence::Assignment,
308 T!["yield"] => Precedence::Yield,
309 T!["and"] => Precedence::KeyAnd,
310 T!["or"] => Precedence::KeyOr,
311 T!["xor"] => Precedence::KeyXor,
312 T!["print"] => Precedence::Print,
313 T!["|>"] => Precedence::Pipe,
314 _ => Precedence::Lowest,
315 }
316 }
317
318 #[inline]
319 pub const fn postfix(kind: &TokenKind) -> Self {
320 match kind {
321 T!["++" | "--"] => Self::IncDec,
322 T!["("] => Self::CallDim,
323 T!["["] => Self::ArrayDim,
324 T!["->" | "?->" | "::"] => Self::ObjectAccess,
325 _ => Self::Lowest,
326 }
327 }
328
329 #[inline]
330 pub const fn associativity(&self) -> Option<Associativity> {
331 Some(match self {
332 Self::MulDivMod
333 | Self::AddSub
334 | Self::Concat
335 | Self::BitShift
336 | Self::BitwiseAnd
337 | Self::BitwiseOr
338 | Self::BitwiseXor
339 | Self::And
340 | Self::Or
341 | Self::KeyAnd
342 | Self::KeyXor
343 | Self::KeyOr
344 | Self::Pipe
345 | Self::ElvisOrConditional
346 | Self::ObjectAccess => Associativity::Left,
347 Self::Pow | Self::NullCoalesce | Self::Assignment | Self::Unary | Self::New => Associativity::Right,
348 Self::Equality | Self::Comparison | Self::Instanceof => Associativity::NonAssociative,
349 _ => return None,
350 })
351 }
352
353 #[inline]
354 pub const fn is_associative(&self) -> bool {
355 self.associativity().is_some()
356 }
357
358 #[inline]
359 pub const fn is_right_associative(&self) -> bool {
360 matches!(self.associativity(), Some(Associativity::Right))
361 }
362
363 #[inline]
364 pub const fn is_left_associative(&self) -> bool {
365 matches!(self.associativity(), Some(Associativity::Left))
366 }
367
368 #[inline]
369 pub const fn is_non_associative(&self) -> bool {
370 matches!(self.associativity(), Some(Associativity::NonAssociative))
371 }
372}
373
374impl TokenKind {
375 #[inline]
376 pub const fn is_keyword(&self) -> bool {
377 matches!(
378 self,
379 TokenKind::Eval
380 | TokenKind::Die
381 | TokenKind::Empty
382 | TokenKind::Isset
383 | TokenKind::Unset
384 | TokenKind::Exit
385 | TokenKind::EndDeclare
386 | TokenKind::EndSwitch
387 | TokenKind::EndWhile
388 | TokenKind::EndForeach
389 | TokenKind::EndFor
390 | TokenKind::EndIf
391 | TokenKind::From
392 | TokenKind::And
393 | TokenKind::Or
394 | TokenKind::Xor
395 | TokenKind::Print
396 | TokenKind::Readonly
397 | TokenKind::Global
398 | TokenKind::Match
399 | TokenKind::Abstract
400 | TokenKind::Array
401 | TokenKind::As
402 | TokenKind::Break
403 | TokenKind::Case
404 | TokenKind::Catch
405 | TokenKind::Class
406 | TokenKind::Clone
407 | TokenKind::Continue
408 | TokenKind::Const
409 | TokenKind::Declare
410 | TokenKind::Default
411 | TokenKind::Do
412 | TokenKind::Echo
413 | TokenKind::ElseIf
414 | TokenKind::Else
415 | TokenKind::Enum
416 | TokenKind::Extends
417 | TokenKind::False
418 | TokenKind::Finally
419 | TokenKind::Final
420 | TokenKind::Fn
421 | TokenKind::Foreach
422 | TokenKind::For
423 | TokenKind::Function
424 | TokenKind::Goto
425 | TokenKind::If
426 | TokenKind::IncludeOnce
427 | TokenKind::Include
428 | TokenKind::Implements
429 | TokenKind::Interface
430 | TokenKind::Instanceof
431 | TokenKind::Namespace
432 | TokenKind::New
433 | TokenKind::Null
434 | TokenKind::Private
435 | TokenKind::PrivateSet
436 | TokenKind::Protected
437 | TokenKind::Public
438 | TokenKind::RequireOnce
439 | TokenKind::Require
440 | TokenKind::Return
441 | TokenKind::Static
442 | TokenKind::Switch
443 | TokenKind::Throw
444 | TokenKind::Trait
445 | TokenKind::True
446 | TokenKind::Try
447 | TokenKind::Use
448 | TokenKind::Var
449 | TokenKind::Yield
450 | TokenKind::While
451 | TokenKind::Insteadof
452 | TokenKind::List
453 | TokenKind::Self_
454 | TokenKind::Parent
455 | TokenKind::DirConstant
456 | TokenKind::FileConstant
457 | TokenKind::LineConstant
458 | TokenKind::FunctionConstant
459 | TokenKind::ClassConstant
460 | TokenKind::MethodConstant
461 | TokenKind::TraitConstant
462 | TokenKind::NamespaceConstant
463 | TokenKind::HaltCompiler
464 )
465 }
466
467 #[inline]
468 pub const fn is_infix(&self) -> bool {
469 matches!(
470 self,
471 T!["**"
472 | ">>="
473 | "<<="
474 | "^="
475 | "&="
476 | "|="
477 | "%="
478 | "**="
479 | "and"
480 | "or"
481 | "xor"
482 | "<=>"
483 | "<<"
484 | ">>"
485 | "&"
486 | "|"
487 | "^"
488 | "%"
489 | "instanceof"
490 | "*"
491 | "/"
492 | "+"
493 | "-"
494 | "."
495 | "<"
496 | ">"
497 | "<="
498 | ">="
499 | "=="
500 | "==="
501 | "!="
502 | "!=="
503 | "<>"
504 | "?"
505 | "&&"
506 | "||"
507 | "="
508 | "+="
509 | "-="
510 | ".="
511 | "??="
512 | "/="
513 | "*="
514 | "??"
515 | "|>"]
516 )
517 }
518
519 #[inline]
520 pub const fn is_postfix(&self) -> bool {
521 matches!(self, T!["++" | "--" | "(" | "[" | "->" | "?->" | "::"])
522 }
523
524 #[inline]
525 pub const fn is_visibility_modifier(&self) -> bool {
526 matches!(self, T!["public" | "protected" | "private" | "private(set)" | "protected(set)" | "public(set)"])
527 }
528
529 #[inline]
530 pub const fn is_modifier(&self) -> bool {
531 matches!(
532 self,
533 T!["public"
534 | "protected"
535 | "private"
536 | "private(set)"
537 | "protected(set)"
538 | "public(set)"
539 | "static"
540 | "final"
541 | "abstract"
542 | "readonly"]
543 )
544 }
545
546 #[inline]
547 pub const fn is_identifier_maybe_soft_reserved(&self) -> bool {
548 if let TokenKind::Identifier = self { true } else { self.is_soft_reserved_identifier() }
549 }
550
551 #[inline]
552 pub const fn is_identifier_maybe_reserved(&self) -> bool {
553 if let TokenKind::Identifier = self { true } else { self.is_reserved_identifier() }
554 }
555
556 #[inline]
557 pub const fn is_soft_reserved_identifier(&self) -> bool {
558 matches!(
559 self,
560 T!["parent" | "self" | "true" | "false" | "list" | "null" | "enum" | "from" | "readonly" | "match"]
561 )
562 }
563
564 #[inline]
565 pub const fn is_reserved_identifier(&self) -> bool {
566 if self.is_soft_reserved_identifier() {
567 return true;
568 }
569
570 matches!(
571 self,
572 T!["static"
573 | "abstract"
574 | "final"
575 | "for"
576 | "private"
577 | "private(set)"
578 | "protected"
579 | "protected(set)"
580 | "public"
581 | "public(set)"
582 | "include"
583 | "include_once"
584 | "eval"
585 | "require"
586 | "require_once"
587 | "or"
588 | "xor"
589 | "and"
590 | "instanceof"
591 | "new"
592 | "clone"
593 | "exit"
594 | "die"
595 | "if"
596 | "elseif"
597 | "else"
598 | "endif"
599 | "echo"
600 | "do"
601 | "while"
602 | "endwhile"
603 | "endfor"
604 | "foreach"
605 | "endforeach"
606 | "declare"
607 | "enddeclare"
608 | "as"
609 | "try"
610 | "catch"
611 | "finally"
612 | "throw"
613 | "use"
614 | "insteadof"
615 | "global"
616 | "var"
617 | "unset"
618 | "isset"
619 | "empty"
620 | "continue"
621 | "goto"
622 | "function"
623 | "const"
624 | "return"
625 | "print"
626 | "yield"
627 | "list"
628 | "switch"
629 | "endswitch"
630 | "case"
631 | "default"
632 | "break"
633 | "array"
634 | "callable"
635 | "extends"
636 | "implements"
637 | "namespace"
638 | "trait"
639 | "interface"
640 | "class"
641 | "__CLASS__"
642 | "__TRAIT__"
643 | "__FUNCTION__"
644 | "__METHOD__"
645 | "__LINE__"
646 | "__FILE__"
647 | "__DIR__"
648 | "__NAMESPACE__"
649 | "__halt_compiler"
650 | "fn"
651 | "match"]
652 )
653 }
654
655 #[inline]
656 pub const fn is_literal(&self) -> bool {
657 matches!(
658 self,
659 T!["true" | "false" | "null" | LiteralFloat | LiteralInteger | LiteralString | PartialLiteralString]
660 )
661 }
662
663 #[inline]
664 pub const fn is_magic_constant(&self) -> bool {
665 matches!(
666 self,
667 T!["__CLASS__"
668 | "__DIR__"
669 | "__FILE__"
670 | "__FUNCTION__"
671 | "__LINE__"
672 | "__METHOD__"
673 | "__NAMESPACE__"
674 | "__TRAIT__"]
675 )
676 }
677
678 #[inline]
679 pub const fn is_cast(&self) -> bool {
680 matches!(
681 self,
682 T!["(string)"
683 | "(binary)"
684 | "(int)"
685 | "(integer)"
686 | "(float)"
687 | "(double)"
688 | "(real)"
689 | "(bool)"
690 | "(boolean)"
691 | "(array)"
692 | "(object)"
693 | "(unset)"
694 | "(void)"]
695 )
696 }
697
698 #[inline]
699 pub const fn is_unary_prefix(&self) -> bool {
700 if self.is_cast() {
701 return true;
702 }
703
704 matches!(self, T!["@" | "!" | "~" | "-" | "+" | "++" | "--" | "&"])
705 }
706
707 #[inline]
708 pub const fn is_trivia(&self) -> bool {
709 matches!(self, T![SingleLineComment | MultiLineComment | DocBlockComment | HashComment | Whitespace])
710 }
711
712 #[inline]
713 pub const fn is_comment(&self) -> bool {
714 matches!(self, T![SingleLineComment | MultiLineComment | DocBlockComment | HashComment])
715 }
716
717 #[inline]
718 pub const fn is_comma(&self) -> bool {
719 matches!(self, T![","])
720 }
721
722 #[inline]
723 pub const fn is_construct(&self) -> bool {
724 matches!(
725 self,
726 T!["isset"
727 | "empty"
728 | "eval"
729 | "include"
730 | "include_once"
731 | "require"
732 | "require_once"
733 | "print"
734 | "unset"
735 | "exit"
736 | "die"]
737 )
738 }
739}
740
741impl<'arena> Token<'arena> {
742 pub const fn new(kind: TokenKind, value: &'arena str, span: Span) -> Self {
743 Self { kind, value, span }
744 }
745}
746
747impl<'arena> std::fmt::Display for Token<'arena> {
748 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
749 write!(f, "{}({})", self.kind, self.value)
750 }
751}