| 1 | 
        
            1686
         | 
        
            darkviper
         | 
        <?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 | 
        
         | 
        
         | 
         * Lexes a template string.
  | 
      
      
        | 15 | 
        
         | 
        
         | 
         *
  | 
      
      
        | 16 | 
        
         | 
        
         | 
         * @package    twig
  | 
      
      
        | 17 | 
        
         | 
        
         | 
         * @author     Fabien Potencier <fabien@symfony.com>
  | 
      
      
        | 18 | 
        
         | 
        
         | 
         */
  | 
      
      
        | 19 | 
        
         | 
        
         | 
        class Twig_Lexer implements Twig_LexerInterface
  | 
      
      
        | 20 | 
        
         | 
        
         | 
        {
 | 
      
      
        | 21 | 
        
         | 
        
         | 
            protected $tokens;
  | 
      
      
        | 22 | 
        
         | 
        
         | 
            protected $code;
  | 
      
      
        | 23 | 
        
         | 
        
         | 
            protected $cursor;
  | 
      
      
        | 24 | 
        
         | 
        
         | 
            protected $lineno;
  | 
      
      
        | 25 | 
        
         | 
        
         | 
            protected $end;
  | 
      
      
        | 26 | 
        
         | 
        
         | 
            protected $state;
  | 
      
      
        | 27 | 
        
         | 
        
         | 
            protected $states;
  | 
      
      
        | 28 | 
        
         | 
        
         | 
            protected $brackets;
  | 
      
      
        | 29 | 
        
         | 
        
         | 
            protected $env;
  | 
      
      
        | 30 | 
        
         | 
        
         | 
            protected $filename;
  | 
      
      
        | 31 | 
        
         | 
        
         | 
            protected $options;
  | 
      
      
        | 32 | 
        
         | 
        
         | 
            protected $regexes;
  | 
      
      
        | 33 | 
        
         | 
        
         | 
        
  | 
      
      
        | 34 | 
        
         | 
        
         | 
            const STATE_DATA            = 0;
  | 
      
      
        | 35 | 
        
         | 
        
         | 
            const STATE_BLOCK           = 1;
  | 
      
      
        | 36 | 
        
         | 
        
         | 
            const STATE_VAR             = 2;
  | 
      
      
        | 37 | 
        
         | 
        
         | 
            const STATE_STRING          = 3;
  | 
      
      
        | 38 | 
        
         | 
        
         | 
            const STATE_INTERPOLATION   = 4;
  | 
      
      
        | 39 | 
        
         | 
        
         | 
        
  | 
      
      
        | 40 | 
        
         | 
        
         | 
            const REGEX_NAME            = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
  | 
      
      
        | 41 | 
        
         | 
        
         | 
            const REGEX_NUMBER          = '/[0-9]+(?:\.[0-9]+)?/A';
  | 
      
      
        | 42 | 
        
         | 
        
         | 
            const REGEX_STRING          = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
  | 
      
      
        | 43 | 
        
         | 
        
         | 
            const REGEX_DQ_STRING_DELIM = '/"/A';
  | 
      
      
        | 44 | 
        
         | 
        
         | 
            const REGEX_DQ_STRING_PART  = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
 | 
      
      
        | 45 | 
        
         | 
        
         | 
            const PUNCTUATION           = '()[]{}?:.,|';
 | 
      
      
        | 46 | 
        
         | 
        
         | 
        
  | 
      
      
        | 47 | 
        
         | 
        
         | 
            public function __construct(Twig_Environment $env, array $options = array())
  | 
      
      
        | 48 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 49 | 
        
         | 
        
         | 
                $this->env = $env;
  | 
      
      
        | 50 | 
        
         | 
        
         | 
        
  | 
      
      
        | 51 | 
        
         | 
        
         | 
                $this->options = array_merge(array(
  | 
      
      
        | 52 | 
        
         | 
        
         | 
                    'tag_comment'     => array('{#', '#}'),
 | 
      
      
        | 53 | 
        
         | 
        
         | 
                    'tag_block'       => array('{%', '%}'),
 | 
      
      
        | 54 | 
        
         | 
        
         | 
                    'tag_variable'    => array('{{', '}}'),
 | 
      
      
        | 55 | 
        
         | 
        
         | 
                    'whitespace_trim' => '-',
  | 
      
      
        | 56 | 
        
         | 
        
         | 
                    'interpolation'   => array('#{', '}'),
 | 
      
      
        | 57 | 
        
         | 
        
         | 
                ), $options);
  | 
      
      
        | 58 | 
        
         | 
        
         | 
        
  | 
      
      
        | 59 | 
        
         | 
        
         | 
                $this->regexes = array(
  | 
      
      
        | 60 | 
        
         | 
        
         | 
                    'lex_var'             => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
  | 
      
      
        | 61 | 
        
         | 
        
         | 
                    'lex_block'           => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
  | 
      
      
        | 62 | 
        
         | 
        
         | 
                    'lex_raw_data'        => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*endraw\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
 | 
      
      
        | 63 | 
        
         | 
        
         | 
                    'operator'            => $this->getOperatorRegex(),
  | 
      
      
        | 64 | 
        
         | 
        
         | 
                    'lex_comment'         => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
  | 
      
      
        | 65 | 
        
         | 
        
         | 
                    'lex_block_raw'       => '/\s*raw\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
  | 
      
      
        | 66 | 
        
         | 
        
         | 
                    'lex_block_line'      => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
  | 
      
      
        | 67 | 
        
         | 
        
         | 
                    'lex_tokens_start'    => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
 | 
      
      
        | 68 | 
        
         | 
        
         | 
                    'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
  | 
      
      
        | 69 | 
        
         | 
        
         | 
                    'interpolation_end'   => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
  | 
      
      
        | 70 | 
        
         | 
        
         | 
                );
  | 
      
      
        | 71 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 72 | 
        
         | 
        
         | 
        
  | 
      
      
        | 73 | 
        
         | 
        
         | 
            /**
  | 
      
      
        | 74 | 
        
         | 
        
         | 
             * Tokenizes a source code.
  | 
      
      
        | 75 | 
        
         | 
        
         | 
             *
  | 
      
      
        | 76 | 
        
         | 
        
         | 
             * @param  string $code     The source code
  | 
      
      
        | 77 | 
        
         | 
        
         | 
             * @param  string $filename A unique identifier for the source code
  | 
      
      
        | 78 | 
        
         | 
        
         | 
             *
  | 
      
      
        | 79 | 
        
         | 
        
         | 
             * @return Twig_TokenStream A token stream instance
  | 
      
      
        | 80 | 
        
         | 
        
         | 
             */
  | 
      
      
        | 81 | 
        
         | 
        
         | 
            public function tokenize($code, $filename = null)
  | 
      
      
        | 82 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 83 | 
        
         | 
        
         | 
                if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
 | 
      
      
        | 84 | 
        
         | 
        
         | 
                    $mbEncoding = mb_internal_encoding();
  | 
      
      
        | 85 | 
        
         | 
        
         | 
                    mb_internal_encoding('ASCII');
 | 
      
      
        | 86 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 87 | 
        
         | 
        
         | 
        
  | 
      
      
        | 88 | 
        
         | 
        
         | 
                $this->code = str_replace(array("\r\n", "\r"), "\n", $code);
 | 
      
      
        | 89 | 
        
         | 
        
         | 
                $this->filename = $filename;
  | 
      
      
        | 90 | 
        
         | 
        
         | 
                $this->cursor = 0;
  | 
      
      
        | 91 | 
        
         | 
        
         | 
                $this->lineno = 1;
  | 
      
      
        | 92 | 
        
         | 
        
         | 
                $this->end = strlen($this->code);
  | 
      
      
        | 93 | 
        
         | 
        
         | 
                $this->tokens = array();
  | 
      
      
        | 94 | 
        
         | 
        
         | 
                $this->state = self::STATE_DATA;
  | 
      
      
        | 95 | 
        
         | 
        
         | 
                $this->states = array();
  | 
      
      
        | 96 | 
        
         | 
        
         | 
                $this->brackets = array();
  | 
      
      
        | 97 | 
        
         | 
        
         | 
                $this->position = -1;
  | 
      
      
        | 98 | 
        
         | 
        
         | 
        
  | 
      
      
        | 99 | 
        
         | 
        
         | 
                // find all token starts in one go
  | 
      
      
        | 100 | 
        
         | 
        
         | 
                preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
  | 
      
      
        | 101 | 
        
         | 
        
         | 
                $this->positions = $matches;
  | 
      
      
        | 102 | 
        
         | 
        
         | 
        
  | 
      
      
        | 103 | 
        
         | 
        
         | 
                while ($this->cursor < $this->end) {
 | 
      
      
        | 104 | 
        
         | 
        
         | 
                    // dispatch to the lexing functions depending
  | 
      
      
        | 105 | 
        
         | 
        
         | 
                    // on the current state
  | 
      
      
        | 106 | 
        
         | 
        
         | 
                    switch ($this->state) {
 | 
      
      
        | 107 | 
        
         | 
        
         | 
                        case self::STATE_DATA:
  | 
      
      
        | 108 | 
        
         | 
        
         | 
                            $this->lexData();
  | 
      
      
        | 109 | 
        
         | 
        
         | 
                            break;
  | 
      
      
        | 110 | 
        
         | 
        
         | 
        
  | 
      
      
        | 111 | 
        
         | 
        
         | 
                        case self::STATE_BLOCK:
  | 
      
      
        | 112 | 
        
         | 
        
         | 
                            $this->lexBlock();
  | 
      
      
        | 113 | 
        
         | 
        
         | 
                            break;
  | 
      
      
        | 114 | 
        
         | 
        
         | 
        
  | 
      
      
        | 115 | 
        
         | 
        
         | 
                        case self::STATE_VAR:
  | 
      
      
        | 116 | 
        
         | 
        
         | 
                            $this->lexVar();
  | 
      
      
        | 117 | 
        
         | 
        
         | 
                            break;
  | 
      
      
        | 118 | 
        
         | 
        
         | 
        
  | 
      
      
        | 119 | 
        
         | 
        
         | 
                        case self::STATE_STRING:
  | 
      
      
        | 120 | 
        
         | 
        
         | 
                            $this->lexString();
  | 
      
      
        | 121 | 
        
         | 
        
         | 
                            break;
  | 
      
      
        | 122 | 
        
         | 
        
         | 
        
  | 
      
      
        | 123 | 
        
         | 
        
         | 
                        case self::STATE_INTERPOLATION:
  | 
      
      
        | 124 | 
        
         | 
        
         | 
                            $this->lexInterpolation();
  | 
      
      
        | 125 | 
        
         | 
        
         | 
                            break;
  | 
      
      
        | 126 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 127 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 128 | 
        
         | 
        
         | 
        
  | 
      
      
        | 129 | 
        
         | 
        
         | 
                $this->pushToken(Twig_Token::EOF_TYPE);
  | 
      
      
        | 130 | 
        
         | 
        
         | 
        
  | 
      
      
        | 131 | 
        
         | 
        
         | 
                if (!empty($this->brackets)) {
 | 
      
      
        | 132 | 
        
         | 
        
         | 
                    list($expect, $lineno) = array_pop($this->brackets);
  | 
      
      
        | 133 | 
        
         | 
        
         | 
                    throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
 | 
      
      
        | 134 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 135 | 
        
         | 
        
         | 
        
  | 
      
      
        | 136 | 
        
         | 
        
         | 
                if (isset($mbEncoding)) {
 | 
      
      
        | 137 | 
        
         | 
        
         | 
                    mb_internal_encoding($mbEncoding);
  | 
      
      
        | 138 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 139 | 
        
         | 
        
         | 
        
  | 
      
      
        | 140 | 
        
         | 
        
         | 
                return new Twig_TokenStream($this->tokens, $this->filename);
  | 
      
      
        | 141 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 142 | 
        
         | 
        
         | 
        
  | 
      
      
        | 143 | 
        
         | 
        
         | 
            protected function lexData()
  | 
      
      
        | 144 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 145 | 
        
         | 
        
         | 
                // if no matches are left we return the rest of the template as simple text token
  | 
      
      
        | 146 | 
        
         | 
        
         | 
                if ($this->position == count($this->positions[0]) - 1) {
 | 
      
      
        | 147 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
  | 
      
      
        | 148 | 
        
         | 
        
         | 
                    $this->cursor = $this->end;
  | 
      
      
        | 149 | 
        
         | 
        
         | 
        
  | 
      
      
        | 150 | 
        
         | 
        
         | 
                    return;
  | 
      
      
        | 151 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 152 | 
        
         | 
        
         | 
        
  | 
      
      
        | 153 | 
        
         | 
        
         | 
                // Find the first token after the current cursor
  | 
      
      
        | 154 | 
        
         | 
        
         | 
                $position = $this->positions[0][++$this->position];
  | 
      
      
        | 155 | 
        
         | 
        
         | 
                while ($position[1] < $this->cursor) {
 | 
      
      
        | 156 | 
        
         | 
        
         | 
                    if ($this->position == count($this->positions[0]) - 1) {
 | 
      
      
        | 157 | 
        
         | 
        
         | 
                        return;
  | 
      
      
        | 158 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 159 | 
        
         | 
        
         | 
                    $position = $this->positions[0][++$this->position];
  | 
      
      
        | 160 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 161 | 
        
         | 
        
         | 
        
  | 
      
      
        | 162 | 
        
         | 
        
         | 
                // push the template text first
  | 
      
      
        | 163 | 
        
         | 
        
         | 
                $text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
  | 
      
      
        | 164 | 
        
         | 
        
         | 
                if (isset($this->positions[2][$this->position][0])) {
 | 
      
      
        | 165 | 
        
         | 
        
         | 
                    $text = rtrim($text);
  | 
      
      
        | 166 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 167 | 
        
         | 
        
         | 
                $this->pushToken(Twig_Token::TEXT_TYPE, $text);
  | 
      
      
        | 168 | 
        
         | 
        
         | 
                $this->moveCursor($textContent.$position[0]);
  | 
      
      
        | 169 | 
        
         | 
        
         | 
        
  | 
      
      
        | 170 | 
        
         | 
        
         | 
                switch ($this->positions[1][$this->position][0]) {
 | 
      
      
        | 171 | 
        
         | 
        
         | 
                    case $this->options['tag_comment'][0]:
  | 
      
      
        | 172 | 
        
         | 
        
         | 
                        $this->lexComment();
  | 
      
      
        | 173 | 
        
         | 
        
         | 
                        break;
  | 
      
      
        | 174 | 
        
         | 
        
         | 
        
  | 
      
      
        | 175 | 
        
         | 
        
         | 
                    case $this->options['tag_block'][0]:
  | 
      
      
        | 176 | 
        
         | 
        
         | 
                        // raw data?
  | 
      
      
        | 177 | 
        
         | 
        
         | 
                        if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 178 | 
        
         | 
        
         | 
                            $this->moveCursor($match[0]);
  | 
      
      
        | 179 | 
        
         | 
        
         | 
                            $this->lexRawData();
  | 
      
      
        | 180 | 
        
         | 
        
         | 
                        // {% line \d+ %}
 | 
      
      
        | 181 | 
        
         | 
        
         | 
                        } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 182 | 
        
         | 
        
         | 
                            $this->moveCursor($match[0]);
  | 
      
      
        | 183 | 
        
         | 
        
         | 
                            $this->lineno = (int) $match[1];
  | 
      
      
        | 184 | 
        
         | 
        
         | 
                        } else {
 | 
      
      
        | 185 | 
        
         | 
        
         | 
                            $this->pushToken(Twig_Token::BLOCK_START_TYPE);
  | 
      
      
        | 186 | 
        
         | 
        
         | 
                            $this->pushState(self::STATE_BLOCK);
  | 
      
      
        | 187 | 
        
         | 
        
         | 
                        }
  | 
      
      
        | 188 | 
        
         | 
        
         | 
                        break;
  | 
      
      
        | 189 | 
        
         | 
        
         | 
        
  | 
      
      
        | 190 | 
        
         | 
        
         | 
                    case $this->options['tag_variable'][0]:
  | 
      
      
        | 191 | 
        
         | 
        
         | 
                        $this->pushToken(Twig_Token::VAR_START_TYPE);
  | 
      
      
        | 192 | 
        
         | 
        
         | 
                        $this->pushState(self::STATE_VAR);
  | 
      
      
        | 193 | 
        
         | 
        
         | 
                        break;
  | 
      
      
        | 194 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 195 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 196 | 
        
         | 
        
         | 
        
  | 
      
      
        | 197 | 
        
         | 
        
         | 
            protected function lexBlock()
  | 
      
      
        | 198 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 199 | 
        
         | 
        
         | 
                if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 200 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::BLOCK_END_TYPE);
  | 
      
      
        | 201 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 202 | 
        
         | 
        
         | 
                    $this->popState();
  | 
      
      
        | 203 | 
        
         | 
        
         | 
                } else {
 | 
      
      
        | 204 | 
        
         | 
        
         | 
                    $this->lexExpression();
  | 
      
      
        | 205 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 206 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 207 | 
        
         | 
        
         | 
        
  | 
      
      
        | 208 | 
        
         | 
        
         | 
            protected function lexVar()
  | 
      
      
        | 209 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 210 | 
        
         | 
        
         | 
                if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 211 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::VAR_END_TYPE);
  | 
      
      
        | 212 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 213 | 
        
         | 
        
         | 
                    $this->popState();
  | 
      
      
        | 214 | 
        
         | 
        
         | 
                } else {
 | 
      
      
        | 215 | 
        
         | 
        
         | 
                    $this->lexExpression();
  | 
      
      
        | 216 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 217 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 218 | 
        
         | 
        
         | 
        
  | 
      
      
        | 219 | 
        
         | 
        
         | 
            protected function lexExpression()
  | 
      
      
        | 220 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 221 | 
        
         | 
        
         | 
                // whitespace
  | 
      
      
        | 222 | 
        
         | 
        
         | 
                if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 223 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 224 | 
        
         | 
        
         | 
        
  | 
      
      
        | 225 | 
        
         | 
        
         | 
                    if ($this->cursor >= $this->end) {
 | 
      
      
        | 226 | 
        
         | 
        
         | 
                        throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->lineno, $this->filename);
 | 
      
      
        | 227 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 228 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 229 | 
        
         | 
        
         | 
        
  | 
      
      
        | 230 | 
        
         | 
        
         | 
                // operators
  | 
      
      
        | 231 | 
        
         | 
        
         | 
                if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 232 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]);
  | 
      
      
        | 233 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 234 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 235 | 
        
         | 
        
         | 
                // names
  | 
      
      
        | 236 | 
        
         | 
        
         | 
                elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 237 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::NAME_TYPE, $match[0]);
  | 
      
      
        | 238 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 239 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 240 | 
        
         | 
        
         | 
                // numbers
  | 
      
      
        | 241 | 
        
         | 
        
         | 
                elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 242 | 
        
         | 
        
         | 
                    $number = (float) $match[0];  // floats
  | 
      
      
        | 243 | 
        
         | 
        
         | 
                    if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
 | 
      
      
        | 244 | 
        
         | 
        
         | 
                        $number = (int) $match[0]; // integers lower than the maximum
  | 
      
      
        | 245 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 246 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::NUMBER_TYPE, $number);
  | 
      
      
        | 247 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 248 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 249 | 
        
         | 
        
         | 
                // punctuation
  | 
      
      
        | 250 | 
        
         | 
        
         | 
                elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
 | 
      
      
        | 251 | 
        
         | 
        
         | 
                    // opening bracket
  | 
      
      
        | 252 | 
        
         | 
        
         | 
                    if (false !== strpos('([{', $this->code[$this->cursor])) {
 | 
      
      
        | 253 | 
        
         | 
        
         | 
                        $this->brackets[] = array($this->code[$this->cursor], $this->lineno);
  | 
      
      
        | 254 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 255 | 
        
         | 
        
         | 
                    // closing bracket
  | 
      
      
        | 256 | 
        
         | 
        
         | 
                    elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
 | 
      
      
        | 257 | 
        
         | 
        
         | 
                        if (empty($this->brackets)) {
 | 
      
      
        | 258 | 
        
         | 
        
         | 
                            throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
 | 
      
      
        | 259 | 
        
         | 
        
         | 
                        }
  | 
      
      
        | 260 | 
        
         | 
        
         | 
        
  | 
      
      
        | 261 | 
        
         | 
        
         | 
                        list($expect, $lineno) = array_pop($this->brackets);
  | 
      
      
        | 262 | 
        
         | 
        
         | 
                        if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
 | 
      
      
        | 263 | 
        
         | 
        
         | 
                            throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
 | 
      
      
        | 264 | 
        
         | 
        
         | 
                        }
  | 
      
      
        | 265 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 266 | 
        
         | 
        
         | 
        
  | 
      
      
        | 267 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
  | 
      
      
        | 268 | 
        
         | 
        
         | 
                    ++$this->cursor;
  | 
      
      
        | 269 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 270 | 
        
         | 
        
         | 
                // strings
  | 
      
      
        | 271 | 
        
         | 
        
         | 
                elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 272 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
  | 
      
      
        | 273 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 274 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 275 | 
        
         | 
        
         | 
                // opening double quoted string
  | 
      
      
        | 276 | 
        
         | 
        
         | 
                elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 277 | 
        
         | 
        
         | 
                    $this->brackets[] = array('"', $this->lineno);
 | 
      
      
        | 278 | 
        
         | 
        
         | 
                    $this->pushState(self::STATE_STRING);
  | 
      
      
        | 279 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 280 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 281 | 
        
         | 
        
         | 
                // unlexable
  | 
      
      
        | 282 | 
        
         | 
        
         | 
                else {
 | 
      
      
        | 283 | 
        
         | 
        
         | 
                    throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
 | 
      
      
        | 284 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 285 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 286 | 
        
         | 
        
         | 
        
  | 
      
      
        | 287 | 
        
         | 
        
         | 
            protected function lexRawData()
  | 
      
      
        | 288 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 289 | 
        
         | 
        
         | 
                if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
 | 
      
      
        | 290 | 
        
         | 
        
         | 
                    throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "block"'), $this->lineno, $this->filename);
 | 
      
      
        | 291 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 292 | 
        
         | 
        
         | 
        
  | 
      
      
        | 293 | 
        
         | 
        
         | 
                $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
  | 
      
      
        | 294 | 
        
         | 
        
         | 
                $this->moveCursor($text.$match[0][0]);
  | 
      
      
        | 295 | 
        
         | 
        
         | 
        
  | 
      
      
        | 296 | 
        
         | 
        
         | 
                if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
 | 
      
      
        | 297 | 
        
         | 
        
         | 
                    $text = rtrim($text);
  | 
      
      
        | 298 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 299 | 
        
         | 
        
         | 
        
  | 
      
      
        | 300 | 
        
         | 
        
         | 
                $this->pushToken(Twig_Token::TEXT_TYPE, $text);
  | 
      
      
        | 301 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 302 | 
        
         | 
        
         | 
        
  | 
      
      
        | 303 | 
        
         | 
        
         | 
            protected function lexComment()
  | 
      
      
        | 304 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 305 | 
        
         | 
        
         | 
                if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
 | 
      
      
        | 306 | 
        
         | 
        
         | 
                    throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename);
 | 
      
      
        | 307 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 308 | 
        
         | 
        
         | 
        
  | 
      
      
        | 309 | 
        
         | 
        
         | 
                $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
  | 
      
      
        | 310 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 311 | 
        
         | 
        
         | 
        
  | 
      
      
        | 312 | 
        
         | 
        
         | 
            protected function lexString()
  | 
      
      
        | 313 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 314 | 
        
         | 
        
         | 
                if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 315 | 
        
         | 
        
         | 
                    $this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
  | 
      
      
        | 316 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
  | 
      
      
        | 317 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 318 | 
        
         | 
        
         | 
                    $this->pushState(self::STATE_INTERPOLATION);
  | 
      
      
        | 319 | 
        
         | 
        
         | 
        
  | 
      
      
        | 320 | 
        
         | 
        
         | 
                } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
 | 
      
      
        | 321 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
  | 
      
      
        | 322 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 323 | 
        
         | 
        
         | 
        
  | 
      
      
        | 324 | 
        
         | 
        
         | 
                } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 325 | 
        
         | 
        
         | 
        
  | 
      
      
        | 326 | 
        
         | 
        
         | 
                    list($expect, $lineno) = array_pop($this->brackets);
  | 
      
      
        | 327 | 
        
         | 
        
         | 
                    if ($this->code[$this->cursor] != '"') {
 | 
      
      
        | 328 | 
        
         | 
        
         | 
                        throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
 | 
      
      
        | 329 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 330 | 
        
         | 
        
         | 
        
  | 
      
      
        | 331 | 
        
         | 
        
         | 
                    $this->popState();
  | 
      
      
        | 332 | 
        
         | 
        
         | 
                    ++$this->cursor;
  | 
      
      
        | 333 | 
        
         | 
        
         | 
        
  | 
      
      
        | 334 | 
        
         | 
        
         | 
                    return;
  | 
      
      
        | 335 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 336 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 337 | 
        
         | 
        
         | 
        
  | 
      
      
        | 338 | 
        
         | 
        
         | 
            protected function lexInterpolation()
  | 
      
      
        | 339 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 340 | 
        
         | 
        
         | 
                $bracket = end($this->brackets);
  | 
      
      
        | 341 | 
        
         | 
        
         | 
                if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
 | 
      
      
        | 342 | 
        
         | 
        
         | 
                    array_pop($this->brackets);
  | 
      
      
        | 343 | 
        
         | 
        
         | 
                    $this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
  | 
      
      
        | 344 | 
        
         | 
        
         | 
                    $this->moveCursor($match[0]);
  | 
      
      
        | 345 | 
        
         | 
        
         | 
                    $this->popState();
  | 
      
      
        | 346 | 
        
         | 
        
         | 
                } else {
 | 
      
      
        | 347 | 
        
         | 
        
         | 
                    $this->lexExpression();
  | 
      
      
        | 348 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 349 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 350 | 
        
         | 
        
         | 
        
  | 
      
      
        | 351 | 
        
         | 
        
         | 
            protected function pushToken($type, $value = '')
  | 
      
      
        | 352 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 353 | 
        
         | 
        
         | 
                // do not push empty text tokens
  | 
      
      
        | 354 | 
        
         | 
        
         | 
                if (Twig_Token::TEXT_TYPE === $type && '' === $value) {
 | 
      
      
        | 355 | 
        
         | 
        
         | 
                    return;
  | 
      
      
        | 356 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 357 | 
        
         | 
        
         | 
        
  | 
      
      
        | 358 | 
        
         | 
        
         | 
                $this->tokens[] = new Twig_Token($type, $value, $this->lineno);
  | 
      
      
        | 359 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 360 | 
        
         | 
        
         | 
        
  | 
      
      
        | 361 | 
        
         | 
        
         | 
            protected function moveCursor($text)
  | 
      
      
        | 362 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 363 | 
        
         | 
        
         | 
                $this->cursor += strlen($text);
  | 
      
      
        | 364 | 
        
         | 
        
         | 
                $this->lineno += substr_count($text, "\n");
  | 
      
      
        | 365 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 366 | 
        
         | 
        
         | 
        
  | 
      
      
        | 367 | 
        
         | 
        
         | 
            protected function getOperatorRegex()
  | 
      
      
        | 368 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 369 | 
        
         | 
        
         | 
                $operators = array_merge(
  | 
      
      
        | 370 | 
        
         | 
        
         | 
                    array('='),
 | 
      
      
        | 371 | 
        
         | 
        
         | 
                    array_keys($this->env->getUnaryOperators()),
  | 
      
      
        | 372 | 
        
         | 
        
         | 
                    array_keys($this->env->getBinaryOperators())
  | 
      
      
        | 373 | 
        
         | 
        
         | 
                );
  | 
      
      
        | 374 | 
        
         | 
        
         | 
        
  | 
      
      
        | 375 | 
        
         | 
        
         | 
                $operators = array_combine($operators, array_map('strlen', $operators));
 | 
      
      
        | 376 | 
        
         | 
        
         | 
                arsort($operators);
  | 
      
      
        | 377 | 
        
         | 
        
         | 
        
  | 
      
      
        | 378 | 
        
         | 
        
         | 
                $regex = array();
  | 
      
      
        | 379 | 
        
         | 
        
         | 
                foreach ($operators as $operator => $length) {
 | 
      
      
        | 380 | 
        
         | 
        
         | 
                    // an operator that ends with a character must be followed by
  | 
      
      
        | 381 | 
        
         | 
        
         | 
                    // a whitespace or a parenthesis
  | 
      
      
        | 382 | 
        
         | 
        
         | 
                    if (ctype_alpha($operator[$length - 1])) {
 | 
      
      
        | 383 | 
        
         | 
        
         | 
                        $regex[] = preg_quote($operator, '/').'(?=[\s()])';
  | 
      
      
        | 384 | 
        
         | 
        
         | 
                    } else {
 | 
      
      
        | 385 | 
        
         | 
        
         | 
                        $regex[] = preg_quote($operator, '/');
  | 
      
      
        | 386 | 
        
         | 
        
         | 
                    }
  | 
      
      
        | 387 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 388 | 
        
         | 
        
         | 
        
  | 
      
      
        | 389 | 
        
         | 
        
         | 
                return '/'.implode('|', $regex).'/A';
 | 
      
      
        | 390 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 391 | 
        
         | 
        
         | 
        
  | 
      
      
        | 392 | 
        
         | 
        
         | 
            protected function pushState($state)
  | 
      
      
        | 393 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 394 | 
        
         | 
        
         | 
                $this->states[] = $this->state;
  | 
      
      
        | 395 | 
        
         | 
        
         | 
                $this->state = $state;
  | 
      
      
        | 396 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 397 | 
        
         | 
        
         | 
        
  | 
      
      
        | 398 | 
        
         | 
        
         | 
            protected function popState()
  | 
      
      
        | 399 | 
        
         | 
        
         | 
            {
 | 
      
      
        | 400 | 
        
         | 
        
         | 
                if (0 === count($this->states)) {
 | 
      
      
        | 401 | 
        
         | 
        
         | 
                    throw new Exception('Cannot pop state without a previous state');
 | 
      
      
        | 402 | 
        
         | 
        
         | 
                }
  | 
      
      
        | 403 | 
        
         | 
        
         | 
        
  | 
      
      
        | 404 | 
        
         | 
        
         | 
                $this->state = array_pop($this->states);
  | 
      
      
        | 405 | 
        
         | 
        
         | 
            }
  | 
      
      
        | 406 | 
        
         | 
        
         | 
        }
  |