Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew capability or improvement to existing behaviourmajorMajor 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