Skip to content

Commit

Permalink
TASK: Simple Nullable Handling
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
mhsdesign committed Apr 9, 2023
1 parent 7efcfd3 commit 3074b07
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class TypeReferenceTranspiler
{
Expand All @@ -43,22 +46,51 @@ public function __construct(
public function transpile(TypeReferenceNode $typeReferenceNode): string
{
$type = $this->scope->resolveTypeReference($typeReferenceNode);
$phpTypeReference = match ($type::class) {

return match ($type::class) {
UnionType::class => $this->transpileUnionType($type, $typeReferenceNode),
default => $this->transpileNonUnionType($type, $typeReferenceNode)
};
}

private function transpileUnionType(UnionType $unionType, TypeReferenceNode $typeReferenceNode): string
{
if (count($unionType->members) === 2) {
$otherTypeIfTypeIsNullable = match (true) {
$unionType->members[0]->is(NullType::get()) => $unionType->members[1],
$unionType->members[1]->is(NullType::get()) => $unionType->members[0],
default => null
};

if ($otherTypeIfTypeIsNullable) {
return $this->transpileNullableType($otherTypeIfTypeIsNullable, $typeReferenceNode);
}
}

throw new \Exception('@TODO Transpilation of complex union types is not implemented');

}

private function transpileNonUnionType(TypeInterface $type, TypeReferenceNode $typeReferenceNode): string
{
return match ($type::class) {
NumberType::class => 'int|float',
StringType::class => 'string',
BooleanType::class => 'bool',
SlotType::class => $this->strategy->getPhpTypeReferenceForSlotType($type, $typeReferenceNode),
ComponentType::class => $this->strategy->getPhpTypeReferenceForComponentType($type, $typeReferenceNode),
EnumType::class => $this->strategy->getPhpTypeReferenceForEnumType($type, $typeReferenceNode),
StructType::class => $this->strategy->getPhpTypeReferenceForStructType($type, $typeReferenceNode),
UnionType::class => throw new \Exception("@TODO: Cannot transpile nested union"),
default => $this->strategy->getPhpTypeReferenceForCustomType($type, $typeReferenceNode)
};
}

return $typeReferenceNode->isOptional
? match ($phpTypeReference) {
'int|float' => 'null|int|float',
default => '?' . $phpTypeReference
}
: $phpTypeReference;
private function transpileNullableType(TypeInterface $type, TypeReferenceNode $typeReferenceNode): string
{
if ($type->is(NumberType::get())) {
return 'null|int|float';
}
return '?' . $this->transpileNonUnionType($type, $typeReferenceNode);
}
}
9 changes: 7 additions & 2 deletions src/TypeSystem/Scope/GlobalScope/GlobalScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@

namespace PackageFactory\ComponentEngine\TypeSystem\Scope\GlobalScope;

use PackageFactory\ComponentEngine\Parser\Ast\ComponentDeclarationNode;
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

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

public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
{
return match ($typeReferenceNode->name) {
$type = match ($typeReferenceNode->name) {
'string' => StringType::get(),
'number' => NumberType::get(),
'boolean' => BooleanType::get(),
'slot' => SlotType::get(),
default => throw new \Exception('@TODO: Unknown Type ' . $typeReferenceNode->name)
};
if ($typeReferenceNode->isOptional) {
$type = UnionType::of($type, NullType::get());
}
return $type;
}
}
8 changes: 7 additions & 1 deletion src/TypeSystem/Scope/ModuleScope/ModuleScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
use PackageFactory\ComponentEngine\Parser\Ast\ModuleNode;
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class ModuleScope implements ScopeInterface
Expand All @@ -45,7 +47,11 @@ public function lookupTypeFor(string $name): ?TypeInterface
public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
{
if ($importNode = $this->moduleNode->imports->get($typeReferenceNode->name)) {
return $this->loader->resolveTypeOfImport($importNode);
$type = $this->loader->resolveTypeOfImport($importNode);
if ($typeReferenceNode->isOptional) {
$type = UnionType::of($type, NullType::get());
}
return $type;
}

if ($this->parentScope) {
Expand Down
2 changes: 1 addition & 1 deletion src/TypeSystem/Type/UnionType/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ final class UnionType implements TypeInterface
/**
* @var TypeInterface[]
*/
private array $members;
public array $members;

private function __construct(TypeInterface ...$members)
{
Expand Down
5 changes: 5 additions & 0 deletions test/Unit/TypeSystem/Scope/Fixtures/DummyScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class DummyScope implements ScopeInterface
Expand All @@ -46,6 +48,9 @@ public function lookupTypeFor(string $name): ?TypeInterface
public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
{
if ($type = $this->typeNameToTypeMap[$typeReferenceNode->name] ?? null) {
if ($typeReferenceNode->isOptional) {
$type = UnionType::of($type, NullType::get());
}
return $type;
}

Expand Down

0 comments on commit 3074b07

Please sign in to comment.