Вход Регистрация
Файл: upload/core/vendor/twig/twig/src/ExpressionParser/Prefix/LiteralExpressionParser.php
Строк: 375
<?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 TwigExpressionParserPrefix;

use 
TwigErrorSyntaxError;
use 
TwigExpressionParserAbstractExpressionParser;
use 
TwigExpressionParserExpressionParserDescriptionInterface;
use 
TwigExpressionParserPrefixExpressionParserInterface;
use 
TwigLexer;
use 
TwigNodeExpressionAbstractExpression;
use 
TwigNodeExpressionArrayExpression;
use 
TwigNodeExpressionBinaryConcatBinary;
use 
TwigNodeExpressionConstantExpression;
use 
TwigNodeExpressionEmptyExpression;
use 
TwigNodeExpressionVariableContextVariable;
use 
TwigParser;
use 
TwigToken;

/**
 * @internal
 */
final class LiteralExpressionParser extends AbstractExpressionParser implements PrefixExpressionParserInterfaceExpressionParserDescriptionInterface
{
    private 
string $type 'literal';

    public function 
parse(Parser $parserToken $token): AbstractExpression
    
{
        
$stream $parser->getStream();
        switch (
true) {
            case 
$token->test(Token::NAME_TYPE):
                
$stream->next();
                switch (
$token->getValue()) {
                    case 
'true':
                    case 
'TRUE':
                        
$this->type 'constant';

                        return new 
ConstantExpression(true$token->getLine());

                    case 
'false':
                    case 
'FALSE':
                        
$this->type 'constant';

                        return new 
ConstantExpression(false$token->getLine());

                    case 
'none':
                    case 
'NONE':
                    case 
'null':
                    case 
'NULL':
                        
$this->type 'constant';

                        return new 
ConstantExpression(null$token->getLine());

                    default:
                        
$this->type 'variable';

                        return new 
ContextVariable($token->getValue(), $token->getLine());
                }

                
// no break
            
case $token->test(Token::NUMBER_TYPE):
                
$stream->next();
                
$this->type 'constant';

                return new 
ConstantExpression($token->getValue(), $token->getLine());

            case 
$token->test(Token::STRING_TYPE):
            case 
$token->test(Token::INTERPOLATION_START_TYPE):
                
$this->type 'string';

                return 
$this->parseStringExpression($parser);

            case 
$token->test(Token::PUNCTUATION_TYPE):
                
// In 4.0, we should always return the node or throw an error for default
                
if ($node match ($token->getValue()) {
                    
'{' => $this->parseMappingExpression($parser),
                    default => 
null,
                }) {
                    return 
$node;
                }

                
// no break
            
case $token->test(Token::OPERATOR_TYPE):
                if (
'[' === $token->getValue()) {
                    return 
$this->parseSequenceExpression($parser);
                }

                if (
preg_match(Lexer::REGEX_NAME$token->getValue(), $matches) && $matches[0] == $token->getValue()) {
                    
// in this context, string operators are variable names
                    
$stream->next();
                    
$this->type 'variable';

                    return new 
ContextVariable($token->getValue(), $token->getLine());
                }

                
// no break
            
default:
                throw new 
SyntaxError(sprintf('Unexpected token "%s" of value "%s".'$token->toEnglish(), $token->getValue()), $token->getLine(), $stream->getSourceContext());
        }
    }

    public function 
getName(): string
    
{
        return 
$this->type;
    }

    public function 
getDescription(): string
    
{
        return 
'A literal value (boolean, string, number, sequence, mapping, ...)';
    }

    public function 
getPrecedence(): int
    
{
        
// not used
        
return 0;
    }

    private function 
parseStringExpression(Parser $parser)
    {
        
$stream $parser->getStream();

        
$nodes = [];
        
// a string cannot be followed by another string in a single expression
        
$nextCanBeString true;
        while (
true) {
            if (
$nextCanBeString && $token $stream->nextIf(Token::STRING_TYPE)) {
                
$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());
                
$nextCanBeString false;
            } elseif (
$stream->nextIf(Token::INTERPOLATION_START_TYPE)) {
                
$nodes[] = $parser->parseExpression();
                
$stream->expect(Token::INTERPOLATION_END_TYPE);
                
$nextCanBeString true;
            } else {
                break;
            }
        }

        
$expr array_shift($nodes);
        foreach (
$nodes as $node) {
            
$expr = new ConcatBinary($expr$node$node->getTemplateLine());
        }

        return 
$expr;
    }

    private function 
parseSequenceExpression(Parser $parser)
    {
        
$this->type 'sequence';

        
$stream $parser->getStream();
        
$stream->expect(Token::OPERATOR_TYPE'[''A sequence 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 sequence element must be followed by a comma');

                
// trailing ,?
                
if ($stream->test(Token::PUNCTUATION_TYPE']')) {
                    break;
                }
            }
            
$first false;

            
// Check for empty slots (comma with no expression)
            
if ($stream->test(Token::PUNCTUATION_TYPE',')) {
                
$node->addElement(new EmptyExpression($stream->getCurrent()->getLine()));
            } else {
                
$node->addElement($parser->parseExpression());
            }
        }
        
$stream->expect(Token::PUNCTUATION_TYPE']''An opened sequence is not properly closed');

        return 
$node;
    }

    private function 
parseMappingExpression(Parser $parser)
    {
        
$this->type 'mapping';

        
$stream $parser->getStream();
        
$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;

            if (
$stream->test(Token::OPERATOR_TYPE'...')) {
                
$node->addElement($parser->parseExpression());

                continue;
            }

            
// a mapping key can be:
            //
            //  * a number -- 12
            //  * a string -- 'a'
            //  * a name, which is equivalent to a string -- a
            //  * an expression, which must be enclosed in parentheses -- (1 + 2)
            
if ($token $stream->nextIf(Token::NAME_TYPE)) {
                
$key = new ConstantExpression($token->getValue(), $token->getLine());

                
// {a} is a shortcut for {a:a}
                
if ($stream->test(Token::PUNCTUATION_TYPE, [',''}'])) {
                    
$value = new ContextVariable($key->getAttribute('value'), $key->getTemplateLine());
                    
$node->addElement($value$key);
                    continue;
                }
            } elseif ((
$token $stream->nextIf(Token::STRING_TYPE)) || $token $stream->nextIf(Token::NUMBER_TYPE)) {
                
$key = new ConstantExpression($token->getValue(), $token->getLine());
            } elseif (
$stream->test(Token::OPERATOR_TYPE'(')) {
                
$key $parser->parseExpression();
            } else {
                
$current $stream->getCurrent();

                throw new 
SyntaxError(sprintf('A mapping key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".'$current->toEnglish(), $current->getValue()), $current->getLine(), $stream->getSourceContext());
            }

            
$stream->expect(Token::PUNCTUATION_TYPE':''A mapping key must be followed by a colon (:)');
            
$value $parser->parseExpression();

            
$node->addElement($value$key);
        }
        
$stream->expect(Token::PUNCTUATION_TYPE'}''An opened mapping is not properly closed');

        return 
$node;
    }
}
Онлайн: 1
Реклама