;; lib/forth/tests/runtime.sx — Tests for lib/forth/runtime.sx

(define forth-test-pass 0)
(define forth-test-fail 0)
(define forth-test-fails (list))

(define
  (forth-test name got expected)
  (if
    (= got expected)
    (set! forth-test-pass (+ forth-test-pass 1))
    (begin
      (set! forth-test-fail (+ forth-test-fail 1))
      (set! forth-test-fails (append forth-test-fails (list {:got got :expected expected :name name}))))))

;; ---------------------------------------------------------------------------
;; 1. Bitwise operations
;; ---------------------------------------------------------------------------

;; AND
(forth-test "and 0b1100 0b1010" (forth-and 12 10) 8)
(forth-test "and 0xFF 0x0F" (forth-and 255 15) 15)
(forth-test "and 0 any" (forth-and 0 42) 0)

;; OR
(forth-test "or 0b1100 0b1010" (forth-or 12 10) 14)
(forth-test "or 0 x" (forth-or 0 7) 7)

;; XOR
(forth-test "xor 0b1100 0b1010" (forth-xor 12 10) 6)
(forth-test "xor x x" (forth-xor 42 42) 0)

;; INVERT
(forth-test "invert 0" (forth-invert 0) -1)
(forth-test "invert -1" (forth-invert -1) 0)
(forth-test "invert 1" (forth-invert 1) -2)

;; LSHIFT RSHIFT
(forth-test "lshift 1 3" (forth-lshift 1 3) 8)
(forth-test "lshift 3 2" (forth-lshift 3 2) 12)
(forth-test "rshift 8 3" (forth-rshift 8 3) 1)
(forth-test "rshift 16 2" (forth-rshift 16 2) 4)

;; 2* 2/
(forth-test "2* 5" (forth-2* 5) 10)
(forth-test "2/ 10" (forth-2/ 10) 5)
(forth-test "2/ 7" (forth-2/ 7) 3)

;; BIT-COUNT
(forth-test "bit-count 0" (forth-bit-count 0) 0)
(forth-test "bit-count 1" (forth-bit-count 1) 1)
(forth-test "bit-count 7" (forth-bit-count 7) 3)
(forth-test "bit-count 255" (forth-bit-count 255) 8)
(forth-test "bit-count 256" (forth-bit-count 256) 1)

;; INTEGER-LENGTH
(forth-test "integer-length 0" (forth-integer-length 0) 0)
(forth-test "integer-length 1" (forth-integer-length 1) 1)
(forth-test "integer-length 4" (forth-integer-length 4) 3)
(forth-test "integer-length 255" (forth-integer-length 255) 8)

;; WITHIN
(forth-test
  "within 5 0 10"
  (forth-within 5 0 10)
  true)
(forth-test
  "within 0 0 10"
  (forth-within 0 0 10)
  true)
(forth-test
  "within 10 0 10"
  (forth-within 10 0 10)
  false)
(forth-test
  "within -1 0 10"
  (forth-within -1 0 10)
  false)

;; Arithmetic ops
(forth-test "negate 5" (forth-negate 5) -5)
(forth-test "negate -3" (forth-negate -3) 3)
(forth-test "abs -7" (forth-abs -7) 7)
(forth-test "min 3 5" (forth-min 3 5) 3)
(forth-test "max 3 5" (forth-max 3 5) 5)
(forth-test "mod 7 3" (forth-mod 7 3) 1)
(forth-test
  "divmod 7 3"
  (forth-divmod 7 3)
  (list 1 2))
(forth-test
  "divmod 10 5"
  (forth-divmod 10 5)
  (list 0 2))

;; ---------------------------------------------------------------------------
;; 2. String buffer
;; ---------------------------------------------------------------------------

(define sb1 (forth-sb-new))
(forth-test "sb? new" (forth-sb? sb1) true)
(forth-test "sb? non-sb" (forth-sb? 42) false)
(forth-test "sb value empty" (forth-sb-value sb1) "")
(forth-test "sb length empty" (forth-sb-length sb1) 0)

(forth-sb-type! sb1 "HELLO")
(forth-test "sb type" (forth-sb-value sb1) "HELLO")
(forth-test "sb length after type" (forth-sb-length sb1) 5)

;; EMIT one char
(define sb2 (forth-sb-new))
(forth-sb-emit! sb2 (nth (string->list "A") 0))
(forth-sb-emit! sb2 (nth (string->list "B") 0))
(forth-sb-emit! sb2 (nth (string->list "C") 0))
(forth-test "sb emit chars" (forth-sb-value sb2) "ABC")

;; Emit integer
(define sb3 (forth-sb-new))
(forth-sb-type! sb3 "n=")
(forth-sb-emit-int! sb3 42)
(forth-test "sb emit-int" (forth-sb-value sb3) "n=42")

(forth-sb-clear! sb1)
(forth-test "sb clear" (forth-sb-value sb1) "")
(forth-test "sb length after clear" (forth-sb-length sb1) 0)

;; Build a word definition-style name
(define sb4 (forth-sb-new))
(forth-sb-type! sb4 ": ")
(forth-sb-type! sb4 "SQUARE")
(forth-sb-type! sb4 " DUP * ;")
(forth-test "sb word def" (forth-sb-value sb4) ": SQUARE DUP * ;")

;; ---------------------------------------------------------------------------
;; 3. Memory / Bytevectors
;; ---------------------------------------------------------------------------

(define m1 (forth-mem-new 8))
(forth-test "mem? yes" (forth-mem? m1) true)
(forth-test "mem? no" (forth-mem? 42) false)
(forth-test "mem size" (forth-mem-size m1) 8)
(forth-test "mem cfetch zero" (forth-cfetch m1 0) 0)

;; C! C@
(forth-cstore m1 0 65)
(forth-cstore m1 1 66)
(forth-test "mem cstore/cfetch 0" (forth-cfetch m1 0) 65)
(forth-test "mem cstore/cfetch 1" (forth-cfetch m1 1) 66)
(forth-cstore m1 2 256)
(forth-test
  "mem cstore wraps 256→0"
  (forth-cfetch m1 2)
  0)
(forth-cstore m1 2 257)
(forth-test
  "mem cstore wraps 257→1"
  (forth-cfetch m1 2)
  1)

;; @ ! (32-bit LE cell)
(define m2 (forth-mem-new 8))
(forth-store m2 0 305419896)
(forth-test "mem store/fetch" (forth-fetch m2 0) 305419896)
(forth-store m2 4 1)
(forth-test "mem fetch byte 4" (forth-cfetch m2 4) 1)
(forth-test "mem fetch byte 5" (forth-cfetch m2 5) 0)

;; FILL ERASE
(define m3 (forth-mem-new 4))
(forth-fill! m3 0 4 42)
(forth-test
  "mem fill"
  (forth-mem->list m3 0 4)
  (list 42 42 42 42))
(forth-erase! m3 1 2)
(forth-test
  "mem erase middle"
  (forth-mem->list m3 0 4)
  (list 42 0 0 42))

;; MOVE
(define m4 (forth-mem-new 4))
(forth-cstore m4 0 1)
(forth-cstore m4 1 2)
(forth-cstore m4 2 3)
(define m5 (forth-mem-new 4))
(forth-move! m4 0 m5 0 3)
(forth-test
  "mem move"
  (forth-mem->list m5 0 3)
  (list 1 2 3))

;; mem->list
(define m6 (forth-mem-new 3))
(forth-cstore m6 0 10)
(forth-cstore m6 1 20)
(forth-cstore m6 2 30)
(forth-test
  "mem->list"
  (forth-mem->list m6 0 3)
  (list 10 20 30))
