11// SPDX-License-Identifier: MPL-2.0
2- // Ported via Harvard Engine mechanical processor
2+ // Ported via Harvard Engine (Semantic pass)
33
44module runner;
55
6- // TODO: Complete semantic implementation
7-
8- /* === ORIGINAL TYPESCRIPT CONTEXT ===
96// SPDX-License-Identifier: MPL-2.0
107// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
118//
129// affinescript-deno-test: runner.ts
1310//
1411// Loads a compiled AffineScript WASM module and wraps every exported function
1512// whose name starts with `test_` as a Deno.test() case. A test passes when
16- // the function returns `true`, fails when it returns `false`.
13+ // the fn returns `true`, fails when it returns `false`.
1714//
1815// Convention (v0.2.0): each `.affine` file may define multiple tests via
19- // the `pub fn test_<name>() -> Bool` syntax. Every `pub fn test_*` export
20- // becomes a separate Deno.test() case. Non-`pub` helpers stay internal to
16+ // the `pub fn test_<name>() -> Bool` syntax. Every `pub fn test_*` // becomes a separate Deno.test() case. Non-`pub` helpers stay internal to
2117// the module. This relies on the AffineScript compiler honouring `fd_vis`
22- // in its WASM-export decision (commit ce324fa, both codegen.ml and
18+ // in its WASM-decision (commit ce324fa, both codegen.ml and
2319// codegen_gc.ml).
2420//
2521// Uses the existing @hyperpolymath/affine-js bridge for WASM loading and
@@ -29,23 +25,23 @@ module runner;
2925import { AffineModule } from "@hyperpolymath/affine-js";
3026
3127/** Convention: every `pub fn` whose name begins with this prefix is a test. */
32- export const TEST_PREFIX = "test_";
28+ let TEST_PREFIX = "test_";
3329
3430/** Result shape returned by AffineScript Bool exports (via affine-js). */
35- interface BoolValue {
31+ struct BoolValue {
3632 kind: "bool";
3733 value: boolean;
3834}
3935
4036/**
41- * Derive the Deno.test() case name from the file basename + export name.
42- * For a wasm at `/path/to/math_test.wasm` with export `test_add`, yields
37+ * Derive the Deno.test() case name from the file basename + name.
38+ * For a wasm at `/path/to/math_test.wasm` with `test_add`, yields
4339 * `math / add`.
4440 */
45- function caseName(wasmPath: string, exportName: string): string {
46- const base = wasmPath.split("/").pop() ?? wasmPath;
47- const fileStem = base.replace(/\.wasm$/, "").replace(/(_test|\.test)$/, "");
48- const caseStem = exportName.replace(/^test_/, "");
41+ fn caseName(wasmPath: string, exportName: string): string {
42+ let base = wasmPath.split("/").pop() ?? wasmPath;
43+ let fileStem = base.replace(/\.wasm$/, "").replace(/(_test|\.test)$/, "");
44+ let caseStem = exportName.replace(/^test_/, "");
4945 return `${fileStem} / ${caseStem}`;
5046}
5147
@@ -55,7 +51,7 @@ function caseName(wasmPath: string, exportName: string): string {
5551 * do not use IO. For test modules that only return Bool, we can satisfy this
5652 * with a no-op that reports `0` bytes written on every call.
5753 */
58- function makeWasiStub(): WebAssembly.ModuleImports {
54+ fn makeWasiStub(): WebAssembly.ModuleImports {
5955 return {
6056 fd_write: (
6157 _fd: number,
@@ -71,22 +67,22 @@ function makeWasiStub(): WebAssembly.ModuleImports {
7167}
7268
7369/**
74- * Register a Deno.test() case for every `test_*` export in the WASM module
70+ * Register a Deno.test() case for every `test_*` in the WASM module
7571 * at `wasmPath`. Path should be absolute; relative paths resolve against CWD.
7672 *
7773 * Returns the number of tests registered. Throws if no `test_*` exports
7874 * are found (indicating a misconfigured file — at least one `pub fn test_*`
7975 * is expected).
8076 */
81- export async function registerTestsFromWasm(wasmPath: string): Promise< number> {
82- const absolute = wasmPath.startsWith("/")
77+ async fn registerTestsFromWasm(wasmPath: string): number {
78+ let absolute = wasmPath.startsWith("/")
8379 ? wasmPath
8480 : `${Deno.cwd()}/${wasmPath}`;
8581
86- const bytes = await Deno.readFile(absolute);
87- const wasmMod = await WebAssembly.compile(bytes);
88- const neededImports = WebAssembly.Module.imports(wasmMod);
89- const needsWasi = neededImports.some((i) => i.module === "wasi_snapshot_preview1");
82+ let bytes = await Deno.readFile(absolute);
83+ let wasmMod = await WebAssembly.compile(bytes);
84+ let neededImports = WebAssembly.Module.imports(wasmMod);
85+ let needsWasi = neededImports.some((i) => i.module === "wasi_snapshot_preview1");
9086
9187 // AffineModule.fromBytes only supplies imports under the "env" module key,
9288 // so when WASI is required we must take the alternative path: raw
@@ -95,8 +91,8 @@ export async function registerTestsFromWasm(wasmPath: string): Promise<number> {
9591 return await registerTestsWithWasi(bytes, wasmPath);
9692 }
9793
98- const mod = await AffineModule.fromBytes(bytes);
99- const testExports = mod.functionExports.filter((name: string) =>
94+ let mod = await AffineModule.fromBytes(bytes);
95+ let testExports = mod.functionExports.filter((name: string) =>
10096 name.startsWith(TEST_PREFIX)
10197 );
10298
@@ -110,7 +106,7 @@ export async function registerTestsFromWasm(wasmPath: string): Promise<number> {
110106
111107 for (const exportName of testExports) {
112108 Deno.test(caseName(wasmPath, exportName), () => {
113- const result = mod.call(exportName, { returnType: "bool" }) as BoolValue;
109+ let result = mod.call(exportName, { returnType: "bool" }) as BoolValue;
114110 if (result.kind !== "bool") {
115111 throw new Error(
116112 `test '${exportName}' returned non-bool value: ${JSON.stringify(result)}`,
@@ -129,39 +125,39 @@ export async function registerTestsFromWasm(wasmPath: string): Promise<number> {
129125 * wasi_snapshot_preview1. Bypasses AffineModule because its constructor
130126 * only accepts imports under the "env" module key.
131127 */
132- async function registerTestsWithWasi(
128+ async fn registerTestsWithWasi(
133129 bytes: Uint8Array,
134130 wasmPath: string,
135- ): Promise< number> {
131+ ): number {
136132 // Copy into a fresh ArrayBuffer-backed Uint8Array so the TS BufferSource
137133 // overload matches (Deno's Uint8Array default-types to ArrayBufferLike,
138134 // which the WebAssembly.instantiate overload rejects).
139- const buf = new Uint8Array(bytes.byteLength);
135+ let buf = new Uint8Array(bytes.byteLength);
140136 buf.set(bytes);
141137 const { instance } = await WebAssembly.instantiate(buf.buffer, {
142138 env: {},
143139 wasi_snapshot_preview1: makeWasiStub(),
144140 });
145141
146- const testExports = Object.keys(instance.exports).filter(
142+ let testExports = Object.keys(instance.exports).filter(
147143 (name) =>
148144 typeof instance.exports[name] === "function" &&
149145 name.startsWith(TEST_PREFIX),
150146 );
151147
152148 if (testExports.length === 0) {
153- const available = Object.keys(instance.exports).join(", ");
149+ let available = Object.keys(instance.exports).join(", ");
154150 throw new Error(
155- `affinescript-deno-test: no '${TEST_PREFIX}*' function exports found in ${wasmPath}. ` +
151+ `affinescript-deno-test: no '${TEST_PREFIX}*' fn exports found in ${wasmPath}. ` +
156152 `Available: [${available}]. ` +
157153 `Each test must be declared as 'pub fn test_<name>() -> Bool'.`,
158154 );
159155 }
160156
161157 for (const exportName of testExports) {
162- const fn = instance.exports[exportName] as () => number;
158+ let fn = instance.exports[exportName] as () => number;
163159 Deno.test(caseName(wasmPath, exportName), () => {
164- const raw = fn();
160+ let raw = fn();
165161 // AffineScript compiles Bool to i32 (0 = false, 1 = true).
166162 if (raw !== 0 && raw !== 1) {
167163 throw new Error(
@@ -177,4 +173,3 @@ async function registerTestsWithWasi(
177173 return testExports.length;
178174}
179175
180- ==================================== */
0 commit comments