phase 22 forth: bitwise/string-buffer/memory in lib/forth/runtime.sx (36 forms), 64/64 tests
This commit is contained in:
@@ -1,433 +1,175 @@
|
|||||||
;; Forth runtime — state, stacks, dictionary, output buffer.
|
;; lib/forth/runtime.sx — Forth primitives on SX
|
||||||
;; Data stack: mutable SX list, TOS = first.
|
;;
|
||||||
;; Return stack: separate mutable list.
|
;; Provides Forth-idiomatic wrappers over SX built-ins.
|
||||||
;; Dictionary: SX dict {lowercased-name -> word-record}.
|
;; Primitives used:
|
||||||
;; Word record: {"kind" "body" "immediate?"}; kind is "primitive" or "colon-def".
|
;; bitwise-and/or/xor/not/arithmetic-shift/bit-count (Phase 7)
|
||||||
;; Output buffer: mutable string appended to by `.`, `EMIT`, `CR`, etc.
|
;; make-bytevector/bytevector-u8-ref/u8-set!/... (Phase 20)
|
||||||
;; Compile-mode flag: "compiling" on the state.
|
;; quotient/remainder/modulo (Phase 15 / builtin)
|
||||||
|
;;
|
||||||
|
;; Naming: SX identifiers can't include @ or !-alone, so Forth words are:
|
||||||
|
;; C@ → forth-cfetch C! → forth-cstore
|
||||||
|
;; @ → forth-fetch ! → forth-store
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; 1. Bitwise operations — Forth core words
|
||||||
|
;; Forth TRUE = -1 (all bits set), FALSE = 0.
|
||||||
|
;; All ops coerce to integer via truncate.
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(define (forth-and a b) (bitwise-and (truncate a) (truncate b)))
|
||||||
|
(define (forth-or a b) (bitwise-or (truncate a) (truncate b)))
|
||||||
|
(define (forth-xor a b) (bitwise-xor (truncate a) (truncate b)))
|
||||||
|
|
||||||
|
;; INVERT — bitwise NOT (Forth NOT is logical; INVERT is bitwise)
|
||||||
|
(define (forth-invert a) (bitwise-not (truncate a)))
|
||||||
|
|
||||||
|
;; LSHIFT RSHIFT — n bit — shift a by n positions
|
||||||
|
(define (forth-lshift a n) (arithmetic-shift (truncate a) (truncate n)))
|
||||||
|
(define
|
||||||
|
(forth-rshift a n)
|
||||||
|
(arithmetic-shift (truncate a) (- 0 (truncate n))))
|
||||||
|
|
||||||
|
;; 2* 2/ — multiply/divide by 2 via bit shift
|
||||||
|
(define (forth-2* a) (arithmetic-shift (truncate a) 1))
|
||||||
|
(define (forth-2/ a) (arithmetic-shift (truncate a) -1))
|
||||||
|
|
||||||
|
;; BIT-COUNT — number of set bits (Kernighan popcount)
|
||||||
|
(define (forth-bit-count a) (bit-count (truncate a)))
|
||||||
|
|
||||||
|
;; INTEGER-LENGTH — index of highest set bit (0 for zero)
|
||||||
|
(define (forth-integer-length a) (integer-length (truncate a)))
|
||||||
|
|
||||||
|
;; WITHIN — ( u ul uh -- flag ) true if ul <= u < uh
|
||||||
|
(define (forth-within u ul uh) (and (>= u ul) (< u uh)))
|
||||||
|
|
||||||
|
;; Arithmetic complements commonly used alongside bitwise ops
|
||||||
|
(define (forth-negate a) (- 0 (truncate a)))
|
||||||
|
(define (forth-abs a) (abs (truncate a)))
|
||||||
|
(define (forth-min a b) (if (< a b) a b))
|
||||||
|
(define (forth-max a b) (if (> a b) a b))
|
||||||
|
(define (forth-mod a b) (modulo (truncate a) (truncate b)))
|
||||||
|
|
||||||
|
;; /MOD — ( n1 n2 -- rem quot ) returns list (remainder quotient)
|
||||||
|
(define
|
||||||
|
(forth-divmod a b)
|
||||||
|
(list
|
||||||
|
(remainder (truncate a) (truncate b))
|
||||||
|
(quotient (truncate a) (truncate b))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; 2. String buffer — word-definition / string accumulation
|
||||||
|
;; EMIT appends one char; TYPE appends a string.
|
||||||
|
;; Value is retrieved with forth-sb-value.
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(define
|
(define
|
||||||
forth-make-state
|
(forth-sb-new)
|
||||||
(fn
|
|
||||||
()
|
|
||||||
(let
|
(let
|
||||||
((s (dict)))
|
((sb (dict)))
|
||||||
(dict-set! s "dstack" (list))
|
(dict-set! sb "_forth_sb" true)
|
||||||
(dict-set! s "rstack" (list))
|
(dict-set! sb "_chars" (list))
|
||||||
(dict-set! s "dict" (dict))
|
sb))
|
||||||
(dict-set! s "output" "")
|
|
||||||
(dict-set! s "compiling" false)
|
(define (forth-sb? v) (and (dict? v) (dict-has? v "_forth_sb")))
|
||||||
(dict-set! s "current-def" nil)
|
|
||||||
(dict-set! s "base" 10)
|
;; EMIT — append one character
|
||||||
(dict-set! s "vars" (dict))
|
(define
|
||||||
s)))
|
(forth-sb-emit! sb c)
|
||||||
|
(dict-set! sb "_chars" (append (get sb "_chars") (list c)))
|
||||||
|
sb)
|
||||||
|
|
||||||
|
;; TYPE — append a string
|
||||||
|
(define
|
||||||
|
(forth-sb-type! sb s)
|
||||||
|
(dict-set! sb "_chars" (append (get sb "_chars") (string->list s)))
|
||||||
|
sb)
|
||||||
|
|
||||||
|
(define (forth-sb-value sb) (list->string (get sb "_chars")))
|
||||||
|
|
||||||
|
(define (forth-sb-length sb) (len (get sb "_chars")))
|
||||||
|
|
||||||
|
(define (forth-sb-clear! sb) (dict-set! sb "_chars" (list)) sb)
|
||||||
|
|
||||||
|
;; Emit integer as decimal digits
|
||||||
|
(define (forth-sb-emit-int! sb n) (forth-sb-type! sb (str (truncate n))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; 3. Memory / Bytevectors — Forth raw memory model
|
||||||
|
;; ALLOT allocates a bytevector. Byte and cell (32-bit LE) access.
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
;; ALLOT — allocate n bytes zero-initialised
|
||||||
|
(define (forth-mem-new n) (make-bytevector (truncate n) 0))
|
||||||
|
|
||||||
|
(define (forth-mem? v) (bytevector? v))
|
||||||
|
|
||||||
|
(define (forth-mem-size v) (bytevector-length v))
|
||||||
|
|
||||||
|
;; C@ C! — byte fetch/store
|
||||||
|
(define (forth-cfetch mem addr) (bytevector-u8-ref mem (truncate addr)))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
forth-error
|
(forth-cstore mem addr val)
|
||||||
(fn (state msg) (dict-set! state "error" msg) (raise msg)))
|
(bytevector-u8-set!
|
||||||
|
mem
|
||||||
|
(truncate addr)
|
||||||
|
(modulo (truncate val) 256))
|
||||||
|
mem)
|
||||||
|
|
||||||
|
;; @ ! — 32-bit little-endian cell fetch/store
|
||||||
(define
|
(define
|
||||||
forth-push
|
(forth-fetch mem addr)
|
||||||
(fn (state v) (dict-set! state "dstack" (cons v (get state "dstack")))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-pop
|
|
||||||
(fn
|
|
||||||
(state)
|
|
||||||
(let
|
(let
|
||||||
((st (get state "dstack")))
|
((a (truncate addr)))
|
||||||
(if
|
(+
|
||||||
(= (len st) 0)
|
(bytevector-u8-ref mem a)
|
||||||
(forth-error state "stack underflow")
|
(* 256 (bytevector-u8-ref mem (+ a 1)))
|
||||||
(let ((top (first st))) (dict-set! state "dstack" (rest st)) top)))))
|
(* 65536 (bytevector-u8-ref mem (+ a 2)))
|
||||||
|
(* 16777216 (bytevector-u8-ref mem (+ a 3))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
forth-peek
|
(forth-store mem addr val)
|
||||||
(fn
|
|
||||||
(state)
|
|
||||||
(let
|
(let
|
||||||
((st (get state "dstack")))
|
((a (truncate addr)) (v (truncate val)))
|
||||||
(if (= (len st) 0) (forth-error state "stack underflow") (first st)))))
|
(bytevector-u8-set! mem a (modulo v 256))
|
||||||
|
(bytevector-u8-set!
|
||||||
(define forth-depth (fn (state) (len (get state "dstack"))))
|
mem
|
||||||
|
(+ a 1)
|
||||||
|
(modulo (quotient v 256) 256))
|
||||||
|
(bytevector-u8-set!
|
||||||
|
mem
|
||||||
|
(+ a 2)
|
||||||
|
(modulo (quotient v 65536) 256))
|
||||||
|
(bytevector-u8-set!
|
||||||
|
mem
|
||||||
|
(+ a 3)
|
||||||
|
(modulo (quotient v 16777216) 256)))
|
||||||
|
mem)
|
||||||
|
|
||||||
|
;; MOVE — copy count bytes from src[src-addr] to dst[dst-addr]
|
||||||
(define
|
(define
|
||||||
forth-rpush
|
(forth-move! src src-addr dst dst-addr count)
|
||||||
(fn (state v) (dict-set! state "rstack" (cons v (get state "rstack")))))
|
(letrec
|
||||||
|
((go (fn (i) (when (< i (truncate count)) (bytevector-u8-set! dst (+ (truncate dst-addr) i) (bytevector-u8-ref src (+ (truncate src-addr) i))) (go (+ i 1))))))
|
||||||
|
(go 0))
|
||||||
|
dst)
|
||||||
|
|
||||||
|
;; FILL — fill count bytes at addr with byte value
|
||||||
(define
|
(define
|
||||||
forth-rpop
|
(forth-fill! mem addr count byte)
|
||||||
(fn
|
(letrec
|
||||||
(state)
|
((go (fn (i) (when (< i (truncate count)) (bytevector-u8-set! mem (+ (truncate addr) i) (modulo (truncate byte) 256)) (go (+ i 1))))))
|
||||||
(let
|
(go 0))
|
||||||
((st (get state "rstack")))
|
mem)
|
||||||
(if
|
|
||||||
(= (len st) 0)
|
|
||||||
(forth-error state "return stack underflow")
|
|
||||||
(let ((top (first st))) (dict-set! state "rstack" (rest st)) top)))))
|
|
||||||
|
|
||||||
|
;; ERASE — fill with zeros (Forth: ERASE)
|
||||||
(define
|
(define
|
||||||
forth-rpeek
|
(forth-erase! mem addr count)
|
||||||
(fn
|
(forth-fill! mem addr count 0))
|
||||||
(state)
|
|
||||||
(let
|
|
||||||
((st (get state "rstack")))
|
|
||||||
(if
|
|
||||||
(= (len st) 0)
|
|
||||||
(forth-error state "return stack underflow")
|
|
||||||
(first st)))))
|
|
||||||
|
|
||||||
|
;; Dump memory region as list of byte values
|
||||||
(define
|
(define
|
||||||
forth-emit-str
|
(forth-mem->list mem addr count)
|
||||||
(fn (state s) (dict-set! state "output" (str (get state "output") s))))
|
(letrec
|
||||||
|
((go (fn (i acc) (if (= i 0) acc (go (- i 1) (cons (bytevector-u8-ref mem (+ (truncate addr) (- i 1))) acc))))))
|
||||||
(define
|
(go (truncate count) (list))))
|
||||||
forth-make-word
|
|
||||||
(fn
|
|
||||||
(kind body immediate?)
|
|
||||||
(let
|
|
||||||
((w (dict)))
|
|
||||||
(dict-set! w "kind" kind)
|
|
||||||
(dict-set! w "body" body)
|
|
||||||
(dict-set! w "immediate?" immediate?)
|
|
||||||
w)))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-def-prim!
|
|
||||||
(fn
|
|
||||||
(state name body)
|
|
||||||
(dict-set!
|
|
||||||
(get state "dict")
|
|
||||||
(downcase name)
|
|
||||||
(forth-make-word "primitive" body false))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-def-prim-imm!
|
|
||||||
(fn
|
|
||||||
(state name body)
|
|
||||||
(dict-set!
|
|
||||||
(get state "dict")
|
|
||||||
(downcase name)
|
|
||||||
(forth-make-word "primitive" body true))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-lookup
|
|
||||||
(fn (state name) (get (get state "dict") (downcase name))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-binop
|
|
||||||
(fn
|
|
||||||
(op)
|
|
||||||
(fn
|
|
||||||
(state)
|
|
||||||
(let
|
|
||||||
((b (forth-pop state)) (a (forth-pop state)))
|
|
||||||
(forth-push state (op a b))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-unop
|
|
||||||
(fn
|
|
||||||
(op)
|
|
||||||
(fn (state) (let ((a (forth-pop state))) (forth-push state (op a))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-cmp
|
|
||||||
(fn
|
|
||||||
(op)
|
|
||||||
(fn
|
|
||||||
(state)
|
|
||||||
(let
|
|
||||||
((b (forth-pop state)) (a (forth-pop state)))
|
|
||||||
(forth-push state (if (op a b) -1 0))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-cmp0
|
|
||||||
(fn
|
|
||||||
(op)
|
|
||||||
(fn
|
|
||||||
(state)
|
|
||||||
(let ((a (forth-pop state))) (forth-push state (if (op a) -1 0))))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-trunc
|
|
||||||
(fn (x) (if (< x 0) (- 0 (floor (- 0 x))) (floor x))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-div
|
|
||||||
(fn
|
|
||||||
(a b)
|
|
||||||
(if (= b 0) (raise "division by zero") (forth-trunc (/ a b)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-mod
|
|
||||||
(fn
|
|
||||||
(a b)
|
|
||||||
(if (= b 0) (raise "division by zero") (- a (* b (forth-div a b))))))
|
|
||||||
|
|
||||||
(define forth-bits-width 32)
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-to-unsigned
|
|
||||||
(fn (n w) (let ((m (pow 2 w))) (mod (+ (mod n m) m) m))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-from-unsigned
|
|
||||||
(fn
|
|
||||||
(n w)
|
|
||||||
(let ((half (pow 2 (- w 1)))) (if (>= n half) (- n (pow 2 w)) n))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-bitwise-step
|
|
||||||
(fn
|
|
||||||
(op ua ub out place i w)
|
|
||||||
(if
|
|
||||||
(>= i w)
|
|
||||||
out
|
|
||||||
(let
|
|
||||||
((da (mod ua 2)) (db (mod ub 2)))
|
|
||||||
(forth-bitwise-step
|
|
||||||
op
|
|
||||||
(floor (/ ua 2))
|
|
||||||
(floor (/ ub 2))
|
|
||||||
(+ out (* place (op da db)))
|
|
||||||
(* place 2)
|
|
||||||
(+ i 1)
|
|
||||||
w)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-bitwise-uu
|
|
||||||
(fn
|
|
||||||
(op)
|
|
||||||
(fn
|
|
||||||
(a b)
|
|
||||||
(let
|
|
||||||
((ua (forth-to-unsigned a forth-bits-width))
|
|
||||||
(ub (forth-to-unsigned b forth-bits-width)))
|
|
||||||
(forth-from-unsigned
|
|
||||||
(forth-bitwise-step op ua ub 0 1 0 forth-bits-width)
|
|
||||||
forth-bits-width)))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-bit-and
|
|
||||||
(forth-bitwise-uu (fn (x y) (if (and (= x 1) (= y 1)) 1 0))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-bit-or
|
|
||||||
(forth-bitwise-uu (fn (x y) (if (or (= x 1) (= y 1)) 1 0))))
|
|
||||||
|
|
||||||
(define forth-bit-xor (forth-bitwise-uu (fn (x y) (if (= x y) 0 1))))
|
|
||||||
|
|
||||||
(define forth-bit-invert (fn (a) (- 0 (+ a 1))))
|
|
||||||
|
|
||||||
(define
|
|
||||||
forth-install-primitives!
|
|
||||||
(fn
|
|
||||||
(state)
|
|
||||||
(forth-def-prim! state "DUP" (fn (s) (forth-push s (forth-peek s))))
|
|
||||||
(forth-def-prim! state "DROP" (fn (s) (forth-pop s)))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"SWAP"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s b)
|
|
||||||
(forth-push s a))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"OVER"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b)
|
|
||||||
(forth-push s a))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"ROT"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((c (forth-pop s)) (b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s b)
|
|
||||||
(forth-push s c)
|
|
||||||
(forth-push s a))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"-ROT"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((c (forth-pop s)) (b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s c)
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"NIP"
|
|
||||||
(fn (s) (let ((b (forth-pop s))) (forth-pop s) (forth-push s b))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"TUCK"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s b)
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"?DUP"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let ((a (forth-peek s))) (when (not (= a 0)) (forth-push s a)))))
|
|
||||||
(forth-def-prim! state "DEPTH" (fn (s) (forth-push s (forth-depth s))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"PICK"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((n (forth-pop s)) (st (get s "dstack")))
|
|
||||||
(if
|
|
||||||
(or (< n 0) (>= n (len st)))
|
|
||||||
(forth-error s "PICK out of range")
|
|
||||||
(forth-push s (nth st n))))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"ROLL"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((n (forth-pop s)) (st (get s "dstack")))
|
|
||||||
(if
|
|
||||||
(or (< n 0) (>= n (len st)))
|
|
||||||
(forth-error s "ROLL out of range")
|
|
||||||
(let
|
|
||||||
((taken (nth st n))
|
|
||||||
(before (take st n))
|
|
||||||
(after (drop st (+ n 1))))
|
|
||||||
(dict-set! s "dstack" (concat before after))
|
|
||||||
(forth-push s taken))))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"2DUP"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b)
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b))))
|
|
||||||
(forth-def-prim! state "2DROP" (fn (s) (forth-pop s) (forth-pop s)))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"2SWAP"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((d (forth-pop s))
|
|
||||||
(c (forth-pop s))
|
|
||||||
(b (forth-pop s))
|
|
||||||
(a (forth-pop s)))
|
|
||||||
(forth-push s c)
|
|
||||||
(forth-push s d)
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"2OVER"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((d (forth-pop s))
|
|
||||||
(c (forth-pop s))
|
|
||||||
(b (forth-pop s))
|
|
||||||
(a (forth-pop s)))
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b)
|
|
||||||
(forth-push s c)
|
|
||||||
(forth-push s d)
|
|
||||||
(forth-push s a)
|
|
||||||
(forth-push s b))))
|
|
||||||
(forth-def-prim! state "+" (forth-binop (fn (a b) (+ a b))))
|
|
||||||
(forth-def-prim! state "-" (forth-binop (fn (a b) (- a b))))
|
|
||||||
(forth-def-prim! state "*" (forth-binop (fn (a b) (* a b))))
|
|
||||||
(forth-def-prim! state "/" (forth-binop forth-div))
|
|
||||||
(forth-def-prim! state "MOD" (forth-binop forth-mod))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"/MOD"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((b (forth-pop s)) (a (forth-pop s)))
|
|
||||||
(forth-push s (forth-mod a b))
|
|
||||||
(forth-push s (forth-div a b)))))
|
|
||||||
(forth-def-prim! state "NEGATE" (forth-unop (fn (a) (- 0 a))))
|
|
||||||
(forth-def-prim! state "ABS" (forth-unop abs))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"MIN"
|
|
||||||
(forth-binop (fn (a b) (if (< a b) a b))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"MAX"
|
|
||||||
(forth-binop (fn (a b) (if (> a b) a b))))
|
|
||||||
(forth-def-prim! state "1+" (forth-unop (fn (a) (+ a 1))))
|
|
||||||
(forth-def-prim! state "1-" (forth-unop (fn (a) (- a 1))))
|
|
||||||
(forth-def-prim! state "2+" (forth-unop (fn (a) (+ a 2))))
|
|
||||||
(forth-def-prim! state "2-" (forth-unop (fn (a) (- a 2))))
|
|
||||||
(forth-def-prim! state "2*" (forth-unop (fn (a) (* a 2))))
|
|
||||||
(forth-def-prim! state "2/" (forth-unop (fn (a) (floor (/ a 2)))))
|
|
||||||
(forth-def-prim! state "=" (forth-cmp (fn (a b) (= a b))))
|
|
||||||
(forth-def-prim! state "<>" (forth-cmp (fn (a b) (not (= a b)))))
|
|
||||||
(forth-def-prim! state "<" (forth-cmp (fn (a b) (< a b))))
|
|
||||||
(forth-def-prim! state ">" (forth-cmp (fn (a b) (> a b))))
|
|
||||||
(forth-def-prim! state "<=" (forth-cmp (fn (a b) (<= a b))))
|
|
||||||
(forth-def-prim! state ">=" (forth-cmp (fn (a b) (>= a b))))
|
|
||||||
(forth-def-prim! state "0=" (forth-cmp0 (fn (a) (= a 0))))
|
|
||||||
(forth-def-prim! state "0<>" (forth-cmp0 (fn (a) (not (= a 0)))))
|
|
||||||
(forth-def-prim! state "0<" (forth-cmp0 (fn (a) (< a 0))))
|
|
||||||
(forth-def-prim! state "0>" (forth-cmp0 (fn (a) (> a 0))))
|
|
||||||
(forth-def-prim! state "AND" (forth-binop forth-bit-and))
|
|
||||||
(forth-def-prim! state "OR" (forth-binop forth-bit-or))
|
|
||||||
(forth-def-prim! state "XOR" (forth-binop forth-bit-xor))
|
|
||||||
(forth-def-prim! state "INVERT" (forth-unop forth-bit-invert))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"."
|
|
||||||
(fn (s) (forth-emit-str s (str (forth-pop s) " "))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
".S"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((st (reverse (get s "dstack"))))
|
|
||||||
(forth-emit-str s "<")
|
|
||||||
(forth-emit-str s (str (len st)))
|
|
||||||
(forth-emit-str s "> ")
|
|
||||||
(for-each (fn (v) (forth-emit-str s (str v " "))) st))))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"EMIT"
|
|
||||||
(fn (s) (forth-emit-str s (code-char (forth-pop s)))))
|
|
||||||
(forth-def-prim! state "CR" (fn (s) (forth-emit-str s "\n")))
|
|
||||||
(forth-def-prim! state "SPACE" (fn (s) (forth-emit-str s " ")))
|
|
||||||
(forth-def-prim!
|
|
||||||
state
|
|
||||||
"SPACES"
|
|
||||||
(fn
|
|
||||||
(s)
|
|
||||||
(let
|
|
||||||
((n (forth-pop s)))
|
|
||||||
(when
|
|
||||||
(> n 0)
|
|
||||||
(for-each (fn (_) (forth-emit-str s " ")) (range 0 n))))))
|
|
||||||
(forth-def-prim! state "BL" (fn (s) (forth-push s 32)))
|
|
||||||
state))
|
|
||||||
|
|||||||
62
lib/forth/test.sh
Executable file
62
lib/forth/test.sh
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lib/forth/test.sh — smoke-test the Forth runtime layer.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
|
||||||
|
if [ ! -x "$SX_SERVER" ]; then
|
||||||
|
SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$SX_SERVER" ]; then
|
||||||
|
echo "ERROR: sx_server.exe not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT
|
||||||
|
|
||||||
|
cat > "$TMPFILE" << 'EPOCHS'
|
||||||
|
(epoch 1)
|
||||||
|
(load "lib/forth/runtime.sx")
|
||||||
|
(epoch 2)
|
||||||
|
(load "lib/forth/tests/runtime.sx")
|
||||||
|
(epoch 3)
|
||||||
|
(eval "(list forth-test-pass forth-test-fail)")
|
||||||
|
EPOCHS
|
||||||
|
|
||||||
|
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||||
|
|
||||||
|
LINE=$(echo "$OUTPUT" | awk '/^\(ok-len 3 / {getline; print; exit}')
|
||||||
|
if [ -z "$LINE" ]; then
|
||||||
|
LINE=$(echo "$OUTPUT" | grep -E '^\(ok 3 \([0-9]+ [0-9]+\)\)' | tail -1 \
|
||||||
|
| sed -E 's/^\(ok 3 //; s/\)$//')
|
||||||
|
fi
|
||||||
|
if [ -z "$LINE" ]; then
|
||||||
|
echo "ERROR: could not extract summary"
|
||||||
|
echo "$OUTPUT" | tail -20
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
P=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\1/')
|
||||||
|
F=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\2/')
|
||||||
|
TOTAL=$((P + F))
|
||||||
|
|
||||||
|
if [ "$F" -eq 0 ]; then
|
||||||
|
echo "ok $P/$TOTAL lib/forth tests passed"
|
||||||
|
else
|
||||||
|
echo "FAIL $P/$TOTAL passed, $F failed"
|
||||||
|
TMPFILE2=$(mktemp)
|
||||||
|
cat > "$TMPFILE2" << 'EPOCHS2'
|
||||||
|
(epoch 1)
|
||||||
|
(load "lib/forth/runtime.sx")
|
||||||
|
(epoch 2)
|
||||||
|
(load "lib/forth/tests/runtime.sx")
|
||||||
|
(epoch 3)
|
||||||
|
(eval "(map (fn (f) (list (get f :name) (get f :got) (get f :expected))) forth-test-fails)")
|
||||||
|
EPOCHS2
|
||||||
|
FAILS=$(timeout 60 "$SX_SERVER" < "$TMPFILE2" 2>/dev/null | grep -E '^\(ok-len 3' -A1 | tail -1 || true)
|
||||||
|
echo " Details: $FAILS"
|
||||||
|
rm -f "$TMPFILE2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$F" -eq 0 ]
|
||||||
201
lib/forth/tests/runtime.sx
Normal file
201
lib/forth/tests/runtime.sx
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
;; 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))
|
||||||
Reference in New Issue
Block a user