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