diff --git a/src/Parser/Ast/BinaryOperandNodes.php b/src/Parser/Ast/BinaryOperandNodes.php deleted file mode 100644 index 94c2c64f..00000000 --- a/src/Parser/Ast/BinaryOperandNodes.php +++ /dev/null @@ -1,102 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace PackageFactory\ComponentEngine\Parser\Ast; - -use PackageFactory\ComponentEngine\Definition\BinaryOperator; -use PackageFactory\ComponentEngine\Parser\Tokenizer\Scanner; -use PackageFactory\ComponentEngine\Parser\Tokenizer\Token; -use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType; - -/** - * @implements \IteratorAggregate - */ -final class BinaryOperandNodes implements \IteratorAggregate, \JsonSerializable -{ - /** - * @var ExpressionNode[] - */ - public readonly array $rest; - - private function __construct( - public readonly ExpressionNode $first, - ExpressionNode ...$rest - ) { - $this->rest = $rest; - } - - /** - * @param ExpressionNode $first - * @param \Iterator $tokens - * @param BinaryOperator $operator - * @return self - */ - public static function fromTokens(ExpressionNode $first, \Iterator $tokens, BinaryOperator $operator): self - { - $precedence = $operator->toPrecedence(); - $operands = [$first]; - - while (true) { - Scanner::skipSpaceAndComments($tokens); - - $operands[] = ExpressionNode::fromTokens($tokens, $precedence); - - Scanner::skipSpaceAndComments($tokens); - - if (Scanner::isEnd($tokens)) { - break; - } - - switch (Scanner::type($tokens)) { - case TokenType::BRACKET_ROUND_CLOSE: - case TokenType::BRACKET_CURLY_CLOSE: - case TokenType::BRACKET_SQUARE_CLOSE: - case TokenType::ARROW_SINGLE: - case TokenType::QUESTIONMARK: - break 2; - case $operator->toTokenType(): - Scanner::skipOne($tokens); - break; - default: - if ($precedence->mustStopAt(Scanner::type($tokens))) { - break 2; - } else { - Scanner::assertType($tokens, $operator->toTokenType()); - } - break; - } - } - - return new self(...$operands); - } - - public function getIterator(): \Traversable - { - yield $this->first; - yield from $this->rest; - } - - public function jsonSerialize(): mixed - { - return [$this->first, ...$this->rest]; - } -} diff --git a/src/Parser/Ast/BinaryOperationNode.php b/src/Parser/Ast/BinaryOperationNode.php index dabac37c..f3dcd3bd 100644 --- a/src/Parser/Ast/BinaryOperationNode.php +++ b/src/Parser/Ast/BinaryOperationNode.php @@ -29,8 +29,9 @@ final class BinaryOperationNode implements \JsonSerializable { private function __construct( + public readonly ExpressionNode $left, public readonly BinaryOperator $operator, - public readonly BinaryOperandNodes $operands + public readonly ExpressionNode $right ) { } @@ -46,11 +47,18 @@ public static function fromTokens(ExpressionNode $left, \Iterator $tokens): self Scanner::skipOne($tokens); - $operands = BinaryOperandNodes::fromTokens($left, $tokens, $operator); + $precedence = $operator->toPrecedence(); + + Scanner::skipSpaceAndComments($tokens); + + $right = ExpressionNode::fromTokens($tokens, $precedence); + + Scanner::skipSpaceAndComments($tokens); return new self( + left: $left, operator: $operator, - operands: $operands + right: $right ); } @@ -60,7 +68,7 @@ public function jsonSerialize(): mixed 'type' => 'BinaryOperationNode', 'payload' => [ 'operator' => $this->operator, - 'operands' => $this->operands + 'operands' => [$this->left, $this->right] ] ]; } diff --git a/src/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspiler.php b/src/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspiler.php index 32196424..b5268b19 100644 --- a/src/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspiler.php +++ b/src/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspiler.php @@ -59,14 +59,10 @@ public function transpile(BinaryOperationNode $binaryOperationNode): string shouldAddQuotesIfNecessary: true ); - $result = $expressionTranspiler->transpile($binaryOperationNode->operands->first); - $operator = sprintf(' %s ', $this->transpileBinaryOperator($binaryOperationNode->operator)); + $left = $expressionTranspiler->transpile($binaryOperationNode->left); + $operator = $this->transpileBinaryOperator($binaryOperationNode->operator); + $right = $expressionTranspiler->transpile($binaryOperationNode->right); - foreach ($binaryOperationNode->operands->rest as $operandNode) { - $result .= $operator; - $result .= $expressionTranspiler->transpile($operandNode); - } - - return sprintf('(%s)', $result); + return sprintf('(%s %s %s)', $left, $operator, $right); } } diff --git a/src/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolver.php b/src/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolver.php index 7f573aa5..cf95fe8b 100644 --- a/src/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolver.php +++ b/src/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolver.php @@ -23,7 +23,6 @@ namespace PackageFactory\ComponentEngine\TypeSystem\Resolver\BinaryOperation; use PackageFactory\ComponentEngine\Definition\BinaryOperator; -use PackageFactory\ComponentEngine\Parser\Ast\BinaryOperandNodes; use PackageFactory\ComponentEngine\Parser\Ast\BinaryOperationNode; use PackageFactory\ComponentEngine\TypeSystem\Resolver\Expression\ExpressionTypeResolver; use PackageFactory\ComponentEngine\TypeSystem\ScopeInterface; @@ -43,13 +42,13 @@ public function resolveTypeOf(BinaryOperationNode $binaryOperationNode): TypeInt { return match ($binaryOperationNode->operator) { BinaryOperator::AND, - BinaryOperator::OR => $this->resolveTypeOfBooleanOperation($binaryOperationNode->operands), + BinaryOperator::OR => $this->resolveTypeOfBooleanOperation($binaryOperationNode), BinaryOperator::PLUS, BinaryOperator::MINUS, BinaryOperator::MULTIPLY_BY, BinaryOperator::DIVIDE_BY, - BinaryOperator::MODULO => $this->resolveTypeOfArithmeticOperation($binaryOperationNode->operands), + BinaryOperator::MODULO => $this->resolveTypeOfArithmeticOperation($binaryOperationNode), BinaryOperator::EQUAL, BinaryOperator::NOT_EQUAL, @@ -60,36 +59,31 @@ public function resolveTypeOf(BinaryOperationNode $binaryOperationNode): TypeInt }; } - private function resolveTypeOfBooleanOperation(BinaryOperandNodes $operandNodes): TypeInterface + private function resolveTypeOfBooleanOperation(BinaryOperationNode $binaryOperationNode): TypeInterface { $expressionTypeResolver = new ExpressionTypeResolver( scope: $this->scope ); - $operandTypes = []; - foreach ($operandNodes as $operandNode) { - $operandTypes[] = $expressionTypeResolver->resolveTypeOf($operandNode); - } - - return UnionType::of(...$operandTypes); + return UnionType::of( + $expressionTypeResolver->resolveTypeOf($binaryOperationNode->left), + $expressionTypeResolver->resolveTypeOf($binaryOperationNode->right) + ); } - private function resolveTypeOfArithmeticOperation(BinaryOperandNodes $operandNodes): TypeInterface + private function resolveTypeOfArithmeticOperation(BinaryOperationNode $binaryOperationNode): TypeInterface { $expressionTypeResolver = new ExpressionTypeResolver( scope: $this->scope ); - $numberType = NumberType::get(); - foreach ($operandNodes as $operandNode) { + foreach ([$binaryOperationNode->left, $binaryOperationNode->right] as $operandNode) { $typeOfOperandNode = $expressionTypeResolver->resolveTypeOf($operandNode); - $typeOfOperandNodeIsNumberType = $typeOfOperandNode->is($numberType); - - if (!$typeOfOperandNodeIsNumberType) { + if (!$typeOfOperandNode->is(NumberType::get())) { throw new \Exception('@TODO: Operand must be of type number'); } } - return $numberType; + return NumberType::get(); } } diff --git a/test/Integration/Examples/Numbers/Numbers.ast.json b/test/Integration/Examples/Numbers/Numbers.ast.json index 793a37fd..56d43578 100644 --- a/test/Integration/Examples/Numbers/Numbers.ast.json +++ b/test/Integration/Examples/Numbers/Numbers.ast.json @@ -1,118 +1,233 @@ { "type": "ModuleNode", "payload": { - "imports": {}, + "imports": [], "exports": [ { "type": "ComponentDeclarationNode", "payload": { "componentName": "Numbers", - "propertyDeclarations": {}, + "propertyDeclarations": [], "returnExpression": { "type": "BinaryOperationNode", "payload": { "operator": "PLUS", "operands": [ { - "type": "NumberLiteralNode", - "payload": { "value": "0", "format": "DECIMAL" } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "1234567890", - "format": "DECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "42", - "format": "DECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0b10000000000000000000000000000000", - "format": "BINARY" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0b01111111100000000000000000000000", - "format": "BINARY" - } - }, - { - "type": "NumberLiteralNode", + "type": "BinaryOperationNode", "payload": { - "value": "0B00000000011111111111111111111111", - "format": "BINARY" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0o755", - "format": "OCTAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0o644", - "format": "OCTAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0xFFFFFFFFFFFFFFFFF", - "format": "HEXADECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0x123456789ABCDEF", - "format": "HEXADECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0xA", - "format": "HEXADECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "1E3", - "format": "DECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "2e6", - "format": "DECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "123.456", - "format": "DECIMAL" - } - }, - { - "type": "NumberLiteralNode", - "payload": { - "value": "0.1e2", - "format": "DECIMAL" + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "BinaryOperationNode", + "payload": { + "operator": "PLUS", + "operands": [ + { + "type": "NumberLiteralNode", + "payload": { + "value": "0", + "format": "DECIMAL" + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "1234567890", + "format": "DECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "42", + "format": "DECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0b10000000000000000000000000000000", + "format": "BINARY" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0b01111111100000000000000000000000", + "format": "BINARY" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0B00000000011111111111111111111111", + "format": "BINARY" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0o755", + "format": "OCTAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0o644", + "format": "OCTAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0xFFFFFFFFFFFFFFFFF", + "format": "HEXADECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0x123456789ABCDEF", + "format": "HEXADECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0xA", + "format": "HEXADECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "1E3", + "format": "DECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "2e6", + "format": "DECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "123.456", + "format": "DECIMAL" + } + } + ] + } + }, + { + "type": "NumberLiteralNode", + "payload": { + "value": "0.1e2", + "format": "DECIMAL" + } + } + ] } }, { diff --git a/test/Integration/Examples/Numbers/Numbers.php b/test/Integration/Examples/Numbers/Numbers.php index b61927f3..58555d25 100644 --- a/test/Integration/Examples/Numbers/Numbers.php +++ b/test/Integration/Examples/Numbers/Numbers.php @@ -10,6 +10,6 @@ final class Numbers extends BaseClass { public function render(): string { - return (string) (0 + 1234567890 + 42 + 0b10000000000000000000000000000000 + 0b01111111100000000000000000000000 + 0b00000000011111111111111111111111 + 0o755 + 0o644 + 0xFFFFFFFFFFFFFFFFF + 0x123456789ABCDEF + 0xA + 1E3 + 2e6 + 123.456 + 0.1e2 + .22); + return (string) (((((((((((((((0 + 1234567890) + 42) + 0b10000000000000000000000000000000) + 0b01111111100000000000000000000000) + 0b00000000011111111111111111111111) + 0o755) + 0o644) + 0xFFFFFFFFFFFFFFFFF) + 0x123456789ABCDEF) + 0xA) + 1E3) + 2e6) + 123.456) + 0.1e2) + .22); } } diff --git a/test/Unit/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspilerTest.php b/test/Unit/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspilerTest.php index d4b5112a..973ee237 100644 --- a/test/Unit/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspilerTest.php +++ b/test/Unit/Target/Php/Transpiler/BinaryOperation/BinaryOperationTranspilerTest.php @@ -99,6 +99,10 @@ public function binaryOperationExamples(): array 'a <= 2' => ['a <= 2', '($this->a <= 2)'], '4 <= b' => ['4 <= b', '(4 <= $this->b)'], 'a <= b' => ['a <= b', '($this->a <= $this->b)'], + + 'true && true && true' => ['true && true && true', '((true && true) && true)'], + '1 === 1 === true' => ['1 === 1 === true', '((1 === 1) === true)'], + '1 + 1 + 1' => ['1 + 1 + 1', '((1 + 1) + 1)'], ]; } @@ -126,4 +130,4 @@ public function transpilesBinaryOperationNodes(string $binaryOperationAsString, $actualTranspilationResult ); } -} \ No newline at end of file +} diff --git a/test/Unit/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolverTest.php b/test/Unit/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolverTest.php index 4dc3e7e0..eddb1041 100644 --- a/test/Unit/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolverTest.php +++ b/test/Unit/TypeSystem/Resolver/BinaryOperation/BinaryOperationTypeResolverTest.php @@ -60,6 +60,10 @@ public function binaryOperationExamples(): array '4 >= 2' => ['4 >= 2', BooleanType::get()], '4 < 2' => ['4 < 2', BooleanType::get()], '4 <= 2' => ['4 <= 2', BooleanType::get()], + + 'true && true && true' => ['true && true && true', BooleanType::get()], + '1 === 1 === true' => ['1 === 1 === true', BooleanType::get()], + '1 + 1 + 1' => ['1 + 1 + 1', NumberType::get()], ]; }