Skip to content

Commit

Permalink
Implement stof's feedback for TypesTokenParser and its test
Browse files Browse the repository at this point in the history
  • Loading branch information
drjayvee committed Aug 29, 2024
1 parent a78e561 commit 77336e1
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 4 deletions.
1 change: 0 additions & 1 deletion src/Node/TypesNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* Represents a types node.
*
* @author Jeroen Versteeg <[email protected]>
* @see https://github.com/twigphp/Twig/issues/4165
*/
#[YieldReady]
class TypesNode extends Node implements NodeCaptureInterface
Expand Down
8 changes: 5 additions & 3 deletions src/TokenParser/TypesTokenParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
* {% types {foo: 'int', bar: 'string'} %}
*
* @author Jeroen Versteeg <[email protected]>
* @see https://github.com/twigphp/Twig/issues/4165
* @internal
*/
final class TypesTokenParser extends AbstractTokenParser
Expand Down Expand Up @@ -57,7 +56,7 @@ private function parseSimpleMappingExpression(TokenStream $stream): ArrayExpress
$first = true;
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
if (!$first) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A mapping value must be followed by a comma');
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A type string must be followed by a comma');

// trailing ,?
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
Expand All @@ -69,7 +68,10 @@ private function parseSimpleMappingExpression(TokenStream $stream): ArrayExpress
$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 (:)');
$isOptional = $stream->nextIf(Token::PUNCTUATION_TYPE, '?') !== null;
$nameExpression->setAttribute('is_optional', $isOptional);

$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A name must be followed by a colon (:)');

$valueToken = $stream->expect(Token::STRING_TYPE);
$valueExpression = new ConstantExpression($valueToken->getValue(), $valueToken->getLine());
Expand Down
81 changes: 81 additions & 0 deletions tests/Node/TypesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

namespace Twig\Tests\Node;

use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\TypesNode;
use Twig\Parser;
use Twig\Source;
use Twig\Test\NodeTestCase;

class TypesTest extends NodeTestCase
Expand Down Expand Up @@ -100,6 +104,83 @@ public function testConstructorThrowsOnInvalidMapping(ArrayExpression $mapping,
new TypesNode($mapping, 1);
}

/** @dataProvider getMappingTests */
public function testMappingParsing(string $template, ArrayExpression $expected): void
{
$env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]);
$stream = $env->tokenize($source = new Source($template, ''));
$parser = new Parser($env);
$expected->setSourceContext($source);

$typesNode = $parser->parse($stream)->getNode('body')->getNode('0');

self::assertEquals($expected, $typesNode->getNode('mapping'));
}

public function getMappingTests(): array
{
return [
// empty mapping
[
'{% types {} %}',
new ArrayExpression([], 1),
],

// simple
[
'{% types {foo: "bar"} %}',
new ArrayExpression([
$this->createNameExpression('foo', false),
new ConstantExpression('bar', 1),
], 1),
['foo' => 'bar'],
],

// trailing comma
[
'{% types {foo: "bar",} %}',
new ArrayExpression([
$this->createNameExpression('foo', false),
new ConstantExpression('bar', 1),
], 1),
['foo' => 'bar'],
],

// optional name
[
'{% types {foo?: "bar"} %}',
new ArrayExpression([
$this->createNameExpression('foo', true),
new ConstantExpression('bar', 1),
], 1),
['foo?' => 'bar'],
],

// multiple pairs, duplicate values
[
'{% types {foo: "foo", bar?: "foo", baz: "baz"} %}',
new ArrayExpression([
$this->createNameExpression('foo', false),
new ConstantExpression('foo', 1),

$this->createNameExpression('bar', true),
new ConstantExpression('foo', 1),

$this->createNameExpression('baz', false),
new ConstantExpression('baz', 1),
], 1),
['foo' => 'foo', 'bar?' => 'foo', 'baz' => 'baz'],
],
];
}

private function createNameExpression(string $name, bool $isOptional): NameExpression
{
$name = new NameExpression($name, 1);
$name->setAttribute('is_optional', $isOptional);
return $name;
}

public function getTests()
{
return [
Expand Down

0 comments on commit 77336e1

Please sign in to comment.