Add useConsistentVariableNodes mode

The parameter case is a bit weird, because the subnode is called
"name" here, rather than "var". Nothing we can do about that in
this version though.

The two parser options might be merged. I've kept it separate,
because I think this variable representation should become the
default (or even only representation) in the future, while I'm
less sure about the Identifier thing.
This commit is contained in:
Nikita Popov 2016-12-23 00:09:59 +01:00
parent 122f449960
commit a947e731c3
8 changed files with 2658 additions and 2516 deletions

View File

@ -36,12 +36,12 @@ semi_reserved:
;
identifier_ex:
T_STRING { $$ = makeIdent($1); }
| semi_reserved { $$ = makeIdent($1); }
T_STRING { $$ = maybeMakeIdent($1); }
| semi_reserved { $$ = maybeMakeIdent($1); }
;
identifier:
T_STRING { $$ = makeIdent($1); }
T_STRING { $$ = maybeMakeIdent($1); }
;
namespace_name_parts:
@ -53,6 +53,10 @@ namespace_name:
namespace_name_parts { $$ = Name[$1]; }
;
plain_variable:
T_VARIABLE { $$ = maybeMakeVar(parseVar($1)); }
;
top_statement:
statement { $$ = $1; }
| function_declaration_statement { $$ = $1; }
@ -209,8 +213,8 @@ catches:
;
catch:
T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}'
{ $$ = Stmt\Catch_[array($3), parseVar($4), $7]; }
T_CATCH '(' name plain_variable ')' '{' inner_statement_list '}'
{ $$ = Stmt\Catch_[array($3), $4, $7]; }
;
optional_finally:
@ -372,16 +376,16 @@ non_empty_parameter_list:
;
parameter:
optional_param_type optional_ref optional_ellipsis T_VARIABLE
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; $this->checkParam($$); }
| optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' static_scalar
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; $this->checkParam($$); }
optional_param_type optional_ref optional_ellipsis plain_variable
{ $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); }
| optional_param_type optional_ref optional_ellipsis plain_variable '=' static_scalar
{ $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); }
;
type:
name { $$ = $1; }
| T_ARRAY { $$ = makeIdent('array'); }
| T_CALLABLE { $$ = makeIdent('callable'); }
| T_ARRAY { $$ = maybeMakeIdent('array'); }
| T_CALLABLE { $$ = maybeMakeIdent('callable'); }
;
optional_param_type:
@ -428,8 +432,8 @@ static_var_list:
;
static_var:
T_VARIABLE { $$ = Stmt\StaticVar[parseVar($1), null]; }
| T_VARIABLE '=' static_scalar { $$ = Stmt\StaticVar[parseVar($1), $3]; }
plain_variable { $$ = Stmt\StaticVar[$1, null]; }
| plain_variable '=' static_scalar { $$ = Stmt\StaticVar[$1, $3]; }
;
class_statement_list:
@ -673,7 +677,7 @@ lexical_var_list:
;
lexical_var:
optional_ref T_VARIABLE { $$ = Expr\ClosureUse[parseVar($2), $1]; }
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
;
function_call:

View File

@ -36,12 +36,12 @@ semi_reserved:
;
identifier_ex:
T_STRING { $$ = makeIdent($1); }
| semi_reserved { $$ = makeIdent($1); }
T_STRING { $$ = maybeMakeIdent($1); }
| semi_reserved { $$ = maybeMakeIdent($1); }
;
identifier:
T_STRING { $$ = makeIdent($1); }
T_STRING { $$ = maybeMakeIdent($1); }
;
namespace_name_parts:
@ -53,6 +53,10 @@ namespace_name:
namespace_name_parts { $$ = Name[$1]; }
;
plain_variable:
T_VARIABLE { $$ = maybeMakeVar(parseVar($1)); }
;
top_statement:
statement { $$ = $1; }
| function_declaration_statement { $$ = $1; }
@ -210,8 +214,8 @@ name_union:
;
catch:
T_CATCH '(' name_union T_VARIABLE ')' '{' inner_statement_list '}'
{ $$ = Stmt\Catch_[$3, parseVar($4), $7]; }
T_CATCH '(' name_union plain_variable ')' '{' inner_statement_list '}'
{ $$ = Stmt\Catch_[$3, $4, $7]; }
;
optional_finally:
@ -374,10 +378,10 @@ non_empty_parameter_list:
;
parameter:
optional_param_type optional_ref optional_ellipsis T_VARIABLE
{ $$ = Node\Param[parseVar($4), null, $1, $2, $3]; $this->checkParam($$); }
| optional_param_type optional_ref optional_ellipsis T_VARIABLE '=' expr
{ $$ = Node\Param[parseVar($4), $6, $1, $2, $3]; $this->checkParam($$); }
optional_param_type optional_ref optional_ellipsis plain_variable
{ $$ = Node\Param[$4, null, $1, $2, $3]; $this->checkParam($$); }
| optional_param_type optional_ref optional_ellipsis plain_variable '=' expr
{ $$ = Node\Param[$4, $6, $1, $2, $3]; $this->checkParam($$); }
;
type_expr:
@ -387,8 +391,8 @@ type_expr:
type:
name { $$ = $this->handleBuiltinTypes($1); }
| T_ARRAY { $$ = makeIdent('array'); }
| T_CALLABLE { $$ = makeIdent('callable'); }
| T_ARRAY { $$ = maybeMakeIdent('array'); }
| T_CALLABLE { $$ = maybeMakeIdent('callable'); }
;
optional_param_type:
@ -432,8 +436,8 @@ static_var_list:
;
static_var:
T_VARIABLE { $$ = Stmt\StaticVar[parseVar($1), null]; }
| T_VARIABLE '=' expr { $$ = Stmt\StaticVar[parseVar($1), $3]; }
plain_variable { $$ = Stmt\StaticVar[$1, null]; }
| plain_variable '=' expr { $$ = Stmt\StaticVar[$1, $3]; }
;
class_statement_list:
@ -647,7 +651,7 @@ lexical_var_list:
;
lexical_var:
optional_ref T_VARIABLE { $$ = Expr\ClosureUse[parseVar($2), $1]; }
optional_ref plain_variable { $$ = Expr\ClosureUse[$2, $1]; }
;
function_call:

