@@ -40,21 +40,26 @@ protected function declaration($name) {
40
40
* - Binary expression where left- and right hand side are literals
41
41
*
42
42
* @see https://wiki.php.net/rfc/const_scalar_exprs
43
+ * @param lang.ast.Result $result
43
44
* @param lang.ast.Node $node
44
45
* @return bool
45
46
*/
46
- protected function isConstant ($ node ) {
47
+ protected function isConstant ($ result , $ node ) {
47
48
if ($ node instanceof Literal) {
48
49
return true ;
49
50
} else if ($ node instanceof ArrayLiteral) {
50
51
foreach ($ node ->values as $ node ) {
51
- if (!$ this ->isConstant ($ node )) return false ;
52
+ if (!$ this ->isConstant ($ result , $ node )) return false ;
52
53
}
53
54
return true ;
54
55
} else if ($ node instanceof ScopeExpression) {
55
- return $ node ->member instanceof Literal;
56
+ return (
57
+ $ node ->member instanceof Literal &&
58
+ is_string ($ node ->type ) &&
59
+ !$ result ->lookup ($ node ->type )->rewriteEnumCase ($ node ->member ->expression )
60
+ );
56
61
} else if ($ node instanceof BinaryExpression) {
57
- return $ this ->isConstant ($ node ->left ) && $ this ->isConstant ($ node ->right );
62
+ return $ this ->isConstant ($ result , $ node ->left ) && $ this ->isConstant ($ result , $ node ->right );
58
63
}
59
64
return false ;
60
65
}
@@ -188,7 +193,7 @@ protected function emitStatic($result, $static) {
188
193
foreach ($ static ->initializations as $ variable => $ initial ) {
189
194
$ result ->out ->write ('static $ ' .$ variable );
190
195
if ($ initial ) {
191
- if ($ this ->isConstant ($ initial )) {
196
+ if ($ this ->isConstant ($ result , $ initial )) {
192
197
$ result ->out ->write ('= ' );
193
198
$ this ->emitOne ($ result , $ initial );
194
199
} else {
@@ -284,7 +289,7 @@ protected function emitParameter($result, $parameter) {
284
289
$ result ->out ->write (($ parameter ->reference ? '& ' : '' ).'$ ' .$ parameter ->name );
285
290
}
286
291
if ($ parameter ->default ) {
287
- if ($ this ->isConstant ($ parameter ->default )) {
292
+ if ($ this ->isConstant ($ result , $ parameter ->default )) {
288
293
$ result ->out ->write ('= ' );
289
294
$ this ->emitOne ($ result , $ parameter ->default );
290
295
} else {
@@ -349,7 +354,76 @@ protected function emitLambda($result, $lambda) {
349
354
$ this ->emitOne ($ result , $ lambda ->body );
350
355
}
351
356
357
+ protected function emitEnumCase ($ result , $ case ) {
358
+ $ result ->out ->write ('public static $ ' .$ case ->name .'; ' );
359
+ }
360
+
361
+ protected function emitEnum ($ result , $ enum ) {
362
+ array_unshift ($ result ->type , $ enum );
363
+ array_unshift ($ result ->meta , []);
364
+ $ result ->locals = [[], []];
365
+
366
+ $ result ->out ->write ('final class ' .$ this ->declaration ($ enum ->name ).' implements \\' .($ enum ->base ? 'BackedEnum ' : 'UnitEnum ' ));
367
+ $ enum ->implements && $ result ->out ->write (', ' .implode (', ' , $ enum ->implements ));
368
+ $ result ->out ->write ('{ ' );
369
+
370
+ $ cases = [];
371
+ foreach ($ enum ->body as $ member ) {
372
+ if ($ member ->is ('enumcase ' )) $ cases []= $ member ;
373
+ $ this ->emitOne ($ result , $ member );
374
+ }
375
+
376
+ // Constructors
377
+ if ($ enum ->base ) {
378
+ $ result ->out ->write ('public $name, $value; ' );
379
+ $ result ->out ->write ('private static $values= []; ' );
380
+ $ result ->out ->write ('private function __construct($name, $value) {
381
+ $this->name= $name;
382
+ $this->value= $value;
383
+ self::$values[$value]= $this;
384
+ } ' );
385
+ $ result ->out ->write ('public static function tryFrom($value) {
386
+ return self::$values[$value] ?? null;
387
+ } ' );
388
+ $ result ->out ->write ('public static function from($value) {
389
+ if ($r= self::$values[$value] ?? null) return $r;
390
+ throw new \Error(\util\Objects::stringOf($value)." is not a valid backing value for enum \"".self::class."\"");
391
+ } ' );
392
+ } else {
393
+ $ result ->out ->write ('public $name; ' );
394
+ $ result ->out ->write ('private function __construct($name) {
395
+ $this->name= $name;
396
+ } ' );
397
+ }
398
+
399
+ // Enum cases
400
+ $ result ->out ->write ('public static function cases() { return [ ' );
401
+ foreach ($ cases as $ case ) {
402
+ $ result ->out ->write ('self::$ ' .$ case ->name .', ' );
403
+ }
404
+ $ result ->out ->write (']; } ' );
405
+
406
+ // Initializations
407
+ $ result ->out ->write ('static function __init() { ' );
408
+ if ($ enum ->base ) {
409
+ foreach ($ cases as $ case ) {
410
+ $ result ->out ->write ('self::$ ' .$ case ->name .'= new self(" ' .$ case ->name .'", ' );
411
+ $ this ->emitOne ($ result , $ case ->expression );
412
+ $ result ->out ->write ('); ' );
413
+ }
414
+ } else {
415
+ foreach ($ cases as $ case ) {
416
+ $ result ->out ->write ('self::$ ' .$ case ->name .'= new self(" ' .$ case ->name .'"); ' );
417
+ }
418
+ }
419
+ $ this ->emitInitializations ($ result , $ result ->locals [0 ]);
420
+ $ this ->emitMeta ($ result , $ enum ->name , $ enum ->annotations , $ enum ->comment );
421
+ $ result ->out ->write ('}} ' .$ enum ->name .'::__init(); ' );
422
+ array_shift ($ result ->type );
423
+ }
424
+
352
425
protected function emitClass ($ result , $ class ) {
426
+ array_unshift ($ result ->type , $ class );
353
427
array_unshift ($ result ->meta , []);
354
428
$ result ->locals = [[], []];
355
429
@@ -373,6 +447,7 @@ protected function emitClass($result, $class) {
373
447
$ this ->emitInitializations ($ result , $ result ->locals [0 ]);
374
448
$ this ->emitMeta ($ result , $ class ->name , $ class ->annotations , $ class ->comment );
375
449
$ result ->out ->write ('}} ' .$ class ->name .'::__init(); ' );
450
+ array_shift ($ result ->type );
376
451
}
377
452
378
453
/** Stores lowercased, unnamespaced name in annotations for BC reasons! */
@@ -516,7 +591,7 @@ protected function emitProperty($result, $property) {
516
591
517
592
$ result ->out ->write (implode (' ' , $ property ->modifiers ).' ' .$ this ->propertyType ($ property ->type ).' $ ' .$ property ->name );
518
593
if (isset ($ property ->expression )) {
519
- if ($ this ->isConstant ($ property ->expression )) {
594
+ if ($ this ->isConstant ($ result , $ property ->expression )) {
520
595
$ result ->out ->write ('= ' );
521
596
$ this ->emitOne ($ result , $ property ->expression );
522
597
} else if (in_array ('static ' , $ property ->modifiers )) {
@@ -569,7 +644,7 @@ protected function emitMethod($result, $method) {
569
644
];
570
645
}
571
646
572
- if (isset ($ param ->default ) && !$ this ->isConstant ($ param ->default )) {
647
+ if (isset ($ param ->default ) && !$ this ->isConstant ($ result , $ param ->default )) {
573
648
$ meta [DETAIL_TARGET_ANNO ][$ param ->name ]['default ' ]= [$ param ->default ];
574
649
}
575
650
}
@@ -909,6 +984,8 @@ protected function emitScope($result, $scope) {
909
984
$ result ->out ->write (')? ' .$ t .':: ' );
910
985
$ this ->emitOne ($ result , $ scope ->member );
911
986
$ result ->out ->write (':null ' );
987
+ } else if ($ scope ->member instanceof Literal && $ result ->lookup ($ scope ->type )->rewriteEnumCase ($ scope ->member ->expression )) {
988
+ $ result ->out ->write ($ scope ->type .'::$ ' .$ scope ->member ->expression );
912
989
} else {
913
990
$ result ->out ->write ($ scope ->type .':: ' );
914
991
$ this ->emitOne ($ result , $ scope ->member );
0 commit comments