;; lib/minikanren/unify.sx — Phase 1 + cons-cell extension.
;;
;; miniKanren-on-SX, built on lib/guest/match.sx. The kit ships the heavy
;; lifting (walk-with, unify-with, occurs-with, extend, empty-subst,
;; mk-var/is-var?/var-name); this file supplies a miniKanren-shaped cfg
;; and a thin public API.
;;
;; Term shapes:
;;   logic var   : (:var NAME)              — kit's mk-var
;;   cons cell   : (:cons HEAD TAIL)        — for relational programming
;;                 (built by mk-cons; lets relations decompose lists by
;;                 head/tail without proper improper pairs in the host)
;;   native list : SX list (a b c)          — also unifies pair-style:
;;                 args = (head, tail) so (1 2 3) ≡ (:cons 1 (:cons 2 (:cons 3 ())))
;;   atom        : number / string / symbol / boolean / nil / ()
;;
;; Substitution: SX dict mapping VAR-NAME → term. Empty = (empty-subst).

(define mk-cons (fn (h t) (list :cons h t)))

(define
  mk-cons-cell?
  (fn (t) (and (list? t) (not (empty? t)) (= (first t) :cons))))

(define mk-cons-head (fn (t) (nth t 1)))
(define mk-cons-tail (fn (t) (nth t 2)))

(define
  mk-list-pair?
  (fn (t) (and (list? t) (not (empty? t)) (not (is-var? t)))))

(define mk-list-pair-head (fn (t) :pair))

(define
  mk-list-pair-args
  (fn
    (t)
    (cond
      ((mk-cons-cell? t) (list (mk-cons-head t) (mk-cons-tail t)))
      (:else (list (first t) (rest t))))))

(define mk-cfg {:ctor-head mk-list-pair-head :var? is-var? :ctor? mk-list-pair? :occurs-check? false :var-name var-name :ctor-args mk-list-pair-args})

(define mk-cfg-occurs {:ctor-head mk-list-pair-head :var? is-var? :ctor? mk-list-pair? :occurs-check? true :var-name var-name :ctor-args mk-list-pair-args})

(define empty-s (empty-subst))

(define mk-fresh-counter 0)

(define
  make-var
  (fn
    ()
    (begin
      (set! mk-fresh-counter (+ mk-fresh-counter 1))
      (mk-var (str "_." mk-fresh-counter)))))

(define mk-var? is-var?)

(define mk-walk (fn (t s) (walk-with mk-cfg t s)))

(define
  mk-walk*
  (fn
    (t s)
    (let
      ((w (mk-walk t s)))
      (cond
        ((mk-cons-cell? w)
          (let
            ((h (mk-walk* (mk-cons-head w) s))
              (tl (mk-walk* (mk-cons-tail w) s)))
            (cond
              ((empty? tl) (list h))
              ((mk-cons-cell? tl) tl)
              ((list? tl) (cons h tl))
              (:else (mk-cons h tl)))))
        ((mk-list-pair? w) (map (fn (a) (mk-walk* a s)) w))
        (:else w)))))

(define mk-unify (fn (u v s) (unify-with mk-cfg u v s)))
(define mk-unify-check (fn (u v s) (unify-with mk-cfg-occurs u v s)))
