mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2025-01-22 05:11:39 +01:00
Add support for null coalesce operator (??) [PHP 7]
Added as Expr\BinaryOp\Coalesce.
This commit is contained in:
parent
dc28449d81
commit
01f4be6b4d
@ -10,6 +10,7 @@
|
||||
%right T_YIELD
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
|
||||
%left '?' ':'
|
||||
%right T_COALESCE
|
||||
%left T_BOOLEAN_OR
|
||||
%left T_BOOLEAN_AND
|
||||
%left '|'
|
||||
@ -590,6 +591,7 @@ expr:
|
||||
| '(' new_expr ')' { $$ = $2; }
|
||||
| expr '?' expr ':' expr { $$ = Expr\Ternary[$1, $3, $5]; }
|
||||
| expr '?' ':' expr { $$ = Expr\Ternary[$1, null, $4]; }
|
||||
| expr T_COALESCE expr { $$ = Expr\BinaryOp\Coalesce[$1, $3]; }
|
||||
| T_ISSET '(' variables_list ')' { $$ = Expr\Isset_[$3]; }
|
||||
| T_EMPTY '(' expr ')' { $$ = Expr\Empty_[$3]; }
|
||||
| T_INCLUDE expr { $$ = Expr\Include_[$2, Expr\Include_::TYPE_INCLUDE]; }
|
||||
|
@ -15,7 +15,9 @@ class Emulative extends \PhpParser\Lexer
|
||||
const T_ELLIPSIS = 1001;
|
||||
const T_POW = 1002;
|
||||
const T_POW_EQUAL = 1003;
|
||||
const T_COALESCE = 1004;
|
||||
|
||||
const PHP_7_0 = '7.0.0dev';
|
||||
const PHP_5_6 = '5.6.0rc1';
|
||||
const PHP_5_5 = '5.5.0beta1';
|
||||
const PHP_5_4 = '5.4.0beta1';
|
||||
@ -45,11 +47,17 @@ class Emulative extends \PhpParser\Lexer
|
||||
$this->newKeywords += $newKeywords;
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, self::PHP_5_6, '<')) {
|
||||
$this->tokenMap[self::T_ELLIPSIS] = Parser::T_ELLIPSIS;
|
||||
$this->tokenMap[self::T_POW] = Parser::T_POW;
|
||||
$this->tokenMap[self::T_POW_EQUAL] = Parser::T_POW_EQUAL;
|
||||
if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->tokenMap[self::T_COALESCE] = Parser::T_COALESCE;
|
||||
|
||||
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->tokenMap[self::T_ELLIPSIS] = Parser::T_ELLIPSIS;
|
||||
$this->tokenMap[self::T_POW] = Parser::T_POW;
|
||||
$this->tokenMap[self::T_POW_EQUAL] = Parser::T_POW_EQUAL;
|
||||
}
|
||||
|
||||
public function startLexing($code) {
|
||||
@ -75,6 +83,12 @@ class Emulative extends \PhpParser\Lexer
|
||||
* inside a string, i.e. a place where they don't have a special meaning).
|
||||
*/
|
||||
protected function preprocessCode($code) {
|
||||
if (version_compare(PHP_VERSION, self::PHP_7_0, '>=')) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
$code = str_replace('??', '~__EMU__COALESCE__~', $code);
|
||||
|
||||
if (version_compare(PHP_VERSION, self::PHP_5_6, '>=')) {
|
||||
return $code;
|
||||
}
|
||||
@ -125,6 +139,10 @@ class Emulative extends \PhpParser\Lexer
|
||||
$replace = array(
|
||||
array(self::T_POW_EQUAL, '**=', $this->tokens[$i + 1][2])
|
||||
);
|
||||
} else if ('COALESCE' === $matches[1]) {
|
||||
$replace = array(
|
||||
array(self::T_COALESCE, '??', $this->tokens[$i + 1][2])
|
||||
);
|
||||
} else {
|
||||
// just ignore all other __EMU__ sequences
|
||||
continue;
|
||||
@ -159,6 +177,8 @@ class Emulative extends \PhpParser\Lexer
|
||||
return '**';
|
||||
} else if ('POWEQUAL' === $matches[1]) {
|
||||
return '**=';
|
||||
} else if ('COALESCE' === $matches[1]) {
|
||||
return '??';
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
|
9
lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php
Normal file
9
lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\Node\Expr\BinaryOp;
|
||||
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
|
||||
class Coalesce extends BinaryOp
|
||||
{
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -262,6 +262,10 @@ class Standard extends PrettyPrinterAbstract
|
||||
return $this->pInfixOp('Expr_BinaryOp_SmallerOrEqual', $node->left, ' <= ', $node->right);
|
||||
}
|
||||
|
||||
public function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node) {
|
||||
return $this->pInfixOp('Expr_BinaryOp_Coalesce', $node->left, ' ?? ', $node->right);
|
||||
}
|
||||
|
||||
public function pExpr_Instanceof(Expr\Instanceof_ $node) {
|
||||
return $this->pInfixOp('Expr_Instanceof', $node->expr, ' instanceof ', $node->class);
|
||||
}
|
||||
|
@ -9,65 +9,66 @@ abstract class PrettyPrinterAbstract
|
||||
{
|
||||
protected $precedenceMap = array(
|
||||
// [precedence, associativity] where for the latter -1 is %left, 0 is %nonassoc and 1 is %right
|
||||
'Expr_BinaryOp_Pow' => array( 0, 1),
|
||||
'Expr_BitwiseNot' => array( 1, 1),
|
||||
'Expr_PreInc' => array( 1, 1),
|
||||
'Expr_PreDec' => array( 1, 1),
|
||||
'Expr_PostInc' => array( 1, -1),
|
||||
'Expr_PostDec' => array( 1, -1),
|
||||
'Expr_UnaryPlus' => array( 1, 1),
|
||||
'Expr_UnaryMinus' => array( 1, 1),
|
||||
'Expr_Cast_Int' => array( 1, 1),
|
||||
'Expr_Cast_Double' => array( 1, 1),
|
||||
'Expr_Cast_String' => array( 1, 1),
|
||||
'Expr_Cast_Array' => array( 1, 1),
|
||||
'Expr_Cast_Object' => array( 1, 1),
|
||||
'Expr_Cast_Bool' => array( 1, 1),
|
||||
'Expr_Cast_Unset' => array( 1, 1),
|
||||
'Expr_ErrorSuppress' => array( 1, 1),
|
||||
'Expr_Instanceof' => array( 2, 0),
|
||||
'Expr_BooleanNot' => array( 3, 1),
|
||||
'Expr_BinaryOp_Mul' => array( 4, -1),
|
||||
'Expr_BinaryOp_Div' => array( 4, -1),
|
||||
'Expr_BinaryOp_Mod' => array( 4, -1),
|
||||
'Expr_BinaryOp_Plus' => array( 5, -1),
|
||||
'Expr_BinaryOp_Minus' => array( 5, -1),
|
||||
'Expr_BinaryOp_Concat' => array( 5, -1),
|
||||
'Expr_BinaryOp_ShiftLeft' => array( 6, -1),
|
||||
'Expr_BinaryOp_ShiftRight' => array( 6, -1),
|
||||
'Expr_BinaryOp_Smaller' => array( 7, 0),
|
||||
'Expr_BinaryOp_SmallerOrEqual' => array( 7, 0),
|
||||
'Expr_BinaryOp_Greater' => array( 7, 0),
|
||||
'Expr_BinaryOp_GreaterOrEqual' => array( 7, 0),
|
||||
'Expr_BinaryOp_Equal' => array( 8, 0),
|
||||
'Expr_BinaryOp_NotEqual' => array( 8, 0),
|
||||
'Expr_BinaryOp_Identical' => array( 8, 0),
|
||||
'Expr_BinaryOp_NotIdentical' => array( 8, 0),
|
||||
'Expr_BinaryOp_BitwiseAnd' => array( 9, -1),
|
||||
'Expr_BinaryOp_BitwiseXor' => array(10, -1),
|
||||
'Expr_BinaryOp_BitwiseOr' => array(11, -1),
|
||||
'Expr_BinaryOp_BooleanAnd' => array(12, -1),
|
||||
'Expr_BinaryOp_BooleanOr' => array(13, -1),
|
||||
'Expr_Ternary' => array(14, -1),
|
||||
'Expr_BinaryOp_Pow' => array( 0, 1),
|
||||
'Expr_BitwiseNot' => array( 10, 1),
|
||||
'Expr_PreInc' => array( 10, 1),
|
||||
'Expr_PreDec' => array( 10, 1),
|
||||
'Expr_PostInc' => array( 10, -1),
|
||||
'Expr_PostDec' => array( 10, -1),
|
||||
'Expr_UnaryPlus' => array( 10, 1),
|
||||
'Expr_UnaryMinus' => array( 10, 1),
|
||||
'Expr_Cast_Int' => array( 10, 1),
|
||||
'Expr_Cast_Double' => array( 10, 1),
|
||||
'Expr_Cast_String' => array( 10, 1),
|
||||
'Expr_Cast_Array' => array( 10, 1),
|
||||
'Expr_Cast_Object' => array( 10, 1),
|
||||
'Expr_Cast_Bool' => array( 10, 1),
|
||||
'Expr_Cast_Unset' => array( 10, 1),
|
||||
'Expr_ErrorSuppress' => array( 10, 1),
|
||||
'Expr_Instanceof' => array( 20, 0),
|
||||
'Expr_BooleanNot' => array( 30, 1),
|
||||
'Expr_BinaryOp_Mul' => array( 40, -1),
|
||||
'Expr_BinaryOp_Div' => array( 40, -1),
|
||||
'Expr_BinaryOp_Mod' => array( 40, -1),
|
||||
'Expr_BinaryOp_Plus' => array( 50, -1),
|
||||
'Expr_BinaryOp_Minus' => array( 50, -1),
|
||||
'Expr_BinaryOp_Concat' => array( 50, -1),
|
||||
'Expr_BinaryOp_ShiftLeft' => array( 60, -1),
|
||||
'Expr_BinaryOp_ShiftRight' => array( 60, -1),
|
||||
'Expr_BinaryOp_Smaller' => array( 70, 0),
|
||||
'Expr_BinaryOp_SmallerOrEqual' => array( 70, 0),
|
||||
'Expr_BinaryOp_Greater' => array( 70, 0),
|
||||
'Expr_BinaryOp_GreaterOrEqual' => array( 70, 0),
|
||||
'Expr_BinaryOp_Equal' => array( 80, 0),
|
||||
'Expr_BinaryOp_NotEqual' => array( 80, 0),
|
||||
'Expr_BinaryOp_Identical' => array( 80, 0),
|
||||
'Expr_BinaryOp_NotIdentical' => array( 80, 0),
|
||||
'Expr_BinaryOp_BitwiseAnd' => array( 90, -1),
|
||||
'Expr_BinaryOp_BitwiseXor' => array(100, -1),
|
||||
'Expr_BinaryOp_BitwiseOr' => array(110, -1),
|
||||
'Expr_BinaryOp_BooleanAnd' => array(120, -1),
|
||||
'Expr_BinaryOp_BooleanOr' => array(130, -1),
|
||||
'Expr_BinaryOp_Coalesce' => array(140, 1),
|
||||
'Expr_Ternary' => array(150, -1),
|
||||
// parser uses %left for assignments, but they really behave as %right
|
||||
'Expr_Assign' => array(15, 1),
|
||||
'Expr_AssignRef' => array(15, 1),
|
||||
'Expr_AssignOp_Plus' => array(15, 1),
|
||||
'Expr_AssignOp_Minus' => array(15, 1),
|
||||
'Expr_AssignOp_Mul' => array(15, 1),
|
||||
'Expr_AssignOp_Div' => array(15, 1),
|
||||
'Expr_AssignOp_Concat' => array(15, 1),
|
||||
'Expr_AssignOp_Mod' => array(15, 1),
|
||||
'Expr_AssignOp_BitwiseAnd' => array(15, 1),
|
||||
'Expr_AssignOp_BitwiseOr' => array(15, 1),
|
||||
'Expr_AssignOp_BitwiseXor' => array(15, 1),
|
||||
'Expr_AssignOp_ShiftLeft' => array(15, 1),
|
||||
'Expr_AssignOp_ShiftRight' => array(15, 1),
|
||||
'Expr_AssignOp_Pow' => array(15, 1),
|
||||
'Expr_BinaryOp_LogicalAnd' => array(16, -1),
|
||||
'Expr_BinaryOp_LogicalXor' => array(17, -1),
|
||||
'Expr_BinaryOp_LogicalOr' => array(18, -1),
|
||||
'Expr_Include' => array(19, -1),
|
||||
'Expr_Assign' => array(160, 1),
|
||||
'Expr_AssignRef' => array(160, 1),
|
||||
'Expr_AssignOp_Plus' => array(160, 1),
|
||||
'Expr_AssignOp_Minus' => array(160, 1),
|
||||
'Expr_AssignOp_Mul' => array(160, 1),
|
||||
'Expr_AssignOp_Div' => array(160, 1),
|
||||
'Expr_AssignOp_Concat' => array(160, 1),
|
||||
'Expr_AssignOp_Mod' => array(160, 1),
|
||||
'Expr_AssignOp_BitwiseAnd' => array(160, 1),
|
||||
'Expr_AssignOp_BitwiseOr' => array(160, 1),
|
||||
'Expr_AssignOp_BitwiseXor' => array(160, 1),
|
||||
'Expr_AssignOp_ShiftLeft' => array(160, 1),
|
||||
'Expr_AssignOp_ShiftRight' => array(160, 1),
|
||||
'Expr_AssignOp_Pow' => array(160, 1),
|
||||
'Expr_BinaryOp_LogicalAnd' => array(170, -1),
|
||||
'Expr_BinaryOp_LogicalXor' => array(180, -1),
|
||||
'Expr_BinaryOp_LogicalOr' => array(190, -1),
|
||||
'Expr_Include' => array(200, -1),
|
||||
);
|
||||
|
||||
protected $noIndentToken;
|
||||
@ -263,4 +264,4 @@ abstract class PrettyPrinterAbstract
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,9 @@ class EmulativeTest extends LexerTest
|
||||
array('**=', array(
|
||||
array(Parser::T_POW_EQUAL, '**='),
|
||||
)),
|
||||
array('??', array(
|
||||
array(Parser::T_COALESCE, '??'),
|
||||
)),
|
||||
array('0b1010110', array(
|
||||
array(Parser::T_LNUMBER, '0b1010110'),
|
||||
)),
|
||||
|
@ -9,6 +9,12 @@ $a ?: $c;
|
||||
// precedence
|
||||
$a ? $b : $c ? $d : $e;
|
||||
$a ? $b : ($c ? $d : $e);
|
||||
|
||||
// null coalesce
|
||||
$a ?? $b;
|
||||
$a ?? $b ?? $c;
|
||||
$a ?? $b ? $c : $d;
|
||||
$a && $b ?? $c;
|
||||
-----
|
||||
array(
|
||||
0: Expr_Ternary(
|
||||
@ -69,4 +75,54 @@ array(
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
4: Expr_BinaryOp_Coalesce(
|
||||
left: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
5: Expr_BinaryOp_Coalesce(
|
||||
left: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
right: Expr_BinaryOp_Coalesce(
|
||||
left: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: c
|
||||
)
|
||||
)
|
||||
)
|
||||
6: Expr_Ternary(
|
||||
cond: Expr_BinaryOp_Coalesce(
|
||||
left: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
if: Expr_Variable(
|
||||
name: c
|
||||
)
|
||||
else: Expr_Variable(
|
||||
name: d
|
||||
)
|
||||
)
|
||||
7: Expr_BinaryOp_Coalesce(
|
||||
left: Expr_BinaryOp_BooleanAnd(
|
||||
left: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: b
|
||||
)
|
||||
)
|
||||
right: Expr_Variable(
|
||||
name: c
|
||||
)
|
||||
)
|
||||
)
|
@ -51,6 +51,7 @@ $a && $b;
|
||||
$a || $b;
|
||||
$a ? $b : $c;
|
||||
$a ?: $c;
|
||||
$a ?? $c;
|
||||
|
||||
$a = $b;
|
||||
$a **= $b;
|
||||
@ -119,6 +120,7 @@ $a && $b;
|
||||
$a || $b;
|
||||
$a ? $b : $c;
|
||||
$a ?: $c;
|
||||
$a ?? $c;
|
||||
$a = $b;
|
||||
$a **= $b;
|
||||
$a *= $b;
|
||||
|
@ -20,6 +20,11 @@ $a ? $b : $c ? $d : $e ? $f : $g;
|
||||
$a ? $b : ($c ? $d : ($e ? $f : $g));
|
||||
$a ? $b ? $c : $d : $f;
|
||||
|
||||
$a ?? $b ?? $c;
|
||||
($a ?? $b) ?? $c;
|
||||
$a ?? ($b ? $c : $d);
|
||||
$a || ($b ?? $c);
|
||||
|
||||
(1 > 0) > (1 < 0);
|
||||
++$a + $b;
|
||||
$a + $b++;
|
||||
@ -47,6 +52,10 @@ $a = $b = $c = $d = ($f and true);
|
||||
$a ? $b : $c ? $d : $e ? $f : $g;
|
||||
$a ? $b : ($c ? $d : ($e ? $f : $g));
|
||||
$a ? $b ? $c : $d : $f;
|
||||
$a ?? $b ?? $c;
|
||||
($a ?? $b) ?? $c;
|
||||
$a ?? ($b ? $c : $d);
|
||||
$a || ($b ?? $c);
|
||||
(1 > 0) > (1 < 0);
|
||||
++$a + $b;
|
||||
$a + $b++;
|
||||
|
Loading…
x
Reference in New Issue
Block a user