Move stdlib out of spec — clean spec/library boundary
spec/ now contains only the language definition (5 files): evaluator.sx, parser.sx, primitives.sx, render.sx, special-forms.sx lib/ contains code written IN the language (8 files): stdlib.sx, types.sx, freeze.sx, content.sx, bytecode.sx, compiler.sx, vm.sx, callcc.sx Test files follow source: spec/tests/ for core language tests, lib/tests/ for library tests (continuations, freeze, types, vm). Updated all consumers: - JS/Python/OCaml bootstrappers: added lib/ to source search paths - OCaml bridge: spec_dir for parser/render, lib_dir for compiler/freeze - JS test runner: scans spec/tests/ (always) + lib/tests/ (--full) - OCaml test runner: scans spec/tests/, lib tests via explicit request - Docker dev mounts: added ./lib:/app/lib:ro Tests: 1041 JS standard, 1322 JS full, 1101 OCaml — all pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
275
lib/stdlib.sx
Normal file
275
lib/stdlib.sx
Normal file
@@ -0,0 +1,275 @@
|
||||
;; ==========================================================================
|
||||
;; stdlib.sx — Standard library functions
|
||||
;;
|
||||
;; Every function here is expressed in SX using the irreducible primitive
|
||||
;; set. They are library functions — in band, auditable, portable.
|
||||
;;
|
||||
;; Depends on: evaluator.sx (special forms)
|
||||
;; Must load before: render.sx, freeze.sx, types.sx, user code
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; Logic + comparison: not, !=, <=, >= stay as primitives.
|
||||
;; Replacing them with SX lambdas changes behavior inside shift/reset
|
||||
;; because the transpiled evaluator code uses them directly.
|
||||
|
||||
(define eq? (fn (a b) (= a b)))
|
||||
(define eqv? (fn (a b) (= a b)))
|
||||
(define equal? (fn (a b) (= a b)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Type predicates
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
;; nil? stays as primitive — host's type-of uses it internally.
|
||||
|
||||
(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")))
|
||||
|
||||
(define zero?
|
||||
(fn (n) (= n 0)))
|
||||
|
||||
(define odd?
|
||||
(fn (n) (= (mod n 2) 1)))
|
||||
|
||||
(define even?
|
||||
(fn (n) (= (mod n 2) 0)))
|
||||
|
||||
(define empty?
|
||||
(fn (coll) (or (nil? coll) (= (len coll) 0))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Arithmetic
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
;; inc and dec stay as primitives — used inside continuation contexts.
|
||||
|
||||
(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 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)
|
||||
(reduce (fn (acc x) (cons x acc)) (list) coll)))
|
||||
|
||||
(define flatten
|
||||
(fn (coll)
|
||||
(reduce
|
||||
(fn (acc x)
|
||||
(if (list? x) (concat acc x) (concat acc (list x))))
|
||||
(list) coll)))
|
||||
|
||||
(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)
|
||||
(map (fn (k) (get d k)) (keys d))))
|
||||
|
||||
(define has-key?
|
||||
(fn (d key)
|
||||
(some (fn (k) (= k key)) (keys d))))
|
||||
|
||||
(define assoc
|
||||
(fn (d key val)
|
||||
(let ((result (merge d (dict))))
|
||||
(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 (fn (s) (upper s)))
|
||||
(define downcase (fn (s) (lower s)))
|
||||
(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)))))
|
||||
|
||||
;; split, join, replace stay as primitives — the stdlib versions cause
|
||||
;; stack overflows due to PRIMITIVES entry shadowing in the transpiled output.
|
||||
|
||||
(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)
|
||||
(let ((r (str s)))
|
||||
(set! r (replace r "&" "&"))
|
||||
(set! r (replace r "<" "<"))
|
||||
(set! r (replace r ">" ">"))
|
||||
(set! r (replace r "\"" """))
|
||||
(set! r (replace r "'" "'"))
|
||||
r)))
|
||||
|
||||
(define parse-datetime
|
||||
(fn (s) (if s (str s) nil)))
|
||||
|
||||
(define assert
|
||||
(fn (condition message)
|
||||
(when (not condition)
|
||||
(error (or message "Assertion failed")))
|
||||
true))
|
||||
Reference in New Issue
Block a user