Skip to content

Commit

Permalink
Use generics in ExpressionParsers
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Feb 6, 2025
1 parent aaf58c5 commit d36c46e
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 46 deletions.
6 changes: 3 additions & 3 deletions src/ExpressionParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ public function parseSubscriptExpression($node)
$parsers = new \ReflectionProperty($this->parser, 'parsers');

if ('.' === $this->parser->getStream()->next()->getValue()) {
return $parsers->getValue($this->parser)->getInfixByClass(DotExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken());
return $parsers->getValue($this->parser)->getByClass(DotExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken());
}

return $parsers->getValue($this->parser)->getInfixByClass(SquareBracketExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken());
return $parsers->getValue($this->parser)->getByClass(SquareBracketExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken());
}

/**
Expand All @@ -189,7 +189,7 @@ public function parseFilterExpressionRaw($node)

$parsers = new \ReflectionProperty($this->parser, 'parsers');

$op = $parsers->getValue($this->parser)->getInfixByClass(FilterExpressionParser::class);
$op = $parsers->getValue($this->parser)->getByClass(FilterExpressionParser::class);
while (true) {
$node = $op->parse($this->parser, $node, $this->parser->getCurrentToken());
if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) {
Expand Down
59 changes: 22 additions & 37 deletions src/ExpressionParser/ExpressionParsers.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,15 @@
final class ExpressionParsers implements \IteratorAggregate
{
/**
* @var array<value-of<ExpressionParserType>, array<string, ExpressionParserInterface>>
* @var array<class-string<ExpressionParserInterface>, array<string, ExpressionParserInterface>>
*/
private array $parsers = [];
private array $parsersByName = [];

/**
* @var array<value-of<ExpressionParserType>, array<class-string<ExpressionParserInterface>, ExpressionParserInterface>>
* @var array<class-string<ExpressionParserInterface>, ExpressionParserInterface>
*/
private array $parsersByClass = [];

/**
* @var array<value-of<ExpressionParserType>, array<string, ExpressionParserInterface>>
*/
private array $aliases = [];

/**
* @var \WeakMap<ExpressionParserInterface, array<ExpressionParserInterface>>|null
*/
Expand All @@ -59,54 +54,44 @@ public function add(array $parsers): self
trigger_deprecation('twig/twig', '3.20', 'Precedence for "%s" must be between -512 and 512, got %d.', $parser->getName(), $parser->getPrecedence());
// throw new \InvalidArgumentException(\sprintf('Precedence for "%s" must be between -1024 and 1024, got %d.', $parser->getName(), $parser->getPrecedence()));
}
$type = ExpressionParserType::getType($parser);
$this->parsers[$type->value][$parser->getName()] = $parser;
$this->parsersByClass[$type->value][get_class($parser)] = $parser;
$interface = $parser instanceof PrefixExpressionParserInterface ? PrefixExpressionParserInterface::class : InfixExpressionParserInterface::class;
$this->parsersByName[$interface][$parser->getName()] = $parser;
$this->parsersByClass[get_class($parser)] = $parser;
foreach ($parser->getAliases() as $alias) {
$this->aliases[$type->value][$alias] = $parser;
$this->parsersByName[$interface][$alias] = $parser;
}
}

return $this;
}

/**
* @param class-string<PrefixExpressionParserInterface> $name
* @template T of ExpressionParserInterface
*
* @param class-string<T> $class
*
* @return T|null
*/
public function getPrefixByClass(string $name): ?PrefixExpressionParserInterface
public function getByClass(string $class): ?ExpressionParserInterface
{
return $this->parsersByClass[ExpressionParserType::Prefix->value][$name] ?? null;
}

public function getPrefix(string $name): ?PrefixExpressionParserInterface
{
return
$this->parsers[ExpressionParserType::Prefix->value][$name]
?? $this->aliases[ExpressionParserType::Prefix->value][$name]
?? null
;
return $this->parsersByClass[$class] ?? null;
}

/**
* @param class-string<InfixExpressionParserInterface> $name
* @template T of ExpressionParserInterface
*
* @param class-string<T> $interface
*
* @return T|null
*/
public function getInfixByClass(string $name): ?InfixExpressionParserInterface
{
return $this->parsersByClass[ExpressionParserType::Infix->value][$name] ?? null;
}

