From 6dfef34a4bf2f3099b6744119e9c5488633e5c0e Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 18:53:28 +0000 Subject: [PATCH] lua: math library (abs/trig/log/pow/min/max/fmod/modf/random/...) +17 tests --- lib/lua/runtime.sx | 113 +++++++++++++++++++++++++++++++++++++++++++++ lib/lua/test.sh | 55 ++++++++++++++++++++++ plans/lua-on-sx.md | 3 +- 3 files changed, 170 insertions(+), 1 deletion(-) diff --git a/lib/lua/runtime.sx b/lib/lua/runtime.sx index 2e99ebe6..dddbbfb4 100644 --- a/lib/lua/runtime.sx +++ b/lib/lua/runtime.sx @@ -878,3 +878,116 @@ (dict-set! string "gmatch" lua-string-gmatch) (dict-set! string "gsub" lua-string-gsub) (dict-set! string "format" lua-string-format) + +;; ── math library ────────────────────────────────────────────── +(define math {}) + +(define lua-math-pi 3.141592653589793) +(define lua-math-huge (/ 1.0 0.0)) + +(define lua-math-abs (fn (x) (abs x))) +(define lua-math-ceil (fn (x) (ceil x))) +(define lua-math-floor (fn (x) (floor x))) +(define lua-math-sqrt (fn (x) (sqrt x))) +(define lua-math-exp (fn (x) (exp x))) +(define lua-math-sin (fn (x) (sin x))) +(define lua-math-cos (fn (x) (cos x))) +(define lua-math-tan (fn (x) (tan x))) +(define lua-math-asin (fn (x) (asin x))) +(define lua-math-acos (fn (x) (acos x))) +(define lua-math-atan (fn (x) (atan x))) +(define lua-math-atan2 (fn (y x) (atan2 y x))) +(define lua-math-pow (fn (a b) (pow a b))) + +(define lua-math-log + (fn (&rest args) + (cond + ((= (len args) 1) (log (first args))) + (else (/ (log (first args)) (log (nth args 1))))))) + +(define lua-math-log10 + (fn (x) (/ (log x) (log 10)))) + +(define lua-math-deg (fn (x) (* x (/ 180 lua-math-pi)))) +(define lua-math-rad (fn (x) (* x (/ lua-math-pi 180)))) + +(define lua-math-min + (fn (&rest args) + (cond + ((= (len args) 0) (error "lua: min: no values")) + ((= (len args) 1) (first args)) + (else + (let ((m (first args))) + (begin + (define + loop + (fn (i) + (when (< i (len args)) + (begin + (when (< (nth args i) m) (set! m (nth args i))) + (loop (+ i 1)))))) + (loop 1) + m)))))) + +(define lua-math-max + (fn (&rest args) + (cond + ((= (len args) 0) (error "lua: max: no values")) + ((= (len args) 1) (first args)) + (else + (let ((m (first args))) + (begin + (define + loop + (fn (i) + (when (< i (len args)) + (begin + (when (> (nth args i) m) (set! m (nth args i))) + (loop (+ i 1)))))) + (loop 1) + m)))))) + +(define lua-math-fmod + (fn (a b) (- a (* b (if (> b 0) (floor (/ a b)) (ceil (/ a b))))))) + +(define lua-math-modf + (fn (x) + (let ((i (if (>= x 0) (floor x) (ceil x)))) + (list (quote lua-multi) i (- x i))))) + +(define __rand-scale 1048576) +(define lua-math-random + (fn (&rest args) + (cond + ((= (len args) 0) + (/ (random-int 0 (- __rand-scale 1)) (* 1.0 __rand-scale))) + ((= (len args) 1) (random-int 1 (first args))) + (else (random-int (first args) (nth args 1)))))) + +(define lua-math-randomseed (fn (s) nil)) + +(dict-set! math "pi" lua-math-pi) +(dict-set! math "huge" lua-math-huge) +(dict-set! math "abs" lua-math-abs) +(dict-set! math "ceil" lua-math-ceil) +(dict-set! math "floor" lua-math-floor) +(dict-set! math "sqrt" lua-math-sqrt) +(dict-set! math "exp" lua-math-exp) +(dict-set! math "log" lua-math-log) +(dict-set! math "log10" lua-math-log10) +(dict-set! math "pow" lua-math-pow) +(dict-set! math "sin" lua-math-sin) +(dict-set! math "cos" lua-math-cos) +(dict-set! math "tan" lua-math-tan) +(dict-set! math "asin" lua-math-asin) +(dict-set! math "acos" lua-math-acos) +(dict-set! math "atan" lua-math-atan) +(dict-set! math "atan2" lua-math-atan2) +(dict-set! math "deg" lua-math-deg) +(dict-set! math "rad" lua-math-rad) +(dict-set! math "min" lua-math-min) +(dict-set! math "max" lua-math-max) +(dict-set! math "fmod" lua-math-fmod) +(dict-set! math "modf" lua-math-modf) +(dict-set! math "random" lua-math-random) +(dict-set! math "randomseed" lua-math-randomseed) diff --git a/lib/lua/test.sh b/lib/lua/test.sh index dafd2e78..1cccca81 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -768,6 +768,42 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 1181) (eval "(lua-eval-ast \"return string.format(\\\"%d%%\\\", 50)\")") +;; ── Phase 6: math library ───────────────────────────────────── +(epoch 1200) +(eval "(lua-eval-ast \"return math.pi > 3.14 and math.pi < 3.15\")") +(epoch 1201) +(eval "(lua-eval-ast \"return math.huge > 1000000\")") +(epoch 1210) +(eval "(lua-eval-ast \"return math.abs(-7)\")") +(epoch 1211) +(eval "(lua-eval-ast \"return math.sqrt(16)\")") +(epoch 1212) +(eval "(lua-eval-ast \"return math.floor(3.7)\")") +(epoch 1213) +(eval "(lua-eval-ast \"return math.ceil(3.2)\")") +(epoch 1220) +(eval "(lua-eval-ast \"return math.max(3, 7, 1, 4)\")") +(epoch 1221) +(eval "(lua-eval-ast \"return math.min(3, 7, 1, 4)\")") +(epoch 1230) +(eval "(lua-eval-ast \"return math.pow(2, 8)\")") +(epoch 1231) +(eval "(lua-eval-ast \"return math.exp(0)\")") +(epoch 1232) +(eval "(lua-eval-ast \"return math.log(1)\")") +(epoch 1233) +(eval "(lua-eval-ast \"return math.log10(100)\")") +(epoch 1240) +(eval "(lua-eval-ast \"return math.sin(0) + math.cos(0)\")") +(epoch 1250) +(eval "(lua-eval-ast \"return math.fmod(10, 3)\")") +(epoch 1251) +(eval "(lua-eval-ast \"local i, f = math.modf(3.5) return i\")") +(epoch 1260) +(eval "(lua-eval-ast \"local r = math.random(100) return r >= 1 and r <= 100\")") +(epoch 1261) +(eval "(lua-eval-ast \"local r = math.random(5, 10) return r >= 5 and r <= 10\")") + EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1151,6 +1187,25 @@ check 1170 "string.gmatch iterator" '3' check 1180 "string.format %s=%d" '"x=42"' check 1181 "string.format %d%%" '"50%"' +# ── Phase 6: math library ───────────────────────────────────── +check 1200 "math.pi in range" 'true' +check 1201 "math.huge big" 'true' +check 1210 "math.abs(-7)" '7' +check 1211 "math.sqrt(16)" '4' +check 1212 "math.floor(3.7)" '3' +check 1213 "math.ceil(3.2)" '4' +check 1220 "math.max(3,7,1,4)" '7' +check 1221 "math.min(3,7,1,4)" '1' +check 1230 "math.pow(2,8)" '256' +check 1231 "math.exp(0)" '1' +check 1232 "math.log(1)" '0' +check 1233 "math.log10(100)" '2' +check 1240 "math.sin(0)+math.cos(0)" '1' +check 1250 "math.fmod(10,3)" '1' +check 1251 "math.modf(3.5) int part" '3' +check 1260 "math.random(n) in range" 'true' +check 1261 "math.random(m,n) in range" 'true' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL Lua-on-SX tests passed" diff --git a/plans/lua-on-sx.md b/plans/lua-on-sx.md index d9dbb099..4e8f7b5f 100644 --- a/plans/lua-on-sx.md +++ b/plans/lua-on-sx.md @@ -69,7 +69,7 @@ Each item: implement → tests → tick box → update progress log. ### Phase 6 — standard library - [x] `string` — `format`, `sub`, `find`, `match`, `gmatch`, `gsub`, `len`, `rep`, `upper`, `lower`, `byte`, `char` -- [ ] `math` — full surface +- [x] `math` — full surface - [ ] `table` — `insert`, `remove`, `concat`, `sort`, `unpack` - [ ] `io` — minimal stub (read/write to SX IO surface) - [ ] `os` — time/date subset @@ -82,6 +82,7 @@ Each item: implement → tests → tick box → update progress log. _Newest first. Agent appends on every commit._ +- 2026-04-24: lua: `math` lib — pi/huge + abs/ceil/floor/sqrt/exp/log/log10/pow/trig (sin/cos/tan/asin/acos/atan/atan2)/deg/rad/min/max (&rest)/fmod/modf/random (0/1/2 arg)/randomseed. Most ops delegate to SX primitives; log w/ base via change-of-base. 309 tests. - 2026-04-24: lua: `string` lib — len/upper/lower/rep/sub (1-idx + neg)/byte/char/find/match/gmatch/gsub/format. Patterns are literal-only (no `%d`/etc.); format is `%s`/`%d`/`%f`/`%%` only. `string.char` uses printable-ASCII lookup + tab/nl/cr. 292 tests. - 2026-04-24: lua: phase 5 — coroutines (create/resume/yield/status/wrap) via `call/cc` (perform/cek-resume not exposed to SX userland). Handles multi-yield + final return + arg passthrough. Fix: body's final return must jump via `caller-k` to the **current** resume's caller, not unwind through the stale first-call continuation. 273 tests. - 2026-04-24: lua: generic `for … in …` — parser split (`=` → num, else `in`), new `lua-for-in` node, transpile to `let`-bound `f,s,var` + recursive `__for_loop`. Added `ipairs`/`pairs`/`next`/`lua-arg` globals. Lua fns now arity-tolerant (`&rest __args` + indexed bind) — needed because generic for always calls iter with 2 args. Noted early-return-in-nested-block as pre-existing limitation. 265 tests.