phase-22 Smalltalk: runtime.sx numeric/char/Array/Dict/Set/Stream
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s

lib/smalltalk/runtime.sx (72 forms):
- Numeric helpers: abs/max/min/gcd/lcm/quo/rem/mod/even?/odd?/floor/ceil/truncate/round.
- Character: st-char-value/from-int/is-letter?/is-digit?/uppercase?/lowercase?/
  separator?/as-uppercase/as-lowercase/digit-value. SX chars via char->integer.
- Array: 1-indexed mutable arrays backed by dict {__st_array__ size "1" v1 ...};
  at/at-put!/do/->list/list->array/copy-from-to.
- Dictionary: any-key hash map via list-of-pairs + linear scan;
  at/at-put!/includes-key?/at-default/remove-key!/keys/values/do/do-associations.
- Set: backed by SX make-set; set-member?/add!/includes?/remove! take (set item) order.
- WriteStream/ReadStream: dict-backed buffers; printString for nil/bool/number/
  string/symbol/char/list/array.

lib/smalltalk/tests/runtime.sx + lib/smalltalk/test.sh: 86/86 pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-01 22:43:04 +00:00
parent 36e6762539
commit 077f4a5d38
4 changed files with 685 additions and 1 deletions

370
lib/smalltalk/runtime.sx Normal file
View File

