;; ---------------------------------------------------------------------------
;; Reader Macros
;; ---------------------------------------------------------------------------

(defcomp ()
  (~docs/page :title "Reader Macros"

    (~docs/section :title "Context" :id "context"
      (p "SX has three hardcoded reader transformations: " (code "`") " → " (code "(quasiquote ...)") ", " (code ",") " → " (code "(unquote ...)") ", " (code ",@") " → " (code "(splice-unquote ...)") ". These are baked into the parser with no extensibility. The " (code "~") " prefix for components and " (code "&") " for param modifiers are just symbol characters, handled at eval time.")
      (p "Reader macros add parse-time transformations triggered by a dispatch character. Motivating use case: a " (code "~md") " component that uses heredoc syntax for markdown source instead of string literals. More broadly: datum comments, raw strings, and custom literal syntax."))

    (~docs/section :title "Design" :id "design"

      (~docs/subsection :title "Dispatch Character: #"
        (p "Lisp tradition. " (code "#") " is NOT in " (code "ident-start") " or " (code "ident-char") " — completely free. Pattern:")
        (~docs/code :src (highlight "#;expr       → (read and discard expr, return next)\n#|...|       → raw string literal\n#'expr       → (quote expr)" "lisp")))

      (~docs/subsection :title "#; — Datum comment"
        (p "Scheme/Racket standard. Reads and discards the next expression. Preserves balanced parens.")
        (~docs/code :src (highlight "(list 1 #;(this is commented out) 2 3)  → (list 1 2 3)" "lisp")))

      (~docs/subsection :title "#|...| — Raw string"
        (p "No escape processing. Everything between " (code "#|") " and " (code "|") " is literal. Enables inline markdown, regex patterns, code examples.")
        (~docs/code :src (highlight "(~md #|## Title\n\nSome **bold** text with \"quotes\" and \\backslashes.|)" "lisp")))

      (~docs/subsection :title "#' — Quote shorthand"
        (p "Currently no single-char quote (" (code "`") " is quasiquote).")
        (~docs/code :src (highlight "#'my-function  →  (quote my-function)" "lisp")))

      (~docs/subsection :title "Extensible dispatch: #name"
        (p "User-defined reader macros via " (code "#name expr") ". The parser reads an identifier after " (code "#") ", looks up a handler in the reader macro registry, and calls it with the next parsed expression. See the " (a :href "/sx/(etc.(plan.reader-macro-demo))" (~tw :tokens "text-violet-600 hover:underline") "#z3 demo") " for a working example that translates SX spec declarations to SMT-LIB.")))


    ;; -----------------------------------------------------------------------
    ;; Implementation
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Implementation" :id "implementation"

      (~docs/subsection :title "1. Spec: parser.sx"
        (p "Add " (code "#") " dispatch to " (code "read-expr") " (after the " (code ",") "/" (code ",@") " case, before number). Add " (code "read-raw-string") " helper function.")
        (~docs/code :src (highlight ";; Reader macro dispatch\n(= ch \"#\")\n  (do (set! pos (inc pos))\n      (if (>= pos len-src)\n        (error \"Unexpected end of input after #\")\n        (let ((dispatch-ch (nth source pos)))\n          (cond\n            ;; #; — datum comment: read and discard next expr\n            (= dispatch-ch \";\")\n              (do (set! pos (inc pos))\n                  (read-expr)    ;; read and discard\n                  (read-expr))   ;; return the NEXT expr\n\n            ;; #| — raw string\n            (= dispatch-ch \"|\")\n              (do (set! pos (inc pos))\n                  (read-raw-string))\n\n            ;; #' — quote shorthand\n            (= dispatch-ch \"'\")\n              (do (set! pos (inc pos))\n                  (list (make-symbol \"quote\") (read-expr)))\n\n            :else\n              (error (str \"Unknown reader macro: #\" dispatch-ch))))))" "lisp"))
        (p "The " (code "read-raw-string") " helper:")
        (~docs/code :src (highlight "(define read-raw-string\n  (fn ()\n    (let ((buf \"\"))\n      (define raw-loop\n        (fn ()\n          (if (>= pos len-src)\n            (error \"Unterminated raw string\")\n            (let ((ch (nth source pos)))\n              (if (= ch \"|\")\n                (do (set! pos (inc pos)) nil)  ;; done\n                (do (set! buf (str buf ch))\n                    (set! pos (inc pos))\n                    (raw-loop)))))))\n      (raw-loop)\n      buf)))" "lisp")))

      (~docs/subsection :title "2. Python: parser.py"
        (p "Add " (code "#") " dispatch to " (code "_parse_expr()") " (after " (code ",") "/" (code ",@") " handling ~line 252). Add " (code "_read_raw_string()") " method to Tokenizer.")
        (~docs/code :src (highlight "# In _parse_expr(), after the comma/splice-unquote block:\nif raw == \"#\":\n    tok._advance(1)  # consume the #\n    if tok.pos >= len(tok.text):\n        raise ParseError(\"Unexpected end of input after #\",\n                         tok.pos, tok.line, tok.col)\n    dispatch = tok.text[tok.pos]\n    if dispatch == \";\":\n        tok._advance(1)\n        _parse_expr(tok)  # read and discard\n        return _parse_expr(tok)  # return next\n    if dispatch == \"|\":\n        tok._advance(1)\n        return tok._read_raw_string()\n    if dispatch == \"'\":\n        tok._advance(1)\n        return [Symbol(\"quote\"), _parse_expr(tok)]\n    raise ParseError(f\"Unknown reader macro: #{dispatch}\",\n                     tok.pos, tok.line, tok.col)" "python"))
        (p "The " (code "_read_raw_string()") " method on Tokenizer:")
        (~docs/code :src (highlight "def _read_raw_string(self) -> str:\n    buf = []\n    while self.pos < len(self.text):\n        ch = self.text[self.pos]\n        if ch == \"|\":\n            self._advance(1)\n            return \"\".join(buf)\n        buf.append(ch)\n        self._advance(1)\n    raise ParseError(\"Unterminated raw string\",\n                     self.pos, self.line, self.col)" "python")))

      (~docs/subsection :title "3. JS: auto-transpiled"
        (p "JS parser comes from bootstrap of parser.sx — spec change handles it automatically."))

      (~docs/subsection :title "4. Rebootstrap both targets"
        (p "Run " (code "bootstrap_js.py") " and " (code "bootstrap_py.py") " to regenerate " (code "sx-ref.js") " and " (code "sx_ref.py") " from the updated parser.sx spec."))

      (~docs/subsection :title "5. Grammar update"
        (p "Add reader macro syntax to the grammar comment at the top of parser.sx:")
        (~docs/code :src (highlight ";;   reader     → '#;' expr          (datum comment)\n;;              | '#|' raw-chars '|' (raw string)\n;;              | \"#'\" expr          (quote shorthand)" "lisp"))))

    ;; -----------------------------------------------------------------------
    ;; Files
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Files" :id "files"
      (div (~tw :tokens "overflow-x-auto rounded border border-stone-200")
        (table (~tw :tokens "w-full text-left text-sm")
          (thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "File")
            (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Change")))
          (tbody
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/parser.sx")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "# dispatch in read-expr, read-raw-string helper, grammar comment"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/parser.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "# dispatch in _parse_expr(), _read_raw_string()"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/sx/ref/sx_ref.py")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Rebootstrap"))
            (tr (~tw :tokens "border-b border-stone-100")
              (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "shared/static/scripts/sx-ref.js")
              (td (~tw :tokens "px-3 py-2 text-stone-700") "Rebootstrap"))))))

    ;; -----------------------------------------------------------------------
    ;; Verification
    ;; -----------------------------------------------------------------------

    (~docs/section :title "Verification" :id "verification"

      (~docs/subsection :title "Parse tests"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm")
          (li "#;(ignored) 42 → 42")
          (li "(list 1 #;2 3) → (list 1 3)")
          (li "#|hello \"world\" \\n| → string: hello \"world\" \\n (literal, no escaping)")
          (li "#|multi\\nline| → string with actual newline")
          (li "#'foo → (quote foo)")
          (li "# at EOF → error")
          (li "#x unknown → error")))

      (~docs/subsection :title "Regression"
        (ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
          (li "All existing parser tests pass after changes")
          (li "Rebootstrapped JS and Python pass their test suites")
          (li "JS parity: SX.parse('#|hello|') returns [\"hello\"]"))))))

;; ---------------------------------------------------------------------------
;; Reader Macro Demo: #z3 — SX Spec to SMT-LIB
;; ---------------------------------------------------------------------------
