Skip to content

Commit b3d9138

Browse files
feature symfony#58052 [ExpressionLanguage] Add support for <<, >>, and ~ bitwise operators (alexandre-daubois)
This PR was merged into the 7.2 branch. Discussion ---------- [ExpressionLanguage] Add support for `<<`, `>>`, and `~` bitwise operators | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT In order to fully support [this idea](symfony#57522 (comment)), I suggest adding to ExpressionLanguage the 3 currently missing bitwise operators, namely left shift, right shift and binary not. ```php use Symfony\Component\ExpressionLanguage\ExpressionLanguage; require 'vendor/autoload.php'; $el = new ExpressionLanguage(); $el->evaluate('1 << 4'); // 16 $el->evaluate('32 >> 4'); // 2 $el->evaluate('~5'); // -6 ``` Commits ------- b470422 [ExpressionLanguage] Add support for `<<`, `>>`, and `~` bitwise operators
2 parents 8c3e20e + b470422 commit b3d9138

File tree

8 files changed

+24
-2
lines changed

8 files changed

+24
-2
lines changed

src/Symfony/Component/ExpressionLanguage/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add support for null-coalescing unknown variables
88
* Add support for comments using `/*` & `*/`
99
* Allow passing any iterable as `$providers` list to `ExpressionLanguage` constructor
10+
* Add support for `<<`, `>>`, and `~` bitwise operators
1011

1112
7.1
1213
---

src/Symfony/Component/ExpressionLanguage/Lexer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function tokenize(string $expression): TokenStream
7272
} elseif (preg_match('{/\*.*?\*/}A', $expression, $match, 0, $cursor)) {
7373
// comments
7474
$cursor += \strlen($match[0]);
75-
} elseif (preg_match('/(?<=^|[\s(])starts with(?=[\s(])|(?<=^|[\s(])ends with(?=[\s(])|(?<=^|[\s(])contains(?=[\s(])|(?<=^|[\s(])matches(?=[\s(])|(?<=^|[\s(])not in(?=[\s(])|(?<=^|[\s(])not(?=[\s(])|(?<=^|[\s(])and(?=[\s(])|\=\=\=|\!\=\=|(?<=^|[\s(])or(?=[\s(])|\|\||&&|\=\=|\!\=|\>\=|\<\=|(?<=^|[\s(])in(?=[\s(])|\.\.|\*\*|\!|\||\^|&|\<|\>|\+|\-|~|\*|\/|%/A', $expression, $match, 0, $cursor)) {
75+
} elseif (preg_match('/(?<=^|[\s(])starts with(?=[\s(])|(?<=^|[\s(])ends with(?=[\s(])|(?<=^|[\s(])contains(?=[\s(])|(?<=^|[\s(])matches(?=[\s(])|(?<=^|[\s(])not in(?=[\s(])|(?<=^|[\s(])not(?=[\s(])|(?<=^|[\s(])and(?=[\s(])|\=\=\=|\!\=\=|(?<=^|[\s(])or(?=[\s(])|\|\||&&|\=\=|\!\=|\>\=|\<\=|(?<=^|[\s(])in(?=[\s(])|\.\.|\*\*|\!|\||\^|&|<<|>>|\<|\>|\+|\-|~|\*|\/|%/A', $expression, $match, 0, $cursor)) {
7676
// operators
7777
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
7878
$cursor += \strlen($match[0]);

src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public function evaluate(array $functions, array $values): mixed
130130
return $left ^ $right;
131131
case '&':
132132
return $left & $right;
133+
case '<<':
134+
return $left << $right;
135+
case '>>':
136+
return $left >> $right;
133137
case '==':
134138
return $left == $right;
135139
case '===':

src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class UnaryNode extends Node
2525
'not' => '!',
2626
'+' => '+',
2727
'-' => '-',
28+
'~' => '~',
2829
];
2930

3031
public function __construct(string $operator, Node $node)
@@ -53,6 +54,7 @@ public function evaluate(array $functions, array $values): mixed
5354
'not',
5455
'!' => !$value,
5556
'-' => -$value,
57+
'~' => ~$value,
5658
default => $value,
5759
};
5860
}

