Skip to content

Commit d02ab3f

Browse files
feature symfony#58341 [ExpressionLanguage] Add support for logical xor operator (HypeMC)
This PR was merged into the 7.2 branch. Discussion ---------- [ExpressionLanguage] Add support for logical `xor` operator | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Contains symfony#58342 Adds support for the logical `xor` operator. Commits ------- 6e0a613 [ExpressionLanguage] Add support for logical `xor` operator
2 parents 40d0089 + 6e0a613 commit d02ab3f

File tree

8 files changed

+26
-2
lines changed

8 files changed

+26
-2
lines changed

src/Symfony/Component/ExpressionLanguage/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Add support for comments using `/*` & `*/`
99
* Allow passing any iterable as `$providers` list to `ExpressionLanguage` constructor
1010
* Add support for `<<`, `>>`, and `~` bitwise operators
11+
* Add support for logical `xor` operator
1112

1213
7.1
1314
---

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(])xor(?=[\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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ public function evaluate(array $functions, array $values): mixed
116116
case 'or':
117117
case '||':
118118
return $left || $this->nodes['right']->evaluate($functions, $values);
119+
case 'xor':
120+
return $left xor $this->nodes['right']->evaluate($functions, $values);
119121
case 'and':
120122
case '&&':
121123
return $left && $this->nodes['right']->evaluate($functions, $values);

src/Symfony/Component/ExpressionLanguage/Parser.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function __construct(
4848
$this->binaryOperators = [
4949
'or' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT],
5050
'||' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT],
51+
'xor' => ['precedence' => 12, 'associativity' => self::OPERATOR_LEFT],
5152
'and' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT],
5253
'&&' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT],
5354
'|' => ['precedence' => 16, 'associativity' => self::OPERATOR_LEFT],

src/Symfony/Component/ExpressionLanguage/Resources/bin/generate_operator_regex.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
throw new Exception('This script must be run from the command line.');
1414
}
1515

16-
$operators = ['not', '!', 'or', '||', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'contains', 'starts with', 'ends with', 'matches', '**', '<<', '>>'];
16+
$operators = ['not', '!', 'or', '||', 'xor', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'contains', 'starts with', 'ends with', 'matches', '**', '<<', '>>'];
1717
$operators = array_combine($operators, array_map('strlen', $operators));
1818
arsort($operators);
1919

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ public static function getTokenizeData()
182182
],
183183
'"/* this is not a comment */"',
184184
],
185+
[
186+
[
187+
new Token('name', 'foo', 1),
188+
new Token('operator', 'xor', 5),
189+
new Token('name', 'bar', 9),
190+
],
191+
'foo xor bar',
192+
],
185193
];
186194
}
187195

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public static function getEvaluateData(): array
2929
return [
3030
[true, new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))],
3131
[true, new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))],
32+
[false, new BinaryNode('xor', new ConstantNode(true), new ConstantNode(true))],
3233
[false, new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))],
3334
[false, new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))],
3435

@@ -86,6 +87,7 @@ public static function getCompileData(): array
8687
return [
8788
['(true || false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))],
8889
['(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))],
90+
['(true xor true)', new BinaryNode('xor', new ConstantNode(true), new ConstantNode(true))],
8991
['(true && false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))],
9092
['(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))],
9193

@@ -140,6 +142,7 @@ public static function getDumpData(): array
140142
return [
141143
['(true or false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))],
142144
['(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))],
145+
['(true xor true)', new BinaryNode('xor', new ConstantNode(true), new ConstantNode(true))],
143146
['(true and false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))],
144147
['(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))],
145148

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,15 @@ public static function getParseData()
244244
'not foo or foo.not',
245245
['foo'],
246246
],
247+
[
248+
new Node\BinaryNode(
249+
'xor',
250+
new Node\NameNode('foo'),
251+
new Node\NameNode('bar'),
252+
),
253+
'foo xor bar',
254+
['foo', 'bar'],
255+
],
247256
[
248257
new Node\BinaryNode('..', new Node\ConstantNode(0), new Node\ConstantNode(3)),
249258
'0..3',

0 commit comments

Comments
 (0)