Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 8 additions & 2 deletions src/Module/Loader/ModuleFile/ModuleFileLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
namespace PackageFactory\ComponentEngine\Module\Loader\ModuleFile;

use PackageFactory\ComponentEngine\Module\LoaderInterface;
use PackageFactory\ComponentEngine\Module\ModuleId;
use PackageFactory\ComponentEngine\Parser\Ast\ComponentDeclarationNode;
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
use PackageFactory\ComponentEngine\Parser\Ast\ImportNode;
Expand All @@ -32,7 +33,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 @@ -54,9 +55,14 @@ public function resolveTypeOfImport(ImportNode $importNode): TypeInterface
);
}

$moduleId = ModuleId::fromSource($source);

return match ($export->declaration::class) {
ComponentDeclarationNode::class => ComponentType::fromComponentDeclarationNode($export->declaration),
EnumDeclarationNode::class => EnumType::fromEnumDeclarationNode($export->declaration),
EnumDeclarationNode::class => EnumStaticType::fromModuleIdAndDeclaration(
$moduleId,
$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\EnumInstanceType;
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->createEnumInstanceMemberType($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 createEnumInstanceMemberType(AccessNode $accessNode, EnumStaticType $enumType): EnumInstanceType
{
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
52 changes: 48 additions & 4 deletions src/TypeSystem/Resolver/Match/MatchTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +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\EnumType;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumInstanceType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

Expand Down Expand Up @@ -67,7 +67,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 +86,57 @@ private function resolveTypeOfBooleanMatch(MatchNode $matchNode): TypeInterface
}
}

private function resolveTypeOfEnumMatch(MatchNode $matchNode): TypeInterface
private function resolveTypeOfEnumMatch(MatchNode $matchNode, EnumInstanceType $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 EnumInstanceType) {
throw new \Error('@TODO Error: Cannot match enum with type of ' . $enumMemberType::class);
}

if ($enumMemberType->isUnspecified()) {
throw new \Error('@TODO Error: Matching enum value should be referenced statically');
}

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

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

$referencedEnumMembers[$enumMemberType->getMemberName()] = true;
}
}

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

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

return UnionType::of(...$types);
}
Expand All @@ -108,7 +152,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 EnumInstanceType => $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);
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);
}

Expand Down
75 changes: 75 additions & 0 deletions src/TypeSystem/Type/EnumType/EnumInstanceType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?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 EnumInstanceType implements TypeInterface
{
private function __construct(
public readonly EnumStaticType $enumStaticType,
private readonly ?string $memberName
) {
if ($memberName !== null && !$enumStaticType->hasMember($memberName)) {
throw new \Exception('@TODO cannot access member ' . $memberName . ' of enum ' . $enumStaticType->enumName);
}
}

public static function createUnspecifiedEnumInstanceType(EnumStaticType $enumStaticType): self
{
return new self(
enumStaticType: $enumStaticType,
memberName: null
);
}

public static function fromStaticEnumTypeAndMemberName(EnumStaticType $enumStaticType, string $enumMemberName): self
{
return new self(
enumStaticType: $enumStaticType,
memberName: $enumMemberName
);
}

public function isUnspecified(): bool
{
return $this->memberName === null;
}

public function getMemberName(): string
{
return $this->memberName ?? throw new \Exception('@TODO Error cannot access memberName of unspecified instance');
}

public function is(TypeInterface $other): bool
{
if ($other === $this) {
return true;
}
if ($other instanceof EnumInstanceType) {
return $other->enumStaticType->is($other->enumStaticType)
&& $other->memberName === $this->memberName;
}
return false;
}
}
Loading