16
16
use Microsoft \PhpParser \Node \ClassInterfaceClause ;
17
17
use Microsoft \PhpParser \Node \ClassMembersNode ;
18
18
use Microsoft \PhpParser \Node \ConstElement ;
19
+ use Microsoft \PhpParser \Node \EnumCaseDeclaration ;
20
+ use Microsoft \PhpParser \Node \EnumMembers ;
19
21
use Microsoft \PhpParser \Node \Expression ;
20
22
use Microsoft \PhpParser \Node \Expression \{
21
23
AnonymousFunctionCreationExpression ,
88
90
DeclareStatement ,
89
91
DoStatement ,
90
92
EmptyStatement ,
93
+ EnumDeclaration ,
91
94
ExpressionStatement ,
92
95
ForeachStatement ,
93
96
ForStatement ,
@@ -292,6 +295,7 @@ private function isListTerminator(int $parseContext) {
292
295
case ParseContext::ClassMembers:
293
296
case ParseContext::BlockStatements:
294
297
case ParseContext::TraitMembers:
298
+ case ParseContext::EnumMembers:
295
299
return $ tokenKind === TokenKind::CloseBraceToken;
296
300
case ParseContext::SwitchStatementElements:
297
301
return $ tokenKind === TokenKind::CloseBraceToken || $ tokenKind === TokenKind::EndSwitchKeyword;
@@ -343,6 +347,9 @@ private function isValidListElement($context, Token $token) {
343
347
case ParseContext::TraitMembers:
344
348
return $ this ->isTraitMemberDeclarationStart ($ token );
345
349
350
+ case ParseContext::EnumMembers:
351
+ return $ this ->isEnumMemberDeclarationStart ($ token );
352
+
346
353
case ParseContext::InterfaceMembers:
347
354
return $ this ->isInterfaceMemberDeclarationStart ($ token );
348
355
@@ -374,6 +381,9 @@ private function getParseListElementFn($context) {
374
381
case ParseContext::InterfaceMembers:
375
382
return $ this ->parseInterfaceElementFn ();
376
383
384
+ case ParseContext::EnumMembers:
385
+ return $ this ->parseEnumElementFn ();
386
+
377
387
case ParseContext::SwitchStatementElements:
378
388
return $ this ->parseCaseOrDefaultStatement ();
379
389
default :
@@ -583,6 +593,9 @@ private function parseStatementFn() {
583
593
case TokenKind::TraitKeyword:
584
594
return $ this ->parseTraitDeclaration ($ parentNode );
585
595
596
+ case TokenKind::EnumKeyword:
597
+ return $ this ->parseEnumDeclaration ($ parentNode );
598
+
586
599
// global-declaration
587
600
case TokenKind::GlobalKeyword:
588
601
return $ this ->parseGlobalDeclaration ($ parentNode );
@@ -715,12 +728,15 @@ private function parseAttributeStatement($parentNode) {
715
728
} elseif ($ parentNode instanceof TraitMembers) {
716
729
// Create a trait element or a MissingMemberDeclaration
717
730
$ statement = $ this ->parseTraitElementFn ()($ parentNode );
731
+ } elseif ($ parentNode instanceof EnumMembers) {
732
+ // Create a enum element or a MissingMemberDeclaration
733
+ $ statement = $ this ->parseEnumElementFn ()($ parentNode );
718
734
} elseif ($ parentNode instanceof InterfaceMembers) {
719
735
// Create an interface element or a MissingMemberDeclaration
720
736
$ statement = $ this ->parseInterfaceElementFn ()($ parentNode );
721
737
} else {
722
738
// Classlikes, anonymous functions, global functions, and arrow functions can have attributes. Global constants cannot.
723
- if (in_array ($ this ->token ->kind , [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword], true ) ||
739
+ if (in_array ($ this ->token ->kind , [TokenKind::ClassKeyword, TokenKind::TraitKeyword, TokenKind::InterfaceKeyword, TokenKind::AbstractKeyword, TokenKind::FinalKeyword, TokenKind::FunctionKeyword, TokenKind::FnKeyword, TokenKind::EnumKeyword ], true ) ||
724
740
$ this ->token ->kind === TokenKind::StaticKeyword && $ this ->lookahead ([TokenKind::FunctionKeyword, TokenKind::FnKeyword])) {
725
741
$ statement = $ this ->parseStatement ($ parentNode );
726
742
} else {
@@ -734,6 +750,8 @@ private function parseAttributeStatement($parentNode) {
734
750
if ($ statement instanceof FunctionLike ||
735
751
$ statement instanceof ClassDeclaration ||
736
752
$ statement instanceof TraitDeclaration ||
753
+ $ statement instanceof EnumDeclaration ||
754
+ $ statement instanceof EnumCaseDeclaration ||
737
755
$ statement instanceof InterfaceDeclaration ||
738
756
$ statement instanceof ClassConstDeclaration ||
739
757
$ statement instanceof PropertyDeclaration ||
@@ -1012,6 +1030,9 @@ private function isStatementStart(Token $token) {
1012
1030
// trait-declaration
1013
1031
case TokenKind::TraitKeyword:
1014
1032
1033
+ // enum-declaration
1034
+ case TokenKind::EnumKeyword:
1035
+
1015
1036
// namespace-definition
1016
1037
case TokenKind::NamespaceKeyword:
1017
1038
@@ -3198,6 +3219,21 @@ private function parseClassConstDeclaration($parentNode, $modifiers) {
3198
3219
return $ classConstDeclaration ;
3199
3220
}
3200
3221
3222
+ private function parseEnumCaseDeclaration ($ parentNode ) {
3223
+ $ classConstDeclaration = new EnumCaseDeclaration ();
3224
+ $ classConstDeclaration ->parent = $ parentNode ;
3225
+ $ classConstDeclaration ->caseKeyword = $ this ->eat1 (TokenKind::CaseKeyword);
3226
+ $ classConstDeclaration ->name = $ this ->eat ($ this ->nameOrKeywordOrReservedWordTokens );
3227
+ $ classConstDeclaration ->equalsToken = $ this ->eatOptional1 (TokenKind::EqualsToken);
3228
+ if ($ classConstDeclaration ->equalsToken !== null ) {
3229
+ // TODO add post-parse rule that checks for invalid assignments
3230
+ $ classConstDeclaration ->assignment = $ this ->parseExpression ($ classConstDeclaration );
3231
+ }
3232
+ $ classConstDeclaration ->semicolon = $ this ->eat1 (TokenKind::SemicolonToken);
3233
+
3234
+ return $ classConstDeclaration ;
3235
+ }
3236
+
3201
3237
/**
3202
3238
* @param Node $parentNode
3203
3239
* @param Token[] $modifiers
@@ -3520,6 +3556,102 @@ private function parseTraitElementFn() {
3520
3556
};
3521
3557
}
3522
3558
3559
+ private function parseEnumDeclaration ($ parentNode ) {
3560
+ $ enumDeclaration = new EnumDeclaration ();
3561
+ $ enumDeclaration ->parent = $ parentNode ;
3562
+
3563
+ $ enumDeclaration ->enumKeyword = $ this ->eat1 (TokenKind::EnumKeyword);
3564
+ $ enumDeclaration ->name = $ this ->eat1 (TokenKind::Name);
3565
+ $ enumDeclaration ->colonToken = $ this ->eatOptional1 (TokenKind::ColonToken);
3566
+ if ($ enumDeclaration ->colonToken !== null ) {
3567
+ $ enumDeclaration ->enumType = $ this ->tryParseParameterTypeDeclaration ($ enumDeclaration )
3568
+ ?: new MissingToken (TokenKind::EnumType, $ this ->token ->fullStart );
3569
+ }
3570
+
3571
+ $ enumDeclaration ->enumMembers = $ this ->parseEnumMembers ($ enumDeclaration );
3572
+
3573
+ return $ enumDeclaration ;
3574
+ }
3575
+
3576
+ private function parseEnumMembers ($ parentNode ) {
3577
+ $ enumMembers = new EnumMembers ();
3578
+ $ enumMembers ->parent = $ parentNode ;
3579
+
3580
+ $ enumMembers ->openBrace = $ this ->eat1 (TokenKind::OpenBraceToken);
3581
+
3582
+ $ enumMembers ->enumMemberDeclarations = $ this ->parseList ($ enumMembers , ParseContext::EnumMembers);
3583
+
3584
+ $ enumMembers ->closeBrace = $ this ->eat1 (TokenKind::CloseBraceToken);
3585
+
3586
+ return $ enumMembers ;
3587
+ }
3588
+
3589
+ private function isEnumMemberDeclarationStart ($ token ) {
3590
+ switch ($ token ->kind ) {
3591
+ // modifiers
3592
+ case TokenKind::PublicKeyword:
3593
+ case TokenKind::ProtectedKeyword:
3594
+ case TokenKind::PrivateKeyword:
3595
+ // case TokenKind::VarKeyword:
3596
+ case TokenKind::StaticKeyword:
3597
+ case TokenKind::AbstractKeyword:
3598
+ case TokenKind::FinalKeyword:
3599
+
3600
+ // method-declaration
3601
+ case TokenKind::FunctionKeyword:
3602
+
3603
+ // trait-use-clauses (enums can use traits)
3604
+ case TokenKind::UseKeyword:
3605
+
3606
+ // cases and constants
3607
+ case TokenKind::CaseKeyword:
3608
+ case TokenKind::ConstKeyword:
3609
+
3610
+ // attributes
3611
+ case TokenKind::AttributeToken:
3612
+ return true ;
3613
+ }
3614
+ return false ;
3615
+ }
3616
+
3617
+ private function parseEnumElementFn () {
3618
+ return function ($ parentNode ) {
3619
+ $ modifiers = $ this ->parseModifiers ();
3620
+
3621
+ $ token = $ this ->getCurrentToken ();
3622
+ switch ($ token ->kind ) {
3623
+ // TODO: CaseKeyword
3624
+ case TokenKind::CaseKeyword:
3625
+ return $ this ->parseEnumCaseDeclaration ($ parentNode );
3626
+
3627
+ case TokenKind::ConstKeyword:
3628
+ return $ this ->parseClassConstDeclaration ($ parentNode , $ modifiers );
3629
+
3630
+ case TokenKind::FunctionKeyword:
3631
+ return $ this ->parseMethodDeclaration ($ parentNode , $ modifiers );
3632
+
3633
+ case TokenKind::QuestionToken:
3634
+ return $ this ->parseRemainingPropertyDeclarationOrMissingMemberDeclaration (
3635
+ $ parentNode ,
3636
+ $ modifiers ,
3637
+ $ this ->eat1 (TokenKind::QuestionToken)
3638
+ );
3639
+ case TokenKind::VariableName:
3640
+ return $ this ->parsePropertyDeclaration ($ parentNode , $ modifiers );
3641
+
3642
+ case TokenKind::UseKeyword:
3643
+ return $ this ->parseTraitUseClause ($ parentNode );
3644
+
3645
+ case TokenKind::AttributeToken:
3646
+ return $ this ->parseAttributeStatement ($ parentNode );
3647
+
3648
+ default :
3649
+ return $ this ->parseRemainingPropertyDeclarationOrMissingMemberDeclaration ($ parentNode , $ modifiers );
3650
+ }
3651
+ };
3652
+ }
3653
+
3654
+
3523
3655
/**
3524
3656
* @param Node $parentNode
3525
3657
* @param Token[] $modifiers
0 commit comments