Skip to main content

Phase 6. import rust grammar

FieldValue
MEPMEP-73 §Phases
StatusLANDED
Started2026-05-29 22:30 (GMT+7)
Landed2026-05-29 22:42 (GMT+7)
Tracking issue(pending)
Tracking PR(pending)
Commit(pending)

Gate

The Mochi parser admits import rust "<crate>@<semver>" as <alias> as a first-class form, on par with import python "..." as ... and import go "..." as .... Three pieces:

  1. "rust" joins knownImportLangs in parser/normalize.go. The grammar already admits any identifier in the lang slot; the validator just stops rejecting rust as a typo.
  2. A new validator validateRustImport (P066) rejects paths that are not in <crate>@<semver> form. Catching the typo at parse time turns import rust "hex" as hex (missing version) or import rust "[email protected]" as hex (upper-case crate name) into a positioned diagnostic instead of a far-later "crate not found" or wrapper-build failure.
  3. A new helper parser.RustImportRef(path) (crate, version string, ok bool) exposes the parsed pair to downstream phases (the build driver in Phase 7 consumes it to look up the wrapper crate in the cache).

Path validation

RustImportRef accepts strings of the shape <crate>@<version> with:

  • Crate name: lower-case ASCII letters, digits, _, -; 1..64 chars; must start with a letter.
  • Version: any non-empty token with no whitespace. Semver parsing itself lives in package3/rust/semver (Phase 1); the parser only enforces the <crate>@<rest> shape so a malformed path doesn't reach the bridge.

The crate-name rule is the conservative subset of Cargo's naming rules. Every crate published since 2018 fits this subset (upper-case names and dotted names were retired by Cargo before then). Staying strict keeps the parser diagnostic on the user's side rather than silently passing typos through.

Cargo accepts a range of version specifiers (=1.0.0, 1.0, ^1.0, ~1.0, 1.0.0-rc.1, 1.0.0+meta); the parser admits all of them without trying to interpret them. The bridge's semver parser (package3/rust/semver) is the source of truth.

Test surface

Parser unit tests in parser/rust_import_test.go cover every shape: plain [email protected], quoted strings, once_cell (underscore), rand-chacha (hyphen), serde@^1.0 (caret), serde@~1.0.195 (tilde), [email protected] (pre-release), [email protected]+meta (build metadata), plus 8 negative cases (missing version, empty crate, empty version, uppercase, digit-start, dotted name, space in version, empty string).

Golden tests in tests/parser/valid/import_rust.mochi and tests/parser/errors/import_rust_*.mochi lock the AST shape and the P066 diagnostic against the rest of the parser corpus. The existing import_unknown_lang.err golden was updated to include rust in the supported-languages help line.

What this does NOT do

  • It does NOT fetch the crate. That is Phase 1 (sparse-index client, already landed) wired up by Phase 7 (build orchestration).
  • It does NOT type-check the alias namespace. The downstream module resolver (Phase 8 will integrate with mochi.lock) ties the alias to the synthesised Phase 5 alias-module file.
  • It does NOT consume the package3/rust/semver package directly. The parser stays free of bridge-implementation dependencies; structural shape validation here, semver semantics in the bridge.

Files changed

FilePurpose
parser/normalize.goadds "rust" to knownImportLangs, adds errInvalidRustImportPath (P066) template + validateRustImport, wires the validator into normalizeStatement
parser/rust_import.goRustImportRef(path) (crate, version string, ok bool) + isCargoCrateName helper
parser/rust_import_test.go16 cases for RustImportRef + 13 cases for isCargoCrateName
tests/parser/valid/import_rust.mochi (+ .golden)three valid forms: [email protected], [email protected], serde@^1.0
tests/parser/errors/import_rust_missing_version.mochi (+ .err)rejects import rust "hex" as hex
tests/parser/errors/import_rust_bad_crate.mochi (+ .err)rejects import rust "[email protected]" as hex
tests/parser/errors/import_unknown_lang.errhelp text updated to include rust
website/docs/implementation/0073/phase-06-import-grammar.mdthis page
website/docs/implementation/0073/index.mdmark Phase 6 LANDED, backfill Phase 5 SHA
website/docs/mep/mep-0073.mdmark Phase 6 LANDED in target matrix

Test set

  • go test ./parser/... (all green)
  • go test ./package3/rust/... (regression: 8 packages green)
  • go test ./types/... (regression: green)

Closeout notes

Phase 6 introduces no external dependencies and no Rust code. The parser stays free of package3/rust/* imports; the path validator only enforces the structural shape (<crate>@<version> with a non-empty crate name in the conservative subset and a non-empty version with no whitespace).

Phase 7 (build orchestration) consumes RustImportRef to dispatch each import rust to the sparse-index client (Phase 1) + rustdoc ingest (Phase 2) + typemap (Phase 3) + wrapper synth (Phase 4) + extern-emit (Phase 5) pipeline. The alias module file emitted by Phase 5 is loaded under the <alias> name from the import statement.

The version-required rule (<crate>@<version>, not just <crate>) is deliberate. MEP-73 binds the synthesised wrapper crate to an exact upstream version because the rustdoc-JSON it consumes is version-specific. A mochi.lock-style ranged constraint (Phase 8) layers on top: the lock file pins the exact version that satisfied the import constraint, and mochi pkg lock --check fails if the lock drifted.