diff --git a/pygeofilter/backends/cql2_json/evaluate.py b/pygeofilter/backends/cql2_json/evaluate.py index 20b0751a..3ca1161b 100644 --- a/pygeofilter/backends/cql2_json/evaluate.py +++ b/pygeofilter/backends/cql2_json/evaluate.py @@ -82,14 +82,7 @@ def isnull(self, node, arg): @handle(ast.Function) def function(self, node, *args): - name = node.name.lower() - if name == "lower": - ret = {"lower": args[0]} - elif name == "upper": - ret = {"upper": args[0]} - else: - ret = {"function": name, "args": [*args]} - return ret + return {"op": node.name, "args": [*args]} @handle(ast.In) def in_(self, node, lhs, *options): diff --git a/pygeofilter/parsers/cql2_json/parser.py b/pygeofilter/parsers/cql2_json/parser.py index e3c2e9a7..53f9bf47 100644 --- a/pygeofilter/parsers/cql2_json/parser.py +++ b/pygeofilter/parsers/cql2_json/parser.py @@ -165,6 +165,12 @@ def walk_cql_json(node: JsonType): # noqa: C901 args = [cast(ast.Node, walk_cql_json(arg)) for arg in args] return BINARY_OP_PREDICATES_MAP[op](*args) + else: + return ast.Function( + op, + [walk_cql_json(arg) for arg in args], + ) + raise ValueError(f"Unable to parse expression node {node!r}") diff --git a/tests/parsers/cql2_json/test_parser.py b/tests/parsers/cql2_json/test_parser.py index 01f2474a..f94a1b14 100644 --- a/tests/parsers/cql2_json/test_parser.py +++ b/tests/parsers/cql2_json/test_parser.py @@ -32,6 +32,7 @@ from pygeoif import geometry from pygeofilter import ast, values +from pygeofilter.backends.cql2_json.evaluate import CQL2Evaluator from pygeofilter.parsers.cql2_json import parse @@ -717,3 +718,25 @@ def test_function_attr_string_arg(): ], ), ) + + +def test_function_spec_format(): + """Parse spec-format function: {"op": "my_func", "args": [...]}""" + result = parse({"op": "my_func", "args": [{"property": "attr"}, 42]}) + assert result == ast.Function("my_func", [ast.Attribute("attr"), 42]) + + +def test_function_old_format_still_works(): + """Parse old format: {"function": {"name": "...", "arguments": [...]}}""" + result = parse( + {"function": {"name": "my_func", "arguments": [{"property": "attr"}]}} + ) + assert result == ast.Function("my_func", [ast.Attribute("attr")]) + + +def test_function_encode_spec_format(): + """Encode Function node to spec format: {"op": ..., "args": [...]}""" + evaluator = CQL2Evaluator(None, None) + node = ast.Function("my_func", [ast.Attribute("attr")]) + result = evaluator.evaluate(node) + assert result == {"op": "my_func", "args": [{"property": "attr"}]}