;; lib/guest/reflective/class-chain.sx — class inheritance walker. ;; ;; Extracted from Smalltalk's `st-method-lookup-walk` (single-parent ;; class chain for message-send dispatch) and CLOS's `clos-specificity` ;; (multi-parent class graph for method-precedence distance). Both walk ;; a class-name → parent-name(s) graph applying a probe at each node; ;; the cfg adapter normalises single-parent and multi-parent classes ;; into a uniform `:parents-of` callback that returns a (possibly ;; empty) list of parent class names. ;; ;; Adapter cfg ;; ----------- ;; :parents-of — fn (class-name) → list of parent class names. ;; Empty list = no parents (root). Single-parent guests ;; return a 1-element list; multi-parent guests (CLOS) ;; may return any number. ;; :class? — fn (name) → bool. False short-circuits the walk — ;; used to skip non-existent class names mid-chain. ;; ;; Public API ;; (refl-class-chain-find-with CFG CLASS-NAME PROBE) ;; Depth-first walk from CLASS-NAME up its parent chain. At each ;; class, calls `(probe class-name)`. Returns the first non-nil ;; probe result, or nil if no class produces one. Probes evaluate ;; left-to-right across siblings in multi-parent guests. ;; ;; (refl-class-chain-depth-with CFG CLASS-NAME ANCESTOR-NAME) ;; Minimum hop count from CLASS-NAME to ANCESTOR-NAME along any ;; parent path. CLASS-NAME itself counts as depth 0. Returns nil ;; if ANCESTOR-NAME is unreachable. ;; ;; (refl-class-chain-ancestors-with CFG CLASS-NAME) ;; Flat list of all reachable ancestor names in DFS order (no ;; dedup; multi-parent guests may want to dedup themselves). ;; ;; Consumer migrations ;; ------------------- ;; - Smalltalk: see `lib/smalltalk/runtime.sx` — `st-method-lookup-walk` ;; becomes a one-line probe through `refl-class-chain-find-with`. ;; - CLOS: see `lib/common-lisp/clos.sx` — `clos-specificity` becomes a ;; thin wrapper around `refl-class-chain-depth-with`. (define refl-find-in-parents-with (fn (cfg parents probe) (cond ((or (nil? parents) (= (length parents) 0)) nil) (:else (let ((hit (refl-class-chain-find-with cfg (first parents) probe))) (cond ((not (nil? hit)) hit) (:else (refl-find-in-parents-with cfg (rest parents) probe)))))))) (define refl-class-chain-find-with (fn (cfg class-name probe) (cond ((nil? class-name) nil) ((not ((get cfg :class?) class-name)) nil) (:else (let ((hit (probe class-name))) (cond ((not (nil? hit)) hit) (:else (refl-find-in-parents-with cfg ((get cfg :parents-of) class-name) probe)))))))) (define refl-class-chain-depth-walk (fn (cfg cur target depth) (cond ((= cur target) depth) ((nil? cur) nil) ((not ((get cfg :class?) cur)) nil) (:else (let ((parents ((get cfg :parents-of) cur))) (let ((results (map (fn (p) (refl-class-chain-depth-walk cfg p target (+ depth 1))) parents))) (let ((non-nil (filter (fn (x) (not (nil? x))) results))) (cond ((or (nil? non-nil) (= (length non-nil) 0)) nil) (:else (reduce (fn (a b) (if (< a b) a b)) (first non-nil) (rest non-nil))))))))))) (define refl-class-chain-depth-with (fn (cfg class-name ancestor-name) (refl-class-chain-depth-walk cfg class-name ancestor-name 0))) (define refl-class-chain-ancestors-with (fn (cfg class-name) (refl-ancestors-walk cfg class-name (list)))) (define refl-ancestors-walk (fn (cfg cn acc) (cond ((nil? cn) acc) ((not ((get cfg :class?) cn)) acc) (:else (let ((parents ((get cfg :parents-of) cn))) (refl-ancestors-walk-list cfg parents (append acc (list cn)))))))) (define refl-ancestors-walk-list (fn (cfg parents acc) (cond ((or (nil? parents) (= (length parents) 0)) acc) (:else (refl-ancestors-walk-list cfg (rest parents) (refl-ancestors-walk cfg (first parents) acc))))))