Skip to content

Commit e7aed5a

Browse files
committed
TASK: Implement ImportParser
1 parent 4e7760f commit e7aed5a

File tree

9 files changed

+611
-0
lines changed

9 files changed

+611
-0
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\Import;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\Node;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\StringLiteral\StringLiteralNode;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class ImportNode extends Node
30+
{
31+
public function __construct(
32+
public readonly Range $rangeInSource,
33+
public readonly StringLiteralNode $path,
34+
public readonly ImportedNameNodes $names
35+
) {
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\Import;
24+
25+
use PackageFactory\ComponentEngine\Domain\VariableName\VariableName;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\Node;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class ImportedNameNode extends Node
30+
{
31+
public function __construct(
32+
public readonly Range $rangeInSource,
33+
public readonly VariableName $value
34+
) {
35+
}
36+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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\Import;
24+
25+
final class ImportedNameNodes
26+
{
27+
/**
28+
* @var ImportedNameNode[]
29+
*/
30+
public readonly array $items;
31+
32+
public function __construct(ImportedNameNode ...$items)
33+
{
34+
if (count($items) === 0) {
35+
throw InvalidImportedNameNodes::becauseTheyWereEmpty();
36+
}
37+
38+
$typeNames = [];
39+
foreach ($items as $item) {
40+
if (isset($typeNames[$item->value->value])) {
41+
throw InvalidImportedNameNodes::becauseTheyContainDuplicates(
42+
duplicateImportedNameNode: $item
43+
);
44+
}
45+
46+
$typeNames[$item->value->value] = true;
47+
}
48+
49+
$this->items = $items;
50+
}
51+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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\Import;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\ASTException;
26+
27+
final class InvalidImportedNameNodes extends ASTException
28+
{
29+
public static function becauseTheyWereEmpty(): self
30+
{
31+
return new self(
32+
code: 1691163487,
33+
message: 'An import statement must import at least one name.'
34+
);
35+
}
36+
37+
public static function becauseTheyContainDuplicates(
38+
ImportedNameNode $duplicateImportedNameNode
39+
): self {
40+
return new self(
41+
code: 1691163492,
42+
message: 'An import statement must not import duplicate names.',
43+
affectedRangeInSource: $duplicateImportedNameNode->rangeInSource
44+
);
45+
}
46+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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\Import;
24+
25+
use PackageFactory\ComponentEngine\Language\AST\Node\Import\InvalidImportedNameNodes;
26+
use PackageFactory\ComponentEngine\Language\Parser\ParserException;
27+
use PackageFactory\ComponentEngine\Parser\Source\Range;
28+
29+
final class ImportCouldNotBeParsed extends ParserException
30+
{
31+
public static function becauseOfInvalidImportedNameNodes(
32+
InvalidImportedNameNodes $cause,
33+
Range $affectedRangeInSource
34+
): self {
35+
return new self(
36+
code: 1691181627,
37+
message: sprintf(
38+
'Import could not be parsed, because of invalid imported names: %s',
39+
$cause->getMessage()
40+
),
41+
affectedRangeInSource: $cause->affectedRangeInSource ?? $affectedRangeInSource
42+
);
43+
}
44+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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\Import;
24+
25+
use PackageFactory\ComponentEngine\Domain\VariableName\VariableName;
26+
use PackageFactory\ComponentEngine\Language\AST\Node\Import\ImportedNameNode;
27+
use PackageFactory\ComponentEngine\Language\AST\Node\Import\ImportedNameNodes;
28+
use PackageFactory\ComponentEngine\Language\AST\Node\Import\ImportNode;
29+
use PackageFactory\ComponentEngine\Language\AST\Node\Import\InvalidImportedNameNodes;
30+
use PackageFactory\ComponentEngine\Language\AST\Node\StringLiteral\StringLiteralNode;
31+
use PackageFactory\ComponentEngine\Language\Parser\StringLiteral\StringLiteralParser;
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 ImportParser
38+
{
39+
private readonly StringLiteralParser $pathParser;
40+
41+
public function __construct()
42+
{
43+
$this->pathParser = new StringLiteralParser();
44+
}
45+
46+
/**
47+
* @param \Iterator<mixed,Token> $tokens
48+
* @return ImportNode
49+
*/
50+
public function parse(\Iterator &$tokens): ImportNode
51+
{
52+
$fromKeywordToken = $this->extractToken($tokens, TokenType::KEYWORD_FROM);
53+
$path = $this->parsePath($tokens);
54+
55+
$this->skipToken($tokens, TokenType::KEYWORD_IMPORT);
56+
$openingBracketToken = $this->extractToken($tokens, TokenType::BRACKET_CURLY_OPEN);
57+
58+
try {
59+
$names = $this->parseNames($tokens);
60+
$closingBracketToken = $this->extractToken($tokens, TokenType::BRACKET_CURLY_CLOSE);
61+
62+
return new ImportNode(
63+
rangeInSource: Range::from(
64+
$fromKeywordToken->boundaries->start,
65+
$closingBracketToken->boundaries->end
66+
),
67+
path: $path,
68+
names: $names
69+
);
70+
} catch (InvalidImportedNameNodes $e) {
71+
throw ImportCouldNotBeParsed::becauseOfInvalidImportedNameNodes(
72+
cause: $e,
73+
affectedRangeInSource: $openingBracketToken->boundaries
74+
);
75+
}
76+
}
77+
78+
/**
79+
* @param \Iterator<mixed,Token> $tokens
80+
* @param TokenType $tokenType
81+
* @return Token
82+
*/
83+
private function extractToken(\Iterator &$tokens, TokenType $tokenType): Token
84+
{
85+
Scanner::assertType($tokens, $tokenType);
86+
$token = $tokens->current();
87+
Scanner::skipOne($tokens);
88+
Scanner::skipSpace($tokens);
89+
90+
return $token;
91+
}
92+
93+
/**
94+
* @param \Iterator<mixed,Token> $tokens
95+
* @param TokenType $tokenType
96+
* @return void
97+
*/
98+
private function skipToken(\Iterator &$tokens, TokenType $tokenType): void
99+
{
100+
Scanner::assertType($tokens, $tokenType);
101+
Scanner::skipOne($tokens);
102+
Scanner::skipSpace($tokens);
103+
}
104+
105+
/**
106+
* @param \Iterator<mixed,Token> $tokens
107+
* @return StringLiteralNode
108+
*/
109+
private function parsePath(\Iterator &$tokens): StringLiteralNode
110+
{
111+
$path = $this->pathParser->parse($tokens);
112+
Scanner::skipSpace($tokens);
113+
114+
return $path;
115+
}
116+
117+
/**
118+
* @param \Iterator<mixed,Token> $tokens
119+
* @return ImportedNameNodes
120+
*/
121+
private function parseNames(\Iterator &$tokens): ImportedNameNodes
122+
{
123+
$items = [];
124+
while (Scanner::type($tokens) !== TokenType::BRACKET_CURLY_CLOSE) {
125+
$items[] = $this->parseName($tokens);
126+
127+
if (Scanner::type($tokens) !== TokenType::BRACKET_CURLY_CLOSE) {
128+
$this->skipToken($tokens, TokenType::COMMA);
129+
}
130+
}
131+
132+
return new ImportedNameNodes(...$items);
133+
}
134+
135+
/**
136+
* @param \Iterator<mixed,Token> $tokens
137+
* @return ImportedNameNode
138+
*/
139+
private function parseName(\Iterator &$tokens): ImportedNameNode
140+
{
141+
$nameToken = $this->extractToken($tokens, TokenType::STRING);
142+
143+
return new ImportedNameNode(
144+
rangeInSource: $nameToken->boundaries,
145+
value: VariableName::from($nameToken->value)
146+
);
147+
}
148+
}

0 commit comments

Comments
 (0)