; lib/artdag/dag.sx — DAG model + structural content addressing. ; A node = {:op :inputs :params :commutative}. inputs are content-ids of upstream ; nodes. The content-id is a deterministic structural digest so identical ; subgraphs collapse to one id (and one cache slot). No clock, no randomness. ; ---- string ordering (no host sort/string= i la) (>= i lb)) false) ((>= i la) true) ((>= i lb) false) (else (let ((ca (char-code (substring a i (+ i 1)))) (cb (char-code (substring b i (+ i 1))))) (cond ((< ca cb) true) ((> ca cb) false) (else (artdag/strstring v)) ((equal? t "string") (str "\"" v "\"")) ((equal? t "keyword") (str ":" (keyword-name v))) ((equal? t "symbol") (str "'" (write-to-string v))) ((equal? t "list") (str "(" (artdag/canon-list v) ")")) ((equal? t "dict") (artdag/canon-dict v)) (else (str "<" t ">" (write-to-string v))))))) ; ---- node + content id ---- (define artdag/node (fn (op inputs params) {:inputs inputs :commutative false :op op :params params})) (define artdag/cnode (fn (op inputs params) {:inputs inputs :commutative true :op op :params params})) (define artdag/node-op (fn (n) (get n :op))) (define artdag/node-inputs (fn (n) (get n :inputs))) (define artdag/node-params (fn (n) (get n :params))) (define artdag/content-id (fn (node) (let ((ins (if (get node :commutative) (artdag/sort-strings (get node :inputs)) (get node :inputs)))) (str "node:" (artdag/canon (list (get node :op) ins (get node :params))))))) (define artdag/id-of artdag/content-id) ; ---- list helpers ---- (define artdag/member? (fn (x xs) (some (fn (y) (equal? y x)) xs))) (define artdag/all-in? (fn (xs placed) (every? (fn (x) (artdag/member? x placed)) xs))) ; ---- build: entries -> validated, content-addressed dag ---- ; entry = (local-name op (input-local-names...) params [commutative?]) (define artdag/entry-name (fn (e) (nth e 0))) (define artdag/entry-op (fn (e) (nth e 1))) (define artdag/entry-inputs (fn (e) (nth e 2))) (define artdag/entry-params (fn (e) (nth e 3))) (define artdag/entry-commutative (fn (e) (if (> (len e) 4) (nth e 4) false))) (define artdag/entries->map (fn (entries) (reduce (fn (m e) (assoc m (artdag/entry-name e) {:inputs (artdag/entry-inputs e) :commutative (artdag/entry-commutative e) :op (artdag/entry-op e) :params (artdag/entry-params e)})) {} entries))) (define artdag/dangling (fn (spec-map) (reduce (fn (acc name) (reduce (fn (a in) (if (has-key? spec-map in) a (cons in a))) acc (get (get spec-map name) :inputs))) (list) (keys spec-map)))) (define artdag/ready-names (fn (spec-map placed) (filter (fn (name) (and (not (artdag/member? name placed)) (artdag/all-in? (get (get spec-map name) :inputs) placed))) (artdag/sort-strings (keys spec-map))))) (define artdag/topo-loop (fn (spec-map placed) (if (= (len placed) (len (keys spec-map))) {:order placed :ok true} (let ((ready (artdag/ready-names spec-map placed))) (if (empty? ready) {:error "cycle" :ok false} (artdag/topo-loop spec-map (concat placed ready))))))) (define artdag/topo (fn (spec-map) (artdag/topo-loop spec-map (list)))) (define artdag/resolve-ids (fn (spec-map order) (reduce (fn (dag name) (let ((spec (get spec-map name))) (let ((resolved (map (fn (in) (get (get dag :names) in)) (get spec :inputs)))) (let ((node {:inputs resolved :commutative (get spec :commutative) :op (get spec :op) :params (get spec :params)})) (let ((id (artdag/content-id node))) {:names (assoc (get dag :names) name id) :order (if (artdag/member? id (get dag :order)) (get dag :order) (concat (get dag :order) (list id))) :nodes (assoc (get dag :nodes) id node)}))))) {:names {} :order (list) :nodes {}} order))) (define artdag/build (fn (entries) (let ((spec-map (artdag/entries->map entries))) (let ((dang (artdag/dangling spec-map))) (if (not (empty? dang)) {:refs dang :error "dangling" :ok false} (let ((topo (artdag/topo spec-map))) (if (not (get topo :ok)) {:error (get topo :error) :ok false} (assoc (artdag/resolve-ids spec-map (get topo :order)) :ok true)))))))) ; ---- dag accessors ---- (define artdag/dag-nodes (fn (dag) (get dag :nodes))) (define artdag/dag-names (fn (dag) (get dag :names))) (define artdag/dag-order (fn (dag) (get dag :order))) (define artdag/dag-id (fn (dag name) (get (get dag :names) name))) (define artdag/dag-get (fn (dag id) (get (get dag :nodes) id))) (define artdag/dag-node-by-name (fn (dag name) (artdag/dag-get dag (artdag/dag-id dag name)))) (define artdag/node-count (fn (dag) (len (keys (get dag :nodes)))))