Skip to content

Rust-like record syntax migration: bare { = block, records use #{ } (resolves #215 families C+D) #218

@hyperpolymath

Description

@hyperpolymath

Decision

To eliminate the block-vs-record-literal parser ambiguity (families
C+D of #215) structurally rather than by a human-facing lookahead
heuristic, AffineScript moves to a Rust-like model:

  • { in expression position is ALWAYS a block.
  • Record/struct literals use the #{ … } sigil — both anonymous
    (#{ x: 1 }) and typed (Foo #{ x: 1 }). This also removes the
    Rust struct-literal-in-if/match-scrutinee hazard entirely.
  • Record patterns (match x { Foo { a } => … }) are unaffected —
    pattern position has no block alternative, so no ambiguity there.

Owner-approved (2026-05-18). Supersedes the lookahead-heuristic option.

Status of the grammar change

Implemented and correct on branch stage-c/pc-brace-disambig:
HASH_LBRACE token (token.ml), #{ lexer rule (lexer.ml), both
ExprRecord productions rerouted (parser.mly). Conflicts dropped
72→68 S/R, 10→7 R/R, build clean. Not merged — breaking; needs the
migration to land atomically with it.

Scope (true cost, measured)

  • ~60 of 353 .affine files in the affinescript repo use the old
    record syntax (test fixtures, golden oracles, examples, stdlib) —
    21 tests currently fail (expected, pre-migration).
  • Wider estate .affine files on top of that.
  • A residual reduce/reduce remains even after migration
    (state 401, LBRACE IF expr block option(else_part) — a distinct
    if-expr-as-statement ambiguity, family-D-residual) plus ~68 S/R;
    tracked as follow-on families, NOT blocking this migration.

Plan

  1. Codemod (linchpin): instrument the origin/main parser (old
    grammar still parses old syntax) to emit each ExprRecord's
    LBRACE byte-offset per file; a script inserts # at those
    offsets (descending). Precise — only true record literals, never
    blocks/declarations/match/if. Validate on a sample.
  2. Atomic affinescript PR: grammar/lexer/token change
    (stage-c/pc-brace-disambig) + codemod-migrated repo .affine
    (stdlib + tests + examples + golden) so the 257 gate goes green
    with the new syntax. Conflict delta reported.
  3. Estate sweep: run the codemod across all estate .affine
    repos, one PR per repo (or batched), each gated.
  4. Residual families (separate, after migration lands): state-401
    R/R + remaining S/R → continue Eliminate all Menhir parser conflicts (75 S/R + 10 R/R) — dedicated grammar-conflict workstream #215 family work.
  5. Docs: update language reference / guides for #{.

Done when

#{ is the sole record-literal syntax estate-wide, affinescript 257
gate green, conflict count reported, residual families re-listed on
#215.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmajorMajor issue — significant scope, broader impact than a feature/bug

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions