feat(parser,desugar): extern abi blocks (clean — replaces #47/#53)#54
Merged
Conversation
Second slice of #43. After the slash-paths PR (#46), the next concrete parse error on `hyperpolymath/hypatia`'s `bridge.eph` is the `extern "gossamer" { type Window; fn window_open(…): Window; … }` block. This commit makes that parse. What lands: * **Pest grammar** (`ephapax-parser/src/ephapax.pest`): new `extern_block` / `extern_item` / `extern_type_item` / `extern_fn_item` rules. Top-level `declaration` rule extended to admit `extern_block`. `"extern"` added to `keyword` and `keyword_boundary` so identifiers can't shadow it. * **Core AST** (`ephapax-syntax`): new `Decl::Extern { abi, items }` variant plus `ExternItem { Type { name } | Fn { name, params, ret_ty } }` enum. Item params/ret carry core `Ty`. * **Surface AST** (`ephapax-surface`): mirror `SurfaceDecl::Extern { abi, items, span }` and `SurfaceExternItem` with the same shape but `SurfaceTy`. The span lives on the block, not on individual items, matching how `DataDecl` carries one block-level span. * **Core parser** (`lib.rs`): `parse_declaration` routes `Rule::extern_block` to a new `parse_extern_block` → `parse_extern_item` pair. The ABI string is unquoted and unescaped via a minimal helper that handles `\"` and `\\` (sufficient for ABI tags; extend if richer escapes appear). * **Surface parser** (`surface.rs`): same plumbing against `SurfaceExternItem`, types staying in `SurfaceTy` until the desugar pass. * **Desugar** (`ephapax-desugar`): `SurfaceDecl::Extern` lowers to `Decl::Extern` by walking each item and mapping its surface types through `desugar_ty`. Extern types pass through as-is (just the name). The block carries no body so there's no expression-level desugaring. * **Downstream stubs** so the workspace builds: every exhaustive match on `Decl` now handles `Decl::Extern` — typecheck (no-op for now, registered as TODO), affine/linear discipline (no body → nothing to check), wasm codegen (skipped — wasm imports are phase 2B), IR s-expr printer (renders the block with `extern-type` / `extern-fn` tagged sub-forms), LSP symbol extractor (`filter_map` to drop extern blocks from the outline — phase 2B will expose them as navigable symbols). Tests: * `parse_empty_extern_block` — `extern "gossamer" { }` round-trips with empty items. * `parse_extern_block_with_type_and_fn_items` — full hypatia shape: `type Window` + `fn window_open(title, body): Window`. * `parse_bridge_eph_shaped_prelude` — slash-pathed module + extern block + regular fn all in one file (the canonical bridge.eph prelude). * `test_parse_extern_block_core` — same shape through the core parser, asserting `ExternItem::Type` / `ExternItem::Fn` and param arity. * `desugar_extern_block` — `SurfaceDecl::Extern` → `Decl::Extern`, with `SurfaceTy::String(region)` → `Ty::String(region)` and `SurfaceTy::Base(I32)` → `Ty::Base(I32)` covered. `cargo test --workspace` passes. `cargo check --workspace` clean. Out of scope (phase 2B follow-up, tracked in #43): * **Typechecker**: register extern types as opaque nominal types and extern fns as ambient bindings in the module env so other decls can call them. * **Wasm codegen**: emit `(import "<abi>" "<name>" (func ...))` for fn items, treat type items as opaque (i32) externs. This is what actually unblocks `bridge.eph → bridge.wasm`. * **LSP**: surface extern items as navigable symbols. After phase 2B, the `compile-eph` / `compile-affine` clap aliases land as trivial 3-line additions (the original #36 ask), and hypatia's `build-gossamer-gui.yml` workflow flips from `::warning::` to an actual wasm artifact. Stacked on #46. Rebase target switches to `main` when #46 merges. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 15, 2026
This was referenced May 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Clean replacement for #47/#53 (which were blocked by sha-different slash-paths commit on main). Cherry-picked f9f8657 onto a fresh branch off current main — single-commit PR, no force-push needed.
Close #53 alongside merging this.
Closes part of #43 (v2 grammar phase 2A).