diff --git a/lib/datalog/scoreboard.json b/lib/datalog/scoreboard.json index b9a3fe27..fcf66576 100644 --- a/lib/datalog/scoreboard.json +++ b/lib/datalog/scoreboard.json @@ -1,8 +1,8 @@ { "lang": "datalog", - "total_passed": 183, + "total_passed": 184, "total_failed": 0, - "total": 183, + "total": 184, "suites": [ {"name":"tokenize","passed":26,"failed":0,"total":26}, {"name":"parse","passed":18,"failed":0,"total":18}, @@ -12,8 +12,8 @@ {"name":"semi_naive","passed":8,"failed":0,"total":8}, {"name":"negation","passed":10,"failed":0,"total":10}, {"name":"aggregates","passed":18,"failed":0,"total":18}, - {"name":"api","passed":13,"failed":0,"total":13}, + {"name":"api","passed":14,"failed":0,"total":14}, {"name":"demo","passed":18,"failed":0,"total":18} ], - "generated": "2026-05-08T09:45:03+00:00" + "generated": "2026-05-08T09:47:44+00:00" } diff --git a/lib/datalog/scoreboard.md b/lib/datalog/scoreboard.md index 6794e91a..c7ba8661 100644 --- a/lib/datalog/scoreboard.md +++ b/lib/datalog/scoreboard.md @@ -1,6 +1,6 @@ # datalog scoreboard -**183 / 183 passing** (0 failure(s)). +**184 / 184 passing** (0 failure(s)). | Suite | Passed | Total | Status | |-------|--------|-------|--------| @@ -12,5 +12,5 @@ | semi_naive | 8 | 8 | ok | | negation | 10 | 10 | ok | | aggregates | 18 | 18 | ok | -| api | 13 | 13 | ok | +| api | 14 | 14 | ok | | demo | 18 | 18 | ok | diff --git a/lib/datalog/tests/api.sx b/lib/datalog/tests/api.sx index 6df9ce37..0431ed1f 100644 --- a/lib/datalog/tests/api.sx +++ b/lib/datalog/tests/api.sx @@ -213,6 +213,23 @@ "?- p(X), q(X).") (list {:X 2} {:X 3})) + ;; Comprehensive integration: recursion + stratified negation + ;; + aggregation + comparison composed in a single program. + ;; (Uses _Anything as a regular var instead of `_` so the + ;; outer rule binds via the reach lit.) + (dl-api-test-set! "integration" + (dl-eval + (str + "edge(a, b). edge(b, c). edge(c, d). edge(a, d). " + "banned(c). " + "reach(X, Y) :- edge(X, Y). " + "reach(X, Z) :- edge(X, Y), reach(Y, Z). " + "safe(X, Y) :- reach(X, Y), not(banned(Y)). " + "reach_count(X, N) :- reach(X, Z), count(N, Y, safe(X, Y)). " + "popular(X) :- reach_count(X, N), >=(N, 2).") + "?- popular(X).") + (list {:X (quote a)})) + ;; dl-rule-from-list with no arrow → fact-style. (dl-api-test-set! "no arrow → fact-like rule" (let diff --git a/plans/datalog-on-sx.md b/plans/datalog-on-sx.md index bfd1f256..74aff180 100644 --- a/plans/datalog-on-sx.md +++ b/plans/datalog-on-sx.md @@ -284,6 +284,14 @@ large graphs. _Newest first._ +- 2026-05-08 — Comprehensive integration test in api suite: a + single program exercising recursion (`reach` transitive closure) + + stratified negation (`safe X Y :- reach X Y, not banned Y`) + + aggregation (`reach_count` via count) + comparison (`>= N 2`) + composed end-to-end via `dl-eval source query-source`. Confirms + the full pipeline (parser → safety → stratifier → semi-naive + + aggregate post-pass → query) on a non-trivial program. + - 2026-05-08 — Bug fix: aggregates work as top-level query goals. `dl-match-lit` (the naive matcher used by `dl-find-bindings`) was missing the `dl-aggregate?` dispatch — it was only present in