Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Module/Loader/ModuleFile/ModuleFileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
use PackageFactory\ComponentEngine\Parser\Source\Source;
use PackageFactory\ComponentEngine\Parser\Tokenizer\Tokenizer;
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

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

return match ($export->declaration::class) {
ComponentDeclarationNode::class => ComponentType::fromComponentDeclarationNode($export->declaration),
EnumDeclarationNode::class => EnumType::fromEnumDeclarationNode($export->declaration),
EnumDeclarationNode::class => EnumStaticType::fromEnumDeclarationNode($export->declaration),
StructDeclarationNode::class => StructType::fromStructDeclarationNode($export->declaration)
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function transpile(ComponentDeclarationNode $componentDeclarationNode): s
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function transpile(IdentifierNode $identifierNode): string
$typeOfIdentifiedValue = $this->scope->lookupTypeFor($identifierNode->value);

return match (true) {
// @TODO: Generate Name via TypeReferenceStrategyInterface Dynamically
$typeOfIdentifiedValue instanceof EnumStaticType => $identifierNode->value,
default => '$this->' . $identifierNode->value
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
use PackageFactory\ComponentEngine\TypeSystem\Type\ComponentType\ComponentType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
Expand All @@ -33,7 +33,7 @@ interface TypeReferenceStrategyInterface
{
public function getPhpTypeReferenceForSlotType(SlotType $slotType, TypeReferenceNode $typeReferenceNode): string;
public function getPhpTypeReferenceForComponentType(ComponentType $componentType, TypeReferenceNode $typeReferenceNode): string;
public function getPhpTypeReferenceForEnumType(EnumType $enumType, TypeReferenceNode $typeReferenceNode): string;
public function getPhpTypeReferenceForEnumType(EnumStaticType $enumType, TypeReferenceNode $typeReferenceNode): string;
public function getPhpTypeReferenceForStructType(StructType $structType, TypeReferenceNode $typeReferenceNode): string;
public function getPhpTypeReferenceForCustomType(TypeInterface $customType, TypeReferenceNode $typeReferenceNode): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
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\EnumType\EnumStaticType;
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\SlotType\SlotType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
Expand All @@ -49,7 +49,7 @@ public function transpile(TypeReferenceNode $typeReferenceNode): 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),
EnumStaticType::class => $this->strategy->getPhpTypeReferenceForEnumType($type, $typeReferenceNode),
StructType::class => $this->strategy->getPhpTypeReferenceForStructType($type, $typeReferenceNode),
default => $this->strategy->getPhpTypeReferenceForCustomType($type, $typeReferenceNode)
};
Expand Down
67 changes: 67 additions & 0 deletions src/TypeSystem/Resolver/Access/AccessTypeResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

/**
* PackageFactory.ComponentEngine - Universal View Components for PHP
* Copyright (C) 2022 Contributors of PackageFactory.ComponentEngine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

declare(strict_types=1);

namespace PackageFactory\ComponentEngine\TypeSystem\Resolver\Access;


use PackageFactory\ComponentEngine\Parser\Ast\AccessNode;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StructType\StructType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
use PackageFactory\ComponentEngine\Definition\AccessType;

final class AccessTypeResolver
{
public function __construct(
private readonly ScopeInterface $scope
) {
}

public function resolveTypeOf(AccessNode $accessNode): TypeInterface
{
$expressionResolver = new ExpressionTypeResolver(scope: $this->scope);
$rootType = $expressionResolver->resolveTypeOf($accessNode->root);

return match ($rootType::class) {
EnumStaticType::class => $this->createEnumMemberType($accessNode, $rootType),
StructType::class => throw new \Exception('@TODO: StructType Access is not implemented'),
default => throw new \Exception('@TODO Error: Cannot access on type ' . $rootType::class)
};
}

private function createEnumMemberType(AccessNode $accessNode, EnumStaticType $enumType): EnumMemberType
{
if (!(
count($accessNode->chain->items) === 1
&& $accessNode->chain->items[0]->accessType === AccessType::MANDATORY
)) {
throw new \Error('@TODO Error: Enum access malformed, only one level member access is allowed.');
}

$enumMemberName = $accessNode->chain->items[0]->accessor->value;

return $enumType->getMemberType($enumMemberName);
}
}
5 changes: 5 additions & 0 deletions src/TypeSystem/Resolver/Expression/ExpressionTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

namespace PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression;

use PackageFactory\ComponentEngine\Parser\Ast\AccessNode;
use PackageFactory\ComponentEngine\Parser\Ast\BinaryOperationNode;
use PackageFactory\ComponentEngine\Parser\Ast\BooleanLiteralNode;
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
Expand All @@ -33,6 +34,7 @@
use PackageFactory\ComponentEngine\Parser\Ast\TagNode;
use PackageFactory\ComponentEngine\Parser\Ast\TemplateLiteralNode;
use PackageFactory\ComponentEngine\Parser\Ast\TernaryOperationNode;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Access\AccessTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\BinaryOperation\BinaryOperationTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\BooleanLiteral\BooleanLiteralTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Identifier\IdentifierTypeResolver;
Expand Down Expand Up @@ -81,6 +83,9 @@ public function resolveTypeOf(ExpressionNode $expressionNode): TypeInterface
TernaryOperationNode::class => (new TernaryOperationTypeResolver(
scope: $this->scope
))->resolveTypeOf($rootNode),
AccessNode::class => (new AccessTypeResolver(
scope: $this->scope
))->resolveTypeOf($rootNode),
default => throw new \Exception('@TODO: Resolve type of ' . $expressionNode->root::class)
};
}
Expand Down
47 changes: 44 additions & 3 deletions src/TypeSystem/Resolver/Match/MatchTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumMemberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
Expand Down Expand Up @@ -67,7 +68,14 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
} else {
$types = [];

$defaultArmPresent = false;
foreach ($matchNode->arms->items as $matchArmNode) {
if ($defaultArmPresent) {
throw new \Exception('@TODO: Multiple illegal default arms');
}
if ($matchArmNode->left === null) {
$defaultArmPresent = true;
}
$types[] = $expressionTypeResolver->resolveTypeOf(
$matchArmNode->right
);
Expand All @@ -79,20 +87,53 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
}
}

private function resolveTypeOfEnumMatch(MatchNode $matchNode): TypeInterface
private function resolveTypeOfEnumMatch(MatchNode $matchNode, EnumType $subjectEnumType): TypeInterface
{
$expressionTypeResolver = new ExpressionTypeResolver(
scope: $this->scope
);
$types = [];

$defaultArmPresent = false;
$referencedEnumMembers = [];

foreach ($matchNode->arms->items as $matchArmNode) {
if ($defaultArmPresent) {
throw new \Exception('@TODO Error: Multiple illegal default arms');
}
if ($matchArmNode->left === null) {
$defaultArmPresent = true;
} else {
foreach ($matchArmNode->left->items as $expressionNode) {
$enumMemberType = $expressionTypeResolver->resolveTypeOf($expressionNode);
if (!$enumMemberType instanceof EnumMemberType) {
throw new \Error('@TODO Error: Cannot match enum with type of ' . $enumMemberType::class);
}

if (!$enumMemberType->enumType->is($subjectEnumType)) {
throw new \Error('@TODO Error: incompatible enum match: got ' . $enumMemberType->enumType->enumName . ' expected ' . $subjectEnumType->enumName);
}

if (isset($referencedEnumMembers[$enumMemberType->memberName])) {
throw new \Error('@TODO Error: Enum path ' . $enumMemberType->memberName . ' was already defined once in this match and cannot be used twice');
}

$referencedEnumMembers[$enumMemberType->memberName] = true;
}
}

$types[] = $expressionTypeResolver->resolveTypeOf(
$matchArmNode->right
);
}

// @TODO: Ensure that match is complete
if (!$defaultArmPresent) {
foreach ($subjectEnumType->getMemberNames() as $member) {
if (!isset($referencedEnumMembers[$member])) {
throw new \Error('@TODO Error: member ' . $member . ' not checked');
}
}
}

return UnionType::of(...$types);
}
Expand All @@ -108,7 +149,7 @@ public function resolveTypeOf(MatchNode $matchNode): TypeInterface

return match (true) {
BooleanType::get()->is($typeOfSubject) => $this->resolveTypeOfBooleanMatch($matchNode),
$typeOfSubject instanceof EnumType => $this->resolveTypeOfEnumMatch($matchNode),
$typeOfSubject instanceof EnumType => $this->resolveTypeOfEnumMatch($matchNode, $typeOfSubject),
default => throw new \Exception('@TODO: Not handled ' . $typeOfSubject::class)
};
}
Expand Down
10 changes: 6 additions & 4 deletions src/TypeSystem/Scope/ComponentScope/ComponentScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
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\NumberType\NumberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class ComponentScope implements ScopeInterface
Expand All @@ -43,7 +41,11 @@ public function lookupTypeFor(string $name): ?TypeInterface
$propertyDeclarationNode = $this->componentDeclarationNode->propertyDeclarations->getPropertyDeclarationNodeOfName($name);
if ($propertyDeclarationNode) {
$typeReferenceNode = $propertyDeclarationNode->type;
return $this->resolveTypeReference($typeReferenceNode);
$type = $this->resolveTypeReference($typeReferenceNode);
if ($type instanceof EnumStaticType) {
$type = $type->toEnumInstanceType();
}
return $type;
}

return $this->parentScope?->lookupTypeFor($name) ?? null;
Expand Down
3 changes: 3 additions & 0 deletions src/TypeSystem/Scope/ModuleScope/ModuleScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public function __construct(

public function lookupTypeFor(string $name): ?TypeInterface
{
if ($importNode = $this->moduleNode->imports->get($name)) {
return $this->loader->resolveTypeOfImport($importNode);
}
return $this->parentScope?->lookupTypeFor($name) ?? null;
}

Expand Down
40 changes: 40 additions & 0 deletions src/TypeSystem/Type/EnumType/EnumMemberType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/**
* PackageFactory.ComponentEngine - Universal View Components for PHP
* Copyright (C) 2022 Contributors of PackageFactory.ComponentEngine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

declare(strict_types=1);

namespace PackageFactory\ComponentEngine\TypeSystem\Type\EnumType;

use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class EnumMemberType implements TypeInterface
{
public function __construct(
public readonly EnumType|EnumStaticType $enumType,
public readonly string $memberName,
public readonly ?TypeInterface $memberBackedValueType
) {
}

public function is(TypeInterface $other): bool
{
return $this->memberBackedValueType?->is($other) ?? false;
}
}
18 changes: 4 additions & 14 deletions src/TypeSystem/Type/EnumType/EnumStaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,14 @@

namespace PackageFactory\ComponentEngine\TypeSystem\Type\EnumType;

use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class EnumStaticType implements TypeInterface
{
private function __construct(public readonly string $enumName)
use EnumTrait;

public function toEnumInstanceType(): EnumType
{
}

public static function fromEnumDeclarationNode(EnumDeclarationNode $enumDeclarationNode): self
{
return new self(
enumName: $enumDeclarationNode->enumName
);
}

public function is(TypeInterface $other): bool
{
return false;
return new EnumType($this->enumName, $this->membersWithType);
}
}
Loading