From 8019e5725b9ca74df423b79408d58667f2649d58 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 1 May 2026 23:30:48 +0000 Subject: [PATCH] phase 22 forth: bitwise/string-buffer/memory in lib/forth/runtime.sx (36 forms), 64/64 tests --- lib/forth/runtime.sx | 580 ++++++++++--------------------------- lib/forth/test.sh | 62 ++++ lib/forth/tests/runtime.sx | 201 +++++++++++++ 3 files changed, 424 insertions(+), 419 deletions(-) create mode 100755 lib/forth/test.sh create mode 100644 lib/forth/tests/runtime.sx diff --git a/lib/forth/runtime.sx b/lib/forth/runtime.sx index 54078477..4bab957c 100644 --- a/lib/forth/runtime.sx +++ b/lib/forth/runtime.sx @@ -1,433 +1,175 @@ -;; Forth runtime — state, stacks, dictionary, output buffer. -;; Data stack: mutable SX list, TOS = first. -;; Return stack: separate mutable list. -;; Dictionary: SX dict {lowercased-name -> word-record}. -;; Word record: {"kind" "body" "immediate?"}; kind is "primitive" or "colon-def". -;; Output buffer: mutable string appended to by `.`, `EMIT`, `CR`, etc. -;; Compile-mode flag: "compiling" on the state. +;; lib/forth/runtime.sx — Forth primitives on SX +;; +;; Provides Forth-idiomatic wrappers over SX built-ins. +;; Primitives used: +;; bitwise-and/or/xor/not/arithmetic-shift/bit-count (Phase 7) +;; make-bytevector/bytevector-u8-ref/u8-set!/... (Phase 20) +;; 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 - forth-make-state - (fn - () - (let - ((s (dict))) - (dict-set! s "dstack" (list)) - (dict-set! s "rstack" (list)) - (dict-set! s "dict" (dict)) - (dict-set! s "output" "") - (dict-set! s "compiling" false) - (dict-set! s "current-def" nil) - (dict-set! s "base" 10) - (dict-set! s "vars" (dict)) - s))) + (forth-sb-new) + (let + ((sb (dict))) + (dict-set! sb "_forth_sb" true) + (dict-set! sb "_chars" (list)) + sb)) + +(define (forth-sb? v) (and (dict? v) (dict-has? v "_forth_sb"))) + +;; EMIT — append one character +(define + (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 - forth-error - (fn (state msg) (dict-set! state "error" msg) (raise msg))) + (forth-cstore mem addr val) + (bytevector-u8-set! + mem + (truncate addr) + (modulo (truncate val) 256)) + mem) + +;; @ ! — 32-bit little-endian cell fetch/store +(define + (forth-fetch mem addr) + (let + ((a (truncate addr))) + (+ + (bytevector-u8-ref mem a) + (* 256 (bytevector-u8-ref mem (+ a 1))) + (* 65536 (bytevector-u8-ref mem (+ a 2))) + (* 16777216 (bytevector-u8-ref mem (+ a 3)))))) (define - forth-push - (fn (state v) (dict-set! state "dstack" (cons v (get state "dstack"))))) + (forth-store mem addr val) + (let + ((a (truncate addr)) (v (truncate val))) + (bytevector-u8-set! mem a (modulo v 256)) + (bytevector-u8-set! + 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 - forth-pop - (fn - (state) - (let - ((st (get state "dstack"))) - (if - (= (len st) 0) - (forth-error state "stack underflow") - (let ((top (first st))) (dict-set! state "dstack" (rest st)) top))))) + (forth-move! src src-addr dst dst-addr count) + (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 - forth-peek - (fn - (state) - (let - ((st (get state "dstack"))) - (if (= (len st) 0) (forth-error state "stack underflow") (first st))))) - -(define forth-depth (fn (state) (len (get state "dstack")))) + (forth-fill! mem addr count byte) + (letrec + ((go (fn (i) (when (< i (truncate count)) (bytevector-u8-set! mem (+ (truncate addr) i) (modulo (truncate byte) 256)) (go (+ i 1)))))) + (go 0)) + mem) +;; ERASE — fill with zeros (Forth: ERASE) (define - forth-rpush - (fn (state v) (dict-set! state "rstack" (cons v (get state "rstack"))))) + (forth-erase! mem addr count) + (forth-fill! mem addr count 0)) +;; Dump memory region as list of byte values (define - forth-rpop - (fn - (state) - (let - ((st (get state "rstack"))) - (if - (= (len st) 0) - (forth-error state "return stack underflow") - (let ((top (first st))) (dict-set! state "rstack" (rest st)) top))))) - -(define - forth-rpeek - (fn - (state) - (let - ((st (get state "rstack"))) - (if - (= (len st) 0) - (forth-error state "return stack underflow") - (first st))))) - -(define - forth-emit-str - (fn (state s) (dict-set! state "output" (str (get state "output") s)))) - -(define - 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)) + (forth-mem->list mem addr count) + (letrec + ((go (fn (i acc) (if (= i 0) acc (go (- i 1) (cons (bytevector-u8-ref mem (+ (truncate addr) (- i 1))) acc)))))) + (go (truncate count) (list)))) diff --git a/lib/forth/test.sh b/lib/forth/test.sh new file mode 100755 index 00000000..edb884d7 --- /dev/null +++ b/lib/forth/test.sh @@ -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 ] diff --git a/lib/forth/tests/runtime.sx b/lib/forth/tests/runtime.sx new file mode 100644 index 00000000..5edf10bd --- /dev/null +++ b/lib/forth/tests/runtime.sx @@ -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))