Skip to content

Commit bf49cfe

Browse files
committed
Implement Null Literals
1 parent 3565f03 commit bf49cfe

12 files changed

Lines changed: 354 additions & 1 deletion

File tree

src/Parser/Ast/ExpressionNode.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
final class ExpressionNode implements \JsonSerializable
3434
{
3535
private function __construct(
36-
public readonly IdentifierNode | NumberLiteralNode | BinaryOperationNode | AccessNode | TernaryOperationNode | TagNode | StringLiteralNode | MatchNode | TemplateLiteralNode | BooleanLiteralNode $root
36+
public readonly IdentifierNode | NumberLiteralNode | BinaryOperationNode | AccessNode | TernaryOperationNode | TagNode | StringLiteralNode | MatchNode | TemplateLiteralNode | BooleanLiteralNode | NullLiteralNode $root
3737
) {
3838
}
3939

@@ -94,6 +94,9 @@ public static function fromTokens(\Iterator $tokens, Precedence $precedence = Pr
9494
case TokenType::KEYWORD_FALSE:
9595
$root = BooleanLiteralNode::fromTokens($tokens);
9696
break;
97+
case TokenType::KEYWORD_NULL:
98+
$root = NullLiteralNode::fromTokens($tokens);
99+
break;
97100
case TokenType::KEYWORD_MATCH:
98101
$root = MatchNode::fromTokens($tokens);
99102
break;

src/Parser/Ast/NullLiteralNode.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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\Parser\Ast;
24+
25+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Scanner;
26+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;
27+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType;
28+
29+
final class NullLiteralNode implements \JsonSerializable
30+
{
31+
private function __construct()
32+
{
33+
}
34+
35+
/**
36+
* @param \Iterator<mixed,Token> $tokens
37+
* @return self
38+
*/
39+
public static function fromTokens(\Iterator $tokens): self
40+
{
41+
Scanner::assertType($tokens, TokenType::KEYWORD_NULL);
42+
Scanner::skipOne($tokens);
43+
44+
return new self();
45+
}
46+
47+
public function jsonSerialize(): mixed
48+
{
49+
return [
50+
'type' => 'NullLiteralNode'
51+
];
52+
}
53+
}

src/Parser/Tokenizer/TokenType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum TokenType: string
3939
case KEYWORD_RETURN = 'KEYWORD_RETURN';
4040
case KEYWORD_TRUE = 'KEYWORD_TRUE';
4141
case KEYWORD_FALSE = 'KEYWORD_FALSE';
42+
case KEYWORD_NULL = 'KEYWORD_NULL';
4243

4344
case CONSTANT = 'CONSTANT';
4445

@@ -114,6 +115,7 @@ public static function fromBuffer(Buffer $buffer): TokenType
114115
$value === 'return' => self::KEYWORD_RETURN,
115116
$value === 'true' => self::KEYWORD_TRUE,
116117
$value === 'false' => self::KEYWORD_FALSE,
118+
$value === 'null' => self::KEYWORD_NULL,
117119

118120
(bool) preg_match(
119121
'/^0[bB][0-1]+$/',

src/Target/Php/Transpiler/Expression/ExpressionTranspiler.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
2929
use PackageFactory\ComponentEngine\Parser\Ast\IdentifierNode;
3030
use PackageFactory\ComponentEngine\Parser\Ast\MatchNode;
31+
use PackageFactory\ComponentEngine\Parser\Ast\NullLiteralNode;
3132
use PackageFactory\ComponentEngine\Parser\Ast\NumberLiteralNode;
3233
use PackageFactory\ComponentEngine\Parser\Ast\StringLiteralNode;
3334
use PackageFactory\ComponentEngine\Parser\Ast\TagNode;
@@ -38,6 +39,7 @@
3839
use PackageFactory\ComponentEngine\Target\Php\Transpiler\BooleanLiteral\BooleanLiteralTranspiler;
3940
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Identifier\IdentifierTranspiler;
4041
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Match\MatchTranspiler;
42+
use PackageFactory\ComponentEngine\Target\Php\Transpiler\NullLiteral\NullLiteralTranspiler;
4143
use PackageFactory\ComponentEngine\Target\Php\Transpiler\NumberLiteral\NumberLiteralTranspiler;
4244
use PackageFactory\ComponentEngine\Target\Php\Transpiler\StringLiteral\StringLiteralTranspiler;
4345
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Tag\TagTranspiler;
@@ -72,6 +74,7 @@ public function transpile(ExpressionNode $expressionNode): string
7274
MatchNode::class => new MatchTranspiler(
7375
scope: $this->scope
7476
),
77+
NullLiteralNode::class => new NullLiteralTranspiler(),
7578
NumberLiteralNode::class => new NumberLiteralTranspiler(),
7679
StringLiteralNode::class => new StringLiteralTranspiler(
7780
shouldAddQuotes: $this->shouldAddQuotesIfNecessary
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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\Target\Php\Transpiler\NullLiteral;
24+
25+
use PackageFactory\ComponentEngine\Parser\Ast\NullLiteralNode;
26+
27+
final class NullLiteralTranspiler
28+
{
29+
public function transpile(NullLiteralNode $nullLiteralNode): string
30+
{
31+
return 'null';
32+
}
33+
}

src/TypeSystem/Resolver/Expression/ExpressionTypeResolver.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
2828
use PackageFactory\ComponentEngine\Parser\Ast\IdentifierNode;
2929
use PackageFactory\ComponentEngine\Parser\Ast\MatchNode;
30+
use PackageFactory\ComponentEngine\Parser\Ast\NullLiteralNode;
3031
use PackageFactory\ComponentEngine\Parser\Ast\NumberLiteralNode;
3132
use PackageFactory\ComponentEngine\Parser\Ast\StringLiteralNode;
3233
use PackageFactory\ComponentEngine\Parser\Ast\TagNode;
@@ -36,6 +37,7 @@
3637
use PackageFactory\ComponentEngine\TypeSystem\Resolver\BooleanLiteral\BooleanLiteralTypeResolver;
3738
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Identifier\IdentifierTypeResolver;
3839
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Match\MatchTypeResolver;
40+
use PackageFactory\ComponentEngine\TypeSystem\Resolver\NullLiteral\NullLiteralTypeResolver;
3941
use PackageFactory\ComponentEngine\TypeSystem\Resolver\NumberLiteral\NumberLiteralTypeResolver;
4042
use PackageFactory\ComponentEngine\TypeSystem\Resolver\StringLiteral\StringLiteralTypeResolver;
4143
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Tag\TagTypeResolver;
@@ -66,6 +68,8 @@ public function resolveTypeOf(ExpressionNode $expressionNode): TypeInterface
6668
MatchNode::class => (new MatchTypeResolver(
6769
scope: $this->scope
6870
))->resolveTypeOf($rootNode),
71+
NullLiteralNode::class => (new NullLiteralTypeResolver())
72+
->resolveTypeOf($rootNode),
6973
NumberLiteralNode::class => (new NumberLiteralTypeResolver())
7074
->resolveTypeOf($rootNode),
7175
StringLiteralNode::class => (new StringLiteralTypeResolver())
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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\Resolver\NullLiteral;
24+
25+
use PackageFactory\ComponentEngine\Parser\Ast\NullLiteralNode;
26+
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
27+
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
28+
29+
final class NullLiteralTypeResolver
30+
{
31+
public function resolveTypeOf(NullLiteralNode $nullLiteralNode): TypeInterface
32+
{
33+
return NullType::get();
34+
}
35+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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\NullType;
24+
25+
use PackageFactory\ComponentEngine\TypeSystem\TypeInterface;
26+
27+
final class NullType implements TypeInterface
28+
{
29+
private static null|self $instance = null;
30+
31+
private function __construct()
32+
{
33+
}
34+
35+
public static function get(): self
36+
{
37+
return self::$instance ??= new self();
38+
}
39+
40+
public function is(TypeInterface $other): bool
41+
{
42+
return $other === self::$instance;
43+
}
44+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\Test\Unit\Target\Php\Transpiler\NullLiteral;
24+
25+
use PackageFactory\ComponentEngine\Parser\Ast\NullLiteralNode;
26+
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
27+
use PackageFactory\ComponentEngine\Target\Php\Transpiler\NullLiteral\NullLiteralTranspiler;
28+
use PHPUnit\Framework\TestCase;
29+
30+
final class NullLiteralTranspilerTest extends TestCase
31+
{
32+
/**
33+
* @test
34+
* @return void
35+
*/
36+
public function transpilesNullLiteralNodes(): void
37+
{
38+
$nullLiteralTranspiler = new NullLiteralTranspiler();
39+
$nullLiteralNode = ExpressionNode::fromString('null')->root;
40+
assert($nullLiteralNode instanceof NullLiteralNode);
41+
42+
$expectedTranspilationResult = 'null';
43+
$actualTranspilationResult = $nullLiteralTranspiler->transpile(
44+
$nullLiteralNode
45+
);
46+
47+
$this->assertEquals(
48+
$expectedTranspilationResult,
49+
$actualTranspilationResult
50+
);
51+
}
52+
}

test/Unit/TypeSystem/Resolver/Expression/ExpressionTypeResolverTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Scope\Fixtures\DummyScope;
2727
use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver;
2828
use PackageFactory\ComponentEngine\TypeSystem\Type\BooleanType\BooleanType;
29+
use PackageFactory\ComponentEngine\TypeSystem\Type\NullType\NullType;
2930
use PackageFactory\ComponentEngine\TypeSystem\Type\NumberType\NumberType;
3031
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
3132
use PackageFactory\ComponentEngine\TypeSystem\Type\UnionType\UnionType;
@@ -171,6 +172,25 @@ public function resolvesMatchToResultingType(string $matchAsString, TypeInterfac
171172
);
172173
}
173174

175+
/**
176+
* @test
177+
* @return void
178+
*/
179+
public function resolvesNullLiteralToNullType(): void
180+
{
181+
$scope = new DummyScope();
182+
$expressionTypeResolver = new ExpressionTypeResolver(scope: $scope);
183+
$expressionNode = ExpressionNode::fromString('null');
184+
185+
$expectedType = NullType::get();
186+
$actualType = $expressionTypeResolver->resolveTypeOf($expressionNode);
187+
188+
$this->assertTrue(
189+
$expectedType->is($actualType),
190+
sprintf('Expected %s, got %s', $expectedType::class, $actualType::class)
191+
);
192+
}
193+
174194
/**
175195
* @test
176196
* @return void

0 commit comments

Comments
 (0)