public function getInfix(string $name): ?InfixExpressionParserInterface
public function getByName(string $interface, string $name): ?ExpressionParserInterface
{
return
$this->parsers[ExpressionParserType::Infix->value][$name]
?? $this->aliases[ExpressionParserType::Infix->value][$name]
?? null
;
return $this->parsersByName[$interface][$name] ?? null;
}

public function getIterator(): \Traversable
{
foreach ($this->parsers as $parsers) {
foreach ($this->parsersByName as $parsers) {
// we don't yield the keys
yield from $parsers;
}
Expand Down
7 changes: 4 additions & 3 deletions src/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Twig\ExpressionParser\ExpressionParserInterface;
use Twig\ExpressionParser\ExpressionParsers;
use Twig\ExpressionParser\ExpressionParserType;
use Twig\ExpressionParser\InfixExpressionParserInterface;
use Twig\ExpressionParser\Prefix\LiteralExpressionParser;
use Twig\ExpressionParser\PrefixExpressionParserInterface;
use Twig\Node\BlockNode;
Expand Down Expand Up @@ -357,16 +358,16 @@ public function getExpressionParser(): ExpressionParser
public function parseExpression(int $precedence = 0): AbstractExpression
{
$token = $this->getCurrentToken();
if ($token->test(Token::OPERATOR_TYPE) && $ep = $this->parsers->getPrefix($token->getValue())) {
if ($token->test(Token::OPERATOR_TYPE) && $ep = $this->parsers->getByName(PrefixExpressionParserInterface::class, $token->getValue())) {
$this->getStream()->next();
$expr = $ep->parse($this, $token);
$this->checkPrecedenceDeprecations($ep, $expr);
} else {
$expr = $this->parsers->getPrefixByClass(LiteralExpressionParser::class)->parse($this, $token);
$expr = $this->parsers->getByClass(LiteralExpressionParser::class)->parse($this, $token);
}

$token = $this->getCurrentToken();
while ($token->test(Token::OPERATOR_TYPE) && ($ep = $this->parsers->getInfix($token->getValue())) && $ep->getPrecedence() >= $precedence) {
while ($token->test(Token::OPERATOR_TYPE) && ($ep = $this->parsers->getByName(InfixExpressionParserInterface::class, $token->getValue())) && $ep->getPrecedence() >= $precedence) {
$this->getStream()->next();
$expr = $ep->parse($this, $expr, $token);
$this->checkPrecedenceDeprecations($ep, $expr);
Expand Down
2 changes: 1 addition & 1 deletion src/TokenParser/ApplyTokenParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function parse(Token $token): Node
$lineno = $token->getLine();
$ref = new LocalVariable(null, $lineno);
$filter = $ref;
$op = $this->parser->getEnvironment()->getExpressionParsers()->getInfixByClass(FilterExpressionParser::class);
$op = $this->parser->getEnvironment()->getExpressionParsers()->getByClass(FilterExpressionParser::class);
while (true) {
$filter = $op->parse($this->parser, $filter, $this->parser->getCurrentToken());
if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) {
Expand Down
6 changes: 4 additions & 2 deletions tests/EnvironmentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\ExpressionParser\Infix\BinaryOperatorExpressionParser;
use Twig\ExpressionParser\InfixExpressionParserInterface;
use Twig\ExpressionParser\Prefix\UnaryOperatorExpressionParser;
use Twig\ExpressionParser\PrefixExpressionParserInterface;
use Twig\Extension\AbstractExtension;
use Twig\Extension\ExtensionInterface;
use Twig\Extension\GlobalsInterface;
Expand Down Expand Up @@ -309,8 +311,8 @@ public function testAddExtension()
$this->assertArrayHasKey('foo_filter', $twig->getFilters());
$this->assertArrayHasKey('foo_function', $twig->getFunctions());
$this->assertArrayHasKey('foo_test', $twig->getTests());
$this->assertNotNull($twig->getExpressionParsers()->getPrefix('foo_unary'));
$this->assertNotNull($twig->getExpressionParsers()->getInfix('foo_binary'));
$this->assertNotNull($twig->getExpressionParsers()->getByName(PrefixExpressionParserInterface::class, 'foo_unary'));
$this->assertNotNull($twig->getExpressionParsers()->getByName(InfixExpressionParserInterface::class, 'foo_binary'));
$this->assertArrayHasKey('foo_global', $twig->getGlobals());
$visitors = $twig->getNodeVisitors();
$found = false;
Expand Down

0 comments on commit d36c46e

Please sign in to comment.