Skip to content

Commit 14f6044

Browse files
committed
TASK: Replace IntegerFormat::fromTokenType with parser-specific method
1 parent 93607f6 commit 14f6044

File tree

4 files changed

+141
-39
lines changed

4 files changed

+141
-39
lines changed

src/Language/AST/Node/IntegerLiteral/IntegerFormat.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,4 @@ enum IntegerFormat: string
3030
case OCTAL = 'OCTAL';
3131
case DECIMAL = 'DECIMAL';
3232
case HEXADECIMAL = 'HEXADECIMAL';
33-
34-
public static function fromTokenType(TokenType $tokenType): self
35-
{
36-
return match ($tokenType) {
37-
TokenType::NUMBER_BINARY => self::BINARY,
38-
TokenType::NUMBER_OCTAL => self::OCTAL,
39-
TokenType::NUMBER_DECIMAL => self::DECIMAL,
40-
TokenType::NUMBER_HEXADECIMAL => self::HEXADECIMAL,
41-
42-
default => throw new \Exception('@TODO: Unknown Integer Format: ' . $tokenType->value)
43-
};
44-
}
4533
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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\IntegerLiteral;
24+
25+
use PackageFactory\ComponentEngine\Language\Parser\ParserException;
26+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;
27+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenTypes;
28+
29+
final class IntegerLiteralCouldNotBeParsed extends ParserException
30+
{
31+
public static function becauseOfUnexpectedEndOfFile(): self
32+
{
33+
return new self(
34+
code: 1691238474,
35+
message: 'Integer literal could not be parsed because of unexpected end of file.'
36+
);
37+
}
38+
39+
public static function becauseOfUnexpectedToken(
40+
TokenTypes $expectedTokenTypes,
41+
Token $actualToken
42+
): self {
43+
return new self(
44+
code: 1691238491,
45+
message: sprintf(
46+
'Integer literal could not be parsed because of unexpected token %s. '
47+
. 'Expected %s instead.',
48+
$actualToken->toDebugString(),
49+
$expectedTokenTypes->toDebugString()
50+
),
51+
affectedRangeInSource: $actualToken->boundaries
52+
);
53+
}
54+
}

src/Language/Parser/IntegerLiteral/IntegerLiteralParser.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
use PackageFactory\ComponentEngine\Language\AST\Node\IntegerLiteral\IntegerLiteralNode;
2727
use PackageFactory\ComponentEngine\Parser\Tokenizer\Scanner;
2828
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;
29+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType;
30+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenTypes;
2931

3032
final class IntegerLiteralParser
3133
{
@@ -35,14 +37,38 @@ final class IntegerLiteralParser
3537
*/
3638
public function parse(\Iterator &$tokens): IntegerLiteralNode
3739
{
40+
if (Scanner::isEnd($tokens)) {
41+
throw IntegerLiteralCouldNotBeParsed::becauseOfUnexpectedEndOfFile();
42+
}
43+
3844
$token = $tokens->current();
3945

4046
Scanner::skipOne($tokens);
4147

4248
return new IntegerLiteralNode(
4349
rangeInSource: $token->boundaries,
44-
format: IntegerFormat::fromTokenType($token->type),
50+
format: $this->getIntegerFormatFromToken($token),
4551
value: $token->value
4652
);
4753
}
54+
55+
private function getIntegerFormatFromToken(Token $token): IntegerFormat
56+
{
57+
return match ($token->type) {
58+
TokenType::NUMBER_BINARY => IntegerFormat::BINARY,
59+
TokenType::NUMBER_OCTAL => IntegerFormat::OCTAL,
60+
TokenType::NUMBER_DECIMAL => IntegerFormat::DECIMAL,
61+
TokenType::NUMBER_HEXADECIMAL => IntegerFormat::HEXADECIMAL,
62+
63+
default => throw IntegerLiteralCouldNotBeParsed::becauseOfUnexpectedToken(
64+
expectedTokenTypes: TokenTypes::from(
65+
TokenType::NUMBER_BINARY,
66+
TokenType::NUMBER_OCTAL,
67+
TokenType::NUMBER_DECIMAL,
68+
TokenType::NUMBER_HEXADECIMAL
69+
),
70+
actualToken: $token
71+
)
72+
};
73+
}
4874
}

test/Unit/Language/Parser/IntegerLiteral/IntegerLiteralParserTest.php

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,26 @@
2424

2525
use PackageFactory\ComponentEngine\Language\AST\Node\IntegerLiteral\IntegerFormat;
2626
use PackageFactory\ComponentEngine\Language\AST\Node\IntegerLiteral\IntegerLiteralNode;
27+
use PackageFactory\ComponentEngine\Language\Parser\IntegerLiteral\IntegerLiteralCouldNotBeParsed;
2728
use PackageFactory\ComponentEngine\Language\Parser\IntegerLiteral\IntegerLiteralParser;
28-
use PackageFactory\ComponentEngine\Parser\Source\Range;
29-
use PackageFactory\ComponentEngine\Parser\Source\Position;
30-
use PackageFactory\ComponentEngine\Parser\Source\Source;
31-
use PackageFactory\ComponentEngine\Parser\Tokenizer\Tokenizer;
32-
use PHPUnit\Framework\TestCase;
29+
use PackageFactory\ComponentEngine\Parser\Source\Path;
30+
use PackageFactory\ComponentEngine\Parser\Tokenizer\Token;
31+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenType;
32+
use PackageFactory\ComponentEngine\Parser\Tokenizer\TokenTypes;
33+
use PackageFactory\ComponentEngine\Test\Unit\Language\Parser\ParserTestCase;
3334

