Skip to content

Commit

Permalink
Introduce Stmt\Block
Browse files Browse the repository at this point in the history
Stmt\Block will be created for { $a; } style blocks, unless these
occur directly inside some structure that is usually combined
with a block.

For example if ($a) { $b; } will continue to use the old
representation (plain array in in If_::$stmts), but a free-standing
{ $b; } will become a Stmt\Block.

Fixes #590.
  • Loading branch information
nikic committed Sep 24, 2023
1 parent f5adbb5 commit a1ccf57
Show file tree
Hide file tree
Showing 19 changed files with 1,700 additions and 1,641 deletions.
38 changes: 16 additions & 22 deletions grammar/php.y
Original file line number Diff line number Diff line change
Expand Up @@ -366,21 +366,13 @@ inner_statement:
;

non_empty_statement:
'{' inner_statement_list '}'
{
if ($2) {
$$ = $2; prependLeadingComments($$);
} else {
makeNop($$);
if (null === $$) { $$ = array(); }
}
}
| T_IF '(' expr ')' statement elseif_list else_single
{ $$ = Stmt\If_[$3, ['stmts' => toArray($5), 'elseifs' => $6, 'else' => $7]]; }
'{' inner_statement_list '}' { $$ = Stmt\Block[$2]; }
| T_IF '(' expr ')' blocklike_statement elseif_list else_single
{ $$ = Stmt\If_[$3, ['stmts' => $5, 'elseifs' => $6, 'else' => $7]]; }
| T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
{ $$ = Stmt\If_[$3, ['stmts' => $6, 'elseifs' => $7, 'else' => $8]]; }
| T_WHILE '(' expr ')' while_statement { $$ = Stmt\While_[$3, $5]; }
| T_DO statement T_WHILE '(' expr ')' ';' { $$ = Stmt\Do_ [$5, toArray($2)]; }
| T_DO blocklike_statement T_WHILE '(' expr ')' ';' { $$ = Stmt\Do_ [$5, $2]; }
| T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement
{ $$ = Stmt\For_[['init' => $3, 'cond' => $5, 'loop' => $7, 'stmts' => $9]]; }
| T_SWITCH '(' expr ')' switch_case_list { $$ = Stmt\Switch_[$3, $5]; }
Expand Down Expand Up @@ -416,14 +408,16 @@ non_empty_statement:
{ $$ = Stmt\TryCatch[$3, $5, $6]; $this->checkTryCatch($$); }
| T_GOTO identifier_not_reserved semi { $$ = Stmt\Goto_[$2]; }
| identifier_not_reserved ':' { $$ = Stmt\Label[$1]; }
| error { $$ = array(); /* means: no statement */ }
| error { $$ = null; /* means: no statement */ }
;

statement:
non_empty_statement
| ';'
{ makeNop($$);
if ($$ === null) $$ = array(); /* means: no statement */ }
| ';' { makeNop($$); }
;

blocklike_statement:
statement { toBlock($1); }
;

catches:
Expand Down Expand Up @@ -554,17 +548,17 @@ non_empty_class_name_list:
;

for_statement:
statement { $$ = toArray($1); }
blocklike_statement
| ':' inner_statement_list T_ENDFOR ';' { $$ = $2; }
;

foreach_statement:
statement { $$ = toArray($1); }
blocklike_statement
| ':' inner_statement_list T_ENDFOREACH ';' { $$ = $2; }
;

declare_statement:
non_empty_statement { $$ = toArray($1); }
non_empty_statement { toBlock($1); }
| ';' { $$ = null; }
| ':' inner_statement_list T_ENDDECLARE ';' { $$ = $2; }
;
Expand Down Expand Up @@ -624,7 +618,7 @@ match_arm:
;

while_statement:
statement { $$ = toArray($1); }
blocklike_statement { $$ = $1; }
| ':' inner_statement_list T_ENDWHILE ';' { $$ = $2; }
;

Expand All @@ -634,7 +628,7 @@ elseif_list:
;

elseif:
T_ELSEIF '(' expr ')' statement { $$ = Stmt\ElseIf_[$3, toArray($5)]; }
T_ELSEIF '(' expr ')' blocklike_statement { $$ = Stmt\ElseIf_[$3, $5]; }
;

new_elseif_list:
Expand All @@ -649,7 +643,7 @@ new_elseif:

else_single:
/* empty */ { $$ = null; }
| T_ELSE statement { $$ = Stmt\Else_[toArray($2)]; }
| T_ELSE blocklike_statement { $$ = Stmt\Else_[$2]; }
;

new_else_single:
Expand Down
18 changes: 5 additions & 13 deletions grammar/phpyLang.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ function ($matches) {
if ('pushNormalizing' === $name) {
assertArgs(2, $args, $name);

return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
. ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
return 'if (' . $args[1] . ' !== null) { ' . $args[0] . '[] = ' . $args[1] . '; } $$ = ' . $args[0] . ';';
}

if ('toArray' == $name) {
if ('toBlock' == $name) {
assertArgs(1, $args, $name);

return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
return 'if (' . $args[0] . ' instanceof Stmt\Block) { $$ = ' . $args[0] . '->stmts; } '
. 'else if (' . $args[0] . ' === null) { $$ = []; } '
. 'else { $$ = [' . $args[0] . ']; }';
}

if ('parseVar' === $name) {
Expand Down Expand Up @@ -122,15 +123,6 @@ function ($matches) {
return $args[0] . ' = $this->maybeCreateZeroLengthNop($this->tokenPos);';
}

if ('prependLeadingComments' === $name) {
assertArgs(1, $args, $name);

return '$comments = $this->getCommentsBeforeToken($this->tokenStartStack[#1]); $stmts = ' . $args[0] . '; '
. 'if (!empty($comments)) {'
. '$stmts[0]->setAttribute(\'comments\', '
. 'array_merge($comments, $stmts[0]->getAttribute(\'comments\', []))); }';
}

return $matches[0];
},
$code
Expand Down
6 changes: 0 additions & 6 deletions lib/PhpParser/Internal/TokenStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,6 @@ public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool {
return false;
}

public function haveBracesInRange(int $startPos, int $endPos): bool {
return $this->haveTokenInRange($startPos, $endPos, '{')
|| $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
|| $this->haveTokenInRange($startPos, $endPos, '}');
}

public function haveTagInRange(int $startPos, int $endPos): bool {
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
Expand Down
29 changes: 29 additions & 0 deletions lib/PhpParser/Node/Stmt/Block.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types=1);

namespace PhpParser\Node\Stmt;

use PhpParser\Node\Stmt;

class Block extends Stmt {
/** @var Stmt[] Statements */
public array $stmts;

/**
* A block of statements.
*
* @param Stmt[] $stmts Statements
* @param array<string, mixed> $attributes Additional attributes
*/
public function __construct(array $stmts, array $attributes = []) {
$this->attributes = $attributes;
$this->stmts = $stmts;
}

public function getType(): string {
return 'Stmt_Block';
}

public function getSubNodeNames(): array {
return ['stmts'];
}
}

0 comments on commit a1ccf57

Please sign in to comment.