Skip to content

Commit 86e1e61

Browse files
committed
FEATURE: WIP Enums ensure that match is complete
- reimplement #9 cleaner - enum static will be converted to enum instance in ComponentScope, but enum static is the imported type - Remove global ButtonType hack in the tests and import it correctly in the Match.afx - ModuleScope::lookupTypeFor didnt work for imported enums (IdentifierResolver Failed) Match related: - multiple default arms -> throws - if there is no default arm and not all enum members are listed -> throws - if enum members are listed twice -> throws - if enum is matched against non enum member -> throws - if enum is matched against other enum -> throws
1 parent 7c4879b commit 86e1e61

File tree

19 files changed

+251
-52
lines changed

19 files changed

+251
-52
lines changed

src/Module/Loader/ModuleFile/ModuleFileLoader.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
use PackageFactory\ComponentEngine\Parser\Source\Source;
3333
use PackageFactory\ComponentEngine\Parser\Tokenizer\Tokenizer;
3434
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
35-
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
35+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
3636
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
3737
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3838

@@ -56,7 +56,7 @@ public function resolveTypeOfImport(ImportNode $importNode): TypeInterface
5656

5757
return match ($export->declaration::class) {
5858
ComponentDeclarationNode::class => ComponentType::fromComponentDeclarationNode($export->declaration),
59-
EnumDeclarationNode::class => EnumType::fromEnumDeclarationNode($export->declaration),
59+
EnumDeclarationNode::class => EnumStaticType::fromEnumDeclarationNode($export->declaration),
6060
StructDeclarationNode::class => StructType::fromStructDeclarationNode($export->declaration)
6161
};
6262
}

src/Target/Php/Transpiler/ComponentDeclaration/ComponentDeclarationTranspiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function transpile(ComponentDeclarationNode $componentDeclarationNode): s
6060
}
6161

6262
foreach ($this->module->imports->items as $importNode) {
63-
// @TODO: Generate Namespaces Dynamically
63+
// @TODO: Generate Namespaces + Name via TypeReferenceStrategyInterface Dynamically
6464
$lines[] = 'use Vendor\\Project\\Component\\' . $importNode->name->value . ';';
6565
}
6666

src/Target/Php/Transpiler/Identifier/IdentifierTranspiler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function transpile(IdentifierNode $identifierNode): string
3737
$typeOfIdentifiedValue = $this->scope->lookupTypeFor($identifierNode->value);
3838

3939
return match (true) {
40+
// @TODO: Generate Name via TypeReferenceStrategyInterface Dynamically
4041
$typeOfIdentifiedValue instanceof EnumStaticType => $identifierNode->value,
4142
default => '$this->' . $identifierNode->value
4243
};

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
2626
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
27-
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
27+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
2828
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
2929
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
3030
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
@@ -33,7 +33,7 @@ interface TypeReferenceStrategyInterface
3333
{
3434
public function getPhpTypeReferenceForSlotType(SlotType $slotType, TypeReferenceNode $typeReferenceNode): string;
3535
public function getPhpTypeReferenceForComponentType(ComponentType $componentType, TypeReferenceNode $typeReferenceNode): string;
36-
public function getPhpTypeReferenceForEnumType(EnumType $enumType, TypeReferenceNode $typeReferenceNode): string;
36+
public function getPhpTypeReferenceForEnumType(EnumStaticType $enumType, TypeReferenceNode $typeReferenceNode): string;
3737
public function getPhpTypeReferenceForStructType(StructType $structType, TypeReferenceNode $typeReferenceNode): string;
3838
public function getPhpTypeReferenceForCustomType(TypeInterface $customType, TypeReferenceNode $typeReferenceNode): string;
3939
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
2727
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
2828
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
29-
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
29+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
3030
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
3131
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
3232
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
@@ -49,7 +49,7 @@ public function transpile(TypeReferenceNode $typeReferenceNode): string
4949
BooleanType::class => 'bool',
5050
SlotType::class => $this->strategy->getPhpTypeReferenceForSlotType($type, $typeReferenceNode),
5151
ComponentType::class => $this->strategy->getPhpTypeReferenceForComponentType($type, $typeReferenceNode),
52-
EnumType::class => $this->strategy->getPhpTypeReferenceForEnumType($type, $typeReferenceNode),
52+
EnumStaticType::class => $this->strategy->getPhpTypeReferenceForEnumType($type, $typeReferenceNode),
5353
StructType::class => $this->strategy->getPhpTypeReferenceForStructType($type, $typeReferenceNode),
5454
default => $this->strategy->getPhpTypeReferenceForCustomType($type, $typeReferenceNode)
5555
};

