Files
rose-ash/spec/stdlib.sx
giles 4ce4762237 Add spec/stdlib.sx: 46 primitives become library functions
The irreducible primitive set drops from 79 to 33. Everything that can
be expressed in SX is now a library function in stdlib.sx, loaded after
evaluator.sx and before render.sx.

Moved to stdlib.sx (pure SX, no host dependency):
- Logic: not
- Comparison: != <= >= eq? eqv? equal?
- Predicates: nil? boolean? number? string? list? dict? continuation?
  empty? odd? even? zero? contains?
- Arithmetic: inc dec abs ceil round min max clamp
- Collections: first last rest nth cons append reverse flatten range
  chunk-every zip-pairs vals has-key? merge assoc dissoc into
- Strings: upcase downcase string-length substring string-contains?
  starts-with? ends-with? split join replace
- Text: pluralize escape assert parse-datetime

Remaining irreducible primitives (33):
  + - * / mod floor pow sqrt = < > type-of symbol-name keyword-name
  str slice index-of upper lower trim char-from-code list dict concat
  get len keys dict-set! append! random-int json-encode format-date
  parse-int format-decimal strip-tags sx-parse error apply

All hosts: JS 957+1080, Python 744, OCaml 952 — zero regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 08:55:57 +00:00

368 lines
8.9 KiB
Plaintext

