-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathTypesTokenParser.php
More file actions
98 lines (83 loc) · 2.78 KB
/
TypesTokenParser.php
File metadata and controls
98 lines (83 loc) · 2.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\TokenParser;
use Twig\Error\SyntaxError;
use Twig\Node\Node;
use Twig\Node\TypesNode;
use Twig\Token;
use Twig\TokenStream;
/**
* Declare variable types.
*
* {% types {foo: 'number', bar?: 'string'} %}
*
* @author Jeroen Versteeg <jeroen@alisqi.com>
*
* @internal
*/
final class TypesTokenParser extends AbstractTokenParser
{
public function parse(Token $token): Node
{
$stream = $this->parser->getStream();
$types = $this->parseSimpleMappingExpression($stream);
$stream->expect(Token::BLOCK_END_TYPE);
return new TypesNode($types, $token->getLine());
}
/**
* @return array<string, array{type: string, optional: bool, docs: ?string}>
*
* @throws SyntaxError
*/
private function parseSimpleMappingExpression(TokenStream $stream): array
{
$enclosed = null !== $stream->nextIf(Token::PUNCTUATION_TYPE, '{');
$types = [];
$first = true;
while (!($stream->test(Token::PUNCTUATION_TYPE, '}') || $stream->test(Token::BLOCK_END_TYPE))) {
if (!$first) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A type string must be followed by a comma');
// trailing ,?
if ($stream->test(Token::PUNCTUATION_TYPE, '}') || $stream->test(Token::BLOCK_END_TYPE)) {
break;
}
}
$first = false;
$nameToken = $stream->expect(Token::NAME_TYPE);
if ($stream->nextIf(Token::OPERATOR_TYPE, '?:')) {
$isOptional = true;
} else {
$isOptional = null !== $stream->nextIf(Token::OPERATOR_TYPE, '?');
$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A type name must be followed by a colon (:)');
}
$valueToken = $stream->expect(Token::STRING_TYPE);
// Check for optional docs="..." attribute
$docs = null;
if ($stream->test(Token::NAME_TYPE, 'docs')) {
$stream->next();
$stream->expect(Token::OPERATOR_TYPE, '=');
$docs = $stream->expect(Token::STRING_TYPE)->getValue();
}
$types[$nameToken->getValue()] = [
'type' => $valueToken->getValue(),
'optional' => $isOptional,
'docs' => $docs,
];
}
if ($enclosed) {
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed');
}
return $types;
}
public function getTag(): string
{
return 'types';
}
}