lua: require/package via package.preload +5 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-24 19:19:36 +00:00
parent 582894121d
commit 24fde8aa2f
3 changed files with 47 additions and 1 deletions

View File

@@ -1315,3 +1315,28 @@
(dict-set! os "rename" lua-os-rename)
(dict-set! os "tmpname" lua-os-tmpname)
(dict-set! os "execute" lua-os-execute)
;; ── package / require ─────────────────────────────────────────
(define package {})
(define __package-loaded {})
(define __package-preload {})
(dict-set! package "loaded" __package-loaded)
(dict-set! package "preload" __package-preload)
(dict-set! package "path" "?;?.lua")
(define
lua-require
(fn (name)
(cond
((has-key? __package-loaded name) (get __package-loaded name))
((has-key? __package-preload name)
(let ((loader (get __package-preload name)))
(let ((m (lua-call loader name)))
(let ((result (if (= m nil) true m)))
(begin
(dict-set! __package-loaded name result)
result)))))
(else (error (str "lua: module '" name "' not found"))))))
(define require lua-require)

View File

@@ -876,6 +876,18 @@ cat > "$TMPFILE" << 'EPOCHS'
(epoch 1507)
(eval "(lua-eval-ast \"return type(os.tmpname())\")")
;; ── Phase 7: require / package ────────────────────────────────
(epoch 1600)
(eval "(lua-eval-ast \"package.preload.mymath = function() local m = {} m.add = function(a, b) return a + b end return m end local mm = require(\\\"mymath\\\") return mm.add(3, 4)\")")
(epoch 1601)
(eval "(lua-eval-ast \"package.preload.counter = function() local n = 0 local m = {} m.inc = function() n = n + 1 return n end return m end local c1 = require(\\\"counter\\\") local c2 = require(\\\"counter\\\") c1.inc() c1.inc() return c2.inc()\")")
(epoch 1602)
(eval "(lua-eval-ast \"local ok, err = pcall(require, \\\"nope\\\") if ok then return 0 else return 1 end\")")
(epoch 1603)
(eval "(lua-eval-ast \"package.preload.x = function() return {val = 42} end require(\\\"x\\\") return package.loaded.x.val\")")
(epoch 1604)
(eval "(lua-eval-ast \"package.preload.noret = function() end local r = require(\\\"noret\\\") return type(r)\")")
EPOCHS
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
@@ -1317,6 +1329,13 @@ check 1505 "os.date(*t).year" '1970'
check 1506 "os.getenv → nil" '1'
check 1507 "os.tmpname → string" '"string"'
# ── Phase 7: require / package ────────────────────────────────
check 1600 "require(preload) + call" '7'
check 1601 "require returns cached" '3'
check 1602 "require unknown module errors" '1'
check 1603 "package.loaded populated" '42'
check 1604 "nil return caches as true" '"boolean"'
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo "ok $PASS/$TOTAL Lua-on-SX tests passed"

View File

@@ -75,13 +75,14 @@ Each item: implement → tests → tick box → update progress log.
- [x] `os` — time/date subset
### Phase 7 — modules + full conformance
- [ ] `require` / `package` via SX `define-library`/`import`
- [x] `require` / `package` via SX `define-library`/`import`
- [ ] Drive PUC-Rio scoreboard to 100%
## Progress log
_Newest first. Agent appends on every commit._
- 2026-04-24: lua: `require`/`package` via preload-only (no filesystem search). `package.loaded` caching, nil-returning modules cache as `true`, unknown modules error. 347 tests.
- 2026-04-24: lua: `os` stub — time/clock monotonic counter, difftime, date (default string / `*t` dict), getenv/remove/rename/tmpname/execute/exit stubs. Phase 6 complete. 342 tests.
- 2026-04-24: lua: `io` stub + `print`/`tostring`/`tonumber` globals. io buffers to internal `__io-buffer` (tests drain it via `io.__buffer()`). print: tab-sep + NL. tostring respects `__tostring` metamethod. 334 tests.
- 2026-04-24: lua: `table` lib — insert (append / at pos, shifts up), remove (last / at pos, shifts down), concat (sep, i, j), sort (insertion sort, optional cmp), unpack + table.unpack, maxn. Caught trap: local helper named `shift` collides with SX's `shift` special form → renamed to `tbl-shift-up`/`tbl-shift-down`. 322 tests.
@@ -110,6 +111,7 @@ _Shared-file issues that need someone else to fix. Minimal repro only._
## Known limitations (own code, not shared)
- **`require` supports `package.preload` only** — no filesystem search (we don't have Lua-file resolution inside sx_server). Users register a loader in `package.preload.name` and `require("name")` calls it with name as arg. Results cached in `package.loaded`; nil return caches as `true` per Lua convention.
- **`os` library is a stub** — `os.time()` returns a monotonic counter (not Unix epoch), `os.clock()` = counter/1000, `os.date()` returns hardcoded "1970-01-01 00:00:00" or a `*t` table with fixed fields; `os.getenv` returns nil; `os.remove`/`rename` return nil+error. No real clock/filesystem access.
- **`io` library is a stub** — `io.write`/`print` append to an internal `__io-buffer` (accessible via `io.__buffer()` which returns + clears it) instead of real stdout. `io.read`/`open`/`lines` return nil. Suitable for tests that inspect output; no actual stdio.
- **`string.find`/`match`/`gmatch`/`gsub` patterns are LITERAL only** — no `%d`/`%a`/`.`/`*`/`+`/etc. Implementing Lua patterns is a separate work item; literal search covers the common case.