;; ==========================================================================
;; stdlib.sx — Standard library functions
;;
;; Every function here CAN be expressed in SX using the irreducible
;; primitive set. They are library functions, not primitives.
;;
;; These were previously platform-provided primitives. Moving them to
;; SX tightens the strange loop — less out-of-band, more auditable,
;; portable, and verifiable.
;;
;; Depends on: evaluator.sx (special forms)
;; Must load before: render.sx, freeze.sx, types.sx, user code
;;
;; Irreducible primitives (the ones that CANNOT be written in SX):
;; Arithmetic: + - * / mod floor pow sqrt
;; Comparison: = < >
;; Types: type-of symbol-name keyword-name
;; Strings: str slice index-of upper lower trim char-from-code
;; Collections: list dict concat get len keys dict-set! append!
;; I/O & host: random-int json-encode format-date parse-int
;; format-decimal strip-tags sx-parse error apply
;; ==========================================================================
;; --------------------------------------------------------------------------
;; Logic
;; --------------------------------------------------------------------------
(define not
(fn (x) (if x false true)))
;; --------------------------------------------------------------------------
;; Comparison
;; --------------------------------------------------------------------------
(define !=
(fn (a b) (not (= a b))))
(define <=
(fn (a b) (or (< a b) (= a b))))
(define >=
(fn (a b) (or (> a b) (= a b))))
;; Aliases — SX uses structural equality for all three
(define eq? =)
(define eqv? =)
(define equal? =)
;; --------------------------------------------------------------------------
;; Type predicates
;; --------------------------------------------------------------------------
(define nil?
(fn (x) (= (type-of x) "nil")))
(define boolean?
(fn (x) (= (type-of x) "boolean")))
(define number?
(fn (x) (= (type-of x) "number")))
(define string?
(fn (x) (= (type-of x) "string")))
(define list?
(fn (x) (= (type-of x) "list")))
(define dict?
(fn (x) (= (type-of x) "dict")))
(define continuation?
(fn (x) (= (type-of x) "continuation")))
;; --------------------------------------------------------------------------
;; Numeric predicates
;; --------------------------------------------------------------------------
(define zero?
(fn (n) (= n 0)))
(define odd?
(fn (n) (= (mod n 2) 1)))
(define even?
(fn (n) (= (mod n 2) 0)))
;; --------------------------------------------------------------------------
;; Arithmetic
;; --------------------------------------------------------------------------
(define inc
(fn (n) (+ n 1)))
(define dec
(fn (n) (- n 1)))
(define abs
(fn (x) (if (< x 0) (- x) x)))
(define ceil
(fn (x)
(let ((f (floor x)))
(if (= x f) f (+ f 1)))))
(define round
(fn (x ndigits)
(if (nil? ndigits)
(floor (+ x 0.5))
(let ((f (pow 10 ndigits)))
(/ (floor (+ (* x f) 0.5)) f)))))
(define min
(fn (a b) (if (< a b) a b)))
(define max
(fn (a b) (if (> a b) a b)))
(define clamp
(fn (x lo hi) (max lo (min hi x))))
;; --------------------------------------------------------------------------
;; Collection accessors
;; --------------------------------------------------------------------------
(define first
(fn (coll)
(if (and coll (> (len coll) 0)) (get coll 0) nil)))
(define last
(fn (coll)
(if (and coll (> (len coll) 0))
(get coll (- (len coll) 1))
nil)))
(define rest
(fn (coll) (if coll (slice coll 1) (list))))
(define nth
(fn (coll n)
(if (and coll (>= n 0) (< n (len coll)))
(get coll n)
nil)))
(define empty?
(fn (coll) (or (nil? coll) (= (len coll) 0))))
(define cons
(fn (x coll) (concat (list x) (or coll (list)))))
(define append
(fn (coll x)
(if (list? x) (concat coll x) (concat coll (list x)))))
;; --------------------------------------------------------------------------
;; Collection transforms
;; --------------------------------------------------------------------------
(define reverse
(fn (coll)
(let ((result (list))
(i (- (len coll) 1)))
(let loop ((i i))
(when (>= i 0)
(append! result (get coll i))
(loop (- i 1))))
result)))
(define flatten
(fn (coll)
(let ((result (list)))
(for-each
(fn (x)
(if (list? x)
(for-each (fn (y) (append! result y)) x)
(append! result x)))
coll)
result)))
(define range
(fn (start end step)
(let ((s (if (nil? step) 1 step))
(result (list)))
(let loop ((i start))
(when (< i end)
(append! result i)
(loop (+ i s))))
result)))
(define chunk-every
(fn (coll n)
(let ((result (list))
(clen (len coll)))
(let loop ((i 0))
(when (< i clen)
(append! result (slice coll i (min (+ i n) clen)))
(loop (+ i n))))
result)))
(define zip-pairs
(fn (coll)
(let ((result (list))
(clen (len coll)))
(let loop ((i 0))
(when (< i (- clen 1))
(append! result (list (get coll i) (get coll (+ i 1))))
(loop (+ i 1))))
result)))
;; --------------------------------------------------------------------------
;; Dict operations
;; --------------------------------------------------------------------------
(define vals
(fn (d)
(let ((result (list)))
(for-each (fn (k) (append! result (get d k))) (keys d))
result)))
(define has-key?
(fn (d key)
(some (fn (k) (= k key)) (keys d))))
(define merge
(fn (a b)
(let ((result (dict)))
(when a
(for-each (fn (k) (dict-set! result k (get a k))) (keys a)))
(when b
(for-each (fn (k) (dict-set! result k (get b k))) (keys b)))
result)))
(define assoc
(fn (d key val)
(let ((result (dict)))
(when d
(for-each (fn (k) (dict-set! result k (get d k))) (keys d)))
(dict-set! result key val)
result)))
(define dissoc
(fn (d key)
(let ((result (dict)))
(for-each
(fn (k)
(when (!= k key)
(dict-set! result k (get d k))))
(keys d))
result)))
(define into
(fn (target coll)
(cond
(list? target)
(if (list? coll)
(concat coll (list))
(let ((result (list)))
(for-each (fn (k) (append! result (list k (get coll k)))) (keys coll))
result))
(dict? target)
(let ((result (dict)))
(for-each
(fn (pair)
(when (and (list? pair) (>= (len pair) 2))
(dict-set! result (get pair 0) (get pair 1))))
coll)
result)
:else target)))
;; --------------------------------------------------------------------------
;; String operations
;; --------------------------------------------------------------------------
(define upcase upper)
(define downcase lower)
(define string-length
(fn (s) (len s)))
(define substring
(fn (s start end) (slice s start end)))
(define string-contains?
(fn (s needle) (!= (index-of s needle) -1)))
(define starts-with?
(fn (s prefix) (= (index-of s prefix) 0)))
(define ends-with?
(fn (s suffix)
(let ((slen (len s))
(plen (len suffix)))
(if (< slen plen) false
(= (slice s (- slen plen)) suffix)))))
(define split
(fn (s sep)
(let ((separator (if (nil? sep) " " sep))
(result (list))
(slen (len s))
(seplen (len separator)))
(let loop ((start 0))
(let ((idx (index-of s separator start)))
(if (= idx -1)
(do (append! result (slice s start)) result)
(do (append! result (slice s start idx))
(loop (+ idx seplen)))))))))
(define join
(fn (sep coll)
(let ((result ""))
(for-each
(fn (x)
(set! result (if (= result "")
(str x)
(str result sep x))))
coll)
result)))
(define replace
(fn (s old new)
(join new (split s old))))
(define contains?
(fn (coll key)
(cond
(string? coll) (!= (index-of coll (str key)) -1)
(dict? coll) (has-key? coll key)
(list? coll) (some (fn (x) (= x key)) coll)
:else false)))
;; --------------------------------------------------------------------------
;; Text utilities
;; --------------------------------------------------------------------------
(define pluralize
(fn (count singular plural)
(if (= count 1)
(or singular "")
(or plural "s"))))
(define escape
(fn (s)
(-> (str s)
(replace "&" "&amp;")
(replace "<" "&lt;")
(replace ">" "&gt;")
(replace "\"" "&quot;")
(replace "'" "&#x27;"))))
(define parse-datetime
(fn (s) (if s (str s) nil)))
(define assert
(fn (condition message)
(when (not condition)
(error (or message "Assertion failed")))
true))