diff options
Diffstat (limited to 'inc/3rdparty/Twig/ExpressionParser.php')
-rw-r--r-- | inc/3rdparty/Twig/ExpressionParser.php | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/inc/3rdparty/Twig/ExpressionParser.php b/inc/3rdparty/Twig/ExpressionParser.php new file mode 100644 index 00000000..9cf19344 --- /dev/null +++ b/inc/3rdparty/Twig/ExpressionParser.php | |||
@@ -0,0 +1,600 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 Fabien Potencier | ||
7 | * (c) 2009 Armin Ronacher | ||
8 | * | ||
9 | * For the full copyright and license information, please view the LICENSE | ||
10 | * file that was distributed with this source code. | ||
11 | */ | ||
12 | |||
13 | /** | ||
14 | * Parses expressions. | ||
15 | * | ||
16 | * This parser implements a "Precedence climbing" algorithm. | ||
17 | * | ||
18 | * @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm | ||
19 | * @see http://en.wikipedia.org/wiki/Operator-precedence_parser | ||
20 | * | ||
21 | * @author Fabien Potencier <fabien@symfony.com> | ||
22 | */ | ||
23 | class Twig_ExpressionParser | ||
24 | { | ||
25 | const OPERATOR_LEFT = 1; | ||
26 | const OPERATOR_RIGHT = 2; | ||
27 | |||
28 | protected $parser; | ||
29 | protected $unaryOperators; | ||
30 | protected $binaryOperators; | ||
31 | |||
32 | public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators) | ||
33 | { | ||
34 | $this->parser = $parser; | ||
35 | $this->unaryOperators = $unaryOperators; | ||
36 | $this->binaryOperators = $binaryOperators; | ||
37 | } | ||
38 | |||
39 | public function parseExpression($precedence = 0) | ||
40 | { | ||
41 | $expr = $this->getPrimary(); | ||
42 | $token = $this->parser->getCurrentToken(); | ||
43 | while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) { | ||
44 | $op = $this->binaryOperators[$token->getValue()]; | ||
45 | $this->parser->getStream()->next(); | ||
46 | |||
47 | if (isset($op['callable'])) { | ||
48 | $expr = call_user_func($op['callable'], $this->parser, $expr); | ||
49 | } else { | ||
50 | $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); | ||
51 | $class = $op['class']; | ||
52 | $expr = new $class($expr, $expr1, $token->getLine()); | ||
53 | } | ||
54 | |||
55 | $token = $this->parser->getCurrentToken(); | ||
56 | } | ||
57 | |||
58 | if (0 === $precedence) { | ||
59 | return $this->parseConditionalExpression($expr); | ||
60 | } | ||
61 | |||
62 | return $expr; | ||
63 | } | ||
64 | |||
65 | protected function getPrimary() | ||
66 | { | ||
67 | $token = $this->parser->getCurrentToken(); | ||
68 | |||
69 | if ($this->isUnary($token)) { | ||
70 | $operator = $this->unaryOperators[$token->getValue()]; | ||
71 | $this->parser->getStream()->next(); | ||
72 | $expr = $this->parseExpression($operator['precedence']); | ||
73 | $class = $operator['class']; | ||
74 | |||
75 | return $this->parsePostfixExpression(new $class($expr, $token->getLine())); | ||
76 | } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) { | ||
77 | $this->parser->getStream()->next(); | ||
78 | $expr = $this->parseExpression(); | ||
79 | $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); | ||
80 | |||
81 | return $this->parsePostfixExpression($expr); | ||
82 | } | ||
83 | |||
84 | return $this->parsePrimaryExpression(); | ||
85 | } | ||
86 | |||
87 | protected function parseConditionalExpression($expr) | ||
88 | { | ||
89 | while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) { | ||
90 | $this->parser->getStream()->next(); | ||
91 | if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) { | ||
92 | $expr2 = $this->parseExpression(); | ||
93 | if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) { | ||
94 | $this->parser->getStream()->next(); | ||
95 | $expr3 = $this->parseExpression(); | ||
96 | } else { | ||
97 | $expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine()); | ||
98 | } | ||
99 | } else { | ||
100 | $this->parser->getStream()->next(); | ||
101 | $expr2 = $expr; | ||
102 | $expr3 = $this->parseExpression(); | ||
103 | } | ||
104 | |||
105 | $expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine()); | ||
106 | } | ||
107 | |||
108 | return $expr; | ||
109 | } | ||
110 | |||
111 | protected function isUnary(Twig_Token $token) | ||
112 | { | ||
113 | return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]); | ||
114 | } | ||
115 | |||
116 | protected function isBinary(Twig_Token $token) | ||
117 | { | ||
118 | return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]); | ||
119 | } | ||
120 | |||
121 | public function parsePrimaryExpression() | ||
122 | { | ||
123 | $token = $this->parser->getCurrentToken(); | ||
124 | switch ($token->getType()) { | ||
125 | case Twig_Token::NAME_TYPE: | ||
126 | $this->parser->getStream()->next(); | ||
127 | switch ($token->getValue()) { | ||
128 | case 'true': | ||
129 | case 'TRUE': | ||
130 | $node = new Twig_Node_Expression_Constant(true, $token->getLine()); | ||
131 | break; | ||
132 | |||
133 | case 'false': | ||
134 | case 'FALSE': | ||
135 | $node = new Twig_Node_Expression_Constant(false, $token->getLine()); | ||
136 | break; | ||
137 | |||
138 | case 'none': | ||
139 | case 'NONE': | ||
140 | case 'null': | ||
141 | case 'NULL': | ||
142 | $node = new Twig_Node_Expression_Constant(null, $token->getLine()); | ||
143 | break; | ||
144 | |||
145 | default: | ||
146 | if ('(' === $this->parser->getCurrentToken()->getValue()) { | ||
147 | $node = $this->getFunctionNode($token->getValue(), $token->getLine()); | ||
148 | } else { | ||
149 | $node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine()); | ||
150 | } | ||
151 | } | ||
152 | break; | ||
153 | |||
154 | case Twig_Token::NUMBER_TYPE: | ||
155 | $this->parser->getStream()->next(); | ||
156 | $node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); | ||
157 | break; | ||
158 | |||
159 | case Twig_Token::STRING_TYPE: | ||
160 | case Twig_Token::INTERPOLATION_START_TYPE: | ||
161 | $node = $this->parseStringExpression(); | ||
162 | break; | ||
163 | |||
164 | default: | ||
165 | if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) { | ||
166 | $node = $this->parseArrayExpression(); | ||
167 | } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) { | ||
168 | $node = $this->parseHashExpression(); | ||
169 | } else { | ||
170 | throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine(), $this->parser->getFilename()); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | return $this->parsePostfixExpression($node); | ||
175 | } | ||
176 | |||
177 | public function parseStringExpression() | ||
178 | { | ||
179 | $stream = $this->parser->getStream(); | ||
180 | |||
181 | $nodes = array(); | ||
182 | // a string cannot be followed by another string in a single expression | ||
183 | $nextCanBeString = true; | ||
184 | while (true) { | ||
185 | if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) { | ||
186 | $token = $stream->next(); | ||
187 | $nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); | ||
188 | $nextCanBeString = false; | ||
189 | } elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) { | ||
190 | $stream->next(); | ||
191 | $nodes[] = $this->parseExpression(); | ||
192 | $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); | ||
193 | $nextCanBeString = true; | ||
194 | } else { | ||
195 | break; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | $expr = array_shift($nodes); | ||
200 | foreach ($nodes as $node) { | ||
201 | $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine()); | ||
202 | } | ||
203 | |||
204 | return $expr; | ||
205 | } | ||
206 | |||
207 | public function parseArrayExpression() | ||
208 | { | ||
209 | $stream = $this->parser->getStream(); | ||
210 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); | ||
211 | |||
212 | $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); | ||
213 | $first = true; | ||
214 | while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { | ||
215 | if (!$first) { | ||
216 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); | ||
217 | |||
218 | // trailing ,? | ||
219 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { | ||
220 | break; | ||
221 | } | ||
222 | } | ||
223 | $first = false; | ||
224 | |||
225 | $node->addElement($this->parseExpression()); | ||
226 | } | ||
227 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed'); | ||
228 | |||
229 | return $node; | ||
230 | } | ||
231 | |||
232 | public function parseHashExpression() | ||
233 | { | ||
234 | $stream = $this->parser->getStream(); | ||
235 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected'); | ||
236 | |||
237 | $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine()); | ||
238 | $first = true; | ||
239 | while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) { | ||
240 | if (!$first) { | ||
241 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma'); | ||
242 | |||
243 | // trailing ,? | ||
244 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) { | ||
245 | break; | ||
246 | } | ||
247 | } | ||
248 | $first = false; | ||
249 | |||
250 | // a hash key can be: | ||
251 | // | ||
252 | // * a number -- 12 | ||
253 | // * a string -- 'a' | ||
254 | // * a name, which is equivalent to a string -- a | ||
255 | // * an expression, which must be enclosed in parentheses -- (1 + 2) | ||
256 | if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) { | ||
257 | $token = $stream->next(); | ||
258 | $key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); | ||
259 | } elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { | ||
260 | $key = $this->parseExpression(); | ||
261 | } else { | ||
262 | $current = $stream->getCurrent(); | ||
263 | |||
264 | throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine(), $this->parser->getFilename()); | ||
265 | } | ||
266 | |||
267 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); | ||
268 | $value = $this->parseExpression(); | ||
269 | |||
270 | $node->addElement($value, $key); | ||
271 | } | ||
272 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed'); | ||
273 | |||
274 | return $node; | ||
275 | } | ||
276 | |||
277 | public function parsePostfixExpression($node) | ||
278 | { | ||
279 | while (true) { | ||
280 | $token = $this->parser->getCurrentToken(); | ||
281 | if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) { | ||
282 | if ('.' == $token->getValue() || '[' == $token->getValue()) { | ||
283 | $node = $this->parseSubscriptExpression($node); | ||
284 | } elseif ('|' == $token->getValue()) { | ||
285 | $node = $this->parseFilterExpression($node); | ||
286 | } else { | ||
287 | break; | ||
288 | } | ||
289 | } else { | ||
290 | break; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | return $node; | ||
295 | } | ||
296 | |||
297 | public function getFunctionNode($name, $line) | ||
298 | { | ||
299 | switch ($name) { | ||
300 | case 'parent': | ||
301 | $args = $this->parseArguments(); | ||
302 | if (!count($this->parser->getBlockStack())) { | ||
303 | throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename()); | ||
304 | } | ||
305 | |||
306 | if (!$this->parser->getParent() && !$this->parser->hasTraits()) { | ||
307 | throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename()); | ||
308 | } | ||
309 | |||
310 | return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line); | ||
311 | case 'block': | ||
312 | return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line); | ||
313 | case 'attribute': | ||
314 | $args = $this->parseArguments(); | ||
315 | if (count($args) < 2) { | ||
316 | throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename()); | ||
317 | } | ||
318 | |||
319 | return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_TemplateInterface::ANY_CALL, $line); | ||
320 | default: | ||
321 | if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) { | ||
322 | $arguments = new Twig_Node_Expression_Array(array(), $line); | ||
323 | foreach ($this->parseArguments() as $n) { | ||
324 | $arguments->addElement($n); | ||
325 | } | ||
326 | |||
327 | $node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line); | ||
328 | $node->setAttribute('safe', true); | ||
329 | |||
330 | return $node; | ||
331 | } | ||
332 | |||
333 | $args = $this->parseArguments(true); | ||
334 | $class = $this->getFunctionNodeClass($name, $line); | ||
335 | |||
336 | return new $class($name, $args, $line); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | public function parseSubscriptExpression($node) | ||
341 | { | ||
342 | $stream = $this->parser->getStream(); | ||
343 | $token = $stream->next(); | ||
344 | $lineno = $token->getLine(); | ||
345 | $arguments = new Twig_Node_Expression_Array(array(), $lineno); | ||
346 | $type = Twig_TemplateInterface::ANY_CALL; | ||
347 | if ($token->getValue() == '.') { | ||
348 | $token = $stream->next(); | ||
349 | if ( | ||
350 | $token->getType() == Twig_Token::NAME_TYPE | ||
351 | || | ||
352 | $token->getType() == Twig_Token::NUMBER_TYPE | ||
353 | || | ||
354 | ($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue())) | ||
355 | ) { | ||
356 | $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno); | ||
357 | |||
358 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { | ||
359 | $type = Twig_TemplateInterface::METHOD_CALL; | ||
360 | foreach ($this->parseArguments() as $n) { | ||
361 | $arguments->addElement($n); | ||
362 | } | ||
363 | } | ||
364 | } else { | ||
365 | throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename()); | ||
366 | } | ||
367 | |||
368 | if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) { | ||
369 | if (!$arg instanceof Twig_Node_Expression_Constant) { | ||
370 | throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename()); | ||
371 | } | ||
372 | |||
373 | $node = new Twig_Node_Expression_MethodCall($node, 'get'.$arg->getAttribute('value'), $arguments, $lineno); | ||
374 | $node->setAttribute('safe', true); | ||
375 | |||
376 | return $node; | ||
377 | } | ||
378 | } else { | ||
379 | $type = Twig_TemplateInterface::ARRAY_CALL; | ||
380 | |||
381 | // slice? | ||
382 | $slice = false; | ||
383 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) { | ||
384 | $slice = true; | ||
385 | $arg = new Twig_Node_Expression_Constant(0, $token->getLine()); | ||
386 | } else { | ||
387 | $arg = $this->parseExpression(); | ||
388 | } | ||
389 | |||
390 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) { | ||
391 | $slice = true; | ||
392 | $stream->next(); | ||
393 | } | ||
394 | |||
395 | if ($slice) { | ||
396 | if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) { | ||
397 | $length = new Twig_Node_Expression_Constant(null, $token->getLine()); | ||
398 | } else { | ||
399 | $length = $this->parseExpression(); | ||
400 | } | ||
401 | |||
402 | $class = $this->getFilterNodeClass('slice', $token->getLine()); | ||
403 | $arguments = new Twig_Node(array($arg, $length)); | ||
404 | $filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine()); | ||
405 | |||
406 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']'); | ||
407 | |||
408 | return $filter; | ||
409 | } | ||
410 | |||
411 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']'); | ||
412 | } | ||
413 | |||
414 | return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno); | ||
415 | } | ||
416 | |||
417 | public function parseFilterExpression($node) | ||
418 | { | ||
419 | $this->parser->getStream()->next(); | ||
420 | |||
421 | return $this->parseFilterExpressionRaw($node); | ||
422 | } | ||
423 | |||
424 | public function parseFilterExpressionRaw($node, $tag = null) | ||
425 | { | ||
426 | while (true) { | ||
427 | $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE); | ||
428 | |||
429 | $name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); | ||
430 | if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) { | ||
431 | $arguments = new Twig_Node(); | ||
432 | } else { | ||
433 | $arguments = $this->parseArguments(true); | ||
434 | } | ||
435 | |||
436 | $class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine()); | ||
437 | |||
438 | $node = new $class($node, $name, $arguments, $token->getLine(), $tag); | ||
439 | |||
440 | if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) { | ||
441 | break; | ||
442 | } | ||
443 | |||
444 | $this->parser->getStream()->next(); | ||
445 | } | ||
446 | |||
447 | return $node; | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * Parses arguments. | ||
452 | * | ||
453 | * @param Boolean $namedArguments Whether to allow named arguments or not | ||
454 | * @param Boolean $definition Whether we are parsing arguments for a function definition | ||
455 | */ | ||
456 | public function parseArguments($namedArguments = false, $definition = false) | ||
457 | { | ||
458 | $args = array(); | ||
459 | $stream = $this->parser->getStream(); | ||
460 | |||
461 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); | ||
462 | while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) { | ||
463 | if (!empty($args)) { | ||
464 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); | ||
465 | } | ||
466 | |||
467 | if ($definition) { | ||
468 | $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name'); | ||
469 | $value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine()); | ||
470 | } else { | ||
471 | $value = $this->parseExpression(); | ||
472 | } | ||
473 | |||
474 | $name = null; | ||
475 | if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) { | ||
476 | $token = $stream->next(); | ||
477 | if (!$value instanceof Twig_Node_Expression_Name) { | ||
478 | throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename()); | ||
479 | } | ||
480 | $name = $value->getAttribute('name'); | ||
481 | |||
482 | if ($definition) { | ||
483 | $value = $this->parsePrimaryExpression(); | ||
484 | |||
485 | if (!$this->checkConstantExpression($value)) { | ||
486 | throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename()); | ||
487 | } | ||
488 | } else { | ||
489 | $value = $this->parseExpression(); | ||
490 | } | ||
491 | } | ||
492 | |||
493 | if ($definition) { | ||
494 | if (null === $name) { | ||
495 | $name = $value->getAttribute('name'); | ||
496 | $value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine()); | ||
497 | } | ||
498 | $args[$name] = $value; | ||
499 | } else { | ||
500 | if (null === $name) { | ||
501 | $args[] = $value; | ||
502 | } else { | ||
503 | $args[$name] = $value; | ||
504 | } | ||
505 | } | ||
506 | } | ||
507 | $stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); | ||
508 | |||
509 | return new Twig_Node($args); | ||
510 | } | ||
511 | |||
512 | public function parseAssignmentExpression() | ||
513 | { | ||
514 | $targets = array(); | ||
515 | while (true) { | ||
516 | $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to'); | ||
517 | if (in_array($token->getValue(), array('true', 'false', 'none'))) { | ||
518 | throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename()); | ||
519 | } | ||
520 | $targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine()); | ||
521 | |||
522 | if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) { | ||
523 | break; | ||
524 | } | ||
525 | $this->parser->getStream()->next(); | ||
526 | } | ||
527 | |||
528 | return new Twig_Node($targets); | ||
529 | } | ||
530 | |||
531 | public function parseMultitargetExpression() | ||
532 | { | ||
533 | $targets = array(); | ||
534 | while (true) { | ||
535 | $targets[] = $this->parseExpression(); | ||
536 | if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) { | ||
537 | break; | ||
538 | } | ||
539 | $this->parser->getStream()->next(); | ||
540 | } | ||
541 | |||
542 | return new Twig_Node($targets); | ||
543 | } | ||
544 | |||
545 | protected function getFunctionNodeClass($name, $line) | ||
546 | { | ||
547 | $env = $this->parser->getEnvironment(); | ||
548 | |||
549 | if (false === $function = $env->getFunction($name)) { | ||
550 | $message = sprintf('The function "%s" does not exist', $name); | ||
551 | if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) { | ||
552 | $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); | ||
553 | } | ||
554 | |||
555 | throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename()); | ||
556 | } | ||
557 | |||
558 | if ($function instanceof Twig_SimpleFunction) { | ||
559 | return $function->getNodeClass(); | ||
560 | } | ||
561 | |||
562 | return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function'; | ||
563 | } | ||
564 | |||
565 | protected function getFilterNodeClass($name, $line) | ||
566 | { | ||
567 | $env = $this->parser->getEnvironment(); | ||
568 | |||
569 | if (false === $filter = $env->getFilter($name)) { | ||
570 | $message = sprintf('The filter "%s" does not exist', $name); | ||
571 | if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) { | ||
572 | $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); | ||
573 | } | ||
574 | |||
575 | throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename()); | ||
576 | } | ||
577 | |||
578 | if ($filter instanceof Twig_SimpleFilter) { | ||
579 | return $filter->getNodeClass(); | ||
580 | } | ||
581 | |||
582 | return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter'; | ||
583 | } | ||
584 | |||
585 | // checks that the node only contains "constant" elements | ||
586 | protected function checkConstantExpression(Twig_NodeInterface $node) | ||
587 | { | ||
588 | if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) { | ||
589 | return false; | ||
590 | } | ||
591 | |||
592 | foreach ($node as $n) { | ||
593 | if (!$this->checkConstantExpression($n)) { | ||
594 | return false; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | return true; | ||
599 | } | ||
600 | } | ||