;; Phase 2 — colon definitions, compile mode, VARIABLE/CONSTANT/VALUE/TO, @/!/+!.
;;
;; Compile-mode representation:
;;   A colon-definition body is a list of "ops", each an SX lambda (fn (s) ...).
;;   : FOO 1 2 + ;    -> body = (push-1 push-2 call-plus)
;;   References to other words are compiled as late-binding thunks so that
;;   self-reference works and redefinitions take effect for future runs.
;;
;; State additions used in Phase 2:
;;   "compiling"   : bool — are we inside :..; ?
;;   "current-def" : dict {:name "..." :body (list)} during compile
;;   "vars"        : dict {"addr-name" -> cell-value} for VARIABLE storage

(define
  forth-compile-token
  (fn
    (state tok)
    (let
      ((w (forth-lookup state tok)))
      (if
        (not (nil? w))
        (if
          (get w "immediate?")
          (forth-execute-word state w)
          (forth-compile-call state tok))
        (let
          ((n (forth-parse-number tok (get (get state "vars") "base"))))
          (if
            (not (nil? n))
            (forth-compile-lit state n)
            (forth-error state (str tok " ?"))))))))

;; Resolve the word NOW (early binding) so that `: X X ;` compiles a call
;; to the prior X — matching standard Forth redefinition semantics.
;; RECURSE is the one exception: it stays late-bound against the not-yet-
;; installed current definition.
;; Inline primitive calls: skip the `forth-execute-word` indirection by
;; appending the word's body fn directly (forth-execute-word body
;; reduces to `((get w "body") state)`, which is exactly what the body
;; fn already is). Saves one frame per call op in every colon-def.
(define
  forth-compile-call
  (fn
    (state name)
    (let
      ((w (forth-lookup state name)))
      (if
        (nil? w)
        (forth-error state (str name " ?"))
        (forth-def-append! state (get w "body"))))))

(define
  forth-compile-recurse
  (fn
    (state name)
    (let
      ((op
        (fn
          (s)
          (let
            ((w (forth-lookup s name)))
            (if
              (nil? w)
              (forth-error s (str "RECURSE: " name " not yet installed"))
              (forth-execute-word s w))))))
      (forth-def-append! state op))))

(define
  forth-compile-lit
  (fn
    (state n)
    (let ((op (fn (s) (forth-push s n)))) (forth-def-append! state op))))

(define
  forth-def-append!
  (fn
    (state op)
    (let
      ((def (get state "current-def")))
      (dict-set! def "body" (concat (get def "body") (list op))))))

(define
  forth-def-length
  (fn (state) (len (get (get state "current-def") "body"))))

(define
  forth-make-branch
  (fn
    (kind target)
    (let ((b (dict))) (dict-set! b "kind" kind) (dict-set! b "target" target) b)))

(define
  forth-make-target
  (fn () (let ((t (dict))) (dict-set! t "v" 0) t)))

(define
  forth-make-colon-body
  (fn
    (ops)
    (let
      ((n (len ops)))
      (fn
        (s)
        (let ((pc (dict))) (dict-set! pc "v" 0) (forth-run-body s ops pc n))))))

;; After `;` finalizes a body, walk it and attach to each does-rebind op
;; the slice of ops that follow it — that slice becomes the runtime body
;; of the just-CREATE'd word when the rebind fires.
(define
  forth-fixup-does!
  (fn
    (ops i n)
    (when
      (< i n)
      (begin
        (let
          ((op (nth ops i)))
          (when
            (and (dict? op) (= (get op "kind") "does-rebind"))
            (dict-set! op "deferred" (drop ops (+ i 1)))))
        (forth-fixup-does! ops (+ i 1) n)))))

(define
  forth-step-op
  (fn
    (s op pc)
    (cond
      ((and (dict? op) (= (get op "kind") "bif"))
        (if
          (= (forth-pop s) 0)
          (dict-set! pc "v" (get (get op "target") "v"))
          (dict-set! pc "v" (+ (get pc "v") 1))))
      ((and (dict? op) (= (get op "kind") "branch"))
        (dict-set! pc "v" (get (get op "target") "v")))
      ((and (dict? op) (= (get op "kind") "leave"))
        (begin
          (forth-rpop s)
          (forth-rpop s)
          (dict-set! pc "v" (get (get op "target") "v"))))
      ((and (dict? op) (= (get op "kind") "loop"))
        (forth-loop-step s op pc))
      ((and (dict? op) (= (get op "kind") "+loop"))
        (forth-plusloop-step s op pc))
      ((and (dict? op) (= (get op "kind") "exit"))
        (dict-set! pc "v" 1000000000))
      ((and (dict? op) (= (get op "kind") "does-rebind"))
        (forth-do-does-rebind s op pc))
      (else (begin (op s) (dict-set! pc "v" (+ (get pc "v") 1)))))))