34-
final class IntegerLiteralParserTest extends TestCase
35+
final class IntegerLiteralParserTest extends ParserTestCase
3536
{
3637
/**
3738
* @test
3839
*/
3940
public function binaryInteger(): void
4041
{
4142
$integerLiteralParser = new IntegerLiteralParser();
42-
$tokens = Tokenizer::fromSource(Source::fromString('0b1010110101'))->getIterator();
43+
$tokens = $this->createTokenIterator('0b1010110101');
4344

4445
$expectedIntegerLiteralNode = new IntegerLiteralNode(
45-
rangeInSource: Range::from(
46-
new Position(0, 0),
47-
new Position(0, 11)
48-
),
46+
rangeInSource: $this->range([0, 0], [0, 11]),
4947
format: IntegerFormat::BINARY,
5048
value: '0b1010110101'
5149
);
@@ -62,13 +60,10 @@ public function binaryInteger(): void
6260
public function octalInteger(): void
6361
{
6462
$integerLiteralParser = new IntegerLiteralParser();
65-
$tokens = Tokenizer::fromSource(Source::fromString('0o755'))->getIterator();
63+
$tokens = $this->createTokenIterator('0o755');
6664

6765
$expectedIntegerLiteralNode = new IntegerLiteralNode(
68-
rangeInSource: Range::from(
69-
new Position(0, 0),
70-
new Position(0, 4)
71-
),
66+
rangeInSource: $this->range([0, 0], [0, 4]),
7267
format: IntegerFormat::OCTAL,
7368
value: '0o755'
7469
);
@@ -85,13 +80,10 @@ public function octalInteger(): void
8580
public function decimalInteger(): void
8681
{
8782
$integerLiteralParser = new IntegerLiteralParser();
88-
$tokens = Tokenizer::fromSource(Source::fromString('1234567890'))->getIterator();
83+
$tokens = $this->createTokenIterator('1234567890');
8984

9085
$expectedIntegerLiteralNode = new IntegerLiteralNode(
91-
rangeInSource: Range::from(
92-
new Position(0, 0),
93-
new Position(0, 9)
94-
),
86+
rangeInSource: $this->range([0, 0], [0, 9]),
9587
format: IntegerFormat::DECIMAL,
9688
value: '1234567890'
9789
);
@@ -108,13 +100,10 @@ public function decimalInteger(): void
108100
public function hexadecimalInteger(): void
109101
{
110102
$integerLiteralParser = new IntegerLiteralParser();
111-
$tokens = Tokenizer::fromSource(Source::fromString('0x123456789ABCDEF'))->getIterator();
103+
$tokens = $this->createTokenIterator('0x123456789ABCDEF');
112104

113105
$expectedIntegerLiteralNode = new IntegerLiteralNode(
114-
rangeInSource: Range::from(
115-
new Position(0, 0),
116-
new Position(0, 16)
117-
),
106+
rangeInSource: $this->range([0, 0], [0, 16]),
118107
format: IntegerFormat::HEXADECIMAL,
119108
value: '0x123456789ABCDEF'
120109
);
@@ -124,4 +113,49 @@ public function hexadecimalInteger(): void
124113
$integerLiteralParser->parse($tokens)
125114
);
126115
}
116+
117+
/**
118+
* @test
119+
*/
120+
public function throwsIfTokenStreamsEndsUnexpectedly(): void
121+
{
122+
$this->assertThrowsParserException(
123+
function () {
124+
$integerLiteralParser = new IntegerLiteralParser();
125+
$tokens = $this->createTokenIterator('');
126+
127+
$integerLiteralParser->parse($tokens);
128+
},
129+
IntegerLiteralCouldNotBeParsed::becauseOfUnexpectedEndOfFile()
130+
);
131+
}
132+
133+
/**
134+
* @test
135+
*/
136+
public function throwsIfUnexpectedTokenIsEncountered(): void
137+
{
138+
$this->assertThrowsParserException(
139+
function () {
140+
$integerLiteralParser = new IntegerLiteralParser();
141+
$tokens = $this->createTokenIterator('foo1234');
142+
143+
$integerLiteralParser->parse($tokens);
144+
},
145+
IntegerLiteralCouldNotBeParsed::becauseOfUnexpectedToken(
146+
expectedTokenTypes: TokenTypes::from(
147+
TokenType::NUMBER_BINARY,
148+
TokenType::NUMBER_OCTAL,
149+
TokenType::NUMBER_DECIMAL,
150+
TokenType::NUMBER_HEXADECIMAL
151+
),
152+
actualToken: new Token(
153+
type: TokenType::STRING,
154+
value: 'foo1234',
155+
boundaries: $this->range([0, 0], [0, 6]),
156+
sourcePath: Path::createMemory()
157+
)
158+
)
159+
);
160+
}
127161
}

0 commit comments

Comments
 (0)