Skip to content

Commit 3074b07

Browse files
committed
TASK: Simple Nullable Handling
- Scopes will check if $typeReferenceNode->isOptional and if so, wrap the type in a union with null - The TypeReferenceTranspiler can now transpile those simple unions, by looking into its members
1 parent 7efcfd3 commit 3074b07

File tree

5 files changed

+59
-11
lines changed

5 files changed

+59
-11
lines changed

src/Target/Php/Transpiler/TypeReference/TypeReferenceTranspiler.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@
2727
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
2828
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
2929
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
30+
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
3031
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
3132
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
3233
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
3334
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
35+
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
36+
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3437

3538
final class TypeReferenceTranspiler
3639
{
@@ -43,22 +46,51 @@ public function __construct(
4346
public function transpile(TypeReferenceNode $typeReferenceNode): string
4447
{
4548
$type = $this->scope->resolveTypeReference($typeReferenceNode);
46-
$phpTypeReference = match ($type::class) {
49+
50+
return match ($type::class) {
51+
UnionType::class => $this->transpileUnionType($type, $typeReferenceNode),
52+
default => $this->transpileNonUnionType($type, $typeReferenceNode)
53+
};
54+
}
55+
56+
private function transpileUnionType(UnionType $unionType, TypeReferenceNode $typeReferenceNode): string
57+
{
58+
if (count($unionType->members) === 2) {
59+
$otherTypeIfTypeIsNullable = match (true) {
60+
$unionType->members[0]->is(NullType::get()) => $unionType->members[1],
61+
$unionType->members[1]->is(NullType::get()) => $unionType->members[0],
62+
default => null
63+
};
64+
65+
if ($otherTypeIfTypeIsNullable) {
66+
return $this->transpileNullableType($otherTypeIfTypeIsNullable, $typeReferenceNode);
67+
}
68+
}
69+
70+
throw new \Exception('@TODO Transpilation of complex union types is not implemented');
71+
72+
}
73+
74+
private function transpileNonUnionType(TypeInterface $type, TypeReferenceNode $typeReferenceNode): string
75+
{
76+
return match ($type::class) {
4777
NumberType::class => 'int|float',
4878
StringType::class => 'string',
4979
BooleanType::class => 'bool',
5080
SlotType::class => $this->strategy->getPhpTypeReferenceForSlotType($type, $typeReferenceNode),
5181
ComponentType::class => $this->strategy->getPhpTypeReferenceForComponentType($type, $typeReferenceNode),
5282
EnumType::class => $this->strategy->getPhpTypeReferenceForEnumType($type, $typeReferenceNode),
5383
StructType::class => $this->strategy->getPhpTypeReferenceForStructType($type, $typeReferenceNode),
84+
UnionType::class => throw new \Exception("@TODO: Cannot transpile nested union"),
5485
default => $this->strategy->getPhpTypeReferenceForCustomType($type, $typeReferenceNode)
5586
};
87+
}
5688

57-
return $typeReferenceNode->isOptional
58-
? match ($phpTypeReference) {
59-
'int|float' => 'null|int|float',
60-
default => '?' . $phpTypeReference
61-
}
62-
: $phpTypeReference;
89+
private function transpileNullableType(TypeInterface $type, TypeReferenceNode $typeReferenceNode): string
90+
{
91+
if ($type->is(NumberType::get())) {
92+
return 'null|int|float';
93+
}
94+
return '?' . $this->transpileNonUnionType($type, $typeReferenceNode);
6395
}
6496
}

src/TypeSystem/Scope/GlobalScope/GlobalScope.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222

2323
namespace PackageFactory\ComponentEngine\TypeSystem\Scope\GlobalScope;
2424

25-
use PackageFactory\ComponentEngine\Parser\Ast\ComponentDeclarationNode;
2625
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
2726
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
2827
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
28+
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
2929
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
3030
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
3131
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
32+
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
3233
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3334

3435
final class GlobalScope implements ScopeInterface
@@ -51,12 +52,16 @@ public function lookupTypeFor(string $name): ?TypeInterface
5152

5253
public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
5354
{
54-
return match ($typeReferenceNode->name) {
55+
$type = match ($typeReferenceNode->name) {
5556
'string' => StringType::get(),
5657
'number' => NumberType::get(),
5758
'boolean' => BooleanType::get(),
5859
'slot' => SlotType::get(),
5960
default => throw new \Exception('@TODO: Unknown Type ' . $typeReferenceNode->name)
6061
};
62+
if ($typeReferenceNode->isOptional) {
63+
$type = UnionType::of($type, NullType::get());
64+
}
65+
return $type;
6166
}
6267
}

src/TypeSystem/Scope/ModuleScope/ModuleScope.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
use PackageFactory\ComponentEngine\Parser\Ast\ModuleNode;
2727
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
2828
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
29+
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
30+
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
2931
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3032

3133
final class ModuleScope implements ScopeInterface
@@ -45,7 +47,11 @@ public function lookupTypeFor(string $name): ?TypeInterface
4547
public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
4648
{
4749
if ($importNode = $this->moduleNode->imports->get($typeReferenceNode->name)) {
48-
return $this->loader->resolveTypeOfImport($importNode);
50+
$type = $this->loader->resolveTypeOfImport($importNode);
51+
if ($typeReferenceNode->isOptional) {
52+
$type = UnionType::of($type, NullType::get());
53+
}
54+
return $type;
4955
}
5056

5157
if ($this->parentScope) {

src/TypeSystem/Type/UnionType/UnionType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class UnionType implements TypeInterface
2929
/**
3030
* @var TypeInterface[]
3131
*/
32-
private array $members;
32+
public array $members;
3333

3434
private function __construct(TypeInterface ...$members)
3535
{

test/Unit/TypeSystem/Scope/Fixtures/DummyScope.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
2626
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
27+
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
28+
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
2729
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
2830

2931
final class DummyScope implements ScopeInterface
@@ -46,6 +48,9 @@ public function lookupTypeFor(string $name): ?TypeInterface
4648
public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
4749
{
4850
if ($type = $this->typeNameToTypeMap[$typeReferenceNode->name] ?? null) {
51+
if ($typeReferenceNode->isOptional) {
52+
$type = UnionType::of($type, NullType::get());
53+
}
4954
return $type;
5055
}
5156

0 commit comments

Comments
 (0)