;; lib/minikanren/matche.sx — Phase 5 piece D: pattern matching over terms.
;;
;;   (matche TARGET
;;     (PATTERN1 g1 g2 ...)
;;     (PATTERN2 g1 ...)
;;     ...)
;;
;; Pattern grammar:
;;   _                 wildcard — fresh anonymous var
;;   x                 plain symbol — fresh var, bind by name
;;   ATOM              literal (number, string, boolean) — must equal
;;   :keyword          keyword literal — emitted bare (keywords self-evaluate
;;                     to their string name in SX, so quoting them changes
;;                     their type from string to keyword)
;;   ()                empty list — must equal
;;   (p1 p2 ... pn)    list pattern — recurse on each element
;;
;; The macro expands to a `conde` whose clauses are
;;   `((fresh (vars-in-pat) (== target pat-expr) body...))`.
;;
;; Repeated symbol names within a pattern produce the same fresh var, so
;; they unify by `==`. Fixed-length list patterns only — head/tail
;; destructuring uses `(fresh (a d) (conso a d target) body)` directly.
;;
;; Note: the macro builds the expansion via `cons` / `list` rather than a
;; quasiquote — quasiquote does not recurse into nested lambda bodies in
;; SX, so `\`(matche-clause (quote ,target) cl)` left literal
;; `(unquote target)` in the output.

(define matche-symbol-var? (fn (s) (symbol? s)))

(define
  matche-collect-vars-acc
  (fn
    (pat acc)
    (cond
      ((matche-symbol-var? pat)
        (if (some (fn (s) (= s pat)) acc) acc (append acc (list pat))))
      ((and (list? pat) (not (empty? pat)))
        (reduce (fn (a p) (matche-collect-vars-acc p a)) acc pat))
      (:else acc))))

(define
  matche-collect-vars
  (fn (pat) (matche-collect-vars-acc pat (list))))

(define
  matche-pattern->expr
  (fn
    (pat)
    (cond
      ((matche-symbol-var? pat) pat)
      ((and (list? pat) (empty? pat)) (list (quote list)))
      ((list? pat) (cons (quote list) (map matche-pattern->expr pat)))
      ((keyword? pat) pat)
      (:else (list (quote quote) pat)))))

(define
  matche-clause
  (fn
    (target cl)
    (let
      ((pat (first cl)) (body (rest cl)))
      (let
        ((vars (matche-collect-vars pat)))
        (let
          ((pat-expr (matche-pattern->expr pat)))
          (list
            (cons
              (quote fresh)
              (cons vars (cons (list (quote ==) target pat-expr) body)))))))))

(defmacro
  matche
  (target &rest clauses)
  (cons (quote conde) (map (fn (cl) (matche-clause target cl)) clauses)))
