Skip to content

Commit 7c4879b

Browse files
committed
TASK: WIP add EnumMemberType
- fail if enum doesnt have this property - resolve property value type
1 parent 5fc4bf1 commit 7c4879b

File tree

6 files changed

+202
-39
lines changed

6 files changed

+202
-39
lines changed

src/TypeSystem/Resolver/Access/AccessTypeResolver.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
use PackageFactory\ComponentEngine\Parser\Ast\AccessNode;
2727
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver;
2828
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
29+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
2930
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
3031
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
3132
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
3233
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
34+
use PackageFactory\ComponentEngine\Definition\AccessType;
3335

3436
final class AccessTypeResolver
3537
{
@@ -42,11 +44,25 @@ public function resolveTypeOf(AccessNode $accessNode): TypeInterface
4244
{
4345
$expressionResolver = new ExpressionTypeResolver(scope: $this->scope);
4446
$rootType = $expressionResolver->resolveTypeOf($accessNode->root);
45-
46-
if (!$rootType instanceof EnumType || !$rootType instanceof EnumStaticType || !$rootType instanceof StructType) {
47-
throw new \Exception('@TODO: Cannot access on type ' . $rootType::class);
47+
48+
return match ($rootType::class) {
49+
EnumType::class, EnumStaticType::class => $this->createEnumMemberType($accessNode, $rootType),
50+
StructType::class => throw new \Exception('@TODO: StructType Access is not implemented'),
51+
default => throw new \Exception('@TODO Error: Cannot access on type ' . $rootType::class)
52+
};
53+
}
54+
55+
private function createEnumMemberType(AccessNode $accessNode, EnumType|EnumStaticType $enumType): EnumMemberType
56+
{
57+
if (!(
58+
count($accessNode->chain->items) === 1
59+
&& $accessNode->chain->items[0]->accessType === AccessType::MANDATORY
60+
)) {
61+
throw new \Error('@TODO Error: Enum access malformed, only one level member access is allowed.');
4862
}
63+
64+
$enumMemberName = $accessNode->chain->items[0]->accessor->value;
4965

50-
throw new \Exception('@TODO: Enum and StructType Access is not implemented');
66+
return $enumType->getMemberType($enumMemberName);
5167
}
5268
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/**
4+
* PackageFactory.ComponentEngine - Universal View Components for PHP
5+
* Copyright (C) 2022 Contributors of PackageFactory.ComponentEngine
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace PackageFactory\ComponentEngine\TypeSystem\Type\EnumType;
24+
25+
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
26+
27+
final class EnumMemberType implements TypeInterface
28+
{
29+
public function __construct(
30+
public readonly EnumType|EnumStaticType $enumType,
31+
public readonly string $memberName,
32+
public readonly ?TypeInterface $memberValueType
33+
) {
34+
}
35+
36+
public function is(TypeInterface $other): bool
37+
{
38+
return $this->memberValueType?->is($other) ?? false;
39+
}
40+
}

src/TypeSystem/Type/EnumType/EnumStaticType.php

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,9 @@
2222

2323
namespace PackageFactory\ComponentEngine\TypeSystem\Type\EnumType;
2424

25-
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
2625
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
2726

2827
final class EnumStaticType implements TypeInterface
2928
{
30-
private function __construct(public readonly string $enumName)
31-
{
32-
}
33-
34-
public static function fromEnumDeclarationNode(EnumDeclarationNode $enumDeclarationNode): self
35-
{
36-
return new self(
37-
enumName: $enumDeclarationNode->enumName
38-
);
39-
}
40-
41-
public function is(TypeInterface $other): bool
42-
{
43-
return false;
44-
}
29+
use EnumTrait;
4530
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/**
4+
* PackageFactory.ComponentEngine - Universal View Components for PHP
5+
* Copyright (C) 2022 Contributors of PackageFactory.ComponentEngine
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace PackageFactory\ComponentEngine\TypeSystem\Type\EnumType;
24+
25+
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
26+
use PackageFactory\ComponentEngine\Parser\Ast\NumberLiteralNode;
27+
use PackageFactory\ComponentEngine\Parser\Ast\StringLiteralNode;
28+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
29+
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
30+
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
31+
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
32+
33+
trait EnumTrait
34+
{
35+
private function __construct(
36+
public readonly string $enumName,
37+
private readonly array $membersWithType,
38+
) {
39+
}
40+
41+
public static function fromEnumDeclarationNode(EnumDeclarationNode $enumDeclarationNode): self
42+
{
43+
$membersWithType = [];
44+
45+
foreach ($enumDeclarationNode->memberDeclarations->items as $memberDeclarationNode) {
46+
$membersWithType[$memberDeclarationNode->name] = match ($memberDeclarationNode->value
47+
? $memberDeclarationNode->value::class
48+
: null
49+
) {
50+
StringLiteralNode::class => StringType::get(),
51+
NumberLiteralNode::class => NumberType::get(),
52+
null => null
53+
};
54+
}
55+
56+
return new self(
57+
enumName: $enumDeclarationNode->enumName,
58+
membersWithType: $membersWithType
59+
);
60+
}
61+
62+
public function getMemberType(string $memberName): EnumMemberType
63+
{
64+
if (!isset($this->membersWithType[$memberName])) {
65+
throw new \Exception('@TODO cannot access member ' . $memberName . ' of enum ' . $this->enumName);
66+
}
67+
return new EnumMemberType(
68+
$this,
69+
$memberName,
70+
$this->membersWithType[$memberName]
71+
);
72+
}
73+
74+
public function is(TypeInterface $other): bool
75+
{
76+
// todo more satisfied check with namespace taken into account
77+
return match ($other::class) {
78+
EnumType::class, EnumStaticType::class => $this->enumName === $other->enumName,
79+
default => false
80+
};
81+
}
82+
}

src/TypeSystem/Type/EnumType/EnumType.php

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,9 @@
2222

2323
namespace PackageFactory\ComponentEngine\TypeSystem\Type\EnumType;
2424

25-
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
2625
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
2726

2827
final class EnumType implements TypeInterface
2928
{
30-
private function __construct(public readonly string $enumName)
31-
{
32-
}
33-
34-
public static function fromEnumDeclarationNode(EnumDeclarationNode $enumDeclarationNode): self
35-
{
36-
return new self(
37-
enumName: $enumDeclarationNode->enumName
38-
);
39-
}
40-
41-
public function is(TypeInterface $other): bool
42-
{
43-
return false;
44-
}
29+
use EnumTrait;
4530
}

test/Unit/TypeSystem/Resolver/Access/AccessTypeResolverTest.php

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@
2323
namespace PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Resolver\Access;
2424

2525
use PackageFactory\ComponentEngine\Parser\Ast\AccessNode;
26+
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
2627
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
2728
use PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Scope\Fixtures\DummyScope;
2829
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Access\AccessTypeResolver;
30+
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
31+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
32+
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
2933
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
34+
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
3035
use PHPUnit\Framework\TestCase;
3136

3237
final class AccessTypeResolverTest extends TestCase
@@ -35,8 +40,52 @@ public function invalidAccessExamples(): iterable
3540
{
3641
yield 'access property on primitive string' => [
3742
'someString.bar',
38-
'@TODO: Cannot access on type PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType'
43+
'@TODO Error: Cannot access on type PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType'
3944
];
45+
46+
yield 'access invalid property on enum' => [
47+
'SomeEnum.NonExistent',
48+
'@TODO cannot access member NonExistent of enum SomeEnum'
49+
];
50+
}
51+
52+
private function resolveAccessType(string $accessAsString, ScopeInterface $scope): TypeInterface
53+
{
54+
$accessTypeResolver = new AccessTypeResolver(
55+
scope: $scope
56+
);
57+
$accessNode = ExpressionNode::fromString($accessAsString)->root;
58+
assert($accessNode instanceof AccessNode);
59+
return $accessTypeResolver->resolveTypeOf($accessNode);
60+
}
61+
62+
/**
63+
* @test
64+
*/
65+
public function access(): void
66+
{
67+
$someEnum = EnumStaticType::fromEnumDeclarationNode(
68+
EnumDeclarationNode::fromString(
69+
'enum SomeEnum { A("Hi") }'
70+
)
71+
);
72+
73+
$scope = new DummyScope([
74+
'SomeEnum' => $someEnum
75+
]);
76+
77+
$accessType = $this->resolveAccessType(
78+
'SomeEnum.A',
79+
$scope
80+
);
81+
82+
$this->assertInstanceOf(EnumMemberType::class, $accessType);
83+
84+
$this->assertTrue($accessType->enumType->is($someEnum));
85+
86+
$this->assertEquals("A", $accessType->memberName);
87+
88+
$this->assertTrue($accessType->memberValueType->is(StringType::get()));
4089
}
4190

4291
/**
@@ -46,9 +95,15 @@ public function invalidAccessExamples(): iterable
4695
public function invalidAccessResultsInError(string $accessAsString, string $expectedErrorMessage): void
4796
{
4897
$this->expectExceptionMessage($expectedErrorMessage);
49-
98+
99+
$someEnum = EnumStaticType::fromEnumDeclarationNode(
100+
EnumDeclarationNode::fromString(
101+
'enum SomeEnum { A }'
102+
)
103+
);
50104
$scope = new DummyScope([
51-
'someString' => StringType::get()
105+
'someString' => StringType::get(),
106+
'SomeEnum' => $someEnum
52107
]);
53108
$accessTypeResolver = new AccessTypeResolver(
54109
scope: $scope

0 commit comments

Comments
 (0)