diff --git a/compiler/functors.py b/compiler/functors.py index fde878c..4ddecad 100755 --- a/compiler/functors.py +++ b/compiler/functors.py @@ -562,6 +562,10 @@ def UnfoldRecursions(self, depth_map, default_iterative, default_depth): new_rules = copy.deepcopy(self.rules) for p, style in should_recurse.items(): depth = depth_map.get(p, {}).get('1', default_depth) + if not depth: + # Do not unfold recursion. This is used e.g. in Clingo + # predicates. + continue if style == 'vertical': self.UnfoldRecursivePredicate(p, my_cover[p], depth, new_rules) elif style == 'horizontal' or style == 'iterative_horizontal': diff --git a/integration_tests/clingo_recursive_test.l b/integration_tests/clingo_recursive_test.l new file mode 100644 index 0000000..4722236 --- /dev/null +++ b/integration_tests/clingo_recursive_test.l @@ -0,0 +1,29 @@ +# +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Testing suppression of recursion unfolding. + +@Engine("duckdb", clingo: true); +@Recursive(N, 0); +N(0); +N(n + 1) :- N(n), n < 9; + +Natural() = Clingo(["N"], []); + +@OrderBy(Naturals, "col0"); +Naturals(x) :- + ExtractClingoCall(x, predicate: "N") = Natural(); + +Test := Naturals(); \ No newline at end of file diff --git a/integration_tests/clingo_recursive_test.txt b/integration_tests/clingo_recursive_test.txt new file mode 100644 index 0000000..bad59ba --- /dev/null +++ b/integration_tests/clingo_recursive_test.txt @@ -0,0 +1,14 @@ ++------+ +| col0 | ++------+ +| 0 | +| 1 | +| 2 | +| 3 | +| 4 | +| 5 | +| 6 | +| 7 | +| 8 | +| 9 | ++------+ \ No newline at end of file diff --git a/integration_tests/run_tests.py b/integration_tests/run_tests.py index 6d4bd01..c6ce01c 100755 --- a/integration_tests/run_tests.py +++ b/integration_tests/run_tests.py @@ -71,6 +71,7 @@ def RunAll(test_presto=False, test_trino=False, test_clingo=True, test_clickhous if test_clingo: from common import duckdb_logica + RunTest('clingo_recursive_test') RunTest('clingo_sum_test') RunTest('duckdb_clingo') RunTest('clingo_basic_test') diff --git a/parser_cpp/logica_parse.cpp b/parser_cpp/logica_parse.cpp index 0d69ba1..b7ec9f1 100644 --- a/parser_cpp/logica_parse.cpp +++ b/parser_cpp/logica_parse.cpp @@ -414,40 +414,6 @@ static SpanString SpanFromJson(const Json& j) { return SpanString(SpanTextFromJson(j)); } -static void ConvertSpanOffsetsToChar(Json& node) { - if (!g_heritage_pool) return; - if (node.is_array()) { - auto& a = node.as_array(); - // Span encoding: ["__hs", idx, start, stop] - if (a.size() == 4 && a[0].is_string() && a[0].as_string() == "__hs") { - const int64_t idx = a[1].as_int(); - const int64_t start_b = a[2].as_int(); - const int64_t stop_b = a[3].as_int(); - a[2] = Json(g_heritage_pool->ByteOffsetToCharOffset(idx, start_b)); - a[3] = Json(g_heritage_pool->ByteOffsetToCharOffset(idx, stop_b)); - return; - } - for (auto& v : a) ConvertSpanOffsetsToChar(v); - return; - } - if (!node.is_object()) return; - auto& o = node.as_object(); - const bool is_span = o.count("__hs") && o.count("start") && o.count("stop") && o.size() <= 3; - if (is_span) { - const int64_t idx = o.at("__hs").as_int(); - const int64_t start_b = o.at("start").as_int(); - const int64_t stop_b = o.at("stop").as_int(); - const int64_t start_c = g_heritage_pool->ByteOffsetToCharOffset(idx, start_b); - const int64_t stop_c = g_heritage_pool->ByteOffsetToCharOffset(idx, stop_b); - o["start"] = Json(start_c); - o["stop"] = Json(stop_c); - return; - } - for (auto& kv : o) { - ConvertSpanOffsetsToChar(kv.second); - } -} - // ------------------------------ // Parsing exception. // ------------------------------