diff --git a/lib/datalog/magic.sx b/lib/datalog/magic.sx index 33d5ba9b..3d593356 100644 --- a/lib/datalog/magic.sx +++ b/lib/datalog/magic.sx @@ -158,3 +158,45 @@ (dl-vars-bound-by-lit lit bound))))))) (get rule :body)) out)))) + +;; ── Magic predicate naming + bound-args extraction ───────────── +;; These are building blocks for the magic-sets *transformation* +;; itself. The transformation (which generates rewritten rules +;; with magic_^ filters) is future work — for now +;; these helpers can be used to inspect what such a transformation +;; would produce. + +;; "magic_p^bf" given relation "p" and adornment "bf". +(define + dl-magic-rel-name + (fn (rel adornment) (str "magic_" rel "^" adornment))) + +;; A magic predicate literal: +;; (magic_^ arg1 arg2 ...) +(define + dl-magic-lit + (fn + (rel adornment bound-args) + (cons (string->symbol (dl-magic-rel-name rel adornment)) bound-args))) + +;; Extract bound args (those at "b" positions in `adornment`) from a +;; literal `(rel arg1 arg2 ... argN)`. Returns the list of arg values. +(define + dl-bound-args + (fn + (lit adornment) + (let ((args (rest lit)) (out (list))) + (do + (define + dl-ba-loop + (fn + (i) + (when + (< i (len args)) + (do + (when + (= (slice adornment i (+ i 1)) "b") + (append! out (nth args i))) + (dl-ba-loop (+ i 1)))))) + (dl-ba-loop 0) + out)))) diff --git a/lib/datalog/scoreboard.json b/lib/datalog/scoreboard.json index ba6de4c0..73ae3b19 100644 --- a/lib/datalog/scoreboard.json +++ b/lib/datalog/scoreboard.json @@ -1,8 +1,8 @@ { "lang": "datalog", - "total_passed": 194, + "total_passed": 199, "total_failed": 0, - "total": 194, + "total": 199, "suites": [ {"name":"tokenize","passed":26,"failed":0,"total":26}, {"name":"parse","passed":18,"failed":0,"total":18}, @@ -13,8 +13,8 @@ {"name":"negation","passed":10,"failed":0,"total":10}, {"name":"aggregates","passed":18,"failed":0,"total":18}, {"name":"api","passed":14,"failed":0,"total":14}, - {"name":"magic","passed":10,"failed":0,"total":10}, + {"name":"magic","passed":15,"failed":0,"total":15}, {"name":"demo","passed":18,"failed":0,"total":18} ], - "generated": "2026-05-08T09:50:50+00:00" + "generated": "2026-05-08T09:53:23+00:00" } diff --git a/lib/datalog/scoreboard.md b/lib/datalog/scoreboard.md index 3399fe66..a8e21498 100644 --- a/lib/datalog/scoreboard.md +++ b/lib/datalog/scoreboard.md @@ -1,6 +1,6 @@ # datalog scoreboard -**194 / 194 passing** (0 failure(s)). +**199 / 199 passing** (0 failure(s)). | Suite | Passed | Total | Status | |-------|--------|-------|--------| @@ -13,5 +13,5 @@ | negation | 10 | 10 | ok | | aggregates | 18 | 18 | ok | | api | 14 | 14 | ok | -| magic | 10 | 10 | ok | +| magic | 15 | 15 | ok | | demo | 18 | 18 | ok | diff --git a/lib/datalog/tests/magic.sx b/lib/datalog/tests/magic.sx index e61e96fb..dff20345 100644 --- a/lib/datalog/tests/magic.sx +++ b/lib/datalog/tests/magic.sx @@ -124,7 +124,30 @@ {:lit (list (quote p) (quote X)) :adornment "f"} {:lit (list (quote is) (quote Y) (list (string->symbol "+") (quote X) 1)) - :adornment "fb"}))))) + :adornment "fb"})) + + ;; Magic predicate naming. + (dl-mt-test! "magic-rel-name" + (dl-magic-rel-name "ancestor" "bf") + "magic_ancestor^bf") + + ;; Bound-args extraction. + (dl-mt-test! "bound-args bf" + (dl-bound-args (list (quote ancestor) (quote tom) (quote X)) "bf") + (list (quote tom))) + + (dl-mt-test! "bound-args mixed" + (dl-bound-args (list (quote p) 1 (quote Y) 3) "bfb") + (list 1 3)) + + (dl-mt-test! "bound-args all-free" + (dl-bound-args (list (quote p) (quote X) (quote Y)) "ff") + (list)) + + ;; Magic literal construction. + (dl-mt-test! "magic-lit" + (dl-magic-lit "ancestor" "bf" (list (quote tom))) + (list (string->symbol "magic_ancestor^bf") (quote tom)))))) (define dl-magic-tests-run! diff --git a/plans/datalog-on-sx.md b/plans/datalog-on-sx.md index b9f7dbc6..560ec828 100644 --- a/plans/datalog-on-sx.md +++ b/plans/datalog-on-sx.md @@ -156,6 +156,8 @@ large graphs. is a constant or a variable already in the bound set. - [ ] Magic transformation: for each adorned predicate, generate a `magic_` relation and rewrite rule bodies to filter through it. + *Building blocks present in `magic.sx`: `dl-magic-rel-name`, + `dl-magic-lit`, `dl-bound-args`. Full rewriter still TODO.* - [x] Sideways information passing strategy (SIPS): left-to-right `dl-rule-sips rule head-adornment` walks body literals tracking the bound set, returning `({:lit :adornment} ...)`. Recognises @@ -289,6 +291,12 @@ large graphs. _Newest first._ +- 2026-05-08 — Phase 6 building blocks for the magic-sets + transformation: `dl-magic-rel-name`, `dl-magic-lit`, + `dl-bound-args`. The rewriter that generates magic seed and + propagation rules is still future work; with these primitives + in place it's a straightforward worklist algorithm. 4 new tests. + - 2026-05-08 — Phase 6 adornments + SIPS in `lib/datalog/magic.sx`. Inspection helpers — `dl-adorn-goal` and `dl-adorn-lit` compute per-arg `b`/`f` patterns under a bound