(define
  forth-do-does-rebind
  (fn
    (s op pc)
    (let
      ((target (get s "last-creator"))
        (deferred (get op "deferred")))
      (when
        (nil? target)
        (forth-error s "DOES>: no recent CREATE"))
      (let
        ((addr (get target "body-addr")))
        (let
          ((new-body (forth-make-does-body addr deferred)))
          (dict-set! target "body" new-body)))
      (dict-set! pc "v" 1000000000))))

(define
  forth-make-does-body
  (fn
    (addr deferred)
    (let
      ((n (len deferred)))
      (fn
        (s)
        (forth-push s addr)
        (let
          ((pc2 (dict)))
          (dict-set! pc2 "v" 0)
          (forth-run-body s deferred pc2 n))))))

(define
  forth-loop-step
  (fn
    (s op pc)
    (let
      ((idx (forth-rpop s)))
      (let
        ((lim (forth-rpeek s)))
        (let
          ((next (+ idx 1)))
          (if
            (>= next lim)
            (begin
              (forth-rpop s)
              (dict-set! pc "v" (+ (get pc "v") 1)))
            (begin
              (forth-rpush s next)
              (dict-set! pc "v" (get (get op "target") "v")))))))))

(define
  forth-plusloop-step
  (fn
    (s op pc)
    (let
      ((inc (forth-pop s)))
      (let
        ((idx (forth-rpop s)))
        (let
          ((lim (forth-rpeek s)))
          (let
            ((next (forth-clip (+ idx inc))))
            (if
              (if (>= inc 0) (>= next lim) (< next lim))
              (begin
                (forth-rpop s)
                (dict-set! pc "v" (+ (get pc "v") 1)))
              (begin
                (forth-rpush s next)
                (dict-set! pc "v" (get (get op "target") "v"))))))))))

