;; lib/guest/ast.sx — canonical AST node shapes.
;;
;; A guest's parser may emit its own AST in whatever shape is convenient
;; for that language's evaluator/transpiler. This file gives a SHARED
;; canonical shape that cross-language tools (formatters, highlighters,
;; debuggers) can target without per-language adapters.
;;
;; Each canonical node is a tagged list: (KIND ...payload).
;;
;; Constructors (return a canonical node):
;;
;;   (ast-literal       VALUE)               — number / string / bool / nil
;;   (ast-var           NAME)                — identifier reference
;;   (ast-app           FN ARGS)             — function application
;;   (ast-lambda        PARAMS BODY)         — anonymous function
;;   (ast-let           BINDINGS BODY)       — local bindings
;;   (ast-letrec        BINDINGS BODY)       — recursive local bindings
;;   (ast-if            TEST THEN ELSE)      — conditional
;;   (ast-match-clause  PATTERN BODY)        — one match arm
;;   (ast-module        NAME BODY)           — module declaration
;;   (ast-import        NAME)                — import directive
;;
;; Predicates: (ast-literal? X), (ast-var? X), …
;; Generic:    (ast? X)         — any canonical node
;;             (ast-kind X)     — :literal / :var / :app / …
;;
;; Accessors  (one per payload field):
;;   (ast-literal-value N)
;;   (ast-var-name N)
;;   (ast-app-fn N)             (ast-app-args N)
;;   (ast-lambda-params N)      (ast-lambda-body N)
;;   (ast-let-bindings N)       (ast-let-body N)
;;   (ast-letrec-bindings N)    (ast-letrec-body N)
;;   (ast-if-test N)            (ast-if-then N)         (ast-if-else N)
;;   (ast-match-clause-pattern N)
;;   (ast-match-clause-body N)
;;   (ast-module-name N)        (ast-module-body N)
;;   (ast-import-name N)

(define ast-literal      (fn (v)         (list :literal v)))
(define ast-var          (fn (n)         (list :var n)))
(define ast-app          (fn (f args)    (list :app f args)))
(define ast-lambda       (fn (ps body)   (list :lambda ps body)))
(define ast-let          (fn (bs body)   (list :let bs body)))
(define ast-letrec       (fn (bs body)   (list :letrec bs body)))
(define ast-if           (fn (t th el)   (list :if t th el)))
(define ast-match-clause (fn (p body)    (list :match-clause p body)))
(define ast-module       (fn (n body)    (list :module n body)))
(define ast-import       (fn (n)         (list :import n)))

(define ast-kind (fn (x) (if (and (list? x) (not (empty? x))) (first x) nil)))

(define
  ast?
  (fn (x)
    (and (list? x)
         (not (empty? x))
         (let ((k (first x)))
           (or (= k :literal) (= k :var) (= k :app)
               (= k :lambda)  (= k :let) (= k :letrec)
               (= k :if)      (= k :match-clause)
               (= k :module)  (= k :import))))))

(define ast-literal?      (fn (x) (and (ast? x) (= (first x) :literal))))
(define ast-var?          (fn (x) (and (ast? x) (= (first x) :var))))
(define ast-app?          (fn (x) (and (ast? x) (= (first x) :app))))
(define ast-lambda?       (fn (x) (and (ast? x) (= (first x) :lambda))))
(define ast-let?          (fn (x) (and (ast? x) (= (first x) :let))))
(define ast-letrec?       (fn (x) (and (ast? x) (= (first x) :letrec))))
(define ast-if?           (fn (x) (and (ast? x) (= (first x) :if))))
(define ast-match-clause? (fn (x) (and (ast? x) (= (first x) :match-clause))))
(define ast-module?       (fn (x) (and (ast? x) (= (first x) :module))))
(define ast-import?       (fn (x) (and (ast? x) (= (first x) :import))))

(define ast-literal-value (fn (n) (nth n 1)))
(define ast-var-name      (fn (n) (nth n 1)))
(define ast-app-fn        (fn (n) (nth n 1)))
(define ast-app-args      (fn (n) (nth n 2)))
(define ast-lambda-params (fn (n) (nth n 1)))
(define ast-lambda-body   (fn (n) (nth n 2)))
(define ast-let-bindings  (fn (n) (nth n 1)))
(define ast-let-body      (fn (n) (nth n 2)))
(define ast-letrec-bindings (fn (n) (nth n 1)))
(define ast-letrec-body     (fn (n) (nth n 2)))
(define ast-if-test       (fn (n) (nth n 1)))
(define ast-if-then       (fn (n) (nth n 2)))
(define ast-if-else       (fn (n) (nth n 3)))
(define ast-match-clause-pattern (fn (n) (nth n 1)))
(define ast-match-clause-body    (fn (n) (nth n 2)))
(define ast-module-name   (fn (n) (nth n 1)))
(define ast-module-body   (fn (n) (nth n 2)))
(define ast-import-name   (fn (n) (nth n 1)))
