Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 38 additions & 0 deletions src/Definition/UnaryOperator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?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\Definition;

use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType;

enum UnaryOperator: string
{
case NOT = 'NOT';

public static function fromTokenType(TokenType $tokenType): self
{
return match ($tokenType) {
TokenType::OPERATOR_BOOLEAN_NOT => self::NOT,
default => throw new \Exception('@TODO: Unknown Unary Operator')
};
}
}
5 changes: 4 additions & 1 deletion src/Parser/Ast/ExpressionNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
final class ExpressionNode implements \JsonSerializable
{
private function __construct(
public readonly IdentifierNode | NumberLiteralNode | BinaryOperationNode | AccessNode | TernaryOperationNode | TagNode | StringLiteralNode | MatchNode | TemplateLiteralNode | BooleanLiteralNode | NullLiteralNode $root
public readonly IdentifierNode | NumberLiteralNode | BinaryOperationNode | UnaryOperationNode | AccessNode | TernaryOperationNode | TagNode | StringLiteralNode | MatchNode | TemplateLiteralNode | BooleanLiteralNode | NullLiteralNode $root
) {
}

Expand Down Expand Up @@ -109,6 +109,9 @@ public static function fromTokens(\Iterator $tokens, Precedence $precedence = Pr
case TokenType::TEMPLATE_LITERAL_START:
$root = TemplateLiteralNode::fromTokens($tokens);
break;
case TokenType::OPERATOR_BOOLEAN_NOT:
$root = UnaryOperationNode::fromTokens($tokens);
break;
default:
$root = IdentifierNode::fromTokens($tokens);
break;
Expand Down
67 changes: 67 additions & 0 deletions src/Parser/Ast/UnaryOperationNode.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\Parser\Ast;

use PackageFactory\ComponentEngine\Definition\Precedence;
use PackageFactory\ComponentEngine\Definition\UnaryOperator;
use PackageFactory\ComponentEngine\Parser\Tokenizer\Scanner;
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;

final class UnaryOperationNode implements \JsonSerializable
{
private function __construct(
public readonly UnaryOperator $operator,
public readonly ExpressionNode $argument
) {
}

/**
* @param \Iterator<mixed,Token> $tokens
*/
public static function fromTokens(\Iterator $tokens): self
{
Scanner::skipSpace($tokens);

$operator = UnaryOperator::fromTokenType(Scanner::type($tokens));

Scanner::skipOne($tokens);

$argument = ExpressionNode::fromTokens($tokens, Precedence::UNARY);

return new self(
operator: $operator,
argument: $argument
);
}

public function jsonSerialize(): mixed
{
return [
'type' => 'UnaryOperationNode',
'payload' => [
'operator' => $this->operator,
'argument' => $this->argument
]
];
}
}
10 changes: 9 additions & 1 deletion src/Parser/Tokenizer/Tokenizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,22 @@ public static function period(\Iterator $fragments): \Iterator
yield from $buffer->flush(TokenType::PERIOD);
}

/**
* @param \Iterator<mixed, Fragment> $fragments
*/
public static function symbol(\Iterator $fragments, ?Buffer $buffer = null): \Iterator
{
$buffer = $buffer ?? Buffer::empty();
$capture = true;

while ($capture && $fragments->valid()) {
/** @var Fragment $fragment */
$fragment = $fragments->current();

if ($buffer->value() === '!' && $fragment->value === '!') {
// chained `!` must be kept as individual fragments/tokens
break;
}

$capture = match (CharacterType::get($fragment->value)) {
CharacterType::ANGLE_CLOSE,
CharacterType::FORWARD_SLASH,
Expand Down
5 changes: 5 additions & 0 deletions src/Target/Php/Transpiler/Expression/ExpressionTranspiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use PackageFactory\ComponentEngine\Parser\Ast\TagNode;
use PackageFactory\ComponentEngine\Parser\Ast\TemplateLiteralNode;
use PackageFactory\ComponentEngine\Parser\Ast\TernaryOperationNode;
use PackageFactory\ComponentEngine\Parser\Ast\UnaryOperationNode;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Access\AccessTranspiler;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\BinaryOperation\BinaryOperationTranspiler;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\BooleanLiteral\BooleanLiteralTranspiler;
Expand All @@ -45,6 +46,7 @@
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Tag\TagTranspiler;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\TemplateLiteral\TemplateLiteralTranspiler;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\TernaryOperation\TernaryOperationTranspiler;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\UnaryOperation\UnaryOperationTranspiler;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;

final class ExpressionTranspiler
Expand All @@ -70,6 +72,9 @@ public function transpile(ExpressionNode $expressionNode): string
BinaryOperationNode::class => new BinaryOperationTranspiler(
scope: $this->scope
),
UnaryOperationNode::class => new UnaryOperationTranspiler(
scope: $this->scope
),
BooleanLiteralNode::class => new BooleanLiteralTranspiler(),
MatchNode::class => new MatchTranspiler(
scope: $this->scope
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?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\Target\Php\Transpiler\UnaryOperation;

use PackageFactory\ComponentEngine\Definition\UnaryOperator;
use PackageFactory\ComponentEngine\Parser\Ast\UnaryOperationNode;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Expression\ExpressionTranspiler;
use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface;

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

private function transpileUnaryOperator(UnaryOperator $operator): string
{
return match ($operator) {
UnaryOperator::NOT => '!'
};
}

public function transpile(UnaryOperationNode $unaryOperationNode): string
{
$expressionTranspiler = new ExpressionTranspiler(
scope: $this->scope,
shouldAddQuotesIfNecessary: true
);

$operator = $this->transpileUnaryOperator($unaryOperationNode->operator);
$argument = $expressionTranspiler->transpile($unaryOperationNode->argument);

return sprintf('(%s%s)', $operator, $argument);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public function binaryOperationExamples(): array
'2 % b' => ['2 % b', '(2 % $this->b)'],
'a % b' => ['a % b', '($this->a % $this->b)'],

'!1 + 1' => ['!1 + 1', '((!1) + 1)'],

'42 * a / 23 + b - 17 * c' => [
'42 * a / 23 + b - 17 * c',
'((((42 * $this->a) / 23) + $this->b) - (17 * $this->c))'
Expand Down Expand Up @@ -126,4 +128,4 @@ public function transpilesBinaryOperationNodes(string $binaryOperationAsString,
$actualTranspilationResult
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?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\Test\Unit\Target\Php\Transpiler\UnaryOperation;

use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
use PackageFactory\ComponentEngine\Parser\Ast\UnaryOperationNode;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\UnaryOperation\UnaryOperationTranspiler;
use PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Scope\Fixtures\DummyScope;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
use PHPUnit\Framework\TestCase;

final class UnaryOperationTranspilerTest extends TestCase
{
/**
* @return array<string,mixed>
*/
public function unaryOperationExamples(): array
{
return [
'!false' => ['!false', '(!false)'],
'!!false' => ['!!false', '(!(!false))'],
'!!!false' => ['!!!false', '(!(!(!false)))'],
'!foo' => ['!foo', '(!$this->foo)'],
];
}

/**
* @dataProvider unaryOperationExamples
* @test
*/
public function transpilesUnaryOperationNodes(string $unaryOperationAsString, string $expectedTranspilationResult): void
{
$transpiler = new UnaryOperationTranspiler(
scope: new DummyScope(['foo' => StringType::get()])
);
$node = ExpressionNode::fromString($unaryOperationAsString)->root;
assert($node instanceof UnaryOperationNode);

$actualTranspilationResult = $transpiler->transpile(
$node
);

$this->assertEquals(
$expectedTranspilationResult,
$actualTranspilationResult
);
}
}