Skip to content

Commit ea7b087

Browse files
committed
TASK: Implement TemplateLiteralParser
1 parent 7ad5fc2 commit ea7b087

File tree

9 files changed

+845
-17
lines changed

9 files changed

+845
-17
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* PackageFactory.ComponentEngine - Universal View Components for PHP
5+
* Copyright (C) 2023 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\Language\AST\Node\TemplateLiteral;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\Expression\ExpressionNode;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\Node;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class TemplateLiteralExpressionSegmentNode extends Node
30+
{
31+
32+
public function __construct(
33+
public readonly Range $rangeInSource,
34+
public readonly ExpressionNode $expression
35+
) {
36+
}
37+
}

src/Language/AST/Node/TemplateLiteral/TemplateLiteralNode.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,14 @@
2222

2323
namespace PackageFactory\ComponentEngine\Language\AST\Node\TemplateLiteral;
2424

25-
use PackageFactory\ComponentEngine\Language\AST\Node\Expression\ExpressionNode;
2625
use PackageFactory\ComponentEngine\Language\AST\Node\Node;
27-
use PackageFactory\ComponentEngine\Language\AST\Node\StringLiteral\StringLiteralNode;
2826
use PackageFactory\ComponentEngine\Parser\Source\Range;
2927

3028
final class TemplateLiteralNode extends Node
3129
{
32-
/**
33-
* @var (StringLiteralNode|ExpressionNode)[]
34-
*/
35-
public readonly array $segments;
36-
3730
public function __construct(
3831
public readonly Range $rangeInSource,
39-
StringLiteralNode | ExpressionNode ...$segments
32+
public readonly TemplateLiteralSegments $segments
4033
) {
41-
$this->segments = $segments;
4234
}
4335
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* PackageFactory.ComponentEngine - Universal View Components for PHP
5+
* Copyright (C) 2023 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\Language\AST\Node\TemplateLiteral;
24+
25+
final class TemplateLiteralSegments
26+
{
27+
/**
28+
* @var (TemplateLiteralStringSegmentNode|TemplateLiteralExpressionSegmentNode)[]
29+
*/
30+
public readonly array $items;
31+
32+
public function __construct(
33+
TemplateLiteralStringSegmentNode | TemplateLiteralExpressionSegmentNode ...$items
34+
) {
35+
$this->items = $items;
36+
}
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/**
4+
* PackageFactory.ComponentEngine - Universal View Components for PHP
5+
* Copyright (C) 2023 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\Language\AST\Node\TemplateLiteral;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\Node;
26+
use PackageFactory\ComponentEngine\Parser\Source\Range;
27+
28+
final class TemplateLiteralStringSegmentNode extends Node
29+
{
30+
31+
public function __construct(
32+
public readonly Range $rangeInSource,
33+
public readonly string $value
34+
) {
35+
}
36+
}

src/Language/Parser/Expression/ExpressionParser.php

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
use PackageFactory\ComponentEngine\Language\AST\Node\BinaryOperation\BinaryOperationNode;
3030
use PackageFactory\ComponentEngine\Language\AST\Node\BinaryOperation\BinaryOperator;
3131
use PackageFactory\ComponentEngine\Language\AST\Node\Expression\ExpressionNode;
32-
use PackageFactory\ComponentEngine\Language\AST\Node\Tag\TagNode;
3332
use PackageFactory\ComponentEngine\Language\AST\Node\TernaryOperation\TernaryOperationNode;
3433
use PackageFactory\ComponentEngine\Language\AST\Node\UnaryOperation\UnaryOperationNode;
3534
use PackageFactory\ComponentEngine\Language\AST\Node\UnaryOperation\UnaryOperator;
@@ -38,6 +37,7 @@
3837
use PackageFactory\ComponentEngine\Language\Parser\NullLiteral\NullLiteralParser;
3938
use PackageFactory\ComponentEngine\Language\Parser\StringLiteral\StringLiteralParser;
4039
use PackageFactory\ComponentEngine\Language\Parser\Tag\TagParser;
40+
use PackageFactory\ComponentEngine\Language\Parser\TemplateLiteral\TemplateLiteralParser;
4141
use PackageFactory\ComponentEngine\Language\Parser\ValueReference\ValueReferenceParser;
4242
use PackageFactory\ComponentEngine\Parser\Source\Range;
4343
use PackageFactory\ComponentEngine\Parser\Tokenizer\Scanner;
@@ -52,16 +52,19 @@ final class ExpressionParser
5252
private readonly StringLiteralParser $stringLiteralParser;
5353
private readonly IntegerLiteralParser $integerLiteralParser;
5454
private readonly ValueReferenceParser $valueReferenceParser;
55+
private readonly TemplateLiteralParser $templateLiteralParser;
5556
private readonly TagParser $tagParser;
5657

5758
public function __construct(
58-
private ?TokenType $stopAt = null
59+
private ?TokenType $stopAt = null,
60+
private Precedence $precedence = Precedence::SEQUENCE
5961
) {
6062
$this->booleanLiteralParser = new BooleanLiteralParser();
6163
$this->nullLiteralParser = new NullLiteralParser();
6264
$this->stringLiteralParser = new StringLiteralParser();
6365
$this->integerLiteralParser = new IntegerLiteralParser();
6466
$this->valueReferenceParser = new ValueReferenceParser();
67+
$this->templateLiteralParser = new TemplateLiteralParser();
6568
$this->tagParser = new TagParser();
6669
}
6770

@@ -131,6 +134,8 @@ public function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
131134
$this->parseValueReference($tokens),
132135
TokenType::TAG_START_OPENING =>
133136
$this->parseTag($tokens),
137+
TokenType::TEMPLATE_LITERAL_START =>
138+
$this->parseTemplateLiteral($tokens),
134139
TokenType::BRACKET_ROUND_OPEN =>
135140
$this->parseBracketedExpression($tokens),
136141
default =>
@@ -146,6 +151,7 @@ public function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
146151
TokenType::NUMBER_HEXADECIMAL,
147152
TokenType::STRING,
148153
TokenType::TAG_START_OPENING,
154+
TokenType::TEMPLATE_LITERAL_START,
149155
TokenType::BRACKET_ROUND_OPEN
150156
),
151157
actualToken: $tokens->current()
@@ -216,6 +222,14 @@ private function withStopAt(TokenType $stopAt): self
216222
return $newExpressionParser;
217223
}
218224

225+
private function withPrecedence(Precedence $precedence): self
226+
{
227+
$newExpressionParser = clone $this;
228+
$newExpressionParser->precedence = $precedence;
229+
230+
return $newExpressionParser;
231+
}
232+
219233
/**
220234
* @param \Iterator<mixed,Token> $tokens
221235
* @return boolean
@@ -224,11 +238,21 @@ private function shouldStop(\Iterator &$tokens): bool
224238
{
225239
Scanner::skipSpaceAndComments($tokens);
226240

227-
if (is_null($this->stopAt)) {
228-
return Scanner::isEnd($tokens);
241+
if (Scanner::isEnd($tokens)) {
242+
return true;
243+
}
244+
245+
$type = Scanner::type($tokens);
246+
247+
if ($this->precedence->mustStopAt($type)) {
248+
return true;
249+
}
250+
251+
if ($this->stopAt && $type === $this->stopAt) {
252+
return true;
229253
}
230254

231-
return Scanner::type($tokens) === $this->stopAt;
255+
return false;
232256
}
233257

234258
/**
@@ -315,6 +339,20 @@ private function parseTag(\Iterator &$tokens): ExpressionNode
315339
);
316340
}
317341

342+
/**
343+
* @param \Iterator<mixed,Token> $tokens
344+
* @return ExpressionNode
345+
*/
346+
private function parseTemplateLiteral(\Iterator &$tokens): ExpressionNode
347+
{
348+
$templateLiteralNode = $this->templateLiteralParser->parse($tokens);
349+
350+
return new ExpressionNode(
351+
rangeInSource: $templateLiteralNode->rangeInSource,
352+
root: $templateLiteralNode
353+
);
354+
}
355+
318356
/**
319357
* @param \Iterator<mixed,Token> $tokens
320358
* @return ExpressionNode
@@ -412,7 +450,9 @@ private function parseAccessType(\Iterator &$tokens): AccessType
412450
private function parseBinaryOperation(\Iterator &$tokens, ExpressionNode $leftOperand): ExpressionNode
413451
{
414452
$operator = $this->parseBinaryOperator($tokens);
415-
$rightOperand = $this->parse($tokens);
453+
$rightOperand = $this
454+
->withPrecedence(Precedence::forBinaryOperator($operator))
455+
->parse($tokens);
416456
$rangeInSource = Range::from(
417457
$leftOperand->rangeInSource->start,
418458
$rightOperand->rangeInSource->end
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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\Language\Parser\Expression;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\BinaryOperation\BinaryOperator;
26+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType;
27+
28+
enum Precedence: int
29+
{
30+
//
31+
// Precedence indices as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
32+
//
33+
34+
case ACCESS = 18;
35+
case UNARY = 15;
36+
case COMPARISON = 10;
37+
case EQUALITY = 9;
38+
case LOGICAL_AND = 5;
39+
case LOGICAL_OR = 4;
40+
case TERNARY = 3;
41+
case SEQUENCE = 1;
42+
43+
public static function forTokenType(TokenType $tokenType): self
44+
{
45+
return match ($tokenType) {
46+
TokenType::BRACKET_ROUND_OPEN,
47+
TokenType::BRACKET_ROUND_CLOSE,
48+
TokenType::BRACKET_SQUARE_OPEN,
49+
TokenType::BRACKET_SQUARE_CLOSE,
50+
TokenType::OPTCHAIN,
51+
TokenType::PERIOD => self::ACCESS,
52+
53+
TokenType::OPERATOR_BOOLEAN_NOT => self::UNARY,
54+
55+
TokenType::COMPARATOR_GREATER_THAN,
56+
TokenType::COMPARATOR_GREATER_THAN_OR_EQUAL,
57+
TokenType::COMPARATOR_LESS_THAN,
58+
TokenType::COMPARATOR_LESS_THAN_OR_EQUAL => self::COMPARISON,
59+
60+
TokenType::COMPARATOR_EQUAL,
61+
TokenType::COMPARATOR_NOT_EQUAL => self::EQUALITY,
62+
63+
TokenType::OPERATOR_BOOLEAN_AND => self::LOGICAL_AND,
64+
65+
TokenType::OPERATOR_BOOLEAN_OR => self::LOGICAL_OR,
66+
67+
TokenType::QUESTIONMARK,
68+
TokenType::COLON => self::TERNARY,
69+
70+
default => self::SEQUENCE
71+
};
72+
}
73+
74+
public static function forBinaryOperator(BinaryOperator $binaryOperator): self
75+
{
76+
return match ($binaryOperator) {
77+
BinaryOperator::AND => self::LOGICAL_AND,
78+
BinaryOperator::OR => self::LOGICAL_OR,
79+
80+
BinaryOperator::EQUAL,
81+
BinaryOperator::NOT_EQUAL => self::EQUALITY,
82+
83+
BinaryOperator::GREATER_THAN,
84+
BinaryOperator::GREATER_THAN_OR_EQUAL,
85+
BinaryOperator::LESS_THAN,
86+
BinaryOperator::LESS_THAN_OR_EQUAL => self::COMPARISON
87+
};
88+
}
89+
90+
public function mustStopAt(TokenType $tokenType): bool
91+
{
92+
return self::forTokenType($tokenType)->value <= $this->value;
93+
}
94+
}

0 commit comments

Comments
 (0)