src/TypeSystem/Resolver/Match/MatchTypeResolver.php

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@
2222

2323
namespace PackageFactory\ComponentEngine\TypeSystem\Resolver\Match;
2424

25+
use PackageFactory\ComponentEngine\Parser\Ast\AccessNode;
2526
use PackageFactory\ComponentEngine\Parser\Ast\BooleanLiteralNode;
2627
use PackageFactory\ComponentEngine\Parser\Ast\MatchNode;
28+
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Access\AccessTypeResolver;
2729
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver;
2830
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
2931
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
32+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
33+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
3034
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
3135
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
3236
use 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
}

src/TypeSystem/Scope/ComponentScope/ComponentScope.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
use PackageFactory\ComponentEngine\Parser\Ast\ComponentDeclarationNode;
2626
use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
2727
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
28-
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
29-
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
30-
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
28+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
3129
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3230

3331
final class ComponentScope implements ScopeInterface
@@ -43,7 +41,11 @@ public function lookupTypeFor(string $name): ?TypeInterface
4341
$propertyDeclarationNode = $this->componentDeclarationNode->propertyDeclarations->getPropertyDeclarationNodeOfName($name);
4442
if ($propertyDeclarationNode) {
4543
$typeReferenceNode = $propertyDeclarationNode->type;
46-
return $this->resolveTypeReference($typeReferenceNode);
44+
$type = $this->resolveTypeReference($typeReferenceNode);
45+
if ($type instanceof EnumStaticType) {
46+
$type = $type->toEnumInstanceType();
47+
}
48+
return $type;
4749
}
4850

4951
return $this->parentScope?->lookupTypeFor($name) ?? null;

src/TypeSystem/Scope/ModuleScope/ModuleScope.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public function __construct(
3939

4040
public function lookupTypeFor(string $name): ?TypeInterface
4141
{
42+
if ($importNode = $this->moduleNode->imports->get($name)) {
43+
return $this->loader->resolveTypeOfImport($importNode);
44+
}
4245
return $this->parentScope?->lookupTypeFor($name) ?? null;
4346
}
4447

src/TypeSystem/Type/EnumType/EnumStaticType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,9 @@
2727
final class EnumStaticType implements TypeInterface
2828
{
2929
use EnumTrait;
30+
31+
public function toEnumInstanceType(): EnumType
32+
{
33+
return new EnumType($this->enumName, $this->membersWithType);
34+
}
3035
}

src/TypeSystem/Type/EnumType/EnumTrait.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
2626
use PackageFactory\ComponentEngine\Parser\Ast\NumberLiteralNode;
2727
use PackageFactory\ComponentEngine\Parser\Ast\StringLiteralNode;
28-
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
2928
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
3029
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
3130
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3231

3332
trait EnumTrait
3433
{
35-
private function __construct(
34+
public function __construct(
3635
public readonly string $enumName,
3736
private readonly array $membersWithType,
3837
) {
@@ -58,10 +57,15 @@ enumName: $enumDeclarationNode->enumName,
5857
membersWithType: $membersWithType
5958
);
6059
}
60+
61+
public function getMemberNames(): array
62+
{
63+
return array_keys($this->membersWithType);
64+
}
6165

6266
public function getMemberType(string $memberName): EnumMemberType
6367
{
64-
if (!isset($this->membersWithType[$memberName])) {
68+
if (!array_key_exists($memberName, $this->membersWithType)) {
6569
throw new \Exception('@TODO cannot access member ' . $memberName . ' of enum ' . $this->enumName);
6670
}
6771
return new EnumMemberType(

0 commit comments

Comments
 (0)