77 InstanceExpression ,
88 InvokeExpression ,
99 Literal ,
10- NewExpression ,
11- ScopeExpression
10+ ScopeExpression ,
11+ TernaryExpression
1212};
1313use lang \ast \syntax \Extension ;
1414use lang \ast \types \{IsArray , IsFunction , IsGeneric , IsMap , IsUnion , IsNullable , IsValue };
1515
16+ /**
17+ * XP Generics extensions
18+ *
19+ * @see https://github.com/xp-framework/rfc/issues/106
20+ * @see https://github.com/xp-framework/rfc/issues/193
21+ * @test lang.ast.syntax.php.unittest.GenericsTest
22+ * @test lang.ast.syntax.php.unittest.CastingTest
23+ */
1624class Generics implements Extension {
1725
1826 /**
@@ -46,13 +54,15 @@ public static function components($types) {
4654 *
4755 * @param lang.ast.Type[] $list
4856 * @param lang.ast.Type[] $components
49- * @return ?string[]
57+ * @param string $prefix
58+ * @param string $suffix
59+ * @return ?string
5060 */
51- private static function generics ($ list , $ components ) {
61+ private static function generics ($ list , $ components, $ prefix = '' , $ suffix = '' ) {
5262 $ contained = false ;
5363 $ generics = [];
5464 foreach ($ list as $ type ) {
55- if ($ generic = self ::generic ($ type , $ components )) {
65+ if ($ generic = self ::generic ($ type , $ components, $ prefix , $ suffix )) {
5666 $ contained = true ;
5767 $ generics []= $ generic ;
5868 } else {
@@ -67,25 +77,27 @@ private static function generics($list, $components) {
6777 *
6878 * @param lang.ast.Type $type
6979 * @param lang.ast.Type[] $components
80+ * @param string $prefix
81+ * @param string $suffix
7082 * @return ?string
7183 */
72- private static function generic ($ type , $ components ) {
84+ private static function generic ($ type , $ components, $ prefix = '' , $ suffix = '' ) {
7385 if ($ type instanceof IsValue && in_array ($ type , $ components )) {
74- return self ::component ($ type );
86+ return $ prefix . self ::component ($ type ). $ suffix ;
7587 } else if ($ type instanceof IsNullable) {
76- if ($ generic = self ::generic ($ type ->element , $ components )) return '? ' .$ generic ;
88+ if ($ generic = self ::generic ($ type ->element , $ components, $ prefix , $ suffix )) return '? ' .$ generic ;
7789 } else if ($ type instanceof IsArray) {
78- if ($ generic = self ::generic ($ type ->component , $ components )) return $ generic .'[] ' ;
90+ if ($ generic = self ::generic ($ type ->component , $ components, $ prefix , $ suffix )) return $ generic .'[] ' ;
7991 } else if ($ type instanceof IsMap) {
80- if ($ generic = self ::generic ($ type ->value , $ components )) return '[: ' .$ generic .'] ' ;
92+ if ($ generic = self ::generic ($ type ->value , $ components, $ prefix , $ suffix )) return '[: ' .$ generic .'] ' ;
8193 } else if ($ type instanceof IsUnion) {
82- if ($ generic = self ::generics ($ type ->components , $ components )) return implode ('| ' , $ generic );
94+ if ($ generic = self ::generics ($ type ->components , $ components, $ prefix , $ suffix )) return implode ('| ' , $ generic );
8395 } else if ($ type instanceof IsGeneric) {
84- if ($ generic = self ::generics ($ type ->components , $ components )) {
96+ if ($ generic = self ::generics ($ type ->components , $ components, $ prefix , $ suffix )) {
8597 return $ type ->base ->name ().'< ' .implode (', ' , $ generic ).'> ' ;
8698 }
8799 } else if ($ type instanceof IsFunction) {
88- if ($ generic = self ::generics (array_merge ([$ type ->returns ], $ type ->signature ), $ components )) {
100+ if ($ generic = self ::generics (array_merge ([$ type ->returns ], $ type ->signature ), $ components, $ prefix , $ suffix )) {
89101 $ return = array_shift ($ generic );
90102 return '(function( ' .implode (', ' , $ generic ).'): ' .$ return .') ' ;
91103 }
@@ -177,6 +189,8 @@ public static function type($type, $values) {
177189 self ::annotate ($ method , self ::method ($ method , $ type ->name ->components ));
178190 }
179191
192+ // Ensure class name is emitted as its base type
193+ $ type ->name = new IsGenericDeclaration ($ type ->name );
180194 return $ type ;
181195 }
182196
@@ -298,5 +312,19 @@ public function setup($language, $emitter) {
298312 }
299313 return $ node ;
300314 });
315+
316+ $ emitter ->transform ('cast ' , function ($ codegen , $ node ) {
317+ $ type = $ codegen ->scope [0 ]->type ;
318+ if ($ type ->name instanceof IsGenericDeclaration) {
319+ if ($ generic = self ::generic ($ node ->type , $ type ->name ->components (), '{$_G[ \'' , '\']} ' )) {
320+ return new TernaryExpression (
321+ new Code ('($_G ?? $_G= self::$__generic) ' ),
322+ new InvokeExpression (new Literal ('cast ' ), [$ node ->expression , new Literal ('" ' .$ generic .'" ' )]),
323+ new Literal ('null ' )
324+ );
325+ }
326+ }
327+ return $ node ;
328+ });
301329 }
302330}
0 commit comments