Skip to content

Commit

Permalink
TASK: Infer types in arms of ternary
Browse files Browse the repository at this point in the history
`nullableString ? nullableString : "fallback"`

will be a string
  • Loading branch information
mhsdesign committed Apr 9, 2023
1 parent 2ffe099 commit e0a9241
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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;
Expand Down Expand Up @@ -55,12 +54,8 @@ public function transpile(TypeReferenceNode $typeReferenceNode): string

private function transpileUnionType(UnionType $unionType, TypeReferenceNode $typeReferenceNode): string
{
if (count($unionType->members) === 2 && $otherMemberTypeIfOneMemberIsNullType = match (NullType::class) {
$unionType->members[0]::class => $unionType->members[1],
$unionType->members[1]::class => $unionType->members[0],
default => null
}) {
return $this->transpileNullableType($otherMemberTypeIfOneMemberIsNullType, $typeReferenceNode);
if (count($unionType->members) === 2 && $unionType->isNullable()) {
return $this->transpileNullableType($unionType->withoutNullable(), $typeReferenceNode);
}

throw new \Exception('@TODO Transpilation of complex union types is not implemented');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
namespace PackageFactory\ComponentEngine\TypeSystem\Resolver\TernaryOperation;

use PackageFactory\ComponentEngine\Parser\Ast\BooleanLiteralNode;
use PackageFactory\ComponentEngine\Parser\Ast\IdentifierNode;
use PackageFactory\ComponentEngine\Parser\Ast\TernaryOperationNode;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\Scope\ShallowScope\ShallowScope;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

Expand All @@ -41,14 +44,39 @@ public function resolveTypeOf(TernaryOperationNode $ternaryOperationNode): TypeI
$expressionTypeResolver = new ExpressionTypeResolver(
scope: $this->scope
);
$conditionNode = $ternaryOperationNode->condition->root;
$conditionNode = $ternaryOperationNode->condition;

if ($conditionNode instanceof BooleanLiteralNode) {
return $conditionNode->value
$rootType = $expressionTypeResolver->resolveTypeOf($conditionNode);

if ($conditionNode->root instanceof BooleanLiteralNode) {
return $conditionNode->root->value
? $expressionTypeResolver->resolveTypeOf($ternaryOperationNode->true)
: $expressionTypeResolver->resolveTypeOf($ternaryOperationNode->false);
}

if ($conditionNode->root instanceof IdentifierNode && $rootType instanceof UnionType && $rootType->isNullable()) {
$trueExpressionTypeResolver = new ExpressionTypeResolver(
scope: new ShallowScope(
$conditionNode->root->value,
$rootType->withoutNullable(),
$this->scope
)
);

$falseExpressionTypeResolver = new ExpressionTypeResolver(
scope: new ShallowScope(
$conditionNode->root->value,
NullType::get(),
$this->scope
)
);

return UnionType::of(
$trueExpressionTypeResolver->resolveTypeOf($ternaryOperationNode->true),
$falseExpressionTypeResolver->resolveTypeOf($ternaryOperationNode->false)
);
}

return UnionType::of(
$expressionTypeResolver->resolveTypeOf($ternaryOperationNode->true),
$expressionTypeResolver->resolveTypeOf($ternaryOperationNode->false)
Expand Down
51 changes: 51 additions & 0 deletions src/TypeSystem/Scope/ShallowScope/ShallowScope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?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\Scope\ShallowScope;

use PackageFactory\ComponentEngine\Parser\Ast\TypeReferenceNode;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class ShallowScope implements ScopeInterface
{
public function __construct(
private readonly string $overriddenName,
private readonly TypeInterface $overriddenType,
private readonly ScopeInterface $parentScope
) {
}

public function lookupTypeFor(string $name): ?TypeInterface
{
if ($this->overriddenName === $name) {
return $this->overriddenType;
}

return $this->parentScope->lookupTypeFor($name);
}

public function resolveTypeReference(TypeReferenceNode $typeReferenceNode): TypeInterface
{
return $this->parentScope->resolveTypeReference($typeReferenceNode);
}
}
23 changes: 23 additions & 0 deletions src/TypeSystem/Type/UnionType/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

namespace PackageFactory\ComponentEngine\TypeSystem\Type\UnionType;

use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;

final class UnionType implements TypeInterface
Expand All @@ -46,6 +47,28 @@ private function __construct(TypeInterface ...$members)
$this->members = $uniqueMembers;
}

public function isNullable(): bool
{
foreach ($this->members as $member) {
if ($member->is(NullType::get())) {
return true;
}
}
return false;
}

public function withoutNullable(): TypeInterface
{
$nonNullMembers = [];
foreach ($this->members as $member) {
if ($member->is(NullType::get())) {
continue;
}
$nonNullMembers[] = $member;
}
return self::of(...$nonNullMembers);
}

public static function of(TypeInterface ...$members): TypeInterface
{
$union = new self(...$members);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use PackageFactory\ComponentEngine\Parser\Ast\TernaryOperationNode;
use PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Scope\Fixtures\DummyScope;
use PackageFactory\ComponentEngine\TypeSystem\Resolver\TernaryOperation\TernaryOperationTypeResolver;
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
Expand All @@ -46,7 +47,13 @@ public function ternaryOperationExamples(): array
'1 < 2 ? variableOfTypeString : variableOfTypeNumber' => [
'1 < 2 ? variableOfTypeString : variableOfTypeNumber',
UnionType::of(NumberType::get(), StringType::get())
]
],
'nullableString ? nullableString : "fallback"' => [
'nullableString ? nullableString : "fallback"', StringType::get()
],
'nullableString ? null : nullableString' => [
'nullableString ? null : nullableString', NullType::get()
],
];
}

Expand All @@ -62,6 +69,7 @@ public function resolvesTernaryOperationToResultingType(string $ternaryExpressio
$scope = new DummyScope([
'variableOfTypeString' => StringType::get(),
'variableOfTypeNumber' => NumberType::get(),
'nullableString' => UnionType::of(StringType::get(), NullType::get())
]);
$ternaryOperationTypeResolver = new TernaryOperationTypeResolver(
scope: $scope
Expand Down

0 comments on commit e0a9241

Please sign in to comment.