Complete the injection of the expression in all syntax errors
This commit is contained in:
parent
dc55db2a9d
commit
7cd744133d
@ -59,12 +59,12 @@ class Lexer
|
|||||||
} elseif (false !== strpos(')]}', $expression[$cursor])) {
|
} elseif (false !== strpos(')]}', $expression[$cursor])) {
|
||||||
// closing bracket
|
// closing bracket
|
||||||
if (empty($brackets)) {
|
if (empty($brackets)) {
|
||||||
throw new SyntaxError(sprintf('Unexpected "%s"', $expression[$cursor]), $cursor);
|
throw new SyntaxError(sprintf('Unexpected "%s"', $expression[$cursor]), $cursor, $expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
list($expect, $cur) = array_pop($brackets);
|
list($expect, $cur) = array_pop($brackets);
|
||||||
if ($expression[$cursor] != strtr($expect, '([{', ')]}')) {
|
if ($expression[$cursor] != strtr($expect, '([{', ')]}')) {
|
||||||
throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur);
|
throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
$tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1);
|
$tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1);
|
||||||
@ -87,8 +87,7 @@ class Lexer
|
|||||||
$cursor += strlen($match[0]);
|
$cursor += strlen($match[0]);
|
||||||
} else {
|
} else {
|
||||||
// unlexable
|
// unlexable
|
||||||
$message = sprintf('Unexpected character "%s"', $expression[$cursor]);
|
throw new SyntaxError(sprintf('Unexpected character "%s"', $expression[$cursor]), $cursor, $expression);
|
||||||
throw new SyntaxError($message, $cursor, $expression);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +98,6 @@ class Lexer
|
|||||||
throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression);
|
throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TokenStream($tokens);
|
return new TokenStream($tokens, $expression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ class Parser
|
|||||||
|
|
||||||
$node = $this->parseExpression();
|
$node = $this->parseExpression();
|
||||||
if (!$stream->isEOF()) {
|
if (!$stream->isEOF()) {
|
||||||
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $stream->current->type, $stream->current->value), $stream->current->cursor);
|
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $stream->current->type, $stream->current->value), $stream->current->cursor, $stream->getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $node;
|
return $node;
|
||||||
@ -195,13 +195,13 @@ class Parser
|
|||||||
default:
|
default:
|
||||||
if ('(' === $this->stream->current->value) {
|
if ('(' === $this->stream->current->value) {
|
||||||
if (false === isset($this->functions[$token->value])) {
|
if (false === isset($this->functions[$token->value])) {
|
||||||
throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor);
|
throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
$node = new Node\FunctionNode($token->value, $this->parseArguments());
|
$node = new Node\FunctionNode($token->value, $this->parseArguments());
|
||||||
} else {
|
} else {
|
||||||
if (!in_array($token->value, $this->names, true)) {
|
if (!in_array($token->value, $this->names, true)) {
|
||||||
throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor);
|
throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
// is the name used in the compiled code different
|
// is the name used in the compiled code different
|
||||||
@ -227,7 +227,7 @@ class Parser
|
|||||||
} elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) {
|
} elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) {
|
||||||
$node = $this->parseHashExpression();
|
$node = $this->parseHashExpression();
|
||||||
} else {
|
} else {
|
||||||
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $token->type, $token->value), $token->cursor);
|
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $token->type, $token->value), $token->cursor, $this->stream->getExpression());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ class Parser
|
|||||||
} else {
|
} else {
|
||||||
$current = $this->stream->current;
|
$current = $this->stream->current;
|
||||||
|
|
||||||
throw new SyntaxError(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"', $current->type, $current->value), $current->cursor);
|
throw new SyntaxError(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"', $current->type, $current->value), $current->cursor, $this->stream->getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
|
$this->stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
|
||||||
@ -327,7 +327,7 @@ class Parser
|
|||||||
// As a result, if $token is NOT an operator OR $token->value is NOT a valid property or method name, an exception shall be thrown.
|
// As a result, if $token is NOT an operator OR $token->value is NOT a valid property or method name, an exception shall be thrown.
|
||||||
($token->type !== Token::OPERATOR_TYPE || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value))
|
($token->type !== Token::OPERATOR_TYPE || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value))
|
||||||
) {
|
) {
|
||||||
throw new SyntaxError('Expected name', $token->cursor);
|
throw new SyntaxError('Expected name', $token->cursor, $this->stream->getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
$arg = new Node\ConstantNode($token->value);
|
$arg = new Node\ConstantNode($token->value);
|
||||||
@ -345,7 +345,7 @@ class Parser
|
|||||||
$node = new Node\GetAttrNode($node, $arg, $arguments, $type);
|
$node = new Node\GetAttrNode($node, $arg, $arguments, $type);
|
||||||
} elseif ('[' === $token->value) {
|
} elseif ('[' === $token->value) {
|
||||||
if ($node instanceof Node\GetAttrNode && Node\GetAttrNode::METHOD_CALL === $node->attributes['type'] && PHP_VERSION_ID < 50400) {
|
if ($node instanceof Node\GetAttrNode && Node\GetAttrNode::METHOD_CALL === $node->attributes['type'] && PHP_VERSION_ID < 50400) {
|
||||||
throw new SyntaxError('Array calls on a method call is only supported on PHP 5.4+', $token->cursor);
|
throw new SyntaxError('Array calls on a method call is only supported on PHP 5.4+', $token->cursor, $this->stream->getExpression());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stream->next();
|
$this->stream->next();
|
||||||
|
@ -17,7 +17,7 @@ class SyntaxError extends \LogicException
|
|||||||
{
|
{
|
||||||
$message = sprintf('%s around position %d', $message, $cursor);
|
$message = sprintf('%s around position %d', $message, $cursor);
|
||||||
if ($expression) {
|
if ($expression) {
|
||||||
$message = sprintf('%s for expression "%s"', $message, $expression);
|
$message = sprintf('%s for expression `%s`', $message, $expression);
|
||||||
}
|
}
|
||||||
$message .= '.';
|
$message .= '.';
|
||||||
|
|
||||||
|
@ -34,12 +34,12 @@ class LexerTest extends TestCase
|
|||||||
public function testTokenize($tokens, $expression)
|
public function testTokenize($tokens, $expression)
|
||||||
{
|
{
|
||||||
$tokens[] = new Token('end of expression', null, strlen($expression) + 1);
|
$tokens[] = new Token('end of expression', null, strlen($expression) + 1);
|
||||||
$this->assertEquals(new TokenStream($tokens), $this->lexer->tokenize($expression));
|
$this->assertEquals(new TokenStream($tokens, $expression), $this->lexer->tokenize($expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Symfony\Component\ExpressionLanguage\SyntaxError
|
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
||||||
* @expectedExceptionMessage Unexpected character "'" around position 33 for expression "service(faulty.expression.example').dummyMethod()".
|
* @expectedExceptionMessage Unexpected character "'" around position 33 for expression `service(faulty.expression.example').dummyMethod()`.
|
||||||
*/
|
*/
|
||||||
public function testTokenizeThrowsErrorWithMessage()
|
public function testTokenizeThrowsErrorWithMessage()
|
||||||
{
|
{
|
||||||
@ -48,8 +48,8 @@ class LexerTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Symfony\Component\ExpressionLanguage\SyntaxError
|
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
||||||
* @expectedExceptionMessage Unclosed "(" around position 7 for expression "service(unclosed.expression.dummyMethod()".
|
* @expectedExceptionMessage Unclosed "(" around position 7 for expression `service(unclosed.expression.dummyMethod()`.
|
||||||
*/
|
*/
|
||||||
public function testTokenizeThrowsErrorOnUnclosedBrace()
|
public function testTokenizeThrowsErrorOnUnclosedBrace()
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ class ParserTest extends TestCase
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
||||||
* @expectedExceptionMessage Variable "foo" is not valid around position 1.
|
* @expectedExceptionMessage Variable "foo" is not valid around position 1 for expression `foo`.
|
||||||
*/
|
*/
|
||||||
public function testParseWithInvalidName()
|
public function testParseWithInvalidName()
|
||||||
{
|
{
|
||||||
@ -31,7 +31,7 @@ class ParserTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
|
||||||
* @expectedExceptionMessage Variable "foo" is not valid around position 1.
|
* @expectedExceptionMessage Variable "foo" is not valid around position 1 for expression `foo`.
|
||||||
*/
|
*/
|
||||||
public function testParseWithZeroInNames()
|
public function testParseWithZeroInNames()
|
||||||
{
|
{
|
||||||
|
@ -22,16 +22,19 @@ class TokenStream
|
|||||||
|
|
||||||
private $tokens;
|
private $tokens;
|
||||||
private $position = 0;
|
private $position = 0;
|
||||||
|
private $expression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param array $tokens An array of tokens
|
* @param array $tokens An array of tokens
|
||||||
|
* @param string $expression
|
||||||
*/
|
*/
|
||||||
public function __construct(array $tokens)
|
public function __construct(array $tokens, $expression = '')
|
||||||
{
|
{
|
||||||
$this->tokens = $tokens;
|
$this->tokens = $tokens;
|
||||||
$this->current = $tokens[0];
|
$this->current = $tokens[0];
|
||||||
|
$this->expression = $expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +53,7 @@ class TokenStream
|
|||||||
public function next()
|
public function next()
|
||||||
{
|
{
|
||||||
if (!isset($this->tokens[$this->position])) {
|
if (!isset($this->tokens[$this->position])) {
|
||||||
throw new SyntaxError('Unexpected end of expression', $this->current->cursor);
|
throw new SyntaxError('Unexpected end of expression', $this->current->cursor, $this->expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
++$this->position;
|
++$this->position;
|
||||||
@ -69,7 +72,7 @@ class TokenStream
|
|||||||
{
|
{
|
||||||
$token = $this->current;
|
$token = $this->current;
|
||||||
if (!$token->test($type, $value)) {
|
if (!$token->test($type, $value)) {
|
||||||
throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', $message ? $message.'. ' : '', $token->type, $token->value, $type, $value ? sprintf(' with value "%s"', $value) : ''), $token->cursor);
|
throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', $message ? $message.'. ' : '', $token->type, $token->value, $type, $value ? sprintf(' with value "%s"', $value) : ''), $token->cursor, $this->expression);
|
||||||
}
|
}
|
||||||
$this->next();
|
$this->next();
|
||||||
}
|
}
|
||||||
@ -83,4 +86,14 @@ class TokenStream
|
|||||||
{
|
{
|
||||||
return $this->current->type === Token::EOF_TYPE;
|
return $this->current->type === Token::EOF_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExpression()
|
||||||
|
{
|
||||||
|
return $this->expression;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user