@@ -0,0 +1,370 @@
;; lib/smalltalk/runtime.sx — Smalltalk primitives on SX
;;
;; Provides Smalltalk-idiomatic wrappers over SX built-ins.
;; Primitives used:
;; make-set/set-add!/set-member?/set-remove!/set->list (Phase 18)
;; char->integer/integer->char/list->string (Phase 5)
;; bitwise-and/or/xor/not/arithmetic-shift (Phase 7)
;; gcd/lcm/quotient/remainder/modulo (Phase 15)
;; ---------------------------------------------------------------------------
;; 0. Internal list helpers (used by Array and Dictionary)
;; ---------------------------------------------------------------------------
(define
(st-list-set-nth lst i newval)
(letrec
((go (fn (ps j) (if (= (len ps) 0) (list) (cons (if (= j i) newval (first ps)) (go (rest ps) (+ j 1)))))))
(go lst 0)))
(define
(st-list-remove-nth lst i)
(letrec
((go (fn (ps j) (if (= (len ps) 0) (list) (if (= j i) (go (rest ps) (+ j 1)) (cons (first ps) (go (rest ps) (+ j 1))))))))
(go lst 0)))
;; ---------------------------------------------------------------------------
;; 1. Numeric helpers
;; Thin wrappers or direct aliases for Smalltalk Number protocol.
;; ---------------------------------------------------------------------------
(define (st-abs x) (abs x))
(define (st-max a b) (if (> a b) a b))
(define (st-min a b) (if (< a b) a b))
(define (st-gcd a b) (gcd a b))
(define (st-lcm a b) (lcm a b))
(define (st-quo a b) (quotient a b))
(define (st-rem a b) (remainder a b))
(define (st-mod a b) (modulo a b))
(define (st-even? n) (= (remainder n 2) 0))
(define (st-odd? n) (not (st-even? n)))
(define (st-sqrt x) (sqrt x))
(define (st-floor x) (floor x))
(define (st-ceiling x) (ceil x))
(define (st-truncated x) (truncate x))
(define (st-rounded x) (round x))
;; ---------------------------------------------------------------------------
;; 2. Character
;; Smalltalk $A = char 65. Operations mirror Character class.
;; ---------------------------------------------------------------------------
(define (st-char-value c) (char->integer c))
(define (st-char-from-int n) (integer->char n))
(define (st-char? v) (= (type-of v) "char"))
(define
(st-char-is-letter? c)
(let
((n (char->integer c)))
(or
(and (>= n 65) (<= n 90))
(and (>= n 97) (<= n 122)))))
(define
(st-char-is-digit? c)
(let ((n (char->integer c))) (and (>= n 48) (<= n 57))))
(define
(st-char-is-uppercase? c)
(let ((n (char->integer c))) (and (>= n 65) (<= n 90))))
(define
(st-char-is-lowercase? c)
(let ((n (char->integer c))) (and (>= n 97) (<= n 122))))
(define
(st-char-is-separator? c)
(let
((n (char->integer c)))
(or
(= n 32)
(= n 9)
(= n 10)
(= n 13))))
(define
(st-char-as-uppercase c)
(let
((n (char->integer c)))
(if
(and (>= n 97) (<= n 122))
(integer->char (- n 32))
c)))
(define
(st-char-as-lowercase c)
(let
((n (char->integer c)))
(if
(and (>= n 65) (<= n 90))
(integer->char (+ n 32))
c)))
(define (st-char-digit-value c) (- (char->integer c) 48))
;; ---------------------------------------------------------------------------
;; 3. Array (1-indexed, mutable, fixed-size)
;; Backed as {:__st_array__ true :size N "1" v1 "2" v2 ...}
;; Unset elements read as nil.
;; ---------------------------------------------------------------------------
(define
(st-array-new n)
(let
((a (dict)))
(dict-set! a "__st_array__" true)
(dict-set! a "size" n)
a))
(define (st-array? v) (and (dict? v) (dict-has? v "__st_array__")))
(define (st-array-size a) (get a "size"))
(define
(st-array-at a i)
(let ((v (get a (str i)))) (if (= v nil) nil v)))
(define (st-array-at-put! a i v) (dict-set! a (str i) v) a)
(define
(st-array-do a fn)
(letrec
((go (fn (i) (when (<= i (st-array-size a)) (fn (st-array-at a i)) (go (+ i 1))))))
(go 1)))
(define
(st-array->list a)
(letrec
((go (fn (i acc) (if (< i 1) acc (go (- i 1) (cons (st-array-at a i) acc))))))
(go (st-array-size a) (list))))
(define
(st-list->array xs)
(let
((a (st-array-new (len xs))))
(letrec
((go (fn (ys i) (when (> (len ys) 0) (st-array-at-put! a i (first ys)) (go (rest ys) (+ i 1))))))
(go xs 1))
a))
(define
(st-array-copy-from-to a start stop)
(let
((result (st-array-new (- stop start -1))))
(letrec
((go (fn (i j) (when (<= i stop) (st-array-at-put! result j (st-array-at a i)) (go (+ i 1) (+ j 1))))))
(go start 1))
result))
;; ---------------------------------------------------------------------------
;; 4. Dictionary (hash map with any key via linear scan)
;; {:__st_dict__ true :size N :_pairs ((key val) ...)}
;; ---------------------------------------------------------------------------
(define
(st-dict-new)
(let
((d (dict)))
(dict-set! d "__st_dict__" true)
(dict-set! d "size" 0)
(dict-set! d "_pairs" (list))
d))
(define (st-dict? v) (and (dict? v) (dict-has? v "__st_dict__")))
(define (st-dict-size d) (get d "size"))
(define
(st-dict-find-idx pairs k)
(letrec
((go (fn (ps i) (cond ((= (len ps) 0) -1) ((= (first (first ps)) k) i) (else (go (rest ps) (+ i 1)))))))
(go pairs 0)))
(define
(st-dict-at d k)
(letrec
((go (fn (ps) (if (= (len ps) 0) nil (if (= (first (first ps)) k) (nth (first ps) 1) (go (rest ps)))))))
(go (get d "_pairs"))))
(define
(st-dict-at-put! d k v)
(let
((pairs (get d "_pairs")) (idx (st-dict-find-idx (get d "_pairs") k)))
(if
(= idx -1)
(begin
(dict-set! d "_pairs" (append pairs (list (list k v))))
(dict-set! d "size" (+ (get d "size") 1)))
(dict-set! d "_pairs" (st-list-set-nth pairs idx (list k v)))))
d)
(define
(st-dict-includes-key? d k)
(not (= (st-dict-find-idx (get d "_pairs") k) -1)))
(define
(st-dict-at-default d k def)
(if (st-dict-includes-key? d k) (st-dict-at d k) def))
(define
(st-dict-remove-key! d k)
(let
((idx (st-dict-find-idx (get d "_pairs") k)))
(when
(not (= idx -1))
(dict-set! d "_pairs" (st-list-remove-nth (get d "_pairs") idx))
(dict-set! d "size" (- (get d "size") 1))))
d)
(define (st-dict-keys d) (map first (get d "_pairs")))
(define
(st-dict-values d)
(map (fn (p) (nth p 1)) (get d "_pairs")))
(define
(st-dict-do d fn)
(for-each (fn (p) (fn (nth p 1))) (get d "_pairs")))
(define
(st-dict-do-associations d fn)
(for-each (fn (p) (fn (first p) (nth p 1))) (get d "_pairs")))
;; ---------------------------------------------------------------------------
;; 5. Set (uniqueness via SX make-set)
;; Note: set-member?/set-add!/set-remove! take (set item) order.
;; ---------------------------------------------------------------------------
(define
(st-set-new)
(let
((s (dict)))
(dict-set! s "__st_set__" true)
(dict-set! s "size" 0)
(dict-set! s "_set" (make-set))
s))
(define (st-set? v) (and (dict? v) (dict-has? v "__st_set__")))
(define (st-set-size s) (get s "size"))
(define
(st-set-add! s v)
(let
((sx (get s "_set")))
(when
(not (set-member? sx v))
(set-add! sx v)
(dict-set! s "size" (+ (get s "size") 1))))
s)
(define (st-set-includes? s v) (set-member? (get s "_set") v))
(define
(st-set-remove! s v)
(let
((sx (get s "_set")))
(when
(set-member? sx v)
(set-remove! sx v)
(dict-set! s "size" (- (get s "size") 1))))
s)
(define (st-set->list s) (set->list (get s "_set")))
(define (st-set-do s fn) (for-each fn (st-set->list s)))
;; ---------------------------------------------------------------------------
;; 6. String / Stream utilities
;; ---------------------------------------------------------------------------
;; Join list of strings with separator
(define
(st-join-strings strs sep)
(if
(= (len strs) 0)
""
(letrec
((go (fn (ss acc) (if (= (len ss) 0) acc (go (rest ss) (str acc sep (first ss)))))))
(go (rest strs) (first strs)))))
;; printString — Smalltalk textual representation
(define
(st-print-string v)
(cond
((= v nil) "nil")
((= v true) "true")
((= v false) "false")
((= (type-of v) "number") (str v))
((= (type-of v) "string") (str "'" v "'"))
((= (type-of v) "symbol") (str "#" (str v)))
((= (type-of v) "char") (str "$" (list->string (list v))))
((= (type-of v) "list")
(str "(" (st-join-strings (map st-print-string v) " ") ")"))
((st-array? v)
(str
"(#("
(st-join-strings (map st-print-string (st-array->list v)) " ")
"))"))
(else (str v))))
;; WriteStream — accumulates strings/chars to a buffer
(define
(st-write-stream-new)
(let
((ws (dict)))
(dict-set! ws "__st_ws__" true)
(dict-set! ws "contents" "")
ws))
(define (st-write-stream? v) (and (dict? v) (dict-has? v "__st_ws__")))
(define
(st-write-stream-put-string! ws s)
(dict-set! ws "contents" (str (get ws "contents") s))
ws)
(define
(st-write-stream-next-put! ws c)
(st-write-stream-put-string! ws (list->string (list c))))
(define
(st-write-stream-print! ws v)
(st-write-stream-put-string! ws (st-print-string v)))
(define (st-write-stream-contents ws) (get ws "contents"))
;; ReadStream — reads characters from a string one at a time
(define
(st-read-stream-new s)
(let
((rs (dict)))
(dict-set! rs "__st_rs__" true)
(dict-set! rs "_chars" (string->list s))
(dict-set! rs "pos" 0)
rs))
(define (st-read-stream? v) (and (dict? v) (dict-has? v "__st_rs__")))
(define
(st-read-stream-at-end? rs)
(>= (get rs "pos") (len (get rs "_chars"))))
(define
(st-read-stream-next rs)
(if
(st-read-stream-at-end? rs)
nil
(let
((c (nth (get rs "_chars") (get rs "pos"))))
(dict-set! rs "pos" (+ (get rs "pos") 1))
c)))
(define
(st-read-stream-peek rs)
(if
(st-read-stream-at-end? rs)
nil
(nth (get rs "_chars") (get rs "pos"))))
(define (st-read-stream-source rs) (list->string (get rs "_chars")))