Skip to content

Commit 7aa7a59

Browse files
committed
TASK: Implement ComponentDeclarationParser
1 parent 23c5f3f commit 7aa7a59

File tree

12 files changed

+1022
-51
lines changed

12 files changed

+1022
-51
lines changed
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) 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\Domain\ComponentName;
24+
25+
final class ComponentName
26+
{
27+
private function __construct(
28+
public readonly string $value
29+
) {
30+
}
31+
32+
public static function from(string $string): self
33+
{
34+
return new self($string);
35+
}
36+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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\AST\Node\ComponentDeclaration;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\Expression\ExpressionNode;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\PropertyDeclaration\PropertyDeclarationNodes;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class ComponentDeclarationNode
30+
{
31+
public function __construct(
32+
public readonly Range $rangeInSource,
33+
public readonly ComponentNameNode $name,
34+
public readonly PropertyDeclarationNodes $props,
35+
public readonly ExpressionNode $return
36+
) {
37+
}
38+
}
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) 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\AST\Node\ComponentDeclaration;
24+
25+
use PackageFactory\ComponentEngine\Domain\ComponentName\ComponentName;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\Node;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class ComponentNameNode extends Node
30+
{
31+
public function __construct(
32+
public readonly Range $rangeInSource,
33+
public readonly ComponentName $value
34+
) {
35+
}
36+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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\ComponentDeclaration;
24+
25+
use PackageFactory\ComponentEngine\Domain\ComponentName\ComponentName;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\ComponentDeclaration\ComponentDeclarationNode;
27+
use PackageFactory\ComponentEngine\Language\AST\Node\ComponentDeclaration\ComponentNameNode;
28+
use PackageFactory\ComponentEngine\Language\AST\Node\Expression\ExpressionNode;
29+
use PackageFactory\ComponentEngine\Language\AST\Node\PropertyDeclaration\PropertyDeclarationNodes;
30+
use PackageFactory\ComponentEngine\Language\Parser\Expression\ExpressionParser;
31+
use PackageFactory\ComponentEngine\Language\Parser\PropertyDeclaration\PropertyDeclarationParser;
32+
use PackageFactory\ComponentEngine\Parser\Source\Range;
33+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Scanner;
34+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;
35+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType;
36+
37+
final class ComponentDeclarationParser
38+
{
39+
private readonly PropertyDeclarationParser $propertyDeclarationParser;
40+
private ?ExpressionParser $returnParser = null;
41+
42+
public function __construct()
43+
{
44+
$this->propertyDeclarationParser = new PropertyDeclarationParser();
45+
}
46+
47+
/**
48+
* @param \Iterator<mixed,Token> $tokens
49+
* @return ComponentDeclarationNode
50+
*/
51+
public function parse(\Iterator &$tokens): ComponentDeclarationNode
52+
{
53+
$componentKeywordToken = $this->extractComponentKeywordToken($tokens);
54+
$name = $this->parseName($tokens);
55+
56+
$this->skipOpeningBracketToken($tokens);
57+
58+
$props = $this->parseProps($tokens);
59+
60+
$this->skipReturnKeywordToken($tokens);
61+
62+
$return = $this->parseReturn($tokens);
63+
$closingBracketToken = $this->extractClosingBracketToken($tokens);
64+
65+
return new ComponentDeclarationNode(
66+
rangeInSource: Range::from(
67+
$componentKeywordToken->boundaries->start,
68+
$closingBracketToken->boundaries->end
69+
),
70+
name: $name,
71+
props: $props,
72+
return: $return
73+
);
74+
}
75+
76+
/**
77+
* @param \Iterator<mixed,Token> $tokens
78+
* @return Token
79+
*/
80+
private function extractComponentKeywordToken(\Iterator &$tokens): Token
81+
{
82+
Scanner::assertType($tokens, TokenType::KEYWORD_COMPONENT);
83+
84+
$componentKeywordToken = $tokens->current();
85+
86+
Scanner::skipOne($tokens);
87+
Scanner::skipSpace($tokens);
88+
89+
return $componentKeywordToken;
90+
}
91+
92+
/**
93+
* @param \Iterator<mixed,Token> $tokens
94+
* @return ComponentNameNode
95+
*/
96+
private function parseName(\Iterator &$tokens): ComponentNameNode
97+
{
98+
Scanner::assertType($tokens, TokenType::STRING);
99+
100+
$componentNameToken = $tokens->current();
101+
102+
Scanner::skipOne($tokens);
103+
Scanner::skipSpace($tokens);
104+
105+
return new ComponentNameNode(
106+
rangeInSource: $componentNameToken->boundaries,
107+
value: ComponentName::from($componentNameToken->value)
108+
);
109+
}
110+
111+
/**
112+
* @param \Iterator<mixed,Token> $tokens
113+
* @return void
114+
*/
115+
private function skipOpeningBracketToken(\Iterator &$tokens): void
116+
{
117+
Scanner::assertType($tokens, TokenType::BRACKET_CURLY_OPEN);
118+
Scanner::skipOne($tokens);
119+
Scanner::skipSpaceAndComments($tokens);
120+
}
121+
122+
/**
123+
* @param \Iterator<mixed,Token> $tokens
124+
* @return PropertyDeclarationNodes
125+
*/
126+
private function parseProps(\Iterator &$tokens): PropertyDeclarationNodes
127+
{
128+
$items = [];
129+
while (Scanner::type($tokens) !== TokenType::KEYWORD_RETURN) {
130+
$items[] = $this->propertyDeclarationParser->parse($tokens);
131+
132+
Scanner::skipSpaceAndComments($tokens);
133+
}
134+
135+
return new PropertyDeclarationNodes(...$items);
136+
}
137+
138+
/**
139+
* @param \Iterator<mixed,Token> $tokens
140+
* @return void
141+
*/
142+
private function skipReturnKeywordToken(\Iterator &$tokens): void
143+
{
144+
Scanner::assertType($tokens, TokenType::KEYWORD_RETURN);
145+
Scanner::skipOne($tokens);
146+
Scanner::skipSpaceAndComments($tokens);
147+
}
148+
149+
/**
150+
* @param \Iterator<mixed,Token> $tokens
151+
* @return ExpressionNode
152+
*/
153+
private function parseReturn(\Iterator &$tokens): ExpressionNode
154+
{
155+
$this->returnParser ??= new ExpressionParser(
156+
stopAt: TokenType::BRACKET_CURLY_CLOSE
157+
);
158+
159+
return $this->returnParser->parse($tokens);
160+
}
161+
162+
/**
163+
* @param \Iterator<mixed,Token> $tokens
164+
* @return Token
165+
*/
166+
private function extractClosingBracketToken(\Iterator &$tokens): Token
167+
{
168+
Scanner::assertType($tokens, TokenType::BRACKET_CURLY_CLOSE);
169+
170+
$closingBracketToken = $tokens->current();
171+
172+
Scanner::skipOne($tokens);
173+
174+
return $closingBracketToken;
175+
}
176+
}

src/Language/Parser/Expression/ExpressionCouldNotBeParsed.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,4 @@ 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-
}
6046
}

