Skip to content

dag grammar: arrow-lambda bodies may be an unbraced let-headed statement sequence - #13182

Merged
gunbai-bot[bot] merged 2 commits into
mainfrom
session/eager-crab-610-lambda
Oct 4, 2026
Merged

gunbai-bot[bot] merged 2 commits into
mainfrom
session/eager-crab-610-lambda

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

An arrow lambda's body may now be an unbraced statement sequence that begins with a let:

rows |> map(r =>
  let slug = case_slug(id: r.id)
  [concat("a_", slug), concat("b_", slug)]
)

Before, the body after => was read only as an expression, so the let refused the parse in 8 corpus files.

Grammar (v2.extdeps.languages.dag)

  • dag_grammar_arrow_lambda_body_expr: after =>, a lambda body is one of two disjoint alternatives:
    • lambda_stmt_body: lambda_let_stmt stmt*. Its head is a statement whose captured child is a let_expr: the let form or either let sugar.
    • an expression, guarded by dag_grammar_let_sugar_head_expr. This is the same guard the expression statement already carries. let itself begins no expression.
  • Disjoint by construction. The overlap roster stays at 0 rows (measured).
  • Where the body ends. No statement begins with , or ), so the statement repetition stops where the enclosing argument list continues. This is the disjoint end the ruling asked for.
  • Accepted-input meaning is unchanged. A body that begins with a let head never parsed as an expression before. So no previously accepted input can change meaning; only previously refused inputs are newly admitted.

Classification (v2.compiler.fold_lowering)

The one decoder is fv_arrow_body, which wraps fv_block_body_optional. It now answers three kinds:

  • FunctionLiteralStatementBody { body } for lambda_stmt_body;
  • FunctionLiteralBlockBody for a braced fn_body;
  • FunctionLiteralExprBody otherwise.

The statement body's captured child is the statement spine. So body_lower_function_value_body lowers it with body_lower_stmt_seq, the same authority a fn body and a match-arm statement body use: a let binds the rest and the last statement is the value. The fold step reads it via fold_step_body, and the exit walk treats it like match_arm_stmt_body.

Controls

All are in v2.test.claim.body_lowering.arrow_lambda_block_body, beside the existing block-body ones and in the same warm producer:

  • fold step, unbraced statement body: normalizes;
  • real site, dag/std/graph.dag build_adjacency_views verbatim: normalizes, and takes the statement route, asserted twice:
    • at the parse: the lambda's body is lambda_stmt_body;
    • at the decoder: arrow_lambda_parts_optional answers FunctionLiteralStatementBody;
  • expression body: takes no statement route (the negative control);
  • the existing five controls (braced arrow block, let+record block, value-position block, fn literal, expression body) still pass.

Corpus

Each file was run through the v2 front end: v2.lens.enforcement.grammar_coverage grammar_coverage_ingest_path, which reads the real file and runs tokenize, parse and normalize. Main and this branch ran with the same binary in one remote dispatch:

file main this branch
dag/test/claim/v1_complexity_capability_census_witness_test.dag parse: parse_declaration_marker_not_test ParsedClean
dag/test/claim/witness_row_cost_projection_witness_test.dag parse: parse_g0_tokens_remain ParsedClean
dag/std/orthogonal_geometry.dag parse: parse_g0_tokens_remain ParsedClean
dag/std/graph.dag parse: parse_g0_tokens_remain ParsedClean
dag/gunbc/emit_copy_qualification_fixture_gen.dag parse: parse_g0_tokens_remain ParsedClean
dag/gunbc/emit_copy_qualification.dag parse: parse_g0_tokens_remain ParsedClean
dag/gunbc/instruments/emit_copy_qualification_transport.dag parse: parse_g0_tokens_remain ParsedClean
dag/gunbc/instruments/opaque_realization_census.dag parse: parse_g0_tokens_remain ParsedClean

All 8 files in the corpus that have the shape (=> at end of line, then a let head) are measured.

The body ends disjointly inside call arguments

  • By construction: the repetition is stmt*. no_statement_begins_with_a_comma_or_a_close_paren_holds reads the prepared grammar's own FIRST row for stmt, the authority the choice proofs read. It holds let and neither , nor ), so the body stops where the argument list continues, not by order.
  • The choice: the lambda_stmt_body | guarded-expression choice is in the overlap roster, which measures 0 rows at this head.
  • Routes: value_arrow_statement_body_ends_at_the_next_argument_holds ends at , (apply(g: a => let b = a + 1 / b, v: v)). graph_adjacency_real_site_* and fold_step_arrow_unbraced_statement_body_lowers_holds end at ).

Prefix table (not landed)

The once-per-grammar 2-prefix table (ruling A) was tried and measured +6%: prepare fill 8.4s vs 7.9s, 4 runs, same binary, with all 63 controls and both rosters (dag, python) identical. It was dropped.

Floor note

alb_verdicts now computes ten verdicts instead of five. It stays the same identity already enrolled warm (v2.test.claim.body_lowering.arrow_lambda_block_body.alb_verdicts). floor_pure_producer_share.dag is not edited, because of the warm-row freeze until #13043.

Failure-mode row

arrow_lambda_block_body_read_as_an_expression gains a receipt that the one body-kind decoder (fv_arrow_body) has a third arm, and cites fv_arrow_body in its evidence.

🤖 Generated with Claude Code

gunbc-ci-auto-heal and others added 2 commits October 4, 2026 01:15
…ence that begins with a let

'(r) =>' then 'let slug = f(r)' then the value, ending at the call's ')' or
',', refused the parse in eight corpus files. The body is now a choice between
lambda_stmt_body (a let-headed statement spine) and the expression, disjoint by
the let-sugar head guard the expression statement already carries. The one
decoder (fold_lowering fv_arrow_body) classifies it as FunctionLiteralStatementBody,
which lowers through the statement spine like every statement sequence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t, roster file untouched

The prepared grammar's FIRST row for stmt holds let and neither ',' nor ')',
and a value-position lambda whose unbraced body is followed by the next
argument normalizes. The arrow_lambda_block_body RFM row records the decoder's
third arm. The floor_pure_producer_share comment edit is reverted (warm-row
freeze): alb_verdicts keeps its enrolled identity.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit 61f9980 Oct 4, 2026
4 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/eager-crab-610-lambda branch October 4, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants