Skip to content

Commit

Permalink
[ExpressionLanguage] Add support for <<, >>, and ~ bitwise oper…
Browse files Browse the repository at this point in the history
…ators
  • Loading branch information
alexandre-daubois committed Aug 21, 2024
1 parent 44c694c commit a3a48b0
Show file tree
Hide file tree
Showing 8 changed files with 24 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CHANGELOG
* Add support for null-coalescing unknown variables
* Add support for comments using `/*` & `*/`
* Allow passing any iterable as `$providers` list to `ExpressionLanguage` constructor
* Add support for `<<`, `>>`, and `~` bitwise operators

7.1
---
Expand Down
2 changes: 1 addition & 1 deletion Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public function tokenize(string $expression): TokenStream
} elseif (preg_match('{/\*.*?\*/}A', $expression, $match, 0, $cursor)) {
// comments
$cursor += \strlen($match[0]);
} 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)) {
} 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)) {
// operators
$tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1);
$cursor += \strlen($match[0]);
Expand Down
4 changes: 4 additions & 0 deletions Node/BinaryNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ public function evaluate(array $functions, array $values): mixed
return $left ^ $right;
case '&':
return $left & $right;
case '<<':
return $left << $right;
case '>>':
return $left >> $right;
case '==':
return $left == $right;
case '===':
Expand Down
2 changes: 2 additions & 0 deletions Node/UnaryNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class UnaryNode extends Node
'not' => '!',
'+' => '+',
'-' => '-',
'~' => '~',
];

public function __construct(string $operator, Node $node)
Expand Down Expand Up @@ -53,6 +54,7 @@ public function evaluate(array $functions, array $values): mixed
'not',
'!' => !$value,
'-' => -$value,
'~' => ~$value,
default => $value,
};
}
Expand Down
3 changes: 3 additions & 0 deletions Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public function __construct(
'!' => ['precedence' => 50],
'-' => ['precedence' => 500],
'+' => ['precedence' => 500],
'~' => ['precedence' => 500],
];
$this->binaryOperators = [
'or' => ['precedence' => 10, 'associativity' => self::OPERATOR_LEFT],
Expand All @@ -67,6 +68,8 @@ public function __construct(
'ends with' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT],
'matches' => ['precedence' => 20, 'associativity' => self::OPERATOR_LEFT],
'..' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT],
'<<' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT],
'>>' => ['precedence' => 25, 'associativity' => self::OPERATOR_LEFT],
'+' => ['precedence' => 30, 'associativity' => self::OPERATOR_LEFT],
'-' => ['precedence' => 30, 'associativity' => self::OPERATOR_LEFT],
'~' => ['precedence' => 40, 'associativity' => self::OPERATOR_LEFT],
Expand Down
5 changes: 4 additions & 1 deletion Tests/LexerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,11 @@ public static function getTokenizeData()
new Token('punctuation', ']', 27),
new Token('operator', '-', 29),
new Token('number', 1990, 31),
new Token('operator', '+', 39),
new Token('operator', '~', 41),
new Token('name', 'qux', 42),
],
'(3 + 5) ~ foo("bar").baz[4] - 1.99E+3',
'(3 + 5) ~ foo("bar").baz[4] - 1.99E+3 + ~qux',
],
[
[new Token('operator', '..', 1)],
Expand Down
6 changes: 6 additions & 0 deletions Tests/Node/BinaryNodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public static function getEvaluateData(): array
[0, new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
[6, new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
[6, new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
[32, new BinaryNode('<<', new ConstantNode(2), new ConstantNode(4))],
[2, new BinaryNode('>>', new ConstantNode(32), new ConstantNode(4))],

[true, new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
[true, new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
Expand Down Expand Up @@ -90,6 +92,8 @@ public static function getCompileData(): array
['(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
['(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
['(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
['(2 << 4)', new BinaryNode('<<', new ConstantNode(2), new ConstantNode(4))],
['(32 >> 4)', new BinaryNode('>>', new ConstantNode(32), new ConstantNode(4))],

['(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
['(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
Expand Down Expand Up @@ -142,6 +146,8 @@ public static function getDumpData(): array
['(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
['(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
['(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
['(2 << 4)', new BinaryNode('<<', new ConstantNode(2), new ConstantNode(4))],
['(32 >> 4)', new BinaryNode('>>', new ConstantNode(32), new ConstantNode(4))],

['(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
['(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
Expand Down
3 changes: 3 additions & 0 deletions Tests/Node/UnaryNodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static function getEvaluateData(): array
[3, new UnaryNode('+', new ConstantNode(3))],
[false, new UnaryNode('!', new ConstantNode(true))],
[false, new UnaryNode('not', new ConstantNode(true))],
[-6, new UnaryNode('~', new ConstantNode(5))],
];
}

Expand All @@ -33,6 +34,7 @@ public static function getCompileData(): array
['(+3)', new UnaryNode('+', new ConstantNode(3))],
['(!true)', new UnaryNode('!', new ConstantNode(true))],
['(!true)', new UnaryNode('not', new ConstantNode(true))],
['(~5)', new UnaryNode('~', new ConstantNode(5))],
];
}

Expand All @@ -43,6 +45,7 @@ public static function getDumpData(): array
['(+ 3)', new UnaryNode('+', new ConstantNode(3))],
['(! true)', new UnaryNode('!', new ConstantNode(true))],
['(not true)', new UnaryNode('not', new ConstantNode(true))],
['(~ 5)', new UnaryNode('~', new ConstantNode(5))],
];
}
}

0 comments on commit a3a48b0

Please sign in to comment.