From ceb47491938cc81c0a09e491cafad14972284203 Mon Sep 17 00:00:00 2001 From: Jeroen Versteeg Date: Tue, 27 Aug 2024 09:54:41 +0200 Subject: [PATCH] Add TypesTokenParser --- src/Extension/CoreExtension.php | 2 + src/Node/TypesNode.php | 2 + src/TokenParser/TypesTokenParser.php | 88 ++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 src/TokenParser/TypesTokenParser.php diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php index 73caf8a5d30..c6a7e2f78f3 100644 --- a/src/Extension/CoreExtension.php +++ b/src/Extension/CoreExtension.php @@ -82,6 +82,7 @@ use Twig\TokenParser\IncludeTokenParser; use Twig\TokenParser\MacroTokenParser; use Twig\TokenParser\SetTokenParser; +use Twig\TokenParser\TypesTokenParser; use Twig\TokenParser\UseTokenParser; use Twig\TokenParser\WithTokenParser; use Twig\TwigFilter; @@ -182,6 +183,7 @@ public function getTokenParsers(): array new ImportTokenParser(), new FromTokenParser(), new SetTokenParser(), + new TypesTokenParser(), new FlushTokenParser(), new DoTokenParser(), new EmbedTokenParser(), diff --git a/src/Node/TypesNode.php b/src/Node/TypesNode.php index 4473aa446f8..27f0b1d2a9d 100644 --- a/src/Node/TypesNode.php +++ b/src/Node/TypesNode.php @@ -2,6 +2,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\ConstantExpression; @@ -13,6 +14,7 @@ * @author Jeroen Versteeg * @see https://github.com/twigphp/Twig/issues/4165 */ +#[YieldReady] class TypesNode extends Node implements NodeCaptureInterface { public function __construct(ArrayExpression $typesNode, int $lineno, ?string $tag = null) diff --git a/src/TokenParser/TypesTokenParser.php b/src/TokenParser/TypesTokenParser.php new file mode 100644 index 00000000000..2bbe183fbee --- /dev/null +++ b/src/TokenParser/TypesTokenParser.php @@ -0,0 +1,88 @@ + + * @see https://github.com/twigphp/Twig/issues/4165 + * @internal + */ +final class TypesTokenParser extends AbstractTokenParser +{ + public function parse(Token $token): Node + { + $stream = $this->parser->getStream(); + + $expression = $this->parseSimpleMappingExpression($stream); + + $stream->expect(Token::BLOCK_END_TYPE); + + return new TypesNode($expression, $token->getLine(), $this->getTag()); + } + + /** + * @throws SyntaxError + * @see ExpressionParser::parseMappingExpression() + */ + private function parseSimpleMappingExpression(TokenStream $stream): ArrayExpression + { + $stream->expect(Token::PUNCTUATION_TYPE, '{', 'A mapping element was expected'); + + $node = new ArrayExpression([], $stream->getCurrent()->getLine()); + + $first = true; + while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) { + if (!$first) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A mapping value must be followed by a comma'); + + // trailing ,? + if ($stream->test(Token::PUNCTUATION_TYPE, '}')) { + break; + } + } + $first = false; + + $nameToken = $stream->expect(Token::NAME_TYPE); + $nameExpression = new NameExpression($nameToken->getValue(), $nameToken->getLine()); + + $stream->expect(Token::PUNCTUATION_TYPE, ':', 'A mapping key must be followed by a colon (:)'); + + $valueToken = $stream->expect(Token::STRING_TYPE); + $valueExpression = new ConstantExpression($valueToken->getValue(), $valueToken->getLine()); + + $node->addElement($valueExpression, $nameExpression); + } + $stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed'); + + return $node; + } + + public function getTag(): string + { + return 'types'; + } +}