feature #34329 [ExpressionLanguage] add XOR operator (ottaviano)

This PR was merged into the 4.4 branch.

Discussion
----------

[ExpressionLanguage] add XOR operator

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | ~
| License       | MIT
| Doc PR        | ~

👋
little PR for adding the best logical operator: `XOR`

Commits
-------

46fe91781d [ExpressionLanguage] add XOR operator
This commit is contained in:
Nicolas Grekas 2019-11-12 15:00:30 +01:00
commit d1d4bc8bad
7 changed files with 53 additions and 1 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
4.4.0
-----
* add `xor` operator
4.0.0 4.0.0
----- -----

View File

@ -73,7 +73,7 @@ class Lexer
// strings // strings
$tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1); $tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1);
$cursor += \strlen($match[0]); $cursor += \strlen($match[0]);
} elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=|or(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) { } elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=|or(?=[\s(])|xor(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) {
// operators // operators
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1); $tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
$cursor += \strlen($match[0]); $cursor += \strlen($match[0]);

View File

@ -104,6 +104,8 @@ class BinaryNode extends Node
case 'or': case 'or':
case '||': case '||':
return $left || $this->nodes['right']->evaluate($functions, $values); return $left || $this->nodes['right']->evaluate($functions, $values);
case 'xor':
return $left xor $this->nodes['right']->evaluate($functions, $values);
case 'and': case 'and':
case '&&': case '&&':
return $left && $this->nodes['right']->evaluate($functions, $values); return $left && $this->nodes['right']->evaluate($functions, $values);

View File

@ -45,6 +45,7 @@ class Parser
$this->binaryOperators = [ $this->binaryOperators = [
'or' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT], 'or' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT],
'||' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT], '||' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT],
'xor' => ['precedence' => 13, 'associativity' => self::OPERATOR_LEFT],
'and' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT], 'and' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT],
'&&' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT], '&&' => ['precedence' => 15, 'associativity' => self::OPERATOR_LEFT],
'|' => ['precedence' => 16, 'associativity' => self::OPERATOR_LEFT], '|' => ['precedence' => 16, 'associativity' => self::OPERATOR_LEFT],

View File

@ -256,4 +256,35 @@ class ExpressionLanguageTest extends TestCase
], ],
]; ];
} }
/**
* @dataProvider getLogicalOperators
*/
public function testLogicalOperators($expression, $expected)
{
$this->assertSame($expected, (new ExpressionLanguage())->evaluate($expression));
}
public function getLogicalOperators()
{
return [
// AND
['true and true', true],
['true and false', false],
['false and true', false],
['false and false', false],
// OR
['true or true', true],
['true or false', true],
['false or true', true],
['false or false', false],
// XOR
['true xor true', false],
['true xor false', true],
['false xor true', true],
['false xor false', false],
];
}
} }

View File

@ -114,6 +114,14 @@ class LexerTest extends TestCase
[new Token('string', '#foo', 1)], [new Token('string', '#foo', 1)],
'"#foo"', '"#foo"',
], ],
[
[
new Token('name', 'a', 1),
new Token('operator', 'xor', 3),
new Token('name', 'b', 7),
],
'a xor b',
],
]; ];
} }
} }

View File

@ -151,6 +151,11 @@ class ParserTest extends TestCase
'bar', 'bar',
['foo' => 'bar'], ['foo' => 'bar'],
], ],
[
new Node\BinaryNode('xor', new Node\ConstantNode(true), new Node\ConstantNode(false)),
'true xor false',
],
]; ];
} }