;; ========================================================================== ;; forms.sx — Server-side definition forms ;; ;; Platform-specific special forms for declaring handlers, pages, queries, ;; and actions. These parse &key parameter lists and create typed definition ;; objects that the server runtime uses for routing and execution. ;; ;; When SX moves to isomorphic execution, these forms will have different ;; platform bindings on client vs server. The spec stays the same — only ;; the constructors (make-handler-def, make-query-def, etc.) change. ;; ;; Platform functions required: ;; make-handler-def(name, params, body, env) → HandlerDef ;; make-query-def(name, params, doc, body, env) → QueryDef ;; make-action-def(name, params, doc, body, env) → ActionDef ;; make-page-def(name, slots, env) → PageDef ;; ========================================================================== ;; -------------------------------------------------------------------------- ;; Shared: parse (&key param1 param2 ...) → list of param name strings ;; -------------------------------------------------------------------------- (define parse-key-params (fn (params-expr) (let ((params (list)) (in-key false)) (for-each (fn (p) (when (= (type-of p) "symbol") (let ((name (symbol-name p))) (cond (= name "&key") (set! in-key true) in-key (append! params name) :else (append! params name))))) params-expr) params))) ;; -------------------------------------------------------------------------- ;; defhandler — (defhandler name (&key param...) body) ;; -------------------------------------------------------------------------- (define sf-defhandler (fn (args env) (let ((name-sym (first args)) (params-raw (nth args 1)) (body (nth args 2)) (name (symbol-name name-sym)) (params (parse-key-params params-raw))) (let ((hdef (make-handler-def name params body env))) (env-set! env (str "handler:" name) hdef) hdef)))) ;; -------------------------------------------------------------------------- ;; defquery — (defquery name (&key param...) "docstring" body) ;; -------------------------------------------------------------------------- (define sf-defquery (fn (args env) (let ((name-sym (first args)) (params-raw (nth args 1)) (name (symbol-name name-sym)) (params (parse-key-params params-raw)) ;; Optional docstring before body (has-doc (and (>= (len args) 4) (= (type-of (nth args 2)) "string"))) (doc (if has-doc (nth args 2) "")) (body (if has-doc (nth args 3) (nth args 2)))) (let ((qdef (make-query-def name params doc body env))) (env-set! env (str "query:" name) qdef) qdef)))) ;; -------------------------------------------------------------------------- ;; defaction — (defaction name (&key param...) "docstring" body) ;; -------------------------------------------------------------------------- (define sf-defaction (fn (args env) (let ((name-sym (first args)) (params-raw (nth args 1)) (name (symbol-name name-sym)) (params (parse-key-params params-raw)) (has-doc (and (>= (len args) 4) (= (type-of (nth args 2)) "string"))) (doc (if has-doc (nth args 2) "")) (body (if has-doc (nth args 3) (nth args 2)))) (let ((adef (make-action-def name params doc body env))) (env-set! env (str "action:" name) adef) adef)))) ;; -------------------------------------------------------------------------- ;; defpage — (defpage name :path "/..." :auth :public :content expr ...) ;; ;; Keyword-slot form: all values after the name are :key value pairs. ;; Values are stored as unevaluated AST — resolved at request time. ;; -------------------------------------------------------------------------- (define sf-defpage (fn (args env) (let ((name-sym (first args)) (name (symbol-name name-sym)) (slots {})) ;; Parse keyword slots from remaining args (let ((i 1) (max-i (len args))) (for-each (fn (idx) (when (and (< idx max-i) (= (type-of (nth args idx)) "keyword")) (when (< (+ idx 1) max-i) (dict-set! slots (keyword-name (nth args idx)) (nth args (+ idx 1)))))) (range 1 max-i 2))) (let ((pdef (make-page-def name slots env))) (env-set! env (str "page:" name) pdef) pdef))))