diff --git a/lib/lua/runtime.sx b/lib/lua/runtime.sx index 71b37373..82cf1ace 100644 --- a/lib/lua/runtime.sx +++ b/lib/lua/runtime.sx @@ -123,7 +123,7 @@ (fn (i) (if - (has? a (str i)) + (not (= (get a (str i)) nil)) (begin (set! n i) (count-loop (+ i 1))) n))) (count-loop 1)))) @@ -152,7 +152,9 @@ (cond ((= (first f) "pos") (begin - (set! t (assoc t (str array-idx) (nth f 1))) + (set! + t + (assoc t (str array-idx) (nth f 1))) (set! array-idx (+ array-idx 1)))) ((= (first f) "kv") (let @@ -169,3 +171,108 @@ (if (= t nil) nil (let ((v (get t (str k)))) (if (= v nil) nil v))))) (define lua-set! (fn (t k v) (assoc t (str k) v))) + +;; --------------------------------------------------------------------------- +;; Helpers for stdlib +;; --------------------------------------------------------------------------- + +;; Apply a char function to every character in a string +(define (lua-str-map s fn) (list->string (map fn (string->list s)))) + +;; Repeat string s n times +(define + (lua-str-rep s n) + (letrec + ((go (fn (acc i) (if (= i 0) acc (go (str acc s) (- i 1)))))) + (go "" n))) + +;; Force a promise created by delay +(define + (lua-force p) + (if + (and (dict? p) (get p :_promise)) + (if (get p :forced) (get p :value) ((get p :thunk))) + p)) + +;; --------------------------------------------------------------------------- +;; math — Lua math library +;; --------------------------------------------------------------------------- + +(define math {:asin asin :floor floor :exp exp :huge 1e+308 :tan tan :sqrt sqrt :log log :abs abs :ceil ceil :sin sin :max (fn (a b) (if (> a b) a b)) :acos acos :min (fn (a b) (if (< a b) a b)) :cos cos :pi 3.14159 :atan atan}) + +;; --------------------------------------------------------------------------- +;; string — Lua string library +;; --------------------------------------------------------------------------- + +(define + (lua-string-find s pat) + (let + ((m (regexp-match (make-regexp pat) s))) + (if (= m nil) nil (list (+ (get m :start) 1) (get m :end))))) + +(define + (lua-string-match s pat) + (let + ((m (regexp-match (make-regexp pat) s))) + (if + (= m nil) + nil + (let + ((groups (get m :groups))) + (if (= (len groups) 0) (get m :match) (first groups)))))) + +(define + (lua-string-gmatch s pat) + (map (fn (m) (get m :match)) (regexp-match-all (make-regexp pat) s))) + +(define + (lua-string-gsub s pat repl) + (regexp-replace-all (make-regexp pat) s repl)) + +(define string {:rep lua-str-rep :sub (fn (s i &rest j-args) (let ((slen (len s)) (j (if (= (len j-args) 0) -1 (first j-args)))) (let ((from (if (< i 0) (let ((r (+ slen i))) (if (< r 0) 0 r)) (- i 1))) (to (if (< j 0) (let ((r (+ slen j 1))) (if (< r 0) 0 r)) (if (> j slen) slen j)))) (if (> from to) "" (substring s from to))))) :len (fn (s) (len s)) :upper (fn (s) (lua-str-map s char-upcase)) :char (fn (&rest codes) (list->string (map (fn (c) (integer->char (truncate c))) codes))) :gmatch lua-string-gmatch :gsub lua-string-gsub :lower (fn (s) (lua-str-map s char-downcase)) :byte (fn (s &rest args) (char->integer (nth (string->list s) (- (if (= (len args) 0) 1 (first args)) 1)))) :match lua-string-match :find lua-string-find :reverse (fn (s) (list->string (reverse (string->list s))))}) + +;; --------------------------------------------------------------------------- +;; table — Lua table library +;; --------------------------------------------------------------------------- + +(define + (lua-table-insert t v) + (assoc t (str (+ (lua-len t) 1)) v)) + +(define + (lua-table-remove t &rest args) + (let + ((n (lua-len t)) + (pos (if (= (len args) 0) (lua-len t) (first args)))) + (letrec + ((slide (fn (t i) (if (< i n) (assoc (slide t (+ i 1)) (str i) (lua-get t (+ i 1))) (assoc t (str n) nil))))) + (slide t pos)))) + +(define + (lua-table-concat t &rest args) + (let + ((sep (if (= (len args) 0) "" (first args))) + (n (lua-len t))) + (letrec + ((go (fn (acc i) (if (> i n) acc (go (str acc (if (= i 1) "" sep) (lua-to-string (lua-get t i))) (+ i 1)))))) + (go "" 1)))) + +(define + (lua-table-sort t) + (let + ((n (lua-len t))) + (letrec + ((collect (fn (i acc) (if (< i 1) acc (collect (- i 1) (cons (lua-get t i) acc))))) + (rebuild + (fn + (t i items) + (if + (= (len items) 0) + t + (rebuild + (assoc t (str i) (first items)) + (+ i 1) + (rest items)))))) + (rebuild t 1 (sort (collect n (list))))))) + +(define table {:sort lua-table-sort :concat lua-table-concat :insert lua-table-insert :remove lua-table-remove}) diff --git a/lib/lua/test.sh b/lib/lua/test.sh index 96a2e495..719f3750 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -633,6 +633,116 @@ check 482 "while i<5 count" '5' check 483 "repeat until i>=3" '3' check 484 "for 1..100 sum" '5050' +# ── Phase 3: stdlib — math, string, table ────────────────────────────────── + +cat >> "$TMPFILE" << 'EPOCHS2' + +;; ── math library ─────────────────────────────────────────────── +(epoch 500) +(eval "(lua-eval-ast \"return math.abs(-7)\")") +(epoch 501) +(eval "(lua-eval-ast \"return math.floor(3.9)\")") +(epoch 502) +(eval "(lua-eval-ast \"return math.ceil(3.1)\")") +(epoch 503) +(eval "(lua-eval-ast \"return math.sqrt(9)\")") +(epoch 504) +(eval "(lua-eval-ast \"return math.sin(0)\")") +(epoch 505) +(eval "(lua-eval-ast \"return math.cos(0)\")") +(epoch 506) +(eval "(lua-eval-ast \"return math.max(3, 7)\")") +(epoch 507) +(eval "(lua-eval-ast \"return math.min(3, 7)\")") +(epoch 508) +(eval "(lua-eval-ast \"return math.pi > 3\")") +(epoch 509) +(eval "(lua-eval-ast \"return math.huge > 0\")") + +;; ── string library ───────────────────────────────────────────── +(epoch 520) +(eval "(lua-eval-ast \"return string.len(\\\"hello\\\")\")") +(epoch 521) +(eval "(lua-eval-ast \"return string.upper(\\\"hello\\\")\")") +(epoch 522) +(eval "(lua-eval-ast \"return string.lower(\\\"WORLD\\\")\")") +(epoch 523) +(eval "(lua-eval-ast \"return string.sub(\\\"hello\\\", 2, 4)\")") +(epoch 524) +(eval "(lua-eval-ast \"return string.rep(\\\"ab\\\", 3)\")") +(epoch 525) +(eval "(lua-eval-ast \"return string.reverse(\\\"hello\\\")\")") +(epoch 526) +(eval "(lua-eval-ast \"return string.byte(\\\"A\\\")\")") +(epoch 527) +(eval "(lua-eval-ast \"return string.char(72, 105)\")") +(epoch 528) +(eval "(lua-eval-ast \"return string.find(\\\"hello\\\", \\\"ll\\\")\")") +(epoch 529) +(eval "(lua-eval-ast \"return string.match(\\\"hello\\\", \\\"ell\\\")\")") +(epoch 530) +(eval "(lua-eval-ast \"return string.gsub(\\\"hello\\\", \\\"l\\\", \\\"r\\\")\")") + +;; ── table library ────────────────────────────────────────────── +(epoch 540) +(eval "(lua-eval-ast \"local t = {10, 20, 30} t = table.insert(t, 40) return t[4]\")") +(epoch 541) +(eval "(lua-eval-ast \"local t = {10, 20, 30} t = table.remove(t) return t[3]\")") +(epoch 542) +(eval "(lua-eval-ast \"local t = {\\\"a\\\", \\\"b\\\", \\\"c\\\"} return table.concat(t, \\\",\\\")\")") +(epoch 543) +(eval "(lua-eval-ast \"local t = {3, 1, 2} t = table.sort(t) return t[1]\")") +(epoch 544) +(eval "(lua-eval-ast \"local t = {3, 1, 2} t = table.sort(t) return t[3]\")") + +;; ── delay / force ────────────────────────────────────────────── +(epoch 550) +(eval "(lua-force (delay (+ 10 5)))") +(epoch 551) +(eval "(lua-force 42)") + +EPOCHS2 + +OUTPUT2=$(timeout 30 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) +OUTPUT="$OUTPUT +$OUTPUT2" + +# math +check 500 "math.abs(-7)" '7' +check 501 "math.floor(3.9)" '3' +check 502 "math.ceil(3.1)" '4' +check 503 "math.sqrt(9)" '3' +check 504 "math.sin(0)" '0' +check 505 "math.cos(0)" '1' +check 506 "math.max(3,7)" '7' +check 507 "math.min(3,7)" '3' +check 508 "math.pi > 3" 'true' +check 509 "math.huge > 0" 'true' + +# string +check 520 "string.len" '5' +check 521 "string.upper" '"HELLO"' +check 522 "string.lower" '"world"' +check 523 "string.sub(2,4)" '"ell"' +check 524 "string.rep(ab,3)" '"ababab"' +check 525 "string.reverse" '"olleh"' +check 526 "string.byte(A)" '65' +check 527 "string.char(72,105)" '"Hi"' +check 528 "string.find ll" '3' +check 529 "string.match ell" '"ell"' +check 530 "string.gsub l->r" '"herro"' + +# table +check 540 "table.insert" '40' +check 541 "table.remove" 'nil' +check 542 "table.concat ," '"a,b,c"' +check 543 "table.sort [1]" '1' +check 544 "table.sort [3]" '3' + +# delay/force +check 550 "lua-force delay" '15' +check 551 "lua-force non-promise" '42' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL Lua-on-SX tests passed"