;; ==========================================================================
;; special-forms.sx — Specification of all SX special forms
;;
;; Special forms are syntactic constructs whose arguments are NOT evaluated
;; before dispatch. Each form has its own evaluation rules — unlike primitives,
;; which receive pre-evaluated values.
;;
;; This file is a SPECIFICATION, not executable code. Bootstrap compilers
;; consume these declarations but implement special forms natively.
;;
;; Format:
;;   (define-special-form "name"
;;     :syntax (name arg1 arg2 ...)
;;     :doc "description"
;;     :tail-position "which subexpressions are in tail position"
;;     :example "(name ...)")
;;
;; ==========================================================================


;; --------------------------------------------------------------------------
;; Control flow
;; --------------------------------------------------------------------------

(define-special-form "if"
  :syntax (if condition then-expr else-expr)
  :doc "If condition is truthy, evaluate then-expr; otherwise evaluate else-expr.
        Both branches are in tail position. The else branch is optional and
        defaults to nil."
  :tail-position "then-expr, else-expr"
  :example "(if (> x 10) \"big\" \"small\")")

(define-special-form "when"
  :syntax (when condition body ...)
  :doc "If condition is truthy, evaluate all body expressions sequentially.
        Returns the value of the last body expression, or nil if condition
        is falsy. Only the last body expression is in tail position."
  :tail-position "last body expression"
  :example "(when (logged-in? user)
  (render-dashboard user))")

