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>
276 lines
6.8 KiB
Plaintext
276 lines
6.8 KiB
Plaintext
;; ==========================================================================
|
|
;; 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))
|