scheme: Phase 8 — define-library + import (minimal) + 7 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s

eval.sx adds module support:

  (define-library NAME EXPR...)
    Where EXPR is one of:
      (export NAME ...)
      (import LIB-NAME ...)
      (begin BODY ...)

  (import LIB-NAME ...)
    Looks up each library by key, copies its exported names
    into the current env.

Library values: {:scm-tag :library :name :exports :env}
Stored in scheme-library-registry keyed by joined library-name
(`(my math)` → `"my/math"`).

Library body runs in a FRESH standard env (each library is its
own namespace). Only :exports are visible after import; private
internal definitions stay in the library's env. Internal calls
between library functions use the library's env, so public-facing
exports can rely on private helpers.

Multiple imports work — each library is independent.

NOT yet supported: cond-expand, include, include-library-
declarations, renaming (`(only ...)`, `(except ...)`, `(prefix ...)`,
`(rename ...)`). Standard R7RS modules use these but the core
two-operation flow (define-library / import) covers most everyday
module use.

7 tests: single export, multi-export, private-not-visible,
internal-calls-private, two-libs-both-imported, unknown-lib-error,
single-symbol library name.

296 total Scheme tests (62+23+49+78+25+20+13+10+9+7).

Phases done: 1, 2, 3, 3.5, 4, 5abc, 6ab, 7, 8, 9, 10.
Deferred: 6c (hygiene/scope-set — research-grade), 11 (conformance).
This commit is contained in:
2026-05-14 06:50:58 +00:00
parent f927fb6515
commit 7e795f95fc
2 changed files with 206 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
;; lib/scheme/tests/modules.sx — define-library + import.
(define scm-mod-pass 0)
(define scm-mod-fail 0)
(define scm-mod-fails (list))
(define
scm-mod-test
(fn
(name actual expected)
(if
(= actual expected)
(set! scm-mod-pass (+ scm-mod-pass 1))
(begin
(set! scm-mod-fail (+ scm-mod-fail 1))
(append! scm-mod-fails {:name name :actual actual :expected expected})))))
(define
scm-mod
(fn
(src)
(scheme-eval-program (scheme-parse-all src) (scheme-standard-env))))
;; ── Basic define-library + import ───────────────────────────────
(scm-mod-test
"simple lib: sq exported"
(scm-mod
"(define-library (my math)\n (export sq)\n (begin (define (sq x) (* x x))))\n (import (my math))\n (sq 5)")
25)
(scm-mod-test
"lib: multiple exports"
(scm-mod
"(define-library (my math)\n (export sq cube)\n (begin\n (define (sq x) (* x x))\n (define (cube x) (* x x x))))\n (import (my math))\n (list (sq 5) (cube 3))")
(list 25 27))
(scm-mod-test
"lib: single-symbol name"
(scm-mod
"(define-library (utils)\n (export greet)\n (begin (define (greet name) (string-append \"hi \" name))))\n (import (utils))\n (string=? (greet \"world\") \"hi world\")")
true)
;; ── Unexported names are not visible ───────────────────────────
(scm-mod-test
"lib: private name not exported"
(scm-mod
"(define-library (my math)\n (export sq)\n (begin\n (define (sq x) (* x x))\n (define (private-helper x) (+ x 1))))\n (import (my math))\n (guard (e (else 'unbound)) private-helper)")
"unbound")
;; ── Library calls its own internals ─────────────────────────────
(scm-mod-test
"lib: internal calls private fn"
(scm-mod
"(define-library (my math)\n (export public-add1)\n (begin\n (define (private-inc x) (+ x 1))\n (define (public-add1 x) (private-inc x))))\n (import (my math))\n (public-add1 41)")
42)
;; ── Two libs, both imported ────────────────────────────────────
(scm-mod-test
"two libs: both imported"
(scm-mod
"(define-library (a) (export af) (begin (define (af) 1)))\n (define-library (b) (export bf) (begin (define (bf) 2)))\n (import (a) (b))\n (+ (af) (bf))")
3)
;; ── Unknown library import errors ──────────────────────────────
(scm-mod-test
"import: unknown lib errors"
(scm-mod "(guard (e (else 'unknown-lib)) (import (no such lib)))")
"unknown-lib")
(define scm-mod-tests-run! (fn () {:total (+ scm-mod-pass scm-mod-fail) :passed scm-mod-pass :failed scm-mod-fail :fails scm-mod-fails}))