(define-special-form "cond"
  :syntax (cond test1 result1 test2 result2 ... :else default)
  :doc "Multi-way conditional. Tests are evaluated in order; the result
        paired with the first truthy test is returned. The :else keyword
        (or the symbol else) matches unconditionally. Supports both
        Clojure-style flat pairs and Scheme-style nested pairs:
          Clojure: (cond test1 result1 test2 result2 :else default)
          Scheme:  (cond (test1 result1) (test2 result2) (else default))"
  :tail-position "all result expressions"
  :example "(cond
  (= status \"active\") (render-active item)
  (= status \"draft\")  (render-draft item)
  :else                (render-unknown item))")

(define-special-form "case"
  :syntax (case expr val1 result1 val2 result2 ... :else default)
  :doc "Match expr against values using equality. Like cond but tests
        a single expression against multiple values. The :else keyword
        matches if no values match."
  :tail-position "all result expressions"
  :example "(case (get request \"method\")
  \"GET\"  (handle-get request)
  \"POST\" (handle-post request)
  :else   (method-not-allowed))")

(define-special-form "and"
  :syntax (and expr ...)
  :doc "Short-circuit logical AND. Evaluates expressions left to right.
        Returns the first falsy value, or the last value if all are truthy.
        Returns true if given no arguments."
  :tail-position "last expression"
  :example "(and (valid? input) (authorized? user) (process input))")

(define-special-form "or"
  :syntax (or expr ...)
  :doc "Short-circuit logical OR. Evaluates expressions left to right.
        Returns the first truthy value, or the last value if all are falsy.
        Returns false if given no arguments."
  :tail-position "last expression"
  :example "(or (get cache key) (fetch-from-db key) \"default\")")


;; --------------------------------------------------------------------------
;; Binding
;; --------------------------------------------------------------------------

(define-special-form "let"
  :syntax (let bindings body ...)
  :doc "Create local bindings and evaluate body in the extended environment.
        Bindings can be Scheme-style ((name val) ...) or Clojure-style
        (name val name val ...). Each binding can see previous bindings.
        Only the last body expression is in tail position.

        Named let: (let name ((x init) ...) body) creates a loop. The name
        is bound to a function that takes the same params and recurses with
        tail-call optimization."
  :tail-position "last body expression; recursive call in named let"
  :example ";; Basic let
(let ((x 10) (y 20))
  (+ x y))

;; Clojure-style
(let (x 10 y 20)
  (+ x y))

;; Named let (loop)
(let loop ((i 0) (acc 0))
  (if (= i 100)
    acc
    (loop (+ i 1) (+ acc i))))")

(define-special-form "let*"
  :syntax (let* bindings body ...)
  :doc "Alias for let. In SX, let is already sequential (each binding
        sees previous ones), so let* is identical to let."
  :tail-position "last body expression"
  :example "(let* ((x 10) (y (* x 2)))
  (+ x y))  ;; → 30")

(define-special-form "letrec"
  :syntax (letrec bindings body ...)
  :doc "Mutually recursive local bindings. All names are bound to nil first,
        then all values are evaluated (so they can reference each other),
        then lambda closures are patched to include the final bindings.
        Used for defining mutually recursive local functions."
  :tail-position "last body expression"
  :example "(letrec ((even? (fn (n) (if (= n 0) true  (odd?  (- n 1)))))
         (odd?  (fn (n) (if (= n 0) false (even? (- n 1))))))
  (even? 10))  ;; → true")

(define-special-form "define"
  :syntax (define name value)
  :doc "Bind name to value in the current environment. If value is a lambda
        and has no name, the lambda's name is set to the symbol name.
        Returns the value."
  :tail-position "none (value is eagerly evaluated)"
  :example "(define greeting \"hello\")
(define double (fn (x) (* x 2)))")

(define-special-form "set!"
  :syntax (set! name value)
  :doc "Mutate an existing binding. The name must already be bound in the
        current environment. Returns the new value."
  :tail-position "none (value is eagerly evaluated)"
  :example "(let (count 0)
  (set! count (+ count 1)))")


;; --------------------------------------------------------------------------
;; Functions and components
;; --------------------------------------------------------------------------

(define-special-form "lambda"
  :syntax (lambda params body)
  :doc "Create a function. Params is a list of parameter names. Body is
        a single expression (the return value). The lambda captures the
        current environment as its closure."
  :tail-position "body"
  :example "(lambda (x y) (+ x y))")

(define-special-form "fn"
  :syntax (fn params body)
  :doc "Alias for lambda."
  :tail-position "body"
  :example "(fn (x) (* x x))")

(define-special-form "defcomp"
  :syntax (defcomp ~name (&key param1 param2 &rest children) body)
  :doc "Define a component. Components are called with keyword arguments
        and optional positional children. The &key marker introduces
        keyword parameters. The &rest (or &children) marker captures
        remaining positional arguments as a list.

        Component names conventionally start with ~ to distinguish them
        from HTML elements. Components are evaluated with a merged
        environment: closure + caller-env + bound-params."
  :tail-position "body"
  :example "(defcomp ~card (&key title subtitle &rest children)
  (div :class \"card\"
    (h2 title)
    (when subtitle (p subtitle))
    children))")

(define-special-form "defisland"
  :syntax (defisland ~name (&key param1 param2 &rest children) body)
  :doc "Define a reactive island. Islands have the same calling convention
        as components (defcomp) but create a reactive boundary. Inside an
        island, signals are tracked — deref subscribes DOM nodes to signals,
        and signal changes update only the affected nodes.

        On the server, islands render as static HTML wrapped in a
        data-sx-island container with serialized initial state. On the
        client, islands hydrate into reactive contexts."
  :tail-position "body"
  :example "(defisland ~counter (&key initial)
  (let ((count (signal (or initial 0))))
    (div :class \"counter\"
      (span (deref count))
      (button :on-click (fn (e) (swap! count inc)) \"+\"))))")

(define-special-form "defmacro"
  :syntax (defmacro name (params ...) body)
  :doc "Define a macro. Macros receive their arguments unevaluated (as raw
        AST) and return a new expression that is then evaluated. The
        returned expression replaces the macro call. Use quasiquote for
        template construction."
  :tail-position "none (expansion is evaluated separately)"
  :example "(defmacro unless (condition &rest body)
  `(when (not ~condition) ~@body))")

(define-special-form "deftype"
  :syntax (deftype name body)
  :doc "Define a named type. The name can be a simple symbol for type aliases
        and records, or a list (name param ...) for parameterized types.
        Body is a type expression: a symbol (alias), (union t1 t2 ...) for
        union types, or {:field1 type1 :field2 type2} for record types.
        Type definitions are metadata for the type checker with no runtime cost."
  :tail-position "none"
  :example "(deftype price number)
(deftype card-props {:title string :price number})
(deftype (maybe a) (union a nil))")

(define-special-form "defeffect"
  :syntax (defeffect name)
  :doc "Declare a named effect. Effects annotate functions and components
        to track side effects. A pure function (:effects [pure]) cannot
        call IO functions. Unannotated functions are assumed to have all
        effects. Effect checking is gradual — annotations opt in."
  :tail-position "none"
  :example "(defeffect io)
(defeffect async)
(define add :effects [pure] (fn (a b) (+ a b)))")


;; --------------------------------------------------------------------------
;; Sequencing and threading
;; --------------------------------------------------------------------------

(define-special-form "begin"
  :syntax (begin expr ...)
  :doc "Evaluate expressions sequentially. Returns the value of the last
        expression. Used when multiple side-effecting expressions need
        to be grouped."
  :tail-position "last expression"
  :example "(begin
  (log \"starting\")
  (process data)
  (log \"done\"))")

(define-special-form "do"
  :syntax (do expr ...)
  :doc "Alias for begin."
  :tail-position "last expression"
  :example "(do (set! x 1) (set! y 2) (+ x y))")

(define-special-form "->"
  :syntax (-> value form1 form2 ...)
  :doc "Thread-first macro. Threads value through a series of function calls,
        inserting it as the first argument of each form. Nested lists are
        treated as function calls; bare symbols become unary calls."
  :tail-position "last form"
  :example "(-> user
    (get \"name\")
    upper
    (str \" says hello\"))
;; Expands to: (str (upper (get user \"name\")) \" says hello\")")


;; --------------------------------------------------------------------------
;; Quoting
;; --------------------------------------------------------------------------

(define-special-form "quote"
  :syntax (quote expr)
  :doc "Return expr as data, without evaluating it. Symbols remain symbols,
        lists remain lists. The reader shorthand is the ' prefix."
  :tail-position "none (not evaluated)"
  :example "'(+ 1 2)  ;; → the list (+ 1 2), not the number 3")

(define-special-form "quasiquote"
  :syntax (quasiquote expr)
  :doc "Template construction. Like quote, but allows unquoting with ~ and
        splicing with ~@. The reader shorthand is the ` prefix.
          `(a ~b ~@c)
        Quotes everything except: ~expr evaluates expr and inserts the
        result; ~@expr evaluates to a list and splices its elements."
  :tail-position "none (template is constructed, not evaluated)"
  :example "`(div :class \"card\" ~title ~@children)")


;; --------------------------------------------------------------------------
;; Continuations
;; --------------------------------------------------------------------------

(define-special-form "reset"
  :syntax (reset body)
  :doc "Establish a continuation delimiter. Evaluates body normally unless
        a shift is encountered, in which case the continuation (the rest
        of the computation up to this reset) is captured and passed to
        the shift's body. Without shift, reset is a no-op wrapper."
  :tail-position "body"
  :example "(reset (+ 1 (shift k (k 10))))  ;; → 11")

(define-special-form "shift"
  :syntax (shift k body)
  :doc "Capture the continuation to the nearest reset as k, then evaluate
        body with k bound. If k is never called, the value of body is
        returned from the reset (abort). If k is called with a value,
        the reset body is re-evaluated with shift returning that value.
        k can be called multiple times."
  :tail-position "body"
  :example ";; Abort: shift body becomes the reset result
(reset (+ 1 (shift k 42)))  ;; → 42

;; Resume: k re-enters the computation
(reset (+ 1 (shift k (k 10))))  ;; → 11

;; Multiple invocations
(reset (* 2 (shift k (+ (k 1) (k 10)))))  ;; → 24")


;; --------------------------------------------------------------------------
;; Guards
;; --------------------------------------------------------------------------

(define-special-form "dynamic-wind"
  :syntax (dynamic-wind before-thunk body-thunk after-thunk)
  :doc "Entry/exit guards. All three arguments are zero-argument functions
        (thunks). before-thunk is called on entry, body-thunk is called
        for the result, and after-thunk is always called on exit (even on
        error). The wind stack is maintained so that when continuations
        jump across dynamic-wind boundaries, the correct before/after
        thunks fire."
  :tail-position "none (all thunks are eagerly called)"
  :example "(dynamic-wind
  (fn () (log \"entering\"))
  (fn () (do-work))
  (fn () (log \"exiting\")))")


;; --------------------------------------------------------------------------
;; Higher-order forms
;;
;; These are syntactic forms (not primitives) because the evaluator
;; handles them directly for performance — avoiding the overhead of
;; constructing argument lists and doing generic dispatch. They could
;; be implemented as primitives but are special-cased in eval-list.
;; --------------------------------------------------------------------------

(define-special-form "map"
  :syntax (map fn coll)
  :doc "Apply fn to each element of coll, returning a list of results."
  :tail-position "none"
  :example "(map (fn (x) (* x x)) (list 1 2 3 4))  ;; → (1 4 9 16)")

(define-special-form "map-indexed"
  :syntax (map-indexed fn coll)
  :doc "Like map, but fn receives two arguments: (index element)."
  :tail-position "none"
  :example "(map-indexed (fn (i x) (str i \": \" x)) (list \"a\" \"b\" \"c\"))")

(define-special-form "filter"
  :syntax (filter fn coll)
  :doc "Return elements of coll for which fn returns truthy."
  :tail-position "none"
  :example "(filter (fn (x) (> x 3)) (list 1 5 2 8 3))  ;; → (5 8)")

(define-special-form "reduce"
  :syntax (reduce fn init coll)
  :doc "Reduce coll to a single value. fn receives (accumulator element)
        and returns the new accumulator. init is the initial value."
  :tail-position "none"
  :example "(reduce (fn (acc x) (+ acc x)) 0 (list 1 2 3 4))  ;; → 10")

(define-special-form "some"
  :syntax (some fn coll)
  :doc "Return the first truthy result of applying fn to elements of coll,
        or nil if none match. Short-circuits on first truthy result."
  :tail-position "none"
  :example "(some (fn (x) (> x 3)) (list 1 2 5 3))  ;; → true")

(define-special-form "every?"
  :syntax (every? fn coll)
  :doc "Return true if fn returns truthy for every element of coll.
        Short-circuits on first falsy result."
  :tail-position "none"
  :example "(every? (fn (x) (> x 0)) (list 1 2 3))  ;; → true")

(define-special-form "for-each"
  :syntax (for-each fn coll)
  :doc "Apply fn to each element of coll for side effects. Returns nil."
  :tail-position "none"
  :example "(for-each (fn (x) (log x)) (list 1 2 3))")


;; --------------------------------------------------------------------------
;; Definition forms (domain-specific)
;;
;; These define named entities in the environment. They are special forms
;; because their arguments have domain-specific structure that the
;; evaluator parses directly.
;; --------------------------------------------------------------------------

(define-special-form "defstyle"
  :syntax (defstyle name expr)
  :doc "Define a named style value. Evaluates expr and binds the result
        to name in the environment. The value is typically a class string
        or a function that returns class strings."
  :tail-position "none"
  :example "(defstyle card-style \"rounded-lg shadow-md p-4 bg-white\")")

(define-special-form "defhandler"
  :syntax (defhandler name (&key params ...) body)
  :doc "Define an event handler function. Used by the SxEngine for
        client-side event handling."
  :tail-position "body"
  :example "(defhandler toggle-menu (&key target)
  (toggle-class target \"hidden\"))")

(define-special-form "defpage"
  :syntax (defpage name &key route method content ...)
  :doc "Define a page route. Declares the URL pattern, HTTP method, and
        content component for server-side page routing."
  :tail-position "none"
  :example "(defpage dashboard-page
  :route \"/dashboard\"
  :content (~dashboard-content))")

(define-special-form "defquery"
  :syntax (defquery name (&key params ...) body)
  :doc "Define a named query for data fetching. Used by the resolver
        system to declare data dependencies."
  :tail-position "body"
  :example "(defquery user-profile (&key user-id)
  (fetch-user user-id))")

(define-special-form "defaction"
  :syntax (defaction name (&key params ...) body)
  :doc "Define a named action for mutations. Like defquery but for
        write operations."
  :tail-position "body"
  :example "(defaction update-profile (&key user-id name email)
  (save-user user-id name email))")
