Skip to content

Commit

Permalink
Allow use of maths symbols for operators (#8)
Browse files Browse the repository at this point in the history
Allow use of maths symbols for operators

≤≠≥∈∉ map to existing operators
⊆⊇∩ non-strict subset, superset, and intersection provides new
functionality; resolves #1
  • Loading branch information
BarnabyShearer authored and spjwebster committed Oct 18, 2019
1 parent e30241d commit 488a209
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 8 deletions.
19 changes: 13 additions & 6 deletions boolrule/boolrule.py
Expand Up @@ -48,7 +48,8 @@ def __repr__(self):
rparen = Suppress(')')

binaryOp = oneOf(
"= == != < > >= <= eq ne lt le gt ge in notin is isnot", caseless=True
"= == != < > >= <= eq ne lt le gt ge in notin is isnot "
"≠ ≤ ≥ ∈ ∉ ⊆ ⊇ ∩", caseless=True
)('operator')

E = CaselessLiteral("E")
Expand Down Expand Up @@ -178,24 +179,30 @@ def _test_tokens(self, tokens, context):

if operator in ('=', '==', 'eq'):
passed = lval == rval
elif operator in ('!=', 'ne'):
elif operator in ('!=', 'ne', '≠'):
passed = lval != rval
elif operator in ('>', 'gt'):
passed = lval > rval
elif operator in ('>=', 'ge'):
elif operator in ('>=', 'ge', '≥'):
passed = lval >= rval
elif operator in ('<', 'lt'):
passed = lval < rval
elif operator in ('<=', 'le'):
elif operator in ('<=', 'le', '≤'):
passed = lval <= rval
elif operator == 'in':
elif operator in ('in', '∈'):
passed = lval in rval
elif operator == 'notin':
elif operator in ('notin', '∉'):
passed = lval not in rval
elif operator == 'is':
passed = lval is rval
elif operator == 'isnot':
passed = lval is not rval
elif operator == '⊆':
passed = all((False for x in lval if x not in rval))
elif operator == '⊇':
passed = all((False for x in rval if x not in lval))
elif operator == '∩':
passed = any((True for x in lval if x in rval))
else:
raise UnknownOperatorException(
"Unknown operator '{}'".format(operator)
Expand Down
48 changes: 46 additions & 2 deletions tests/test_boolrule.py
Expand Up @@ -9,9 +9,26 @@
@pytest.mark.parametrize('s,expected', [
('5 > 3', True),
('5 < 3', False),
('5 > 5', False),
('3 >= 5', False),
('5 >= 3', True),
('5 >= 5', True),
('5 <= 3', False),
('3 <= 5', True),
('3 <= 5', True),
('5 ≥ 3', True),
('5 ≥ 5', True),
('3 ≤ 3', True),
('3 ≤ 5', True),
('7 == true', False),
('true == true', True),
('None is None', True),
('1 != 2', True),
('1 != 1', False),
('2 != true', True),
('1 ≠ 2', True),
('1 ≠ 1', False),
('2 ≠ true', True),
])
def test_simple_comparisons(s, expected):
boolrule = BoolRule(s)
Expand All @@ -37,7 +54,6 @@ def test_nested_logical_combinations(s, expected):
assert boolrule.test() == expected



@pytest.mark.parametrize('s,context,expected', [
('foo = "bar" AND baz > 10', {'foo': 'bar', 'baz': 20}, True),
('foo = "bar" AND baz > 10', {'foo': 'bar', 'baz': 9}, False),
Expand All @@ -56,12 +72,41 @@ def test_subsitution_values(s, context, expected):
('x in (5, 6, 7)', {'x': 5}, True),
('x in (5, 6, 7)', {'x': 8}, False),
('x in (5, 6, 7, y)', {'x': 99, 'y': 99}, True),
('x ∈ (5, 6, 7)', {'x': 5}, True),
('x ∈ (5, 6, 7)', {'x': 8}, False),
('x ∈ (5, 6, 7, y)', {'x': 99, 'y': 99}, True),
('x ∉ (5, 6, 7)', {'x': 5}, False),
('x ∉ (5, 6, 7)', {'x': 8}, True),
('x ∉ (5, 6, 7, y)', {'x': 99, 'y': 99}, False),
])
def test_list_membership(s, context, expected):
boolrule = BoolRule(s)
assert boolrule.test(context) == expected


@pytest.mark.parametrize('s,expected', [
('(1, 2, 3) ⊆ (1, 2, 3)', True),
('(1, 2, 3) ⊇ (1, 2, 3)', True),
('(1, 2, 3) ⊆ (1, 2, 3, 4)', True),
('(1, 2, 3, 4) ⊇ (1, 2, 3)', True),
('(1, 2, 3) ⊆ (1, 2)', False),
('(1, 2) ⊇ (1, 2, 3)', False),
])
def test_subset(s, expected):
boolrule = BoolRule(s)
assert boolrule.test() == expected


@pytest.mark.parametrize('s,expected', [
('(1, 2, 3) ∩ (1, 2, 3)', True),
('(4) ∩ (3, 4, 5)', True),
('(1, 2, 3) ∩ (4, 5, 6)', False),
])
def test_intersects(s, expected):
boolrule = BoolRule(s)
assert boolrule.test() == expected


@pytest.mark.parametrize('s,context', [
('foo < bar', None),
('foo < bar', {}),
Expand All @@ -85,4 +130,3 @@ def test_missing_vars_raises_exception(s, context):
# with self.assertRaises(ParseException):
# rule = BoolRule(query[0])
# rule.test(query[1])

0 comments on commit 488a209

Please sign in to comment.