From 1602d9bd525bb7d3ed8d4b4f2312d1c54726cc2d Mon Sep 17 00:00:00 2001 From: nikic <+@ni-po.com> Date: Fri, 31 Dec 2010 17:26:55 +0100 Subject: [PATCH] track brackets nesting in lexer --- lib/Twig/Lexer.php | 43 +++++++++++++++++++++++++++++++++---------- 1 files changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index f0a1ab5..3dfeba6 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -24,6 +24,7 @@ class Twig_Lexer implements Twig_LexerInterface protected $lineno; protected $end; protected $state; + protected $brackets; protected $env; protected $filename; @@ -34,10 +35,10 @@ class Twig_Lexer implements Twig_LexerInterface const STATE_BLOCK = 1; const STATE_VAR = 2; - const REGEX_NAME = '/[A-Za-z_][A-Za-z0-9_]*/A'; - const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A'; - const REGEX_STRING = '/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; - const REGEX_PUNCTUATION = '/[\[\](){}?:.,|]/A'; + const REGEX_NAME = '/[A-Za-z_][A-Za-z0-9_]*/A'; + const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A'; + const REGEX_STRING = '/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; + const PUNCTUATION = '()[]{}?:.,|'; public function __construct(Twig_Environment $env, array $options = array()) { @@ -72,6 +73,7 @@ class Twig_Lexer implements Twig_LexerInterface $this->end = strlen($this->code); $this->tokens = array(); $this->state = self::STATE_DATA; + $this->brackets = array(); while ($this->cursor < $this->end) { // dispatch to the lexing functions depending @@ -93,6 +95,11 @@ class Twig_Lexer implements Twig_LexerInterface $this->pushToken(Twig_Token::EOF_TYPE); + if (!empty($this->brackets)) { + list($expect, $lineno) = array_pop($this->brackets); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + if (isset($mbEncoding)) { mb_internal_encoding($mbEncoding); } @@ -167,7 +174,7 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexBlock() { - if (preg_match('/\s*'.preg_quote($this->options['tag_block'][1], '/').'/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*'.preg_quote($this->options['tag_block'][1], '/').'/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; @@ -185,7 +192,7 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexVar() { - if (preg_match('/\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A', $this->code, $match, null, $this->cursor)) { + if (empty($this->brackets) && preg_match('/\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A', $this->code, $match, null, $this->cursor)) { $this->pushToken(Twig_Token::VAR_END_TYPE); $this->moveCursor($match[0]); $this->state = self::STATE_DATA; @@ -222,9 +229,25 @@ class Twig_Lexer implements Twig_LexerInterface $this->moveCursor($match[0]); } // punctuation - elseif (preg_match(self::REGEX_PUNCTUATION, $this->code, $match, null, $this->cursor)) { - $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $match[0]); - $this->moveCursor($match[0]); + elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) { + // opening bracket + if (false !== strpos('([{', $this->code[$this->cursor])) { + $this->brackets[] = array($this->code[$this->cursor], $this->lineno); + } + // closing bracket + elseif (false !== strpos(')]}', $this->code[$this->cursor])) { + if (empty($this->brackets)) { + throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + } + + list($expect, $lineno) = array_pop($this->brackets); + if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) { + throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + } + } + + $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]); + ++$this->cursor; } // strings elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) { @@ -270,7 +293,7 @@ class Twig_Lexer implements Twig_LexerInterface $regex = array(); foreach ($operators as $operator => $length) { // an operator that ends with a character must be followed by - // a whitespace or a parenthese + // a whitespace or a parenthesis if (ctype_alpha($operator[$length - 1])) { $regex[] = preg_quote($operator, '/').'(?=[ ()])'; } else { -- 1.7.2.5