Skip to content

Commit ac90fc2

Browse files
committed
BUGFIX: parenthesis in expressions
previously `ExpressionNode::fromString("(foo)")` couldnt be parsed (it worked though when used in return statement of a module) but the real fix, is that you couldnt write `return ((foo))` as $tokens is reassigned but not passed by ref
1 parent 21c6727 commit ac90fc2

4 files changed

Lines changed: 62 additions & 7 deletions

File tree

src/Parser/Ast/ExpressionNode.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ private function __construct(
3939

4040
public static function fromString(string $expressionAsString): self
4141
{
42+
$tokens = Tokenizer::fromSource(
43+
Source::fromString($expressionAsString)
44+
)->getIterator();
4245
return self::fromTokens(
43-
Tokenizer::fromSource(
44-
Source::fromString($expressionAsString)
45-
)->getIterator()
46+
$tokens
4647
);
4748
}
4849

@@ -51,7 +52,7 @@ public static function fromString(string $expressionAsString): self
5152
* @param Precedence $precedence
5253
* @return self
5354
*/
54-
public static function fromTokens(\Iterator $tokens, Precedence $precedence = Precedence::SEQUENCE): self
55+
public static function fromTokens(\Iterator &$tokens, Precedence $precedence = Precedence::SEQUENCE): self
5556
{
5657
Scanner::skipSpaceAndComments($tokens);
5758

@@ -62,7 +63,7 @@ public static function fromTokens(\Iterator $tokens, Precedence $precedence = Pr
6263
$lookAhead->shift();
6364

6465
while (true) {
65-
switch ($lookAhead->type()) {
66+
switch (Scanner::isEnd($lookAhead->tokens) ?: $lookAhead->type()) {
6667
case TokenType::STRING:
6768
case TokenType::COLON:
6869
case TokenType::COMMA:

src/Parser/Tokenizer/LookAhead.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ public function getIterator(): \Iterator
5858
yield $token;
5959
}
6060

61-
yield from $this->tokens;
61+
if (!Scanner::isEnd($this->tokens)) {
62+
yield from $this->tokens;
63+
}
6264
}
6365

6466
public function shift(): void

src/Parser/Tokenizer/Scanner.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,20 @@ public static function isEnd(\Iterator $tokens): bool
165165
{
166166
return !$tokens->valid();
167167
}
168+
169+
/**
170+
* @param \Iterator<mixed,Token> $tokens
171+
*/
172+
public static function debugPrint(\Iterator $tokens): string
173+
{
174+
$tokensAsArray = [];
175+
while ($tokens->valid()) {
176+
$tokensAsArray[] = [
177+
"type" => $tokens->current()->type,
178+
"value" => $tokens->current()->value
179+
];
180+
$tokens->next();
181+
}
182+
return json_encode($tokensAsArray, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
183+
}
168184
}

test/Unit/Target/Php/Transpiler/Identifier/IdentifierTranspilerTest.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
namespace PackageFactory\ComponentEngine\Test\Unit\Target\Php\Transpiler\Identifier;
2424

2525
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
26+
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
2627
use PackageFactory\ComponentEngine\Parser\Ast\IdentifierNode;
28+
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Expression\ExpressionTranspiler;
2729
use PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Scope\Fixtures\DummyScope;
2830
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Identifier\IdentifierTranspiler;
2931
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
32+
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
3033
use PHPUnit\Framework\TestCase;
3134

3235
final class IdentifierTranspilerTest extends TestCase
@@ -80,4 +83,37 @@ public function transpilesIdentifierNodesReferringToEnums(): void
8083
$actualTranspilationResult
8184
);
8285
}
83-
}
86+
87+
88+
public function identifierInParenthesisExamples(): mixed
89+
{
90+
// @todo find a better place for these tests, as we actually test the ExpressionNode
91+
return [
92+
'(foo)' => ['(foo)', '$this->foo'],
93+
'((foo))' => ['((foo))', '$this->foo'],
94+
'(((foo)))' => ['(((foo)))', '$this->foo']
95+
];
96+
}
97+
98+
/**
99+
* @dataProvider identifierInParenthesisExamples
100+
* @test
101+
*/
102+
public function identifierInParenthesis(string $expression, string $expectedTranspilationResult): void
103+
{
104+
$expressionTranspiler = new ExpressionTranspiler(
105+
scope: new DummyScope([
106+
"foo" => StringType::get()
107+
])
108+
);
109+
110+
$actualTranspilationResult = $expressionTranspiler->transpile(
111+
ExpressionNode::fromString($expression)
112+
);
113+
114+
$this->assertEquals(
115+
$expectedTranspilationResult,
116+
$actualTranspilationResult
117+
);
118+
}
119+
}

0 commit comments

Comments
 (0)