Skip to content

Parse six high-impact real-world Ruby front-end constructs (Round 1) - #3

Merged
tannevaled merged 1 commit into
mainfrom
parser-100pct-round1
Jun 26, 2026
Merged

Parse six high-impact real-world Ruby front-end constructs (Round 1)#3
tannevaled merged 1 commit into
mainfrom
parser-100pct-round1

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Round 1 of the parse-conformance push toward 100% on real Ruby apps (Rails / Puppet / stdlib / RuboCop / Chef). Six high-impact front-end constructs, each verified valid against MRI 4.0 (ruby -c) and covered by MRI-parity table tests.

Rails parse-acceptance sweep: 21.12% → 83.09% (723 → 2844 of 3423 .rb files), zero regressions (every file that parsed before still parses).

Constructs landed

  1. masgn to non-local targets (the single biggest gap, ~1692 occurrences). The multiple-assignment LHS parser now accepts instance/class/global variables, constants, scoped constants, attribute targets (obj.x), and index targets (arr[i]), with splats on any of them plus nameless * and trailing ,. Non-local targets populate MultiAssign.Targets; attribute/index targets lower to their setter-call shape (x= / []=). The all-locals fast path keeps Targets nil so existing consumers are untouched. A bracket-aware lookahead (scanMlhsTargetTail/scanBalanced) drives the disambiguation.

    • @a, @b, @c = x, y, z · @@a, @@b = … · $a, $b = … · obj.x, obj.y = … · arr[i], h[k] = … · @a, *@b = … · *@a, @b = …
  2. rescue/else/ensure without an explicit begin on do…end blocks and class/module/singleton-class bodies (def bodies were already supported), folded into a Begin via the shared parseRescueTail. Brace blocks still reject it, as MRI does.

  3. Anonymous + ordered params: bare *, **, & in def and lambda parameter lists (sentinel names */**/&), and kwargs / double-splat followed by a &block param (def f(**options, &block), def f(*args, **opts, &block)). **nil is recorded too.

  4. Special global variables in the lexer ($$ $! $@ $/ $\ $; $, $. $< $> $? $* $" $: $+ …), including inside string interpolation ("PID-#{$$}"); previously lexed as ILLEGAL.

  5. Value-omitted shorthand keyword arguments (Ruby 3.4) in calls, super, and array literals: foo(format:, name:)foo(format: format, name: name). Array literals now share the call-argument grammar, so trailing key: value pairs collapse into one implicit hash and a trailing comma is accepted in both calls and arrays.

  6. Single-quoted symbols :'foo.bar' / :'data-remote' (no interpolation, honoring \' and \\), alongside the existing :"…" form.

Quality

  • 100% coverage retained (CI gate); go test -race green; gofmt + go vet clean; CGO=0.
  • New MRI-parity tests: masgn_targets_test.go, round1_features_test.go, lexer/lexer_round1_test.go.

Downstream (rbgo) follow-ups

These constructs now parse; some need a matching rbgo compiler case to execute (out of scope here — no rbgo changes in this PR):

🤖 Generated with Claude Code

Round 1 of the parse-conformance push toward 100% on real Ruby apps
(Rails/Puppet/stdlib/RuboCop/Chef). Each construct is verified valid
against MRI (ruby -c) and covered by MRI-parity table tests.

1. masgn to non-local targets (the biggest gap): the multiple-assignment
   LHS parser now accepts instance/class/global variables, constants,
   scoped constants, attribute targets (obj.x), and index targets
   (arr[i]), with splats on any of them and a nameless `*`/trailing `,`.
   Non-local targets populate MultiAssign.Targets (attribute/index
   targets lower to their setter-call shape: x= / []=); the all-locals
   fast path keeps Targets nil. A general bracket-aware lookahead
   (scanMlhsTargetTail/scanBalanced) drives the disambiguation.

2. rescue/else/ensure without an explicit begin on do...end blocks and
   class/module/singleton-class bodies (def bodies already supported),
   folded into a Begin via the shared parseRescueTail. Brace blocks
   still reject it, as MRI does.

3. Anonymous and ordered parameters: bare `*`, `**`, `&` in def and
   lambda parameter lists (recorded with sentinel names *, **, &), and
   kwargs / double-splat followed by a &block param (the **opts, &block
   ordering that previously failed). `**nil` is recorded too.

4. Special global variables in the lexer ($$ $! $@ $/ $\ $; $, $. $< $>
   $? $* $" $: $+ and the like), including inside string interpolation
   ("PID-#{$$}"); previously lexed as ILLEGAL.

5. Value-omitted shorthand keyword arguments (Ruby 3.4) in calls, super,
   and array literals: foo(format:, name:) == foo(format: format,
   name: name). Array literals now share the call-argument grammar so
   trailing key: value pairs collapse into one implicit hash, and a
   trailing comma is accepted in both calls and arrays.

6. Single-quoted symbols :'foo.bar' / :'data-remote' (no interpolation,
   honoring \' and \\), alongside the existing :"..." form.

Rails parse-acceptance sweep: 21.12% -> 83.09% (723 -> 2844 / 3423
files), zero regressions. 100% coverage retained; gofmt + go vet clean;
CGO=0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 14edf19 into main Jun 26, 2026
9 checks passed
@tannevaled
tannevaled deleted the parser-100pct-round1 branch June 26, 2026 12:01
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.

1 participant