Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 15 additions & 21 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,24 @@ jobs:
with:
node-version: '25'

- name: Install npm deps
run: npm install --no-audit --no-fund --silent

# Phase 0 NOTE (issue #48): Track A's parser migration replaced
# ReScript (.res + rescript.json + rescript build) with AffineScript
# (.affine + affinescript.json + @hyperpolymath/affinescript compiler).
# The compiler isn't yet declared as an npm dep in package.json so
# `npm install` can't fetch it on a clean CI runner. Detect the
# AffineScript or ReScript binary if either is present after install,
# build with whichever is available, otherwise skip and let downstream
# steps handle the artefact-absent case (they're already guarded).
- name: Build parser (AffineScript preferred, ReScript fallback)
# NOTE (issue #48): npm is banned estate-wide (Deno is the JS vehicle)
# and Track A retired the ReScript parser in favour of AffineScript
# (src/parser/*.affine — no .res, no package.json). There is therefore
# no `npm install` step. The AffineScript compiler is not yet wired into
# CI, so this builds the parser only if an `affinescript` toolchain is on
# PATH; otherwise it skips and the (already-guarded) downstream steps skip
# on absent src/parser/Parser.mjs. The named .mjs test scripts are
# dependency-free (node: + relative imports only), so they run on the
# setup-node runtime without any package manager.
- name: Build parser (AffineScript, if a compiler is available)
run: |
if [ -x node_modules/.bin/affinescript ]; then
if command -v affinescript >/dev/null 2>&1; then
echo "Building with AffineScript..."
node_modules/.bin/affinescript build
elif [ -x node_modules/.bin/rescript ]; then
echo "Building with ReScript (migration fallback)..."
node_modules/.bin/rescript build
affinescript build
else
echo "::warning::No parser compiler available — skipping build."
echo "::warning::Track A parser-migration is incomplete; see #48."
echo "::warning::Downstream smoke / aspect / property / benchmark"
echo "::warning::steps will skip if src/parser/Parser.mjs is absent."
echo "::warning::AffineScript compiler not on PATH — skipping parser build."
echo "::warning::Track A parser-migration (ReScript->AffineScript) is not yet wired into CI; see #48."
echo "::warning::Downstream smoke / property / benchmark steps skip if src/parser/Parser.mjs is absent."
fi

- name: Run smoke test (skip if parser artefacts absent)
Expand Down
70 changes: 48 additions & 22 deletions crates/typed-wasm-codegen/src/bin/tw.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//
//! `tw` — the typed-wasm build CLI (codegen v0).
//!
Expand Down Expand Up @@ -119,27 +119,53 @@ fn build(rest: &[String]) -> ExitCode {
}
};

// v0 gate: confirm the input is (structurally) the example-01 schema
// before emitting its baked IR. The general front-end -> IR seam is
// deferred per ADR-0004 (tracked by #127).
let is_example01 = src.contains("region Players")
&& src.contains("region Enemies")
&& src.contains("memory game_memory");
if !is_example01 {
eprintln!(
"tw build: codegen v0 only supports the example-01 schema \
(regions Players + Enemies and `memory game_memory`)."
);
eprintln!(
" General .twasm front-end -> IR lowering is tracked \
in ADR-0004 and issue #127."
);
return ExitCode::FAILURE;
}

// v0 emits only the example-01 module; render the bytes once, then
// write the requested artifact(s).
let bytes = typed_wasm_codegen::emit_example01();
// Try to parse the .twasm file using the Rust parser (issue #127).
// This parser handles the paint-type schemas and example-01.
let bytes = match typed_wasm_codegen::parser::parse_module(&src) {
Ok(module) => {
let bytes = typed_wasm_codegen::emit(&module);
// Try to self-verify the emitted module
if let Err(diagnostics) = typed_wasm_codegen::self_verify(&module) {
for msg in diagnostics {
eprintln!("tw build: self-verify warning: {}", msg);
}
}
bytes
}
Err(e) => {
// Fall back to string-matching for hardcoded schemas
eprintln!("tw build: parsing failed ({}), falling back to v0 string-matching: {}", input, e);
let is_example01 = src.contains("region Players")
&& src.contains("region Enemies")
&& src.contains("memory game_memory");
let is_paint_type_tile = src.contains("region RGBA16F")
&& src.contains("region TileHeader")
&& src.contains("region Tile")
&& src.contains("memory tile_memory");
let is_paint_type_layer = src.contains("region LayerName")
&& src.contains("region Layer")
&& src.contains("region LayerStack")
&& src.contains("memory layer_memory");

if is_example01 {
typed_wasm_codegen::emit_example01()
} else if is_paint_type_tile {
typed_wasm_codegen::emit_paint_type_tile()
} else if is_paint_type_layer {
typed_wasm_codegen::emit_paint_type_layer()
} else {
eprintln!(
"tw build: codegen v0 only supports example-01, paint-type-tile, \
or paint-type-layer schemas."
);
eprintln!(
" General .twasm front-end -> IR lowering is tracked \
in ADR-0004 and issue #127."
);
return ExitCode::FAILURE;
}
}
};
let base = output.unwrap_or_else(|| input.clone());
let mut wrote: Vec<String> = Vec::new();

Expand Down
Loading
Loading