Skip to content

Commit b145c02

Browse files
feat(cli): Phase D — compile-eph / compile-affine aliases (closes #36) (#78)
Closes #36 `clap` visible aliases so the CLI runs as `compile-eph` / `compile-affine`, both routing to `compile`. Probed by hypatia's `build-gossamer-gui.yml`. ## Provenance Salvaged from the diverged local v2-grammar branch (was ahead 10 / behind 2 on `main`). That branch's extern/v2-grammar phases (A/B/C/E/F–H/I/J) are **deliberately not included**: `main` already shipped a more complete extern implementation with a different API (`Extern { abi, items, span }` + full desugar + cross-module registry), so replaying them would be a re-port, not a merge. Only the genuinely-net-new, upstream-compatible Phase D is taken. ## Contents - `src/ephapax-cli/src/main.rs` — `visible_alias` for `compile-eph` / `compile-affine` - `src/ephapax-cli/Cargo.toml` — `wasmparser` dev-dep (test asserts wasm magic) - `tests/v2-grammar/fixtures/extern-callsite.eph` — carried from the dropped Phase B commit (`6f7f316`) so the acceptance test is self-contained ## Verification - `cargo build -p ephapax-cli` — clean on current `main` - `cargo test -p ephapax-cli --test v2_grammar_phase_d_aliases` — **2/2 pass** - `extern-callsite.eph` compiles to valid wasm (`\0asm`, 702 bytes) via both aliases The full local v2-grammar branch is retained locally (not lost); re-porting the remaining phases onto `main`'s extern API is a separate author-led effort. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ad178e7 commit b145c02

4 files changed

Lines changed: 87 additions & 1 deletion

File tree

src/ephapax-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ serde_json.workspace = true
3636
[dev-dependencies]
3737
wasmtime.workspace = true
3838
proptest = { workspace = true }
39+
wasmparser = "0.221"
3940
tempfile = "3"

src/ephapax-cli/src/main.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,14 @@ enum Commands {
7373
mode: String,
7474
},
7575

76-
/// Compile to WebAssembly
76+
/// Compile to WebAssembly.
77+
///
78+
/// Aliased as `compile-eph` and `compile-affine` for downstream
79+
/// consumers (notably hypatia's `build-gossamer-gui` workflow, which
80+
/// probes for these names in order). All three names route to the same
81+
/// surface-parse → desugar → typecheck → wasm pipeline.
82+
/// Closes hyperpolymath/ephapax#36.
83+
#[command(alias = "compile-eph", alias = "compile-affine")]
7784
Compile {
7885
/// Input file
7986
file: PathBuf,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
//
3+
// Phase D: clap aliases for the `compile` subcommand. Closes #36.
4+
// Asserts that invoking the CLI as `compile-eph` and `compile-affine`
5+
// both route to the same `Compile` subcommand. Probed by hypatia's
6+
// `build-gossamer-gui.yml` workflow (see #36 for the probe order).
7+
8+
use std::process::Command;
9+
10+
fn ephapax_bin() -> String {
11+
// CARGO_BIN_EXE_ephapax is set by cargo for integration tests.
12+
env!("CARGO_BIN_EXE_ephapax").to_string()
13+
}
14+
15+
fn fixture(name: &str) -> String {
16+
format!(
17+
"{}/../../tests/v2-grammar/fixtures/{}",
18+
env!("CARGO_MANIFEST_DIR"),
19+
name
20+
)
21+
}
22+
23+
#[test]
24+
fn compile_eph_alias_routes_to_compile() {
25+
let out = tempfile::NamedTempFile::new().expect("temp file");
26+
let status = Command::new(ephapax_bin())
27+
.arg("compile-eph")
28+
.arg(fixture("extern-callsite.eph"))
29+
.arg("-o")
30+
.arg(out.path())
31+
.output()
32+
.expect("ephapax compile-eph must run");
33+
assert!(
34+
status.status.success(),
35+
"compile-eph alias failed: stdout={} stderr={}",
36+
String::from_utf8_lossy(&status.stdout),
37+
String::from_utf8_lossy(&status.stderr)
38+
);
39+
let bytes = std::fs::read(out.path()).expect("output wasm exists");
40+
assert!(bytes.starts_with(b"\0asm"), "wasm magic bytes present");
41+
}
42+
43+
#[test]
44+
fn compile_affine_alias_routes_to_compile() {
45+
let out = tempfile::NamedTempFile::new().expect("temp file");
46+
let status = Command::new(ephapax_bin())
47+
.arg("compile-affine")
48+
.arg(fixture("extern-callsite.eph"))
49+
.arg("-o")
50+
.arg(out.path())
51+
.output()
52+
.expect("ephapax compile-affine must run");
53+
assert!(
54+
status.status.success(),
55+
"compile-affine alias failed: stdout={} stderr={}",
56+
String::from_utf8_lossy(&status.stdout),
57+
String::from_utf8_lossy(&status.stderr)
58+
);
59+
let bytes = std::fs::read(out.path()).expect("output wasm exists");
60+
assert!(bytes.starts_with(b"\0asm"), "wasm magic bytes present");
61+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Extern fn callsite — proves Phase B of #43: the extern signature is
3+
// in scope from the function body. `entry()` returns the result of an
4+
// `extern "wasm"`-declared identity function on a literal.
5+
//
6+
// The `type` extern item is currently dropped by desugar (Phase B+ will
7+
// register it as an abstract type). The `fn` extern item desugars to
8+
// `Decl::Extern`, gets registered in the typechecker env by the
9+
// pre-pass, and the call resolves cleanly.
10+
11+
module hyperpolymath/ephapax/test
12+
13+
extern "wasm" {
14+
fn host_identity(x: I32): I32
15+
}
16+
17+
fn entry(): I32 = host_identity(7)

0 commit comments

Comments
 (0)