Sigil 0.22.2
A patch for the seams a game project hit in its first two days: between the two backends, between a game and the web loader, between what a docstring says and what a procedure does. It also repairs a 0.22.1 regression that stopped script-style source files from building. Everything here came from Crash The Stack's toolchain notes, each item with the measurement that found it.
Existing projects build without changes. A declaration such as sigil: "0.22" already admits 0.22.2. The one behaviour a page might notice: sigil_wasm_dispatch_event now reports a handler's positive integer instead of 0, so a page that treated any non-zero return as failure should treat only a negative one that way.
Script-style source files build again
0.22.1 rejected any source whose declaration forms the bytecode-input identity extractor could not model, and that included a script-style file under src/: a top-level (import ...) followed by expressions and no define-library, loaded by the wasm runtime at startup. sigil build died with unsupported dependency declaration or feature condition on a repository 0.22.0 built.
A script's top-level imports are now modeled declaration positions: they are dependency edges, so the script compiles after the modules it imports, and its cache identity is complete. A form the extractor still cannot model (an import inside a procedure body, an include, a (library ...) feature test) no longer fails the build: the file compiles fresh, the build prints one UNCACHED line naming the form, and the package declines cache reuse until the form is gone. The contract is in the build cache reference.
The two backends answer alike
(map f a b) with two lists raised map: expected 2 argument(s), got 3 in the bytecode VM and ran under native codegen, which silently dropped the extra list; one source tree gave a green browser build and a native game that drew nothing. Core map and for-each now take any number of lists and stop at the shortest, as R7RS says.
A define inside a cond-expand clause in a library or procedure body is now a define of that body on every backend. Before, the bytecode compiler raised GET_CELL: expected upvalue cell at the call and native codegen resolved the name to an immediate, so a module that differed per target had to cond-expand inside every body.
write prints a symbol as |...| when the reader would not read the bare name back as that symbol: |1| (a keyboard's digit key), |a b|, |foo:|. Written data round-trips through read again; display is unchanged.
A conformance fixture now runs a list of stdlib forms through the differential harness on bytecode, native ELF and native wasm and fails on any disagreement; make test-conformance runs the bytecode and native rungs and is part of make test. A second fixture records a known gap for its own ticket: native codegen does not check call arity, so a two-argument procedure called with three runs as if called with two.
The web loader keeps early dispatches and answers questions
A dispatch made from SigilWasmBridges.setInstance, where a page first learns the wasm instance, reached a runtime with no VM and was dropped with a success-shaped return. The loader now publishes globalThis.SigilWebApp: a dispatch made before sigil_wasm_start is queued and delivered, in order, right after it; SigilWebApp.ready resolves then, and document receives a sigil-web-app-ready event for scripts that ran before the loader.
SigilWebApp.dispatch() returns the handler's integer, so a page can ask the app a question through the return value instead of polling localStorage. A pointerdown on a canvas is forwarded as ("pointerdown", "x,y,mods") in canvas-buffer pixels with a modifier bitmask, unless the page's own listener handled it, so a game page no longer re-implements pointer forwarding.
Structs: update through a procedure, compare by fields
(struct-update s field: proc ...) copies a struct with each named field passed through its procedure, replacing the read-apply-rebuild idiom (session s trace: (trace-hint (session-trace s))). (struct-equal? a b) compares two instances of one type field by field. equal? on instances is still identity in this release.
One package, native and web configs
Three sigil-build gaps met by a package that builds both a native binary and a browser application:
- A dev-dependency is built only when tests are built (
--tests,sigil test). A plainsigil build --config webused to compile the test runner's whole closure for wasm and fail in mbedtls two and a half minutes in. - A task's
configs:gate is honoured.buildcan be declared once for the wasm config, linking a web application, while the native configs keep the default task. - A dependency library's
native-init:is called in a wasm build: the build generates a weaksigil_wasm_app_registerthat calls each one, so no hand-writtenapp-register.cis needed, and a package that still ships its own keeps winning.
Docstrings say what raises
Procedures in (sigil fs) and (sigil io) that can raise carry a raises: line, and those that answer #f say so; the browser storage facades document that they never raise and what a missing value looks like. read-file-bytes takes missing: like read-file-string, and with-current-directory raises when it cannot enter the directory instead of running its thunk in the old one.