Repository navigation
deps: parser v0.4.0 — do...end as a paren-less command argument (+223 examples, +11 stdlib files) - #677
Merged
Conversation
github.com/go-ruby-parser/parser v0.3.0 -> v0.4.0. v0.4.0 splits the parser's single `noDo` flag into `noDo` (MRI's COND_P()) and `noDoBlock` (MRI's CMDARG_P()), read through one `atDoBlock()` helper, so the CMDARG reset that a nested statement body performs becomes expressible. That unblocks the `do...end`-block-as-argument family: a `do...end` inside a paren-less command argument now attaches to the call it follows instead of being refused. go.mod / go.sum only. No replace directive. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps
github.com/go-ruby-parser/parserv0.3.0 → v0.4.0.go.mod/go.sumonly; noreplacedirective (checked withgrep -n replace go.modbefore pushing).v0.4.0 splits the parser's single
noDoflag intonoDo(MRI'sCOND_P()) andnoDoBlock(MRI'sCMDARG_P()), read through oneatDoBlock()helper. One flag could not express the reset MRI performs on entering a nested statement body, because MRI keeps two independent bit-stacks that are reset in different places. The effect for rbgo: ado…endwritten inside a paren-less command argument now attaches to the call it follows instead of being refused.What moved — ruby/spec ratchet
Measured on the pinned corpus (
SPEC_SHA 87b1631,/Users/Shared/rbgo-measure/rubyspec-corpus) with the vendoredspec_helper.rbshim, one rbgo process per file,TIMEOUT=25, 2-way — this corpus is not reproducible under full parallelism.origin/main(3e8e3cc) and this branch were measured back to back in the same conditions, with a purpose-built per-file runner that emits one row per input and fails if rows ≠ inputs (2206 in, 2206 out, both runs).origin/main, parser v0.3.0FLOOR(22475) is not touched — it is raised in the wave's consolidated PR.CI's own ratchet lane on this branch (full parallelism,
TIMEOUT=60) reads 22 724 passing,+249over the floor, with files loaded: 2196, failed to load: 10 — the same per-file status split this local run produced (2196 ok; 7 whole-file failures + 3 timeouts). The 13-example gap between 22 711 and 22 724 is the load/timeout sensitivity the corpus is known for, and it is why the delta was taken from two back-to-back local runs rather than by comparing a local number against a remote one.Every file whose number changed:
core/file/printf_spec.rbcore/time/utc_spec.rblanguage/string_spec.rbNo file moved down. Whole-file failures 9 → 7, timeouts 3 → 3 (the same three files), files loading OK 2194 → 2196.
The two zeros were parse errors, not hangs — established by running each file directly rather than inferring it:
core/file/printf_spec.rb:9—it_behaves_like :kernel_sprintf, -> format, *args { … File.open(@filename, "w") do |f| … end … }: ado…endin a lambda's brace body that is itself a paren-less command argument.core/time/utc_spec.rb:89—guard -> { with_timezone 'right/UTC' do … end } do … end: the same shape.language/string_spec.rbgains exactly the example ruby/spec names "allows a dynamic string to parse a nested do…end block as an argument to a call without parens, interpolated", reported onmainaserror SyntaxError parse error at line 1: unexpected "do" after statement. That is the string-interpolation entry (tSTRING_DBEG).A second corpus: MRI 4.0.5's own stdlib
The ratchet can only see shapes ruby/spec happens to contain.
scripts/conformance/rspec/parsesweeprun over MRI 4.0.5'srubylibdir(728 files) measures the front end on a population the corpus does not cover:11 files newly accepted, 0 regressed (every row joined, 728 in / 728 out), all previously
unexpected "do" after statement:The same sweep over the whole pinned ruby/spec tree (4413
.rbfiles, not justlanguage/+core/): 4405 → 4407; the two gained are exactly the two spec files above, and the 6 that remain are 2 fixtures that MRI also rejects on purpose plus 4 unrelated gaps (listed below).Verified in both directions, against local MRI 4.0.5
Every shape below was run byte for byte through
ruby,ruby -c,ruby --parser=parse.y -cand both rbgo binaries. The witness prints which call actually received the block (block_given?), so it measures attachment, not just parseability.Newly accepted, and attaching to the call MRI attaches it to — all were
parse erroronmain:Refusal direction — 17 shapes MRI rejects or accepts, checked for over-acceptance. An over-accepting parser is a silent regression, so this is the half that matters most. No shape moved from refused to accepted against MRI:
Two pre-existing over-acceptances were found, identical on v0.3.0 and v0.4.0, so neither is caused by this PR:
foo :a, y 1andcase y do end; endareSyntaxErrorin MRI under both its parsers and are accepted by rbgo. Reported, not touched.Unchanged shapes that had to stay unchanged, and did:
foo :a, y do end(block isfoo's),foo(:a, y do end)(block isy's),foo :a, y(1) do end,foo :a, bar_x = y do end,while [1].each { |x| y do end }; end,while y(); end,for i in [1] do (y do end); end,def m(x = y do end); end.Bibliography for each decision, read from
parse.yatv3_4_0(cached under/Users/Shared/rbgo-measure/mri-c/): the three-waykeyword_do/keyword_do_cond/keyword_do_blockdecision inparser_yylex(10519-10527);COND_PUSH(1)inexpr_value_do(3430);CMDARG_PUSH(1)incommand_argswith its lookahead cancellation for(/[(4252-4265); andCOND_PUSH(0); CMDARG_PUSH(0)in the lexer cases for((11193),[(11221) and{(11245) and in thetSTRING_DBEGaction (6181).Gates
CGO_ENABLED=0 GOWORK=off go test ./...— full suite, green (internal/vm175.0s; exit 0)CGO_ENABLED=0 go build ./...andCGO_ENABLED=0 GOOS=wasip1 GOARCH=wasm go build ./...— greengo vet ./...— clean;gofmt -l internal/ cmd/— empty-race -coverpkg=$(go list ./internal/... | paste -sd, -)on this branch and on a detached worktree atorigin/main, and compared as a LIST, never as a total. Both sides:internal/vm99.0%, reported total 99.9%, 17 functions below 100% — and the two below-100 lists are byte-identical (sha182afa873…). No function was orphaned: the only two at0.0%(internal/vm/bigdecimal_bind.go:35 Inspect,internal/vm/date_bind.go:44 Inspect) are at0.0%onorigin/mainas well.internal/vm/prelude_frozen_gen.go: regenerated withgo run ./cmd/freeze-preludefrom the repo root, byte-identical (sha1153c04b5…). Because a byte-identical regeneration is not by itself evidence that anything ran, the file was also deleted and regenerated from scratch under both parser versions: both reproduce the same sha1. So the generator does write, andprelude.rb's lowering is genuinely unaffected by the bump.Left undone, with reasons
FLOORnot raised — it belongs to the wave's consolidated PR. Measured total on this branch: 22711.The COND half of the same fix is still missing, upstream of this repo. MRI's lexer pushes
COND_PUSH(0)andCMDARG_PUSH(0)at(,[,{and in thetSTRING_DBEGaction. v0.4.0 clearsnoDoBlock(CMDARG) at those entries but never clearsnoDo(COND), so inside awhile/untilcondition these six shapes are still refused although MRI 4.0.5 accepts them under both its parsers — identical on v0.3.0, so a pre-existing gap and not a regression here:Owner:
go-ruby-parser/parser. v0.4.0's comment inparseStatementsstates the MRI rule correctly ("only the bracket and interpolation entries also push COND 0") but no bracket or interpolation entry point acts on it.Four further parser gaps, each reduced to a one-liner, found by the stdlib sweep and identical on v0.3.0 and v0.4.0 (owner:
go-ruby-parser/parser):def M::name(v); end/def self::m; endexpected CONST, got …rbconfig.rb:310,mkmf.rb:327thenon the line after the conditionunexpected token "then"ripper/filter.rb:65,ripper/lexer.rb:279whenwith its value on the next lineunexpected token "\n"rubygems/request.rb:125-> x: { }— lambda kwarg + brace bodyexpected {, got "x"core/proc/parameters_spec.rb:65BEGIN { }/END { }(language/fixtures/begin_file.rb,shared/kernel/fixtures/END.rb) are also still refused.Two real rbgo defects this PR makes visible for the first time, in
core/file/printf_spec.rb, which contributed 0 before:sprintfdoes not preserve the encoding of the format string (UTF-8whereUS-ASCIIis expected), and notoo many arguments for format stringwarning is emitted when$VERBOSEis true. Not touched — this PR isgo.mod-only.Four premises the measurement contradicted
"MRI through our shim is a per-file ceiling." It is a valuable second measurement but it is not an upper bound. rbgo scores above MRI-through-the-shim on 31 files (+190 examples) and below on 531 (−1406); corpus total for MRI through the shim is 23927 against rbgo's 22711.
On
core/file/printf_spec.rbrbgo's 161 beats MRI's 160, and the two failure sets are disjoint. The cause is a real 3.x → 4.0 drift the pinned corpus cannot see: the spec asserts/no implicit conversion of nil into Integer/forsprintf("%c", nil), while MRI 4.0.5 raisesTypeError: no implicit conversion from nil to integer— so MRI fails its own spec on three examples there and rbgo passes them by emitting the 3.x wording. Nothing was weakened for this; it is a decision about the corpus pin (and about which wording rbgo should target), not about this PR.On other files the shim limits MRI, not the reverse. In
core/dir/glob_spec.rbMRI reads 16 to rbgo's 92 because everyDir.globassertion returns[]under the shim's fixture setup. So an above-ceiling reading on a file is a reason to look at the shim, never a reason to celebrate."
mainreports 100.0% while ~17 functions sit below it." Locally the count is exactly 17 and the reported total is 99.9% — onorigin/mainas much as on the branch — so the CI gate expression (cov >= 100.0) would fail on unmodifiedmainif run on this host. In CI's ubuntu lane on this branch, by contrast, the below-100 list is empty and the total is 100.0%. So those 17 belong to this machine's environment (the two at0.0%are the bigdecimal and dateInspectbindings), not tomain. The method still holds — compare the LIST, never the total — but the number does not describe CI, which is why the local comparison was made against a detachedorigin/mainon the same host rather than against a remembered figure.🤖 Generated with Claude Code