;; ========================================================================== ;; page-helpers.sx — Pure data-transformation page helpers ;; ;; These functions take raw data (from Python I/O edge) and return ;; structured dicts for page rendering. No I/O — pure transformations ;; only. Bootstrapped to every host. ;; ========================================================================== ;; -------------------------------------------------------------------------- ;; categorize-special-forms ;; ;; Parses define-special-form declarations from special-forms.sx AST, ;; categorizes each form by name lookup, returns dict of category → forms. ;; -------------------------------------------------------------------------- (define special-form-category-map {"if" "Control Flow" "when" "Control Flow" "cond" "Control Flow" "case" "Control Flow" "and" "Control Flow" "or" "Control Flow" "let" "Binding" "let*" "Binding" "letrec" "Binding" "define" "Binding" "set!" "Binding" "lambda" "Functions & Components" "fn" "Functions & Components" "defcomp" "Functions & Components" "defmacro" "Functions & Components" "begin" "Sequencing & Threading" "do" "Sequencing & Threading" "->" "Sequencing & Threading" "quote" "Quoting" "quasiquote" "Quoting" "reset" "Continuations" "shift" "Continuations" "dynamic-wind" "Guards" "map" "Higher-Order Forms" "map-indexed" "Higher-Order Forms" "filter" "Higher-Order Forms" "reduce" "Higher-Order Forms" "some" "Higher-Order Forms" "every?" "Higher-Order Forms" "for-each" "Higher-Order Forms" "defstyle" "Domain Definitions" "defhandler" "Domain Definitions" "defpage" "Domain Definitions" "defquery" "Domain Definitions" "defaction" "Domain Definitions"}) (define extract-define-kwargs (fn ((expr :as list)) ;; Extract keyword args from a define-special-form expression. ;; Returns dict of keyword-name → string value. ;; Walks items pairwise: when item[i] is a keyword, item[i+1] is its value. (let ((result {}) (items (slice expr 2)) (n (len items))) (for-each (fn ((idx :as number)) (when (and (< (+ idx 1) n) (= (type-of (nth items idx)) "keyword")) (let ((key (keyword-name (nth items idx))) (val (nth items (+ idx 1)))) (dict-set! result key (if (= (type-of val) "list") (str "(" (join " " (map serialize val)) ")") (str val)))))) (range 0 n)) result))) (define categorize-special-forms (fn ((parsed-exprs :as list)) ;; parsed-exprs: result of parse-all on special-forms.sx ;; Returns dict of category-name → list of form dicts. (let ((categories {})) (for-each (fn (expr) (when (and (= (type-of expr) "list") (>= (len expr) 2) (= (type-of (first expr)) "symbol") (= (symbol-name (first expr)) "define-special-form")) (let ((name (nth expr 1)) (kwargs (extract-define-kwargs expr)) (category (or (get special-form-category-map name) "Other"))) (when (not (has-key? categories category)) (dict-set! categories category (list))) (append! (get categories category) {"name" name "syntax" (or (get kwargs "syntax") "") "doc" (or (get kwargs "doc") "") "tail-position" (or (get kwargs "tail-position") "") "example" (or (get kwargs "example") "")})))) parsed-exprs) categories))) ;; -------------------------------------------------------------------------- ;; build-reference-data ;; ;; Takes a slug and raw reference data, returns structured dict for rendering. ;; -------------------------------------------------------------------------- (define build-ref-items-with-href (fn ((items :as list) (base-path :as string) (detail-keys :as list) (n-fields :as number)) ;; items: list of lists (tuples), each with n-fields elements ;; base-path: e.g. "/geography/hypermedia/reference/attributes/" ;; detail-keys: list of strings (keys that have detail pages) ;; n-fields: 2 or 3 (number of fields per tuple) (map (fn ((item :as list)) (if (= n-fields 3) ;; [name, desc/value, exists/desc] (let ((name (nth item 0)) (field2 (nth item 1)) (field3 (nth item 2))) {"name" name "desc" field2 "exists" field3 "href" (if (and field3 (some (fn ((k :as string)) (= k name)) detail-keys)) (str base-path name) nil)}) ;; [name, desc] (let ((name (nth item 0)) (desc (nth item 1))) {"name" name "desc" desc "href" (if (some (fn ((k :as string)) (= k name)) detail-keys) (str base-path name) nil)}))) items))) (define build-reference-data (fn ((slug :as string) (raw-data :as dict) (detail-keys :as list)) ;; slug: "attributes", "headers", "events", "js-api" ;; raw-data: dict with the raw data lists for this slug ;; detail-keys: list of names that have detail pages (case slug "attributes" {"req-attrs" (build-ref-items-with-href (get raw-data "req-attrs") "/geography/hypermedia/reference/attributes/" detail-keys 3) "beh-attrs" (build-ref-items-with-href (get raw-data "beh-attrs") "/geography/hypermedia/reference/attributes/" detail-keys 3) "uniq-attrs" (build-ref-items-with-href (get raw-data "uniq-attrs") "/geography/hypermedia/reference/attributes/" detail-keys 3)} "headers" {"req-headers" (build-ref-items-with-href (get raw-data "req-headers") "/geography/hypermedia/reference/headers/" detail-keys 3) "resp-headers" (build-ref-items-with-href (get raw-data "resp-headers") "/geography/hypermedia/reference/headers/" detail-keys 3)} "events" {"events-list" (build-ref-items-with-href (get raw-data "events-list") "/geography/hypermedia/reference/events/" detail-keys 2)} "js-api" {"js-api-list" (map (fn ((item :as list)) {"name" (nth item 0) "desc" (nth item 1)}) (get raw-data "js-api-list"))} ;; default: attributes :else {"req-attrs" (build-ref-items-with-href (get raw-data "req-attrs") "/geography/hypermedia/reference/attributes/" detail-keys 3) "beh-attrs" (build-ref-items-with-href (get raw-data "beh-attrs") "/geography/hypermedia/reference/attributes/" detail-keys 3) "uniq-attrs" (build-ref-items-with-href (get raw-data "uniq-attrs") "/geography/hypermedia/reference/attributes/" detail-keys 3)}))) ;; -------------------------------------------------------------------------- ;; build-attr-detail / build-header-detail / build-event-detail ;; ;; Lookup a slug in a detail dict, reshape for page rendering. ;; -------------------------------------------------------------------------- (define build-attr-detail (fn ((slug :as string) detail) ;; detail: dict with "description", "example", "handler", "demo" keys or nil (if (nil? detail) {"attr-not-found" true} {"attr-not-found" nil "attr-title" slug "attr-description" (get detail "description") "attr-example" (get detail "example") "attr-handler" (get detail "handler") "attr-demo" (get detail "demo") "attr-wire-id" (if (has-key? detail "handler") (str "ref-wire-" (replace (replace slug ":" "-") "*" "star")) nil)}))) (define build-header-detail (fn ((slug :as string) detail) (if (nil? detail) {"header-not-found" true} {"header-not-found" nil "header-title" slug "header-direction" (get detail "direction") "header-description" (get detail "description") "header-example" (get detail "example") "header-demo" (get detail "demo")}))) (define build-event-detail (fn ((slug :as string) detail) (if (nil? detail) {"event-not-found" true} {"event-not-found" nil "event-title" slug "event-description" (get detail "description") "event-example" (get detail "example") "event-demo" (get detail "demo")}))) ;; -------------------------------------------------------------------------- ;; build-component-source ;; ;; Reconstruct defcomp/defisland source from component metadata. ;; -------------------------------------------------------------------------- (define build-component-source (fn ((comp-data :as dict)) ;; comp-data: dict with "type", "name", "params", "has-children", "body-sx", "affinity" (let ((comp-type (get comp-data "type")) (name (get comp-data "name")) (params (get comp-data "params")) (has-children (get comp-data "has-children")) (body-sx (get comp-data "body-sx")) (affinity (get comp-data "affinity"))) (if (= comp-type "not-found") (str ";; component " name " not found") (let ((param-strs (if (empty? params) (if has-children (list "&rest" "children") (list)) (if has-children (append (cons "&key" params) (list "&rest" "children")) (cons "&key" params)))) (params-sx (str "(" (join " " param-strs) ")")) (form-name (if (= comp-type "island") "defisland" "defcomp")) (affinity-str (if (and (= comp-type "component") (not (nil? affinity)) (not (= affinity "auto"))) (str " :affinity " affinity) ""))) (str "(" form-name " " name " " params-sx affinity-str "\n " body-sx ")")))))) ;; -------------------------------------------------------------------------- ;; build-bundle-analysis ;; ;; Compute per-page bundle stats from pre-extracted component data. ;; -------------------------------------------------------------------------- (define build-bundle-analysis (fn ((pages-raw :as list) (components-raw :as dict) (total-components :as number) (total-macros :as number) (pure-count :as number) (io-count :as number)) ;; pages-raw: list of {:name :path :direct :needed-names} ;; components-raw: dict of name → {:is-pure :affinity :render-target :io-refs :deps :source} (let ((pages-data (list))) (for-each (fn ((page :as dict)) (let ((needed-names (get page "needed-names")) (n (len needed-names)) (pct (if (> total-components 0) (round (* (/ n total-components) 100)) 0)) (savings (- 100 pct)) (pure-in-page 0) (io-in-page 0) (page-io-refs (list)) (comp-details (list))) ;; Walk needed components (for-each (fn ((comp-name :as string)) (let ((info (get components-raw comp-name))) (when (not (nil? info)) (if (get info "is-pure") (set! pure-in-page (+ pure-in-page 1)) (do (set! io-in-page (+ io-in-page 1)) (for-each (fn ((ref :as string)) (when (not (some (fn ((r :as string)) (= r ref)) page-io-refs)) (append! page-io-refs ref))) (or (get info "io-refs") (list))))) (append! comp-details {"name" comp-name "is-pure" (get info "is-pure") "affinity" (get info "affinity") "render-target" (get info "render-target") "io-refs" (or (get info "io-refs") (list)) "deps" (or (get info "deps") (list)) "source" (get info "source")})))) needed-names) (append! pages-data {"name" (get page "name") "path" (get page "path") "direct" (get page "direct") "needed" n "pct" pct "savings" savings "io-refs" (len page-io-refs) "pure-in-page" pure-in-page "io-in-page" io-in-page "components" comp-details}))) pages-raw) {"pages" pages-data "total-components" total-components "total-macros" total-macros "pure-count" pure-count "io-count" io-count}))) ;; -------------------------------------------------------------------------- ;; build-routing-analysis ;; ;; Classify pages by routing mode (client vs server). ;; -------------------------------------------------------------------------- (define build-routing-analysis (fn ((pages-raw :as list)) ;; pages-raw: list of {:name :path :has-data :content-src} (let ((pages-data (list)) (client-count 0) (server-count 0)) (for-each (fn ((page :as dict)) (let ((has-data (get page "has-data")) (content-src (or (get page "content-src") "")) (mode nil) (reason "")) (cond has-data (do (set! mode "server") (set! reason "Has :data expression — needs server IO") (set! server-count (+ server-count 1))) (empty? content-src) (do (set! mode "server") (set! reason "No content expression") (set! server-count (+ server-count 1))) :else (do (set! mode "client") (set! client-count (+ client-count 1)))) (append! pages-data {"name" (get page "name") "path" (get page "path") "mode" mode "has-data" has-data "content-expr" (if (> (len content-src) 80) (str (slice content-src 0 80) "...") content-src) "reason" reason}))) pages-raw) {"pages" pages-data "total-pages" (+ client-count server-count) "client-count" client-count "server-count" server-count}))) ;; -------------------------------------------------------------------------- ;; build-affinity-analysis ;; ;; Package component affinity info + page render plans for display. ;; -------------------------------------------------------------------------- (define build-affinity-analysis (fn ((demo-components :as list) (page-plans :as list)) {"components" demo-components "page-plans" page-plans}))