Skip to content

Commit 97b9aba

Browse files
committed
feat(cql2-text): add array predicate support (A_EQUALS, A_CONTAINS, A_CONTAINEDBY, A_OVERLAPS)
- Add 'a_containedBy' key to ARRAY_PREDICATES_MAP in cql2.py to match the camelCase used in CQL2-JSON (spec-compliant); the lowercase key is retained for the text parser - Add array_predicate rule to the CQL2-text grammar: A_EQUALS, A_CONTAINS, A_CONTAINEDBY, A_OVERLAPS with two array_arg operands - Inline empty_array and array as atom alternatives so LALR(1) can unambiguously parse '(1, 2, 3)' vs '(x)' by lookahead on the comma after the first element - Add binary_array_predicate, array, and empty_array transformer methods to CQLTransformer; import ARRAY_PREDICATES_MAP from pygeofilter.cql2 - Add four tests: A_EQUALS/A_CONTAINS/A_OVERLAPS with a literal array and A_EQUALS with an empty array
1 parent f799198 commit 97b9aba

4 files changed

Lines changed: 42 additions & 1 deletion

File tree

pygeofilter/cql2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"a_equals": ast.ArrayEquals,
5757
"a_contains": ast.ArrayContains,
5858
"a_containedby": ast.ArrayContainedBy,
59+
"a_containedBy": ast.ArrayContainedBy,
5960
"a_overlaps": ast.ArrayOverlaps,
6061
}
6162

pygeofilter/parsers/cql2_text/grammar.lark

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
| "EXCLUDE"i -> exclude
6262
| spatial_predicate
6363
| temporal_predicate
64+
| array_predicate
6465

6566

6667
?temporal_predicate: expression _binary_temporal_predicate_func expression -> binary_temporal_predicate
@@ -95,6 +96,14 @@
9596
| "S_EQUALS"i
9697

9798

99+
?array_predicate: _array_predicate_func "(" expression "," expression ")" -> binary_array_predicate
100+
101+
!_array_predicate_func: "A_EQUALS"i
102+
| "A_CONTAINS"i
103+
| "A_CONTAINEDBY"i
104+
| "A_OVERLAPS"i
105+
106+
98107
?expression: sum
99108

100109
?sum: product
@@ -109,6 +118,8 @@
109118
| attribute
110119
| literal
111120
| "-" atom -> neg
121+
| "(" ")" -> empty_array
122+
| "(" expression ("," expression)+ ")" -> array
112123
| "(" expression ")"
113124

114125
func.2: attribute "(" expression ("," expression)* ")" -> function

pygeofilter/parsers/cql2_text/parser.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from lark import Lark, logger, v_args
3232

3333
from ... import ast, values
34-
from ...cql2 import SPATIAL_PREDICATES_MAP, TEMPORAL_PREDICATES_MAP
34+
from ...cql2 import ARRAY_PREDICATES_MAP, SPATIAL_PREDICATES_MAP, TEMPORAL_PREDICATES_MAP
3535
from ..iso8601 import ISO8601Transformer
3636
from ..wkt import WKTTransformer
3737

@@ -124,6 +124,15 @@ def during_or_after(self, node, period):
124124
def after(self, node, dt):
125125
return ast.TimeAfter(node, dt)
126126

127+
def binary_array_predicate(self, func, lhs, rhs):
128+
return ARRAY_PREDICATES_MAP[func.lower()](lhs, rhs)
129+
130+
def array(self, *items):
131+
return list(items)
132+
133+
def empty_array(self):
134+
return []
135+
127136
def binary_spatial_predicate(self, op, lhs, rhs):
128137
op = op.lower()
129138
return SPATIAL_PREDICATES_MAP[op](lhs, rhs)

tests/parsers/cql2_text/test_parser.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,23 @@ def test_not_eq():
477477
assert result == ast.Not(
478478
ast.Equal(ast.Attribute("attr"), 2)
479479
)
480+
481+
482+
def test_array_equals():
483+
result = parse("A_EQUALS(attr, (1, 2, 3))")
484+
assert result == ast.ArrayEquals(ast.Attribute("attr"), [1, 2, 3])
485+
486+
487+
def test_array_contains():
488+
result = parse("A_CONTAINS(attr, (1, 2, 3))")
489+
assert result == ast.ArrayContains(ast.Attribute("attr"), [1, 2, 3])
490+
491+
492+
def test_array_overlaps():
493+
result = parse("A_OVERLAPS(attr, (1, 2, 3))")
494+
assert result == ast.ArrayOverlaps(ast.Attribute("attr"), [1, 2, 3])
495+
496+
497+
def test_array_empty():
498+
result = parse("A_EQUALS(attr, ())")
499+
assert result == ast.ArrayEquals(ast.Attribute("attr"), [])

0 commit comments

Comments
 (0)