Skip to content

Commit

Permalink
Merge pull request #18 from PackageFactory/bugfix/parenthesisInExpres…
Browse files Browse the repository at this point in the history
…sions

BUGFIX: edgecasy parenthesis in expressions
  • Loading branch information
grebaldi authored Aug 5, 2023
2 parents 1b0aeb3 + 8556d4e commit 279738d
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 7 deletions.
15 changes: 11 additions & 4 deletions src/Parser/Ast/ExpressionNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ private function __construct(

public static function fromString(string $expressionAsString): self
{
$tokens = Tokenizer::fromSource(
Source::fromString($expressionAsString)
)->getIterator();
return self::fromTokens(
Tokenizer::fromSource(
Source::fromString($expressionAsString)
)->getIterator()
$tokens
);
}

Expand All @@ -51,7 +52,7 @@ public static function fromString(string $expressionAsString): self
* @param Precedence $precedence
* @return self
*/
public static function fromTokens(\Iterator $tokens, Precedence $precedence = Precedence::SEQUENCE): self
public static function fromTokens(\Iterator &$tokens, Precedence $precedence = Precedence::SEQUENCE): self
{
Scanner::skipSpaceAndComments($tokens);

Expand Down Expand Up @@ -122,8 +123,14 @@ public static function fromTokens(\Iterator $tokens, Precedence $precedence = Pr
}

Scanner::skipSpaceAndComments($tokens);
if (Scanner::isEnd($tokens) || $precedence->mustStopAt(Scanner::type($tokens))) {
return new self(
root: $root
);
}

while (!Scanner::isEnd($tokens) && !$precedence->mustStopAt(Scanner::type($tokens))) {
Scanner::skipSpaceAndComments($tokens);
switch (Scanner::type($tokens)) {
case TokenType::OPERATOR_BOOLEAN_AND:
case TokenType::OPERATOR_BOOLEAN_OR:
Expand Down
9 changes: 7 additions & 2 deletions src/Parser/Tokenizer/LookAhead.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ public function getIterator(): \Iterator
yield $token;
}

yield from $this->tokens;
if (!Scanner::isEnd($this->tokens)) {
yield from $this->tokens;
}
}

public function shift(): void
Expand All @@ -68,8 +70,11 @@ public function shift(): void
Scanner::skipOne($this->tokens);
}

public function type(): TokenType
public function type(): ?TokenType
{
if (Scanner::isEnd($this->tokens)) {
return null;
}
return Scanner::type($this->tokens);
}
}
22 changes: 22 additions & 0 deletions src/Parser/Tokenizer/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,26 @@ public static function isEnd(\Iterator $tokens): bool
{
return !$tokens->valid();
}

/**
* @param \Iterator<mixed,Token> $tokens
*/
public static function debugPrint(\Iterator &$tokens): string
{
$tokens = (function(): \Generator {
throw new \Exception('Once debugged, $tokens is empty.');
// @phpstan-ignore-next-line
yield;
})();

$tokensAsArray = [];
while ($tokens->valid()) {
$tokensAsArray[] = [
"type" => $tokens->current()->type,
"value" => $tokens->current()->value
];
$tokens->next();
}
return json_encode($tokensAsArray, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
}
}
5 changes: 4 additions & 1 deletion src/Parser/Tokenizer/Tokenizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ public static function fromSource(Source $source): Tokenizer
*/
public function getIterator(): \Iterator
{
yield from self::block($this->source->getIterator());
$fragments = $this->source->getIterator();
while ($fragments->valid()) {
yield from self::block($fragments);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@

use PackageFactory\ComponentEngine\Module\ModuleId;
use PackageFactory\ComponentEngine\Parser\Ast\EnumDeclarationNode;
use PackageFactory\ComponentEngine\Parser\Ast\ExpressionNode;
use PackageFactory\ComponentEngine\Parser\Ast\IdentifierNode;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Expression\ExpressionTranspiler;
use PackageFactory\ComponentEngine\Test\Unit\TypeSystem\Scope\Fixtures\DummyScope;
use PackageFactory\ComponentEngine\Target\Php\Transpiler\Identifier\IdentifierTranspiler;
use PackageFactory\ComponentEngine\TypeSystem\Type\EnumType\EnumStaticType;
use PackageFactory\ComponentEngine\TypeSystem\Type\StringType\StringType;
use PHPUnit\Framework\TestCase;

final class IdentifierTranspilerTest extends TestCase
Expand Down Expand Up @@ -82,4 +85,36 @@ public function transpilesIdentifierNodesReferringToEnums(): void
$actualTranspilationResult
);
}

public static function identifierInParenthesisExamples(): mixed
{
// @todo find a better place for these tests, as we actually test the ExpressionNode
return [
'(foo)' => ['(foo)', '$this->foo'],
'((foo))' => ['((foo))', '$this->foo'],
'(((foo)))' => ['(((foo)))', '$this->foo']
];
}

/**
* @dataProvider identifierInParenthesisExamples
* @test
*/
public function identifierInParenthesis(string $expression, string $expectedTranspilationResult): void
{
$expressionTranspiler = new ExpressionTranspiler(
scope: new DummyScope([
"foo" => StringType::get()
])
);

$actualTranspilationResult = $expressionTranspiler->transpile(
ExpressionNode::fromString($expression)
);

$this->assertEquals(
$expectedTranspilationResult,
$actualTranspilationResult
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static function ternaryOperationExamples(): array
{
return [
'true ? 42 : "foo"' => ['true ? 42 : "foo"', '(true ? 42 : \'foo\')'],
'(true) ? 42 : "foo"' => ['(true) ? 42 : "foo"', '(true ? 42 : \'foo\')'],
'a ? 42 : "foo"' => ['a ? 42 : "foo"', '($this->a ? 42 : \'foo\')'],
'true ? b : "foo"' => ['true ? b : "foo"', '(true ? $this->b : \'foo\')'],
'true ? 42 : c' => ['true ? 42 : c', '(true ? 42 : $this->c)'],
Expand Down

0 comments on commit 279738d

Please sign in to comment.