Crate Layout
formawasm is intentionally flat. Every top-level concern lives in its own src/ module; only the per-IR-variant lowering family is grouped under a lower/ subdirectory.
src/
lib.rs # public surface re-exports
backend.rs # WasmBackend, Backend impl, optional wasm-opt + validation
preflight.rs # rejection of unsupported IR shapes
survey.rs # public-surface classification
layout.rs # memory-layout planning (struct, enum, array, range,
# optional, string, dictionary, vtable)
types.rs # IR ResolvedType → wasm valtype mapping
ident.rs # source-name → kebab-case helper
string_pool.rs # compile-time string-literal interning
module.rs # core-Wasm ModuleBuilder
module_lowering.rs # IrModule → core wasm bytes (orchestration)
wit.rs # WIT auto-generation
component.rs # core module + WIT → component
dwarf.rs # (feature: dwarf) DWARF debug-section emission
lower/ # per-IrExpr lowering (one submodule per family)
mod.rs # shared types + lower_expr dispatcher
aggregate.rs # struct/enum/tuple instantiation, field access
binary_op.rs # type-dispatched binary operators
block.rs # Block + Let + per-source scratch-local planning
call.rs # FunctionCall, MethodCall (static + virtual)
control.rs # If, For (over Range and Array)
literal.rs # numeric / boolean / string literals
optional.rs # Some-wrap coercion at let / return / args / fields
reference.rs # Reference, LetRef, SelfFieldRef
unary_op.rs # Neg, Not
bin/
formawasm.rs # source.fv → output.wasm CLI driver
docs/ # this book (mdBook source)
tests/ # one file per IR construct + per phase milestone
plans/ # forward-looking plan notes (decisions in flight)
Module responsibilities
lib.rs
Public re-exports. Anything a downstream crate imports (use formawasm::WasmBackend) goes through here. Also re-exports the upstream IR types (IrModule, IrFunction, ResolvedType) so callers don't need a separate formalang dependency.
backend.rs
WasmBackend itself. Implements formalang::pipeline::Backend, holds the validation toggle, and orchestrates the seven-stage pipeline. The optimize_core_module free function lives here too — both behind the wasm-opt cargo feature.
preflight.rs
The rejection pass. Every PreflightError variant carries a human-readable breadcrumb pointing at the offending IR node. Failure means an upstream pipeline step was skipped or an upstream invariant was violated.
survey.rs
Classifies top-level items into exports / imports / internal types. Returns a PublicSurface that downstream stages consume — lower_module for function-index allocation, wit::emit_wit for which items appear in the WIT file.
layout.rs
The memory-layout planner. One plan_* function per type family, each producing a record of per-field offsets, total size, and alignment. All sizes follow the Component-Model canonical ABI. The lowerer resolves i32_load / i32_store offsets against the constants this module returns.
module_lowering.rs
The bridge between IrModule and module::ModuleBuilder. Walks the IR module, builds a FunctionMap ahead of time so recursive calls resolve, then lowers each function body and plugs it into a fresh ModuleBuilder before returning the encoded module bytes.
lower/
Per-IrExpr lowering. Each lower_* function appends instructions to the caller's InstructionSink and assumes the surrounding stack discipline is maintained by the caller. Helpers do not emit a closing end — that's the function-body framer's job.
The submodules are split by IR variant family rather than by phase, so adding a new operator (e.g. a new BinaryOp::Bitwise) means editing exactly one file.
wit.rs
WIT text generation. Walks the public surface, emits the world block plus any interface types declarations, and returns a String ready for wit-component::ComponentEncoder.
component.rs
The final wrap. Takes core module bytes + WIT text, runs them through wit-component::ComponentEncoder, returns the component bytes.
string_pool.rs
Compile-time string-literal interning. Every string literal lowered by lower::literal is added to the pool; on module finalization, the pool's bytes are written into a passive data segment with a single per-literal (ptr, len) header pair.
dwarf.rs
Behind the dwarf cargo feature: DWARF .debug_info / .debug_abbrev / .debug_line / .debug_str custom-section construction from the IrSpan data formalang attaches to every IR node.
Public-API surface
What's pub from this crate (per lib.rs):
WasmBackend,WasmBackendError— the headline type and its error.Backend(re-exported from formalang) — the traitWasmBackendimplements.IrModule,IrFunction,ResolvedType,Pipeline, etc. — re-exports so callers don't need a separateformalangdep.lower_module,emit_wit,wrap_componentplus their error types — for callers who want to drive individual stages.plan_struct,plan_enum,plan_array, … plus their*Layoutrecords — so layout-aware tools can introspect the same memory shapes the backend uses.PublicSurface— for callers who want to inspect the surface classification without re-walking the IR.
Internal helpers (string_pool, ident) stay pub(crate). The boundary between "public" and "internal" is enforced at lint time: #[deny(unreachable_pub)] keeps the surface from leaking accidentally.
Tests
tests/ mirrors the lowering layout: one file per IR variant (lower_struct_inst.rs, lower_array.rs, lower_match.rs, …), one file per layout family (layout_struct.rs, layout_array.rs, …), plus per-phase milestone tests (milestone_1b.rs, milestone_2.rs, …, sieve.rs). See Testing for conventions.
tests/snapshots/ holds insta snapshots of the WIT emitter output — every WIT shape we generate is captured as a .snap file so divergence trips a review-gated diff.