;; Parse input tokens until one ends in `"`. Returns joined content with
;; single spaces between tokens (emulating standard Forth S"-style parse).
(define
  forth-parse-quote
  (fn
    (state)
    (forth-parse-quote-loop state (list))))

(define
  forth-parse-quote-loop
  (fn
    (state parts)
    (let
      ((tok (forth-next-token! state)))
      (if
        (nil? tok)
        (forth-error state "unterminated string")
        (let
          ((n (len tok)))
          (if
            (and (> n 0) (= (substr tok (- n 1) 1) "\""))
            (let
              ((final (substr tok 0 (- n 1))))
              (forth-join-parts (concat parts (list final)) " "))
            (forth-parse-quote-loop state (concat parts (list tok)))))))))

(define
  forth-join-parts
  (fn
    (parts sep)
    (if
      (= (len parts) 0)
      ""
      (forth-join-loop (rest parts) sep (first parts)))))

(define
  forth-join-loop
  (fn
    (xs sep acc)
    (if
      (= (len xs) 0)
      acc
      (forth-join-loop (rest-of xs) sep (str acc sep (first xs))))))

(define
  forth-find-do
  (fn
    (cs)
    (if
      (= (len cs) 0)
      nil
      (if
        (and (dict? (first cs)) (= (get (first cs) "kind") "do"))
        (first cs)
        (forth-find-do (rest cs))))))

;; Run a colon-def body. The PC is a one-cell dict so step-op can mutate
;; it for branches/loops/exit. As an explicit TCO, when we reach the
;; final op AND it's a plain function (not a branch dict), call it in
;; tail position — no recurse, no post-increment — so chains of
;; colon-def `: A ... B ;` calls don't accumulate continuation frames.
(define
  forth-run-body
  (fn
    (s ops pc n)
    (when
      (< (get pc "v") n)
      (let
        ((cur (get pc "v")))
        (let
          ((op (nth ops cur)))
          (if
            (and (not (dict? op)) (= (+ cur 1) n))
            (op s)
            (begin
              (forth-step-op s op pc)
              (forth-run-body s ops pc n))))))))

;; Override forth-interpret-token to branch on compile mode.
(define
  forth-interpret-token
  (fn
    (state tok)
    (if
      (get state "compiling")
      (forth-compile-token state tok)
      (let
        ((w (forth-lookup state tok)))
        (if
          (not (nil? w))
          (forth-execute-word state w)
          (let
            ((n (forth-parse-number tok (get (get state "vars") "base"))))
            (if
              (not (nil? n))
              (forth-push state n)
              (forth-error state (str tok " ?")))))))))

;; Install `:` and `;` plus VARIABLE, CONSTANT, VALUE, TO, @, !, +!, RECURSE.
(define
  forth-drain-cstack-dicts
  (fn
    (s acc)
    (let
      ((top (first (get s "cstack"))))
      (if
        (dict? top)
        (forth-drain-cstack-dicts s (cons (forth-cpop s) acc))
        acc))))

;; Track the most recently defined word name for IMMEDIATE.
(define
  forth-install-compiler!
  (fn
    (state)
    (forth-def-prim!
      state
      ":"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)))
          (when (nil? name) (forth-error s ": expects name"))
          (let
            ((def (dict)))
            (dict-set! def "name" name)
            (dict-set! def "body" (list))
            (dict-set! s "current-def" def)
            (dict-set! s "compiling" true)))))
    (forth-def-prim-imm!
      state
      ";"
      (fn
        (s)
        (let
          ((def (get s "current-def")))
          (when (nil? def) (forth-error s "; outside definition"))
          (let
            ((ops (get def "body")))
            (begin
              (forth-fixup-does! ops 0 (len ops))
              (let
                ((body-fn (forth-make-colon-body ops)))
                (dict-set!
                  (get s "dict")
                  (downcase (get def "name"))
                  (forth-make-word "colon-def" body-fn false))
                (dict-set! s "last-defined" (get def "name"))
                (dict-set! s "current-def" nil)
                (dict-set! s "compiling" false)))))))
    (forth-def-prim-imm!
      state
      "IMMEDIATE"
      (fn
        (s)
        (let
          ((def-name (get (get s "current-def") "name"))
            (target
              (if
                (nil? (get s "current-def"))
                (forth-last-defined s)
                (get (get s "current-def") "name"))))
          (let
            ((w (forth-lookup s target)))
            (when (not (nil? w)) (dict-set! w "immediate?" true))))))
    (forth-def-prim-imm!
      state
      "RECURSE"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "RECURSE only in definition"))
        (let
          ((name (get (get s "current-def") "name")))
          (forth-compile-recurse s name))))
    (forth-def-prim-imm!
      state
      "IF"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "IF outside definition"))
        (let
          ((target (forth-make-target)))
          (forth-def-append! s (forth-make-branch "bif" target))
          (forth-cpush s target))))
    (forth-def-prim-imm!
      state
      "ELSE"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "ELSE outside definition"))
        (let
          ((new-target (forth-make-target)))
          (forth-def-append! s (forth-make-branch "branch" new-target))
          (let
            ((if-target (forth-cpop s)))
            (dict-set! if-target "v" (forth-def-length s)))
          (forth-cpush s new-target))))
    (forth-def-prim-imm!
      state
      "THEN"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "THEN outside definition"))
        (let
          ((target (forth-cpop s)))
          (dict-set! target "v" (forth-def-length s)))))
    (forth-def-prim-imm!
      state
      "BEGIN"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "BEGIN outside definition"))
        (forth-cpush s (forth-def-length s))))
    (forth-def-prim-imm!
      state
      "UNTIL"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "UNTIL outside definition"))
        (let
          ((back-pc (forth-cpop s)))
          (let
            ((target (forth-make-target)))
            (dict-set! target "v" back-pc)
            (forth-def-append! s (forth-make-branch "bif" target))))))
    (forth-def-prim-imm!
      state
      "AGAIN"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "AGAIN outside definition"))
        (let
          ((back-pc (forth-cpop s)))
          (let
            ((target (forth-make-target)))
            (dict-set! target "v" back-pc)
            (forth-def-append! s (forth-make-branch "branch" target))))))
    (forth-def-prim-imm!
      state
      "WHILE"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "WHILE outside definition"))
        (let
          ((target (forth-make-target)))
          (forth-def-append! s (forth-make-branch "bif" target))
          (forth-cpush s target))))
    (forth-def-prim-imm!
      state
      "REPEAT"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "REPEAT outside definition"))
        (let
          ((inner (forth-cpop s)))
          (let
            ((extras (forth-drain-cstack-dicts s (list))))
            (let
              ((back-pc (forth-cpop s)))
              (let
                ((b-target (forth-make-target)))
                (dict-set! b-target "v" back-pc)
                (forth-def-append! s (forth-make-branch "branch" b-target))
                (dict-set! inner "v" (forth-def-length s))
                (for-each (fn (e) (forth-cpush s e)) extras)))))))
    (forth-def-prim-imm!
      state
      "DO"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "DO outside definition"))
        (let
          ((op (fn (ss) (let ((start (forth-pop ss))) (let ((limit (forth-pop ss))) (forth-rpush ss limit) (forth-rpush ss start))))))
          (forth-def-append! s op))
        (let
          ((marker (dict)))
          (dict-set! marker "kind" "do")
          (dict-set! marker "back" (forth-def-length s))
          (dict-set! marker "leaves" (list))
          (forth-cpush s marker))))
    (forth-def-prim-imm!
      state
      "LOOP"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "LOOP outside definition"))
        (let
          ((marker (forth-cpop s)))
          (when
            (or (not (dict? marker)) (not (= (get marker "kind") "do")))
            (forth-error s "LOOP without DO"))
          (let
            ((target (forth-make-target)))
            (dict-set! target "v" (get marker "back"))
            (forth-def-append! s (forth-make-branch "loop" target)))
          (let
            ((exit-pc (forth-def-length s)))
            (for-each
              (fn (t) (dict-set! t "v" exit-pc))
              (get marker "leaves"))))))
    (forth-def-prim-imm!
      state
      "+LOOP"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "+LOOP outside definition"))
        (let
          ((marker (forth-cpop s)))
          (when
            (or (not (dict? marker)) (not (= (get marker "kind") "do")))
            (forth-error s "+LOOP without DO"))
          (let
            ((target (forth-make-target)))
            (dict-set! target "v" (get marker "back"))
            (forth-def-append! s (forth-make-branch "+loop" target)))
          (let
            ((exit-pc (forth-def-length s)))
            (for-each
              (fn (t) (dict-set! t "v" exit-pc))
              (get marker "leaves"))))))
    (forth-def-prim!
      state
      "CHAR"
      (fn
        (s)
        (let
          ((tok (forth-next-token! s)))
          (when
            (or (nil? tok) (= (len tok) 0))
            (forth-error s "CHAR expects a word"))
          (forth-push s (char-code (substr tok 0 1))))))
    (forth-def-prim-imm!
      state
      "[CHAR]"
      (fn
        (s)
        (let
          ((tok (forth-next-token! s)))
          (when
            (or (nil? tok) (= (len tok) 0))
            (forth-error s "[CHAR] expects a word"))
          (let
            ((c (char-code (substr tok 0 1))))
            (if
              (get s "compiling")
              (forth-def-append! s (fn (ss) (forth-push ss c)))
              (forth-push s c))))))
    (forth-def-prim!
      state
      "KEY"
      (fn
        (s)
        (let
          ((kb (or (get s "keybuf") "")))
          (if
            (= (len kb) 0)
            (forth-error s "KEY: no input available")
            (begin
              (forth-push s (char-code (substr kb 0 1)))
              (dict-set!
                s
                "keybuf"
                (substr kb 1 (- (len kb) 1))))))))
    (forth-def-prim!
      state
      "ACCEPT"
      (fn
        (s)
        (let
          ((n1 (forth-pop s)) (addr (forth-pop s)))
          (let
            ((kb (or (get s "keybuf") "")))
            (let
              ((n (if (< n1 (len kb)) n1 (len kb))))
              (forth-mem-write-string! s addr (substr kb 0 n))
              (dict-set! s "keybuf" (substr kb n (- (len kb) n)))
              (forth-push s n))))))
    (forth-def-prim-imm!
      state
      "S\""
      (fn
        (s)
        (let
          ((content (forth-parse-quote s)))
          (if
            (get s "compiling")
            (let
              ((addr (forth-alloc-bytes! s (len content))))
              (forth-mem-write-string! s addr content)
              (forth-def-append! s (fn (ss) (forth-push ss addr)))
              (forth-def-append! s (fn (ss) (forth-push ss (len content)))))
            (let
              ((addr (forth-alloc-bytes! s (len content))))
              (forth-mem-write-string! s addr content)
              (forth-push s addr)
              (forth-push s (len content)))))))
    (forth-def-prim-imm!
      state
      "C\""
      (fn
        (s)
        (let
          ((content (forth-parse-quote s)))
          (let
            ((addr (forth-alloc-bytes! s (+ 1 (len content)))))
            (forth-mem-write! s addr (len content))
            (forth-mem-write-string! s (+ addr 1) content)
            (if
              (get s "compiling")
              (forth-def-append! s (fn (ss) (forth-push ss addr)))
              (forth-push s addr))))))
    (forth-def-prim-imm!
      state
      ".\""
      (fn
        (s)
        (let
          ((content (forth-parse-quote s)))
          (if
            (get s "compiling")
            (forth-def-append! s (fn (ss) (forth-emit-str ss content)))
            (forth-emit-str s content)))))
    (forth-def-prim-imm!
      state
      "LEAVE"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "LEAVE outside definition"))
        (let
          ((marker (forth-find-do (get s "cstack"))))
          (when (nil? marker) (forth-error s "LEAVE without DO"))
          (let
            ((target (forth-make-target)))
            (forth-def-append! s (forth-make-branch "leave" target))
            (dict-set!
              marker
              "leaves"
              (concat (get marker "leaves") (list target)))))))
    (forth-def-prim!
      state
      "VARIABLE"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)))
          (when (nil? name) (forth-error s "VARIABLE expects name"))
          (dict-set! (get s "vars") (downcase name) 0)
          (forth-def-prim!
            s
            name
            (fn (ss) (forth-push ss (downcase name)))))))
    (forth-def-prim!
      state
      "CONSTANT"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)) (v (forth-pop s)))
          (when (nil? name) (forth-error s "CONSTANT expects name"))
          (forth-def-prim! s name (fn (ss) (forth-push ss v))))))
    (forth-def-prim!
      state
      "VALUE"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)) (v (forth-pop s)))
          (when (nil? name) (forth-error s "VALUE expects name"))
          (dict-set! (get s "vars") (downcase name) v)
          (forth-def-prim!
            s
            name
            (fn
              (ss)
              (forth-push ss (get (get ss "vars") (downcase name))))))))
    (forth-def-prim!
      state
      "TO"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)) (v (forth-pop s)))
          (when (nil? name) (forth-error s "TO expects name"))
          (dict-set! (get s "vars") (downcase name) v))))
    (forth-def-prim!
      state
      "@"
      (fn
        (s)
        (let
          ((addr (forth-pop s)))
          (cond
            ((= addr "@@state")
              (forth-push s (if (get s "compiling") -1 0)))
            ((= addr "@@in") (forth-push s 0))
            ((string? addr)
              (forth-push s (or (get (get s "vars") addr) 0)))
            (else (forth-push s (forth-mem-read s addr)))))))
    (forth-def-prim!
      state
      "!"
      (fn
        (s)
        (let
          ((addr (forth-pop s)) (v (forth-pop s)))
          (if
            (string? addr)
            (dict-set! (get s "vars") addr v)
            (forth-mem-write! s addr v)))))
    (forth-def-prim!
      state
      "+!"
      (fn
        (s)
        (let
          ((addr (forth-pop s)) (v (forth-pop s)))
          (if
            (string? addr)
            (let
              ((cur (or (get (get s "vars") addr) 0)))
              (dict-set! (get s "vars") addr (+ cur v)))
            (forth-mem-write! s addr (+ (forth-mem-read s addr) v))))))
    (forth-def-prim! state "HERE" (fn (s) (forth-push s (get s "here"))))
    (forth-def-prim!
      state
      "ALLOT"
      (fn
        (s)
        (let
          ((n (forth-pop s)))
          (dict-set! s "here" (+ (get s "here") n)))))
    (forth-def-prim!
      state
      ","
      (fn
        (s)
        (let
          ((v (forth-pop s)) (addr (forth-alloc-bytes! s 1)))
          (forth-mem-write! s addr v))))
    (forth-def-prim!
      state
      "C,"
      (fn
        (s)
        (let
          ((v (forth-pop s)) (addr (forth-alloc-bytes! s 1)))
          (forth-mem-write! s addr v))))
    (forth-def-prim!
      state
      "CREATE"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)))
          (when (nil? name) (forth-error s "CREATE expects name"))
          (let
            ((addr (get s "here")))
            (forth-def-prim! s name (fn (ss) (forth-push ss addr)))
            (let
              ((w (forth-lookup s name)))
              (dict-set! w "body-addr" addr)
              (dict-set! s "last-creator" w))))))
    (forth-def-prim!
      state
      "CELL+"
      (fn (s) (forth-push s (+ (forth-pop s) 1))))
    (forth-def-prim! state "CELLS" (fn (s) nil))
    (forth-def-prim! state "ALIGN" (fn (s) nil))
    (forth-def-prim! state "ALIGNED" (fn (s) nil))
    (forth-def-prim!
      state
      "EXECUTE"
      (fn (s) (let ((w (forth-pop s))) (forth-execute-word s w))))
    (forth-def-prim!
      state
      "'"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)))
          (when (nil? name) (forth-error s "' expects name"))
          (let
            ((w (forth-lookup s name)))
            (when (nil? w) (forth-error s (str name " ?")))
            (forth-push s w)))))
    (forth-def-prim-imm!
      state
      "[']"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)))
          (when (nil? name) (forth-error s "['] expects name"))
          (let
            ((w (forth-lookup s name)))
            (when (nil? w) (forth-error s (str name " ?")))
            (if
              (get s "compiling")
              (forth-def-append! s (fn (ss) (forth-push ss w)))
              (forth-push s w))))))
    (forth-def-prim-imm!
      state
      "LITERAL"
      (fn
        (s)
        (let
          ((v (forth-pop s)))
          (when
            (not (get s "compiling"))
            (forth-error s "LITERAL outside compile mode"))
          (forth-def-append! s (fn (ss) (forth-push ss v))))))
    (forth-def-prim-imm!
      state
      "POSTPONE"
      (fn
        (s)
        (let
          ((name (forth-next-token! s)))
          (when (nil? name) (forth-error s "POSTPONE expects name"))
          (let
            ((w (forth-lookup s name)))
            (when (nil? w) (forth-error s (str name " ?")))
            (if
              (get w "immediate?")
              (forth-def-append! s (fn (ss) (forth-execute-word ss w)))
              (forth-def-append!
                s
                (fn
                  (ss)
                  (forth-def-append!
                    ss
                    (fn (sss) (forth-execute-word sss w))))))))))
    (forth-def-prim!
      state
      ">BODY"
      (fn
        (s)
        (let
          ((w (forth-pop s)))
          (forth-push s (or (get w "body-addr") 0)))))
    (forth-def-prim-imm! state "\\" (fn (s) nil))
    (forth-def-prim-imm!
      state
      "SLITERAL"
      (fn
        (s)
        (let
          ((u (forth-pop s)) (c-addr (forth-pop s)))
          (let
            ((content (forth-mem-read-string s c-addr u)))
            (let
              ((new-addr (forth-alloc-bytes! s u)))
              (forth-mem-write-string! s new-addr content)
              (forth-def-append! s (fn (ss) (forth-push ss new-addr)))
              (forth-def-append! s (fn (ss) (forth-push ss u))))))))
    (forth-def-prim!
      state
      ">NUMBER"
      (fn
        (s)
        (let
          ((u (forth-pop s))
            (addr (forth-pop s))
            (hi (forth-pop s))
            (lo (forth-pop s)))
          (let
            ((d-lo (forth-to-unsigned lo 32))
              (d-hi (forth-to-unsigned hi 32))
              (b (get (get s "vars") "base")))
            (let
              ((result (forth-numparse-loop s addr u (forth-from-unsigned d-lo 32) (forth-from-unsigned d-hi 32) b)))
              (forth-push s (nth result 0))
              (forth-push s (nth result 1))
              (forth-push s (nth result 2))
              (forth-push s (nth result 3)))))))
    (forth-def-prim!
      state
      "COMPARE"
      (fn
        (s)
        (let
          ((u2 (forth-pop s))
            (a2 (forth-pop s))
            (u1 (forth-pop s))
            (a1 (forth-pop s)))
          (forth-push s (forth-compare-bytes-loop s a1 u1 a2 u2 0)))))
    (forth-def-prim!
      state
      "SEARCH"
      (fn
        (s)
        (let
          ((u2 (forth-pop s))
            (a2 (forth-pop s))
            (u1 (forth-pop s))
            (a1 (forth-pop s)))
          (let
            ((idx (forth-search-bytes s a1 u1 a2 u2 0)))
            (if
              (< idx 0)
              (begin
                (forth-push s a1)
                (forth-push s u1)
                (forth-push s 0))
              (begin
                (forth-push s (+ a1 idx))
                (forth-push s (- u1 idx))
                (forth-push s -1)))))))
    (forth-def-prim! state "R/O" (fn (s) (forth-push s 0)))
    (forth-def-prim! state "W/O" (fn (s) (forth-push s 1)))
    (forth-def-prim! state "R/W" (fn (s) (forth-push s 2)))
    (forth-def-prim!
      state
      "BIN"
      (fn (s) (forth-push s (+ (forth-pop s) 4))))
    (forth-def-prim!
      state
      "OPEN-FILE"
      (fn
        (s)
        (let
          ((fam (forth-pop s)) (u (forth-pop s)) (addr (forth-pop s)))
          (let
            ((path (forth-mem-read-string s addr u)))
            (let
              ((existing (get (get s "by-path") path)))
              (if
                (nil? existing)
                (begin (forth-push s 0) (forth-push s 1))
                (let
                  ((fid (get s "next-fileid")))
                  (let
                    ((entry (dict)))
                    (dict-set! entry "content" (get existing "content"))
                    (dict-set! entry "pos" 0)
                    (dict-set! entry "path" path)
                    (dict-set! (get s "files") (str fid) entry)
                    (dict-set! s "next-fileid" (+ fid 1))
                    (forth-push s fid)
                    (forth-push s 0)))))))))
    (forth-def-prim!
      state
      "CREATE-FILE"
      (fn
        (s)
        (let
          ((fam (forth-pop s)) (u (forth-pop s)) (addr (forth-pop s)))
          (let
            ((path (forth-mem-read-string s addr u)))
            (let
              ((fid (get s "next-fileid")))
              (let
                ((entry (dict)))
                (dict-set! entry "content" "")
                (dict-set! entry "pos" 0)
                (dict-set! entry "path" path)
                (dict-set! (get s "files") (str fid) entry)
                (dict-set! (get s "by-path") path entry)
                (dict-set! s "next-fileid" (+ fid 1))
                (forth-push s fid)
                (forth-push s 0)))))))
    (forth-def-prim!
      state
      "CLOSE-FILE"
      (fn (s) (let ((fid (forth-pop s))) (forth-push s 0))))
    (forth-def-prim!
      state
      "READ-FILE"
      (fn
        (s)
        (let
          ((fid (forth-pop s)) (u1 (forth-pop s)) (addr (forth-pop s)))
          (let
            ((entry (get (get s "files") (str fid))))
            (if
              (nil? entry)
              (begin (forth-push s 0) (forth-push s 1))
              (let
                ((content (get entry "content")) (pos (get entry "pos")))
                (let
                  ((avail (- (len content) pos)))
                  (let
                    ((n (if (< u1 avail) u1 avail)))
                    (when
                      (> n 0)
                      (forth-mem-write-string! s addr (substr content pos n)))
                    (dict-set! entry "pos" (+ pos n))
                    (forth-push s n)
                    (forth-push s 0)))))))))
    (forth-def-prim!
      state
      "WRITE-FILE"
      (fn
        (s)
        (let
          ((fid (forth-pop s)) (u (forth-pop s)) (addr (forth-pop s)))
          (let
            ((entry (get (get s "files") (str fid))))
            (if
              (nil? entry)
              (forth-push s 1)
              (begin
                (dict-set!
                  entry
                  "content"
                  (str
                    (get entry "content")
                    (forth-mem-read-string s addr u)))
                (forth-push s 0)))))))
    (forth-def-prim!
      state
      "FILE-POSITION"
      (fn
        (s)
        (let
          ((fid (forth-pop s)))
          (let
            ((entry (get (get s "files") (str fid))))
            (if
              (nil? entry)
              (begin
                (forth-push s 0)
                (forth-push s 0)
                (forth-push s 1))
              (begin
                (forth-push s (get entry "pos"))
                (forth-push s 0)
                (forth-push s 0)))))))
    (forth-def-prim!
      state
      "FILE-SIZE"
      (fn
        (s)
        (let
          ((fid (forth-pop s)))
          (let
            ((entry (get (get s "files") (str fid))))
            (if
              (nil? entry)
              (begin
                (forth-push s 0)
                (forth-push s 0)
                (forth-push s 1))
              (begin
                (forth-push s (len (get entry "content")))
                (forth-push s 0)
                (forth-push s 0)))))))
    (forth-def-prim!
      state
      "REPOSITION-FILE"
      (fn
        (s)
        (let
          ((fid (forth-pop s)) (hi (forth-pop s)) (lo (forth-pop s)))
          (let
            ((entry (get (get s "files") (str fid))))
            (if
              (nil? entry)
              (forth-push s 1)
              (begin (dict-set! entry "pos" lo) (forth-push s 0)))))))
    (forth-def-prim!
      state
      "DELETE-FILE"
      (fn
        (s)
        (let
          ((u (forth-pop s)) (addr (forth-pop s)))
          (forth-push s 1))))
    (forth-def-prim!
      state
      "WITHIN"
      (fn
        (s)
        (let
          ((n3 (forth-pop s)) (n2 (forth-pop s)) (n1 (forth-pop s)))
          (let
            ((a (forth-to-unsigned (- n1 n2) 32))
              (b (forth-to-unsigned (- n3 n2) 32)))
            (forth-push s (if (< a b) -1 0))))))
    (forth-def-prim!
      state
      "ABORT"
      (fn
        (s)
        (dict-set! s "dstack" (list))
        (dict-set! s "rstack" (list))
        (dict-set! s "cstack" (list))
        (forth-error s "ABORT")))
    (forth-def-prim-imm!
      state
      "ABORT\""
      (fn
        (s)
        (let
          ((msg (forth-parse-quote s)))
          (if
            (get s "compiling")
            (forth-def-append!
              s
              (fn
                (ss)
                (when
                  (not (= (forth-pop ss) 0))
                  (begin
                    (dict-set! ss "dstack" (list))
                    (dict-set! ss "rstack" (list))
                    (dict-set! ss "cstack" (list))
                    (forth-error ss (str "ABORT: " msg))))))
            (when
              (not (= (forth-pop s) 0))
              (begin
                (dict-set! s "dstack" (list))
                (dict-set! s "rstack" (list))
                (dict-set! s "cstack" (list))
                (forth-error s (str "ABORT: " msg))))))))
    (forth-def-prim-imm!
      state
      "EXIT"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "EXIT outside definition"))
        (let
          ((op (dict)))
          (dict-set! op "kind" "exit")
          (forth-def-append! s op))))
    (forth-def-prim-imm!
      state
      "DOES>"
      (fn
        (s)
        (when
          (not (get s "compiling"))
          (forth-error s "DOES> outside definition"))
        (let
          ((op (dict)))
          (dict-set! op "kind" "does-rebind")
          (forth-def-append! s op))))
    (forth-def-prim! state "UNLOOP" (fn (s) (forth-rpop s) (forth-rpop s)))
    (forth-def-prim-imm! state "[" (fn (s) (dict-set! s "compiling" false)))
    (forth-def-prim! state "]" (fn (s) (dict-set! s "compiling" true)))
    (forth-def-prim! state "STATE" (fn (s) (forth-push s "@@state")))
    (forth-def-prim!
      state
      "EVALUATE"
      (fn
        (s)
        (let
          ((u (forth-pop s)) (addr (forth-pop s)))
          (let
            ((src (forth-mem-read-string s addr u)))
            (let
              ((saved-input (get s "input")))
              (dict-set! s "input" (forth-tokens src))
              (forth-interpret-loop s)
              (dict-set! s "input" saved-input))))))
    (forth-def-prim!
      state
      "SOURCE"
      (fn (s) (forth-push s 0) (forth-push s 0)))
    (forth-def-prim! state ">IN" (fn (s) (forth-push s "@@in")))
    (forth-def-prim!
      state
      "WORD"
      (fn
        (s)
        (let
          ((delim (forth-pop s)) (tok (forth-next-token! s)))
          (let
            ((str-out (or tok "")))
            (let
              ((addr (forth-alloc-bytes! s (+ 1 (len str-out)))))
              (forth-mem-write! s addr (len str-out))
              (forth-mem-write-string! s (+ addr 1) str-out)
              (forth-push s addr))))))
    (forth-def-prim!
      state
      "FIND"
      (fn
        (s)
        (let
          ((c-addr (forth-pop s)))
          (let
            ((u (forth-mem-read s c-addr)))
            (let
              ((str-name (forth-mem-read-string s (+ c-addr 1) u)))
              (let
                ((w (forth-lookup s str-name)))
                (if
                  (nil? w)
                  (begin (forth-push s c-addr) (forth-push s 0))
                  (begin
                    (forth-push s w)
                    (forth-push
                      s
                      (if (get w "immediate?") 1 -1))))))))))
    (forth-def-prim!
      state
      "U<"
      (forth-cmp
        (fn
          (a b)
          (<
            (forth-to-unsigned a 32)
            (forth-to-unsigned b 32)))))
    (forth-def-prim!
      state
      "U>"
      (forth-cmp
        (fn
          (a b)
          (>
            (forth-to-unsigned a 32)
            (forth-to-unsigned b 32)))))
    (forth-def-prim!
      state
      "2@"
      (fn
        (s)
        (let
          ((addr (forth-pop s)))
          (if
            (string? addr)
            (forth-error s "2@ on var unsupported")
            (begin
              (forth-push s (forth-mem-read s (+ addr 1)))
              (forth-push s (forth-mem-read s addr)))))))
    (forth-def-prim!
      state
      "2!"
      (fn
        (s)
        (let
          ((addr (forth-pop s)) (a (forth-pop s)) (b (forth-pop s)))
          (forth-mem-write! s addr a)
          (forth-mem-write! s (+ addr 1) b))))
    state))