View File

@ -210,13 +210,20 @@ function resolveMacros($code) {
. 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
}
if ('makeIdent' == $name) {
if ('maybeMakeIdent' == $name) {
assertArgs(1, $args, $name);
return '($this->useIdentifierNodes ? new Node\Identifier(' . $args[0] . ', '
. '$this->startAttributeStack[#1] + $this->endAttributes) : ' . $args[0] . ')';
}
if ('maybeMakeVar' == $name) {
assertArgs(1, $args, $name);
return '($this->useConsistentVariableNodes ? new Expr\Variable(' . $args[0] . ', '
. '$this->startAttributeStack[#1] + $this->endAttributes) : ' . $args[0] . ')';
}
return $matches[0];
},
$code

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -113,20 +113,26 @@ abstract class ParserAbstract implements Parser
/** @var bool Whether to create Identifier nodes for non-namespaced names */
protected $useIdentifierNodes;
/** @var bool Whether to consistently use Variable nodes */
protected $useConsistentVariableNodes;
/**
* Creates a parser instance.
*
* Options: If "useIdentifierNodes" is enabled, the parser will create Identifier nodes for
* non-namespaced names. Otherwise plain strings will be used.
* Options:
* * useIdentifierNodes: If this option is enabled, the parser will create Identifier nodes for
* non-namespaced names. Otherwise plain strings will be used.
* * useConsistentVariableNodes: If this option is enabled, the parser will create Variable
* nodes in more places (like function parameters, catch clause variables, etc.)
*
* @param Lexer $lexer A lexer
* @param array $options Options array. Currently only "useIdentifierNodes" is supporter.
* @param array $options Options array.
*/
public function __construct(Lexer $lexer, array $options = array()) {
$this->lexer = $lexer;
$this->errors = array();
$this->useIdentifierNodes = !empty($options['useIdentifierNodes']);
$this->useConsistentVariableNodes = !empty($options['useConsistentVariableNodes']);
if (isset($options['throwOnError'])) {
throw new \LogicException(

View File

@ -16,7 +16,10 @@ class CodeParsingTest extends CodeTestAbstract
$modes = [];
}
$parserOptions = ['useIdentifierNodes' => isset($modes['ident'])];
$parserOptions = [
'useIdentifierNodes' => isset($modes['ident']),
'useConsistentVariableNodes' => isset($modes['consistentVars']),
];
$lexer = new Lexer\Emulative(array('usedAttributes' => array(
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'

View File

@ -0,0 +1,106 @@
Consistent variable mode
-----
<?php
function test($param1, $param2 = 0) {
static $foo, $bar = 0;
}
function() use ($foo, &$bar) {};
try {} catch (Exception $var) {}
-----
!!consistentVars
array(
0: Stmt_Function(
byRef: false
name: test
params: array(
0: Param(
type: null
byRef: false
variadic: false
name: Expr_Variable(
name: param1
)
default: null
)
1: Param(
type: null
byRef: false
variadic: false
name: Expr_Variable(
name: param2
)
default: Scalar_LNumber(
value: 0
)
)
)
returnType: null
stmts: array(
0: Stmt_Static(
vars: array(
0: Stmt_StaticVar(
name: Expr_Variable(
name: foo
)
default: null
)
1: Stmt_StaticVar(
name: Expr_Variable(
name: bar
)
default: Scalar_LNumber(
value: 0
)
)
)
)
)
)
1: Expr_Closure(
static: false
byRef: false
params: array(
)
uses: array(
0: Expr_ClosureUse(
var: Expr_Variable(
name: foo
)
byRef: false
)
1: Expr_ClosureUse(
var: Expr_Variable(
name: bar
)
byRef: true
)
)
returnType: null
stmts: array(
)
)
2: Stmt_TryCatch(
stmts: array(
)
catches: array(
0: Stmt_Catch(
types: array(
0: Name(
parts: array(
0: Exception
)
)
)
var: Expr_Variable(
name: var
)
stmts: array(
)
)
)
finally: null
)
)