Skip to content

Commit b9a0854

Browse files
committed
feat(cql2-json): support CQL2 spec function format in parser and encoder
Add support for the standard CQL2-JSON function call format where functions are expressed as {"op": "func_name", "args": [...]} at the top level, in addition to the existing {"function": {"name": ..., "arguments": [...]}} backward-compatible format. Changes: - parser.py: add else branch after BINARY_OP_PREDICATES_MAP check to treat unknown ops as Function nodes instead of raising ValueError - evaluate.py: simplify Function handler to always emit {"op": name, "args": [...]} (spec-compliant format), removing non-standard lower/upper/function variants - test_parser.py: add three new tests covering spec-format parsing, old-format backward compatibility, and spec-format encoding
1 parent f799198 commit b9a0854

3 files changed

Lines changed: 30 additions & 8 deletions

File tree

pygeofilter/backends/cql2_json/evaluate.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,7 @@ def isnull(self, node, arg):
8282

8383
@handle(ast.Function)
8484
def function(self, node, *args):
85-
name = node.name.lower()
86-
if name == "lower":
87-
ret = {"lower": args[0]}
88-
elif name == "upper":
89-
ret = {"upper": args[0]}
90-
else:
91-
ret = {"function": name, "args": [*args]}
92-
return ret
85+
return {"op": node.name, "args": [*args]}
9386

9487
@handle(ast.In)
9588
def in_(self, node, lhs, *options):

pygeofilter/parsers/cql2_json/parser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ def walk_cql_json(node: JsonType): # noqa: C901
165165
args = [cast(ast.Node, walk_cql_json(arg)) for arg in args]
166166
return BINARY_OP_PREDICATES_MAP[op](*args)
167167

168+
else:
169+
return ast.Function(
170+
op,
171+
[walk_cql_json(arg) for arg in args],
172+
)
173+
168174
raise ValueError(f"Unable to parse expression node {node!r}")
169175

170176

tests/parsers/cql2_json/test_parser.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from pygeoif import geometry
3333

3434
from pygeofilter import ast, values
35+
from pygeofilter.backends.cql2_json.evaluate import CQL2Evaluator
3536
from pygeofilter.parsers.cql2_json import parse
3637

3738

@@ -717,3 +718,25 @@ def test_function_attr_string_arg():
717718
],
718719
),
719720
)
721+
722+
723+
def test_function_spec_format():
724+
"""Parse spec-format function: {"op": "my_func", "args": [...]}"""
725+
result = parse({"op": "my_func", "args": [{"property": "attr"}, 42]})
726+
assert result == ast.Function("my_func", [ast.Attribute("attr"), 42])
727+
728+
729+
def test_function_old_format_still_works():
730+
"""Parse old format: {"function": {"name": "...", "arguments": [...]}}"""
731+
result = parse(
732+
{"function": {"name": "my_func", "arguments": [{"property": "attr"}]}}
733+
)
734+
assert result == ast.Function("my_func", [ast.Attribute("attr")])
735+
736+
737+
def test_function_encode_spec_format():
738+
"""Encode Function node to spec format: {"op": ..., "args": [...]}"""
739+
evaluator = CQL2Evaluator(None, None)
740+
node = ast.Function("my_func", [ast.Attribute("attr")])
741+
result = evaluator.evaluate(node)
742+
assert result == {"op": "my_func", "args": [{"property": "attr"}]}

0 commit comments

Comments
 (0)