2222
2323namespace PackageFactory \ComponentEngine \TypeSystem \Resolver \Match ;
2424
25+ use PackageFactory \ComponentEngine \Parser \Ast \AccessNode ;
2526use PackageFactory \ComponentEngine \Parser \Ast \BooleanLiteralNode ;
2627use PackageFactory \ComponentEngine \Parser \Ast \MatchNode ;
28+ use PackageFactory \ComponentEngine \TypeSystem \Resolver \Access \AccessTypeResolver ;
2729use PackageFactory \ComponentEngine \TypeSystem \Resolver \Expression \ExpressionTypeResolver ;
2830use PackageFactory \ComponentEngine \TypeSystem \ScopeInterface ;
2931use PackageFactory \ComponentEngine \TypeSystem \Type \BooleanType \BooleanType ;
32+ use PackageFactory \ComponentEngine \TypeSystem \Type \EnumType \EnumMemberType ;
33+ use PackageFactory \ComponentEngine \TypeSystem \Type \EnumType \EnumStaticType ;
3034use PackageFactory \ComponentEngine \TypeSystem \Type \EnumType \EnumType ;
3135use PackageFactory \ComponentEngine \TypeSystem \Type \UnionType \UnionType ;
3236use PackageFactory \ComponentEngine \TypeSystem \TypeInterface ;
@@ -67,7 +71,14 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
6771 } else {
6872 $ types = [];
6973
74+ $ defaultArmPresent = false ;
7075 foreach ($ matchNode ->arms ->items as $ matchArmNode ) {
76+ if ($ defaultArmPresent ) {
77+ throw new \Exception ('@TODO: Multiple illegal default arms ' );
78+ }
79+ if ($ matchArmNode ->left === null ) {
80+ $ defaultArmPresent = true ;
81+ }
7182 $ types [] = $ expressionTypeResolver ->resolveTypeOf (
7283 $ matchArmNode ->right
7384 );
@@ -79,20 +90,60 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
7990 }
8091 }
8192
82- private function resolveTypeOfEnumMatch (MatchNode $ matchNode ): TypeInterface
93+ private function resolveTypeOfEnumMatch (MatchNode $ matchNode, EnumType $ subjectEnumType ): TypeInterface
8394 {
8495 $ expressionTypeResolver = new ExpressionTypeResolver (
8596 scope: $ this ->scope
8697 );
8798 $ types = [];
8899
100+ $ accessTypeResolver = new AccessTypeResolver (scope: $ this ->scope );
101+
102+ $ defaultArmPresent = false ;
103+ $ referencedEnumMembers = [];
104+
89105 foreach ($ matchNode ->arms ->items as $ matchArmNode ) {
106+ if ($ defaultArmPresent ) {
107+ throw new \Exception ('@TODO Error: Multiple illegal default arms ' );
108+ }
109+ if ($ matchArmNode ->left === null ) {
110+ $ defaultArmPresent = true ;
111+ } else {
112+ foreach ($ matchArmNode ->left ->items as $ expressionNode ) {
113+ $ accessNode = $ expressionNode ->root ;
114+ if (!$ accessNode instanceof AccessNode
115+ || !($ enumMemberType = $ accessTypeResolver ->resolveTypeOf ($ accessNode )) instanceof EnumMemberType) {
116+ throw new \Error ('@TODO Error: To be matched enum value should be referenced like: `Enum.B` ' );
117+ }
118+
119+ if (!$ enumMemberType ->enumType instanceof EnumStaticType) {
120+ throw new \Exception ('@TODO Error: To be matched enum must be referenced static ' );
121+ }
122+
123+ if (!$ enumMemberType ->enumType ->is ($ subjectEnumType )) {
124+ throw new \Error ('@TODO Error: incompatible enum match: got ' . $ enumMemberType ->enumType ->enumName . ' expected ' . $ subjectEnumType ->enumName );
125+ }
126+
127+ if (isset ($ referencedEnumMembers [$ enumMemberType ->memberName ])) {
128+ throw new \Error ('@TODO Error: Enum path ' . $ enumMemberType ->memberName . ' was already defined once in this match and cannot be used twice ' );
129+ }
130+
131+ $ referencedEnumMembers [$ enumMemberType ->memberName ] = true ;
132+ }
133+ }
134+
90135 $ types [] = $ expressionTypeResolver ->resolveTypeOf (
91136 $ matchArmNode ->right
92137 );
93138 }
94139
95- // @TODO: Ensure that match is complete
140+ if (!$ defaultArmPresent ) {
141+ foreach ($ subjectEnumType ->getMemberNames () as $ member ) {
142+ if (!isset ($ referencedEnumMembers [$ member ])) {
143+ throw new \Error ('@TODO Error: member ' . $ member . ' not checked ' );
144+ }
145+ }
146+ }
96147
97148 return UnionType::of (...$ types );
98149 }
@@ -108,7 +159,7 @@ public function resolveTypeOf(MatchNode $matchNode): TypeInterface
108159
109160 return match (true ) {
110161 BooleanType::get ()->is ($ typeOfSubject ) => $ this ->resolveTypeOfBooleanMatch ($ matchNode ),
111- $ typeOfSubject instanceof EnumType => $ this ->resolveTypeOfEnumMatch ($ matchNode ),
162+ $ typeOfSubject instanceof EnumType => $ this ->resolveTypeOfEnumMatch ($ matchNode, $ typeOfSubject ),
112163 default => throw new \Exception ('@TODO: Not handled ' . $ typeOfSubject ::class)
113164 };
114165 }
0 commit comments