;; forth-next-token!: during `:`, VARIABLE, CONSTANT, etc. we need to pull
;; the next token from the *input stream* (not the dict/stack). Phase-1
;; interpreter fed tokens one at a time via for-each, so a parsing word
;; can't reach ahead. We rework `forth-interpret` to keep the remaining
;; token list on the state so parsing words can consume from it.

(define forth-last-defined (fn (state) (get state "last-defined")))

(define
  forth-next-token!
  (fn
    (state)
    (let
      ((rest (get state "input")))
      (if
        (or (nil? rest) (= (len rest) 0))
        nil
        (let
          ((tok (first rest)))
          (dict-set! state "input" (rest-of rest))
          tok)))))

;; Rewritten forth-interpret: drives a token list stored in state so that
;; parsing words like `:`, `VARIABLE`, `CONSTANT`, `TO` can consume the
;; following token.
(define rest-of (fn (l) (rest l)))

(define
  forth-interpret
  (fn
    (state src)
    (dict-set! state "input" (forth-tokens src))
    (forth-interpret-loop state)
    state))

;; Re-export forth-boot to include the compiler primitives too.
(define
  forth-interpret-loop
  (fn
    (state)
    (let
      ((tok (forth-next-token! state)))
      (if
        (nil? tok)
        state
        (begin
          (forth-interpret-token state tok)
          (forth-interpret-loop state))))))

(define
  forth-boot
  (fn
    ()
    (let
      ((s (forth-make-state)))
      (forth-install-primitives! s)
      (forth-install-compiler! s)
      s)))