src/Language/Parser/Tag/TagCouldNotBeParsed.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use PackageFactory\ComponentEngine\Domain\TagName\TagName;
2626
use PackageFactory\ComponentEngine\Language\Parser\ParserException;
2727
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;
29+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenTypes;
2830

2931
final class TagCouldNotBeParsed extends ParserException
3032
{
@@ -36,11 +38,27 @@ public static function becauseOfClosingTagNameMismatch(
3638
return new self(
3739
code: 1690976372,
3840
message: sprintf(
39-
'TagNode could not be parsed, because the closing tag name "%s" did not match the opening tag name "%s".',
41+
'Tag could not be parsed, because the closing tag name "%s" did not match the opening tag name "%s".',
4042
$actualTagName,
4143
$expectedTagName->value
4244
),
4345
affectedRangeInSource: $affectedRangeInSource
4446
);
4547
}
48+
49+
public static function becauseOfUnexpectedToken(
50+
TokenTypes $expectedTokenTypes,
51+
Token $actualToken
52+
): self {
53+
return new self(
54+
code: 1691156112,
55+
message: sprintf(
56+
'Tag could not be parsed because of unexpected token %s. '
57+
. 'Expected %s instead.',
58+
$actualToken->toDebugString(),
59+
$expectedTokenTypes->toDebugString()
60+
),
61+
affectedRangeInSource: $actualToken->boundaries
62+
);
63+
}
4664
}

0 commit comments

Comments
 (0)