mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-26 00:31:22 +02:00
Add support for pipe operator
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
%left '+' '-' '.'
|
%left '+' '-' '.'
|
||||||
#endif
|
#endif
|
||||||
#if PHP8
|
#if PHP8
|
||||||
|
%left T_PIPE
|
||||||
%left '.'
|
%left '.'
|
||||||
%left T_SL T_SR
|
%left T_SL T_SR
|
||||||
%left '+' '-'
|
%left '+' '-'
|
||||||
@@ -1085,6 +1086,9 @@ expr:
|
|||||||
| expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
|
| expr T_IS_SMALLER_OR_EQUAL expr { $$ = Expr\BinaryOp\SmallerOrEqual[$1, $3]; }
|
||||||
| expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; }
|
| expr '>' expr { $$ = Expr\BinaryOp\Greater [$1, $3]; }
|
||||||
| expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
|
| expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr\BinaryOp\GreaterOrEqual[$1, $3]; }
|
||||||
|
#if PHP8
|
||||||
|
| expr T_PIPE expr { $$ = Expr\BinaryOp\Pipe[$1, $3]; }
|
||||||
|
#endif
|
||||||
| expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; }
|
| expr T_INSTANCEOF class_name_reference { $$ = Expr\Instanceof_[$1, $3]; }
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
||||||
|
@@ -215,6 +215,9 @@ class ConstExprEvaluator {
|
|||||||
case '<': return $this->evaluate($l) < $this->evaluate($r);
|
case '<': return $this->evaluate($l) < $this->evaluate($r);
|
||||||
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
|
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
|
||||||
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
|
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
|
||||||
|
case '|>':
|
||||||
|
$lval = $this->evaluate($l);
|
||||||
|
return $this->evaluate($r)($lval);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \Exception('Should not happen');
|
throw new \Exception('Should not happen');
|
||||||
|
@@ -6,8 +6,7 @@ use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
|||||||
use PhpParser\PhpVersion;
|
use PhpParser\PhpVersion;
|
||||||
use PhpParser\Token;
|
use PhpParser\Token;
|
||||||
|
|
||||||
class PipeOperatorEmulator extends TokenEmulator
|
class PipeOperatorEmulator extends TokenEmulator {
|
||||||
{
|
|
||||||
public function getPhpVersion(): PhpVersion {
|
public function getPhpVersion(): PhpVersion {
|
||||||
return PhpVersion::fromComponents(8, 5);
|
return PhpVersion::fromComponents(8, 5);
|
||||||
}
|
}
|
||||||
@@ -16,8 +15,7 @@ class PipeOperatorEmulator extends TokenEmulator
|
|||||||
return \strpos($code, '|>') !== false;
|
return \strpos($code, '|>') !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function emulate(string $code, array $tokens): array
|
public function emulate(string $code, array $tokens): array {
|
||||||
{
|
|
||||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
$token = $tokens[$i];
|
$token = $tokens[$i];
|
||||||
if ($token->text === '|' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '>') {
|
if ($token->text === '|' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '>') {
|
||||||
@@ -30,8 +28,7 @@ class PipeOperatorEmulator extends TokenEmulator
|
|||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reverseEmulate(string $code, array $tokens): array
|
public function reverseEmulate(string $code, array $tokens): array {
|
||||||
{
|
|
||||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||||
$token = $tokens[$i];
|
$token = $tokens[$i];
|
||||||
if ($token->id === \T_PIPE) {
|
if ($token->id === \T_PIPE) {
|
||||||
@@ -45,4 +42,4 @@ class PipeOperatorEmulator extends TokenEmulator
|
|||||||
}
|
}
|
||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
lib/PhpParser/Node/Expr/BinaryOp/Pipe.php
Normal file
15
lib/PhpParser/Node/Expr/BinaryOp/Pipe.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node\Expr\BinaryOp;
|
||||||
|
|
||||||
|
use PhpParser\Node\Expr\BinaryOp;
|
||||||
|
|
||||||
|
class Pipe extends BinaryOp {
|
||||||
|
public function getOperatorSigil(): string {
|
||||||
|
return '|>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): string {
|
||||||
|
return 'Expr_BinaryOp_Pipe';
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -425,6 +425,10 @@ class Standard extends PrettyPrinterAbstract {
|
|||||||
return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence);
|
return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pExpr_BinaryOp_Pipe(BinaryOp\Pipe $node, int $precedence, int $lhsPrecedence): string {
|
||||||
|
return $this->pInfixOp(BinaryOp\Pipe::class, $node->left, ' |> ', $node->right, $precedence, $lhsPrecedence);
|
||||||
|
}
|
||||||
|
|
||||||
protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string {
|
protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string {
|
||||||
return $this->pPostfixOp(
|
return $this->pPostfixOp(
|
||||||
Expr\Instanceof_::class, $node->expr,
|
Expr\Instanceof_::class, $node->expr,
|
||||||
|
@@ -59,9 +59,11 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
|||||||
BinaryOp\Mod::class => [ 40, 41, 40],
|
BinaryOp\Mod::class => [ 40, 41, 40],
|
||||||
BinaryOp\Plus::class => [ 50, 51, 50],
|
BinaryOp\Plus::class => [ 50, 51, 50],
|
||||||
BinaryOp\Minus::class => [ 50, 51, 50],
|
BinaryOp\Minus::class => [ 50, 51, 50],
|
||||||
|
// FIXME: This precedence is incorrect for PHP 8.
|
||||||
BinaryOp\Concat::class => [ 50, 51, 50],
|
BinaryOp\Concat::class => [ 50, 51, 50],
|
||||||
BinaryOp\ShiftLeft::class => [ 60, 61, 60],
|
BinaryOp\ShiftLeft::class => [ 60, 61, 60],
|
||||||
BinaryOp\ShiftRight::class => [ 60, 61, 60],
|
BinaryOp\ShiftRight::class => [ 60, 61, 60],
|
||||||
|
BinaryOp\Pipe::class => [ 65, 66, 65],
|
||||||
BinaryOp\Smaller::class => [ 70, 70, 70],
|
BinaryOp\Smaller::class => [ 70, 70, 70],
|
||||||
BinaryOp\SmallerOrEqual::class => [ 70, 70, 70],
|
BinaryOp\SmallerOrEqual::class => [ 70, 70, 70],
|
||||||
BinaryOp\Greater::class => [ 70, 70, 70],
|
BinaryOp\Greater::class => [ 70, 70, 70],
|
||||||
@@ -1372,7 +1374,7 @@ abstract class PrettyPrinterAbstract implements PrettyPrinter {
|
|||||||
BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
|
BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class,
|
||||||
BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
|
BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class,
|
||||||
BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
|
BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class,
|
||||||
BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class,
|
BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class, BinaryOp\Pipe::class,
|
||||||
];
|
];
|
||||||
foreach ($binaryOps as $binaryOp) {
|
foreach ($binaryOps as $binaryOp) {
|
||||||
$this->fixupMap[$binaryOp] = [
|
$this->fixupMap[$binaryOp] = [
|
||||||
|
@@ -102,6 +102,12 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: lib/PhpParser/Lexer/Emulative.php
|
path: lib/PhpParser/Lexer/Emulative.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^Constant T_PIPE not found\.$#'
|
||||||
|
identifier: constant.notFound
|
||||||
|
count: 2
|
||||||
|
path: lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: '#^If condition is always false\.$#'
|
message: '#^If condition is always false\.$#'
|
||||||
identifier: if.alwaysFalse
|
identifier: if.alwaysFalse
|
||||||
|
@@ -8,7 +8,7 @@ use PhpParser\Node\Scalar;
|
|||||||
class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
||||||
/** @dataProvider provideTestEvaluate */
|
/** @dataProvider provideTestEvaluate */
|
||||||
public function testEvaluate($exprString, $expected): void {
|
public function testEvaluate($exprString, $expected): void {
|
||||||
$parser = new Parser\Php7(new Lexer());
|
$parser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||||
$expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
$expr = $parser->parse('<?php ' . $exprString . ';')[0]->expr;
|
||||||
$evaluator = new ConstExprEvaluator();
|
$evaluator = new ConstExprEvaluator();
|
||||||
$this->assertSame($expected, $evaluator->evaluateDirectly($expr));
|
$this->assertSame($expected, $evaluator->evaluateDirectly($expr));
|
||||||
@@ -71,6 +71,7 @@ class ConstExprEvaluatorTest extends \PHPUnit\Framework\TestCase {
|
|||||||
['true || (1/0)', true],
|
['true || (1/0)', true],
|
||||||
['true or (1/0)', true],
|
['true or (1/0)', true],
|
||||||
['true xor false', true],
|
['true xor false', true],
|
||||||
|
['"foo" |> "strlen"', 3],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
test/code/parser/expr/pipe.test
Normal file
75
test/code/parser/expr/pipe.test
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
Pipe operator
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
$a |> $b |> $c;
|
||||||
|
$a . $b |> $c . $d;
|
||||||
|
$a |> $b == $c;
|
||||||
|
$c == $a |> $b;
|
||||||
|
-----
|
||||||
|
array(
|
||||||
|
0: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_BinaryOp_Concat(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
right: Expr_BinaryOp_Concat(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: d
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
2: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Equal(
|
||||||
|
left: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
3: Stmt_Expression(
|
||||||
|
expr: Expr_BinaryOp_Equal(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: c
|
||||||
|
)
|
||||||
|
right: Expr_BinaryOp_Pipe(
|
||||||
|
left: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
right: Expr_Variable(
|
||||||
|
name: b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
16
test/code/prettyPrinter/expr/pipe.test
Normal file
16
test/code/prettyPrinter/expr/pipe.test
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Pipe operator
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
$a |> $b |> $c;
|
||||||
|
$a . $b |> $c . $d;
|
||||||
|
$a |> $b == $c;
|
||||||
|
$c == $a |> $b;
|
||||||
|
($a == $b) |> ($c == $d);
|
||||||
|
$a . ($b |> $c) . $d;
|
||||||
|
-----
|
||||||
|
$a |> $b |> $c;
|
||||||
|
$a . $b |> $c . $d;
|
||||||
|
$a |> $b == $c;
|
||||||
|
$c == $a |> $b;
|
||||||
|
($a == $b) |> ($c == $d);
|
||||||
|
$a . ($b |> $c) . $d;
|
Reference in New Issue
Block a user