Merge pull request #226 from TysonAndre/fix-varname

Improve parsing of `${` template strings
This commit is contained in:
Rob Lourens 2018-02-14 10:16:52 -08:00 коммит произвёл GitHub
Родитель d3477319ad 9d8c500217
Коммит cdac5b2405
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 882 добавлений и 1 удалений

Просмотреть файл

@ -1015,8 +1015,10 @@ class Parser {
case TokenKind::DollarOpenBraceToken:
case TokenKind::OpenBraceDollarToken:
$expression->children[] = $this->eat(TokenKind::DollarOpenBraceToken, TokenKind::OpenBraceDollarToken);
// TODO: Reject ${var->prop} and ${(var->prop)} without rejecting ${var+otherVar}
// Currently, this fails to reject ${var->prop} (because `var` has TokenKind::Name instead of StringVarname)
if ($this->getCurrentToken()->kind === TokenKind::StringVarname) {
$expression->children[] = $this->parseSimpleVariable($expression);
$expression->children[] = $this->parseComplexDollarTemplateStringExpression($expression);
} else {
$expression->children[] = $this->parseExpression($expression);
}
@ -1038,6 +1040,24 @@ class Parser {
}
}
/**
* This is used to parse the contents of `"${...}"` expressions.
*
* Supported: x, x[0], x[$y]
* Not supported: $x->p1, x[0][1], etc.
* @see parseTemplateStringExpression
*
* Precondition: getCurrentToken()->kind === TokenKind::StringVarname
*/
private function parseComplexDollarTemplateStringExpression($parentNode) {
$var = $this->parseSimpleVariable($parentNode);
$token = $this->getCurrentToken();
if ($token->kind === TokenKind::OpenBracketToken) {
return $this->parseTemplateStringSubscriptExpression($var);
}
return $var;
}
/**
* Double-quoted and heredoc strings support a basic set of expression types, described in http://php.net/manual/en/language.types.string.php#language.types.string.parsing
* Supported: $x, $x->p, $x[0], $x[$y]
@ -1073,6 +1093,9 @@ class Parser {
$subscriptExpression->accessExpression = $this->parseSimpleVariable($subscriptExpression);
} elseif ($token->kind === TokenKind::IntegerLiteralToken) {
$subscriptExpression->accessExpression = $this->parseNumericLiteralExpression($subscriptExpression);
} elseif ($token->kind === TokenKind::StringLiteralToken) {
// TODO: investigate if this should add other uncommon types of tokens for strings/numbers mentioned in parsePrimaryExpression()
$subscriptExpression->accessExpression = $this->parseStringLiteralExpression($subscriptExpression);
} elseif ($token->kind === TokenKind::Name) {
$subscriptExpression->accessExpression = $this->parseTemplateStringSubscriptStringLiteral($subscriptExpression);
} else {

Просмотреть файл

@ -0,0 +1,3 @@
<?php
$a = "{$var->method()}";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,102 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "OpenBraceDollarToken",
"textLength": 1
},
{
"CallExpression": {
"callableExpression": {
"MemberAccessExpression": {
"dereferencableExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"arrowToken": {
"kind": "ArrowToken",
"textLength": 2
},
"memberName": {
"kind": "Name",
"textLength": 6
}
}
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"argumentExpressionList": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
<?php
$a = "{$var['offset'][0]}";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,119 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "OpenBraceDollarToken",
"textLength": 1
},
{
"SubscriptExpression": {
"postfixExpression": {
"SubscriptExpression": {
"postfixExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"openBracketOrBrace": {
"kind": "OpenBracketToken",
"textLength": 1
},
"accessExpression": {
"StringLiteral": {
"startQuote": null,
"children": {
"kind": "StringLiteralToken",
"textLength": 8
},
"endQuote": null
}
},
"closeBracketOrBrace": {
"kind": "CloseBracketToken",
"textLength": 1
}
}
},
"openBracketOrBrace": {
"kind": "OpenBracketToken",
"textLength": 1
},
"accessExpression": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
},
"closeBracketOrBrace": {
"kind": "CloseBracketToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
<?php
$a = "{$x::static_method(2+2)}\n";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,141 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "OpenBraceDollarToken",
"textLength": 1
},
{
"CallExpression": {
"callableExpression": {
"ScopedPropertyAccessExpression": {
"scopeResolutionQualifier": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"doubleColon": {
"kind": "ColonColonToken",
"textLength": 2
},
"memberName": {
"kind": "Name",
"textLength": 13
}
}
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"argumentExpressionList": {
"ArgumentExpressionList": {
"children": [
{
"ArgumentExpression": {
"byRefToken": null,
"dotDotDotToken": null,
"expression": {
"BinaryExpression": {
"leftOperand": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
},
"operator": {
"kind": "PlusToken",
"textLength": 1
},
"rightOperand": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
}
}
}
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
},
{
"kind": "EncapsedAndWhitespace",
"textLength": 2
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,2 @@
<?php
$a = "{$x::static_method(2+2)}";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,137 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "OpenBraceDollarToken",
"textLength": 1
},
{
"CallExpression": {
"callableExpression": {
"ScopedPropertyAccessExpression": {
"scopeResolutionQualifier": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"doubleColon": {
"kind": "ColonColonToken",
"textLength": 2
},
"memberName": {
"kind": "Name",
"textLength": 13
}
}
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"argumentExpressionList": {
"ArgumentExpressionList": {
"children": [
{
"ArgumentExpression": {
"byRefToken": null,
"dotDotDotToken": null,
"expression": {
"BinaryExpression": {
"leftOperand": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
},
"operator": {
"kind": "PlusToken",
"textLength": 1
},
"rightOperand": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
}
}
}
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
<?php
$a = "${var['offset']}";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,99 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "DollarOpenBraceToken",
"textLength": 2
},
{
"SubscriptExpression": {
"postfixExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "StringVarname",
"textLength": 3
}
}
},
"openBracketOrBrace": {
"kind": "OpenBracketToken",
"textLength": 1
},
"accessExpression": {
"StringLiteral": {
"startQuote": null,
"children": {
"kind": "StringLiteralToken",
"textLength": 8
},
"endQuote": null
}
},
"closeBracketOrBrace": {
"kind": "CloseBracketToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
<?php
$a = "{$varCallable()}";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,90 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "OpenBraceDollarToken",
"textLength": 1
},
{
"CallExpression": {
"callableExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 12
}
}
},
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"argumentExpressionList": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,3 @@
<?php
$data = "<a ${t_url_href}${t_url_target}>${p_match[1]}</a>";

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,143 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 5
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"StringLiteral": {
"startQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
},
"children": [
{
"kind": "EncapsedAndWhitespace",
"textLength": 3
},
{
"kind": "DollarOpenBraceToken",
"textLength": 2
},
{
"Variable": {
"dollar": null,
"name": {
"kind": "StringVarname",
"textLength": 10
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
},
{
"kind": "DollarOpenBraceToken",
"textLength": 2
},
{
"Variable": {
"dollar": null,
"name": {
"kind": "StringVarname",
"textLength": 12
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
},
{
"kind": "EncapsedAndWhitespace",
"textLength": 1
},
{
"kind": "DollarOpenBraceToken",
"textLength": 2
},
{
"SubscriptExpression": {
"postfixExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "StringVarname",
"textLength": 7
}
}
},
"openBracketOrBrace": {
"kind": "OpenBracketToken",
"textLength": 1
},
"accessExpression": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
},
"closeBracketOrBrace": {
"kind": "CloseBracketToken",
"textLength": 1
}
}
},
{
"kind": "CloseBraceToken",
"textLength": 1
},
{
"kind": "EncapsedAndWhitespace",
"textLength": 4
}
],
"endQuote": {
"kind": "DoubleQuoteToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}