src/Symfony/Component/ExpressionLanguage/Parser.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public function __construct(
4343
'!' => ['precedence' => 50],
4444
'-' => ['precedence' => 500],
4545
'+' => ['precedence' => 500],
46+
'~' => ['precedence' => 500],
4647
];
4748
$this->binaryOperators = [
4849
'or' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT],
@@ -67,6 +68,8 @@ public function __construct(
6768
'ends with' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT],
6869
'matches' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT],
6970
'..' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT],
71+
'<<' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT],
72+
'>>' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT],
7073
'+' => ['precedence' => 30, 'associativity' => self::OPERATOR_LEFT],
7174
'-' => ['precedence' => 30, 'associativity' => self::OPERATOR_LEFT],
7275
'~' => ['precedence' => 40, 'associativity' => self::OPERATOR_LEFT],

src/Symfony/Component/ExpressionLanguage/Tests/LexerTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,11 @@ public static function getTokenizeData()
9797
new Token('punctuation', ']', 27),
9898
new Token('operator', '-', 29),
9999
new Token('number', 1990, 31),
100+
new Token('operator', '+', 39),
101+
new Token('operator', '~', 41),
102+
new Token('name', 'qux', 42),
100103
],
101-
'(3 + 5) ~ foo("bar").baz[4] - 1.99E+3',
104+
'(3 + 5) ~ foo("bar").baz[4] - 1.99E+3 + ~qux',
102105
],
103106
[
104107
[new Token('operator', '..', 1)],

src/Symfony/Component/ExpressionLanguage/Tests/Node/BinaryNodeTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public static function getEvaluateData(): array
3535
[0, new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
3636
[6, new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
3737
[6, new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
38+
[32, new BinaryNode('<<', new ConstantNode(2), new ConstantNode(4))],
39+
[2, new BinaryNode('>>', new ConstantNode(32), new ConstantNode(4))],
3840

3941
[true, new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
4042
[true, new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
@@ -90,6 +92,8 @@ public static function getCompileData(): array
9092
['(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
9193
['(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
9294
['(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
95+
['(2 << 4)', new BinaryNode('<<', new ConstantNode(2), new ConstantNode(4))],
96+
['(32 >> 4)', new BinaryNode('>>', new ConstantNode(32), new ConstantNode(4))],
9397

9498
['(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
9599
['(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
@@ -142,6 +146,8 @@ public static function getDumpData(): array
142146
['(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
143147
['(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
144148
['(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
149+
['(2 << 4)', new BinaryNode('<<', new ConstantNode(2), new ConstantNode(4))],
150+
['(32 >> 4)', new BinaryNode('>>', new ConstantNode(32), new ConstantNode(4))],
145151

146152
['(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
147153
['(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],

src/Symfony/Component/ExpressionLanguage/Tests/Node/UnaryNodeTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static function getEvaluateData(): array
2323
[3, new UnaryNode('+', new ConstantNode(3))],
2424
[false, new UnaryNode('!', new ConstantNode(true))],
2525
[false, new UnaryNode('not', new ConstantNode(true))],
26+
[-6, new UnaryNode('~', new ConstantNode(5))],
2627
];
2728
}
2829

@@ -33,6 +34,7 @@ public static function getCompileData(): array
3334
['(+3)', new UnaryNode('+', new ConstantNode(3))],
3435
['(!true)', new UnaryNode('!', new ConstantNode(true))],
3536
['(!true)', new UnaryNode('not', new ConstantNode(true))],
37+
['(~5)', new UnaryNode('~', new ConstantNode(5))],
3638
];
3739
}
3840

@@ -43,6 +45,7 @@ public static function getDumpData(): array
4345
['(+ 3)', new UnaryNode('+', new ConstantNode(3))],
4446
['(! true)', new UnaryNode('!', new ConstantNode(true))],
4547
['(not true)', new UnaryNode('not', new ConstantNode(true))],
48+
['(~ 5)', new UnaryNode('~', new ConstantNode(5))],
4649
];
4750
}
4851
}

0 commit comments

Comments
 (0)