diff --git a/lib/datalog/db.sx b/lib/datalog/db.sx index 09f92315..43b4d840 100644 --- a/lib/datalog/db.sx +++ b/lib/datalog/db.sx @@ -327,3 +327,40 @@ (fn (k) (set! total (+ total (len (get facts k))))) (keys facts)) total)))) + +;; Returns {: tuple-count} for debugging. Includes +;; relations with any tuples plus all rule-head relations (so empty +;; IDB shows as 0). Skips empty EDB-only entries that are placeholders +;; from internal `dl-ensure-rel!` calls. +(define + dl-summary + (fn + (db) + (let + ((facts (get db :facts)) + (out {}) + (rule-heads (list))) + (do + (for-each + (fn + (rule) + (let ((h (dl-rel-name (get rule :head)))) + (when + (and (not (nil? h)) (not (dl-member-string? h rule-heads))) + (append! rule-heads h)))) + (dl-rules db)) + (for-each + (fn + (k) + (let ((c (len (get facts k)))) + (when + (or (> c 0) (dl-member-string? k rule-heads)) + (dict-set! out k c)))) + (keys facts)) + ;; Add rule heads that have no facts (yet). + (for-each + (fn + (k) + (when (not (has-key? out k)) (dict-set! out k 0))) + rule-heads) + out)))) diff --git a/lib/datalog/scoreboard.json b/lib/datalog/scoreboard.json index 3bc3e1e5..4f2c96eb 100644 --- a/lib/datalog/scoreboard.json +++ b/lib/datalog/scoreboard.json @@ -1,13 +1,13 @@ { "lang": "datalog", - "total_passed": 169, + "total_passed": 173, "total_failed": 0, - "total": 169, + "total": 173, "suites": [ {"name":"tokenize","passed":26,"failed":0,"total":26}, {"name":"parse","passed":18,"failed":0,"total":18}, {"name":"unify","passed":28,"failed":0,"total":28}, - {"name":"eval","passed":18,"failed":0,"total":18}, + {"name":"eval","passed":22,"failed":0,"total":22}, {"name":"builtins","passed":19,"failed":0,"total":19}, {"name":"semi_naive","passed":8,"failed":0,"total":8}, {"name":"negation","passed":10,"failed":0,"total":10}, @@ -15,5 +15,5 @@ {"name":"api","passed":11,"failed":0,"total":11}, {"name":"demo","passed":15,"failed":0,"total":15} ], - "generated": "2026-05-08T09:27:29+00:00" + "generated": "2026-05-08T09:30:37+00:00" } diff --git a/lib/datalog/scoreboard.md b/lib/datalog/scoreboard.md index 70c2fe0f..4242dc6f 100644 --- a/lib/datalog/scoreboard.md +++ b/lib/datalog/scoreboard.md @@ -1,13 +1,13 @@ # datalog scoreboard -**169 / 169 passing** (0 failure(s)). +**173 / 173 passing** (0 failure(s)). | Suite | Passed | Total | Status | |-------|--------|-------|--------| | tokenize | 26 | 26 | ok | | parse | 18 | 18 | ok | | unify | 28 | 28 | ok | -| eval | 18 | 18 | ok | +| eval | 22 | 22 | ok | | builtins | 19 | 19 | ok | | semi_naive | 8 | 8 | ok | | negation | 10 | 10 | ok | diff --git a/lib/datalog/tests/eval.sx b/lib/datalog/tests/eval.sx index d6b6f8e6..a47140f7 100644 --- a/lib/datalog/tests/eval.sx +++ b/lib/datalog/tests/eval.sx @@ -215,7 +215,36 @@ (dl-query (dl-program "p(1, 2, 3). p(4, 5, 6).") (list (quote p) (quote _) (quote X) (quote _))) - (list {:X 2} {:X 5}))))) + (list {:X 2} {:X 5})) + + ;; dl-summary: relation -> tuple-count for inspection. + (dl-et-test! "dl-summary basic" + (dl-summary + (let + ((db (dl-program "p(1). p(2). q(3)."))) + (do (dl-saturate! db) db))) + {:p 2 :q 1}) + + (dl-et-test! "dl-summary empty IDB shown" + (dl-summary + (let + ((db (dl-program "r(X) :- s(X)."))) + (do (dl-saturate! db) db))) + {:r 0}) + + (dl-et-test! "dl-summary mixed EDB and IDB" + (dl-summary + (let + ((db (dl-program + "parent(a, b). + ancestor(X, Y) :- parent(X, Y). + ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z)."))) + (do (dl-saturate! db) db))) + {:parent 1 :ancestor 1}) + + (dl-et-test! "dl-summary empty db" + (dl-summary (dl-make-db)) + {})))) (define dl-eval-tests-run!