Skip to content

Commit 23c5f3f

Browse files
committed
TASK: Implement MatchParser
1 parent ea7b087 commit 23c5f3f

File tree

11 files changed

+1917
-4
lines changed

11 files changed

+1917
-4
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\Match;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\ASTException;
26+
27+
final class InvalidMatchArmNodes extends ASTException
28+
{
29+
public static function becauseTheyWereEmpty(): self
30+
{
31+
return new self(
32+
code: 1691150119,
33+
message: 'A match statement must contain at least one match arm.'
34+
);
35+
}
36+
37+
public static function becauseTheyContainMoreThanOneDefaultMatchArmNode(
38+
MatchArmNode $secondDefaultMatchArmNode
39+
): self
40+
{
41+
return new self(
42+
code: 1691150238,
43+
message: 'A match statement must not contain more than one default match arm.',
44+
affectedRangeInSource: $secondDefaultMatchArmNode->rangeInSource
45+
);
46+
}
47+
}

src/Language/AST/Node/Match/MatchArmNode.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@
2929

3030
final class MatchArmNode extends Node
3131
{
32-
private function __construct(
32+
public function __construct(
3333
public readonly Range $rangeInSource,
3434
public readonly null | ExpressionNodes $left,
3535
public readonly ExpressionNode $right
3636
) {
3737
}
38+
39+
public function isDefault(): bool
40+
{
41+
return is_null($this->left);
42+
}
3843
}

src/Language/AST/Node/Match/MatchArmNodes.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,26 @@ final class MatchArmNodes
2929
*/
3030
public readonly array $items;
3131

32+
private ?MatchArmNode $defaultArm = null;
33+
3234
public function __construct(MatchArmNode ...$items)
3335
{
36+
if (count($items) === 0) {
37+
throw InvalidMatchArmNodes::becauseTheyWereEmpty();
38+
}
39+
40+
foreach ($items as $item) {
41+
if ($item->isDefault()) {
42+
if (is_null($this->defaultArm)) {
43+
$this->defaultArm = $item;
44+
} else {
45+
throw InvalidMatchArmNodes::becauseTheyContainMoreThanOneDefaultMatchArmNode(
46+
secondDefaultMatchArmNode: $item
47+
);
48+
}
49+
}
50+
}
51+
3452
$this->items = $items;
3553
}
3654
}

src/Language/Parser/Expression/ExpressionCouldNotBeParsed.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,18 @@ public static function becauseOfUnexpectedToken(
4343
affectedRangeInSource: $actualToken->boundaries
4444
);
4545
}
46+
47+
public static function becauseOfUnexpectedExceedingToken(
48+
Token $exceedingToken
49+
): self {
50+
return new self(
51+
code: 1691141293,
52+
message: sprintf(
53+
'Expression could not be parsed because token stream was expected to end, '
54+
. 'but continued with %s instead.',
55+
$exceedingToken->toDebugString()
56+
),
57+
affectedRangeInSource: $exceedingToken->boundaries
58+
);
59+
}
4660
}

src/Language/Parser/Expression/ExpressionParser.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use PackageFactory\ComponentEngine\Language\AST\Node\UnaryOperation\UnaryOperator;
3535
use PackageFactory\ComponentEngine\Language\Parser\BooleanLiteral\BooleanLiteralParser;
3636
use PackageFactory\ComponentEngine\Language\Parser\IntegerLiteral\IntegerLiteralParser;
37+
use PackageFactory\ComponentEngine\Language\Parser\Match\MatchParser;
3738
use PackageFactory\ComponentEngine\Language\Parser\NullLiteral\NullLiteralParser;
3839
use PackageFactory\ComponentEngine\Language\Parser\StringLiteral\StringLiteralParser;
3940
use PackageFactory\ComponentEngine\Language\Parser\Tag\TagParser;
@@ -54,6 +55,7 @@ final class ExpressionParser
5455
private readonly ValueReferenceParser $valueReferenceParser;
5556
private readonly TemplateLiteralParser $templateLiteralParser;
5657
private readonly TagParser $tagParser;
58+
private readonly MatchParser $matchParser;
5759

5860
public function __construct(
5961
private ?TokenType $stopAt = null,
@@ -66,6 +68,7 @@ public function __construct(
6668
$this->valueReferenceParser = new ValueReferenceParser();
6769
$this->templateLiteralParser = new TemplateLiteralParser();
6870
$this->tagParser = new TagParser();
71+
$this->matchParser = new MatchParser();
6972
}
7073

7174
/**
@@ -113,7 +116,7 @@ public function parse(\Iterator &$tokens): ExpressionNode
113116
* @param \Iterator<mixed,Token> $tokens
114117
* @return ExpressionNode
115118
*/
116-
public function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
119+
private function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
117120
{
118121
$result = match (Scanner::type($tokens)) {
119122
TokenType::OPERATOR_BOOLEAN_NOT =>
@@ -136,6 +139,8 @@ public function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
136139
$this->parseTag($tokens),
137140
TokenType::TEMPLATE_LITERAL_START =>
138141
$this->parseTemplateLiteral($tokens),
142+
TokenType::KEYWORD_MATCH =>
143+
$this->parseMatch($tokens),
139144
TokenType::BRACKET_ROUND_OPEN =>
140145
$this->parseBracketedExpression($tokens),
141146
default =>
@@ -152,6 +157,7 @@ public function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
152157
TokenType::STRING,
153158
TokenType::TAG_START_OPENING,
154159
TokenType::TEMPLATE_LITERAL_START,
160+
TokenType::KEYWORD_MATCH,
155161
TokenType::BRACKET_ROUND_OPEN
156162
),
157163
actualToken: $tokens->current()
@@ -173,7 +179,7 @@ public function parseUnaryStatement(\Iterator &$tokens): ExpressionNode
173179
* @param \Iterator<mixed,Token> $tokens
174180
* @return ExpressionNode
175181
*/
176-
public function parseUnaryOperation(\Iterator &$tokens): ExpressionNode
182+
private function parseUnaryOperation(\Iterator &$tokens): ExpressionNode
177183
{
178184
$startingToken = $tokens->current();
179185

@@ -353,6 +359,20 @@ private function parseTemplateLiteral(\Iterator &$tokens): ExpressionNode
353359
);
354360
}
355361

362+
/**
363+
* @param \Iterator<mixed,Token> $tokens
364+
* @return ExpressionNode
365+
*/
366+
private function parseMatch(\Iterator &$tokens): ExpressionNode
367+
{
368+
$matchNode = $this->matchParser->parse($tokens);
369+
370+
return new ExpressionNode(
371+
rangeInSource: $matchNode->rangeInSource,
372+
root: $matchNode
373+
);
374+
}
375+
356376
/**
357377
* @param \Iterator<mixed,Token> $tokens
358378
* @return ExpressionNode
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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\Parser\Match;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\Match\InvalidMatchArmNodes;
26+
use PackageFactory\ComponentEngine\Language\Parser\ParserException;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class MatchCouldNotBeParsed extends ParserException
30+
{
31+
public static function becauseOfInvalidMatchArmNodes(
32+
InvalidMatchArmNodes $cause,
33+
?Range $affectedRangeInSource = null
34+
): self {
35+
return new self(
36+
code: 1691152175,
37+
message: sprintf(
38+
'Match could not be parsed because of invalid match arm nodes: %s.',
39+
$cause->getMessage()
40+
),
41+
affectedRangeInSource: $affectedRangeInSource ?? $cause->affectedRangeInSource,
42+
cause: $cause
43+
);
44+
}
45+
}

0 commit comments

Comments
 (0)