;; Hayes conformance test runner.
;; Installs T{ -> }T as Forth primitives that snapshot and compare dstack,
;; plus stub TESTING / HEX / DECIMAL so the Hayes Core file can stream
;; through the interpreter without halting on unsupported metadata words.

(define hayes-pass 0)
(define hayes-fail 0)
(define hayes-error 0)
(define hayes-start-depth 0)
(define hayes-actual (list))
(define hayes-actual-set false)
(define hayes-failures (list))
(define hayes-first-error "")
(define hayes-error-hist (dict))

(define
  hayes-reset!
  (fn
    ()
    (set! hayes-pass 0)
    (set! hayes-fail 0)
    (set! hayes-error 0)
    (set! hayes-start-depth 0)
    (set! hayes-actual (list))
    (set! hayes-actual-set false)
    (set! hayes-failures (list))
    (set! hayes-first-error "")
    (set! hayes-error-hist (dict))))

(define
  hayes-slice
  (fn
    (state base)
    (let
      ((n (- (forth-depth state) base)))
      (if (<= n 0) (list) (take (get state "dstack") n)))))

(define
  hayes-truncate!
  (fn
    (state base)
    (let
      ((n (- (forth-depth state) base)))
      (when (> n 0) (dict-set! state "dstack" (drop (get state "dstack") n))))))

(define
  hayes-install!
  (fn
    (state)
    (forth-def-prim!
      state
      "T{"
      (fn
        (s)
        (set! hayes-start-depth (forth-depth s))
        (set! hayes-actual-set false)
        (set! hayes-actual (list))))
    (forth-def-prim!
      state
      "->"
      (fn
        (s)
        (set! hayes-actual (hayes-slice s hayes-start-depth))
        (set! hayes-actual-set true)
        (hayes-truncate! s hayes-start-depth)))
    (forth-def-prim!
      state
      "}T"
      (fn
        (s)
        (let
          ((expected (hayes-slice s hayes-start-depth)))
          (hayes-truncate! s hayes-start-depth)
          (if
            (and hayes-actual-set (= expected hayes-actual))
            (set! hayes-pass (+ hayes-pass 1))
            (begin
              (set! hayes-fail (+ hayes-fail 1))
              (set!
                hayes-failures
                (concat
                  hayes-failures
                  (list
                    (dict
                      "kind"
                      "fail"
                      "expected"
                      (str expected)
                      "actual"
                      (str hayes-actual))))))))))
    (forth-def-prim! state "TESTING" (fn (s) nil))
    ;; HEX/DECIMAL are real primitives now (runtime.sx) — no stub needed.
    state))

(define
  hayes-boot
  (fn () (let ((s (forth-boot))) (hayes-install! s) (hayes-reset!) s)))

;; Run a single preprocessed chunk (string of Forth source) on the shared
;; state. Catch any raised error and move on — the chunk boundary is a
;; safe resume point.
(define
  hayes-bump-error-key!
  (fn
    (err)
    (let
      ((msg (str err)))
      (let
        ((space-idx (index-of msg " ")))
        (let
          ((key
            (if
              (> space-idx 0)
              (substr msg 0 space-idx)
              msg)))
          (dict-set!
            hayes-error-hist
            key
            (+ 1 (or (get hayes-error-hist key) 0))))))))

(define
  hayes-run-chunk
  (fn
    (state src)
    (guard
      (err
        ((= 1 1)
          (begin
            (set! hayes-error (+ hayes-error 1))
            (when
              (= (len hayes-first-error) 0)
              (set! hayes-first-error (str err)))
            (hayes-bump-error-key! err)
            (dict-set! state "dstack" (list))
            (dict-set! state "rstack" (list))
            (dict-set! state "compiling" false)
            (dict-set! state "current-def" nil)
            (dict-set! state "cstack" (list))
            (dict-set! state "input" (list)))))
      (forth-interpret state src))))

(define
  hayes-summary
  (fn
    ()
    (dict
      "pass"
      hayes-pass
      "fail"
      hayes-fail
      "error"
      hayes-error
      "total"
      (+ (+ hayes-pass hayes-fail) hayes-error)
      "first-error"
      hayes-first-error
      "error-hist"
      hayes-error-hist)))
