Skip to content

README: v0.4.0 parses all three former limitations, and the performance figures re-measured - #36

Merged
tannevaled merged 1 commit into
mainfrom
wave34-docs-parser
Sep 26, 2026
Merged

tannevaled merged 1 commit into
mainfrom
wave34-docs-parser

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Re-measured on 2026-09-26 against MRI 4.0.5 on the same host (darwin/arm64). main is exactly v0.4.0.

The three "Known limitations" all parse now

Verified by calling parser.Parse on each at v0.4.0:

PARSES   parenless kwargs                   "foo a: 1"
PARSES   parenless splat                    "foo *a"
PARSES   parenless block arg                "foo &blk"
PARSES   default block param                "[1].each { |a = 1| a }"
PARSES   positional find-pattern Class(a)   "case x\nin Point(a, b)\n  a\nend"
REFUSED  BEGIN block                        "BEGIN { 1 }" -> unexpected "{" after statement
REFUSED  END block                          "END { 1 }"   -> unexpected "{" after statement

The two refusals are the probe's own control: a check that answers "parses" to everything is not a check. BEGIN { } / END { } are now the only listed gap.

Added a completeness figure a reader can verify: of 47 top-level CRuby 4.0.5 stdlib files MRI accepts, this parser accepts 46 (mkmf.rb is the refusal, expected CONST, got "log_open"), and it parses all 80 _spec.rb files in the pinned ruby/spec language/ corpus.

The performance claim did not reproduce

The README claimed this parser "beats MRI's reference C parser" — 2.1× on real stdlib files, ~6× on Ripper.sexp, 24–30× on Ripper.lex. Measured on 46 real CRuby 4.0.5 stdlib files that both engines accept (768 KB, 20 in-process iterations each):

per file
parser.Parse (full Go AST) 0.45 ms
RubyVM::AbstractSyntaxTree.parse 0.29 ms 1.6× faster than us
Ripper.sexp 0.86 ms 1.9× slower than us — not ~6×
lexer.Tokenize 0.36 ms
Ripper.lex 2.16 ms 6.0× slower than us — not 24–30×

So the honest statement is: comfortably quicker than Ripper, which is what most Ruby tooling actually uses, and roughly 1.6× slower than the C parser inside CRuby. The section now says that, and records that the old figures did not reproduce without claiming the original method was wrong — the corpus and host both differ from that run, and the note tells readers not to quote the old numbers either way.

Two honesty notes I built into the measurement: my first corpus was the eight largest stdlib files and included one this parser refuses, which is a biased sample and not "both-accept" — the table above is the corrected run. And the two lexers' token counts are not comparable (82 k vs 125 k over the same corpus, since Ripper emits whitespace and comment tokens), so only the per-file times are quoted.

Checked and left alone

  • The Usage example compiles and runs against v0.4.0: prog is *ast.Program with 2 top-level nodes (*ast.MethodDef, *ast.Call), so prog.Body is walkable as documented.
  • The WebAssembly section is accurate: ci.yml really does have a goos: [js, wasip1] matrix, and both targets build here. (Worth noting the contrast with the interpreter, where the browser target is broken and ungated.)
  • The CI claim of six 64-bit targets on every push and a 100 % coverage gate on all three OS lanes matches ci.yml.

Also names the current release, and notes that go-embedded-ruby's interpreter still pins v0.3.0, so the consumer is one release behind.

🤖 Generated with Claude Code

…ce figures re-measured

Measured on 2026-09-26, darwin/arm64, against MRI 4.0.5 on the same host.

- Known limitations: all three entries now parse at v0.4.0 (main is exactly v0.4.0).
  Verified by calling parser.Parse on each: paren-less command calls with keyword,
  splat and block args; default block parameters; and the positional Class(a)
  find-pattern. The probe also carried a control that must fail, and it did:
  BEGIN { } / END { } are now the only listed refusal.

- Added a completeness figure that can be checked: of 47 top-level CRuby 4.0.5
  stdlib files MRI accepts, this parser accepts 46; mkmf.rb is the refusal.

- Performance: the claim that go-ruby-parser "beats MRI's reference C parser" did
  not reproduce, and neither did the two ratios beside it. On 46 real CRuby 4.0.5
  stdlib files both engines accept (768 KB, 20 in-process iterations):

      go-ruby-parser Parse       0.45 ms/file
      RubyVM::AST.parse          0.29 ms/file   -> MRI's C parser is 1.6x FASTER
      Ripper.sexp                0.86 ms/file   -> we are 1.9x faster, not ~6x
      lexer.Tokenize             0.36 ms/file
      Ripper.lex                 2.16 ms/file   -> we are 6.0x faster, not 24-30x

  The section now states these numbers, notes that the two lexers' token counts are
  not comparable (82k vs 125k over the same corpus, since Ripper emits whitespace
  and comment tokens), and records that the old figures did not reproduce without
  claiming the original method was wrong -- the corpus and host both differ.

- Names the current release, v0.4.0, and notes the interpreter still pins v0.3.0 and
  that there are no GitHub release notes.

The Usage example was compiled and run against v0.4.0: prog is *ast.Program with 2
top-level nodes (*ast.MethodDef, *ast.Call). The WebAssembly section's claim that CI
builds both js/wasm and wasip1/wasm is correct -- ci.yml has that matrix and both
targets build here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 06a4070 into main Sep 26, 2026
11 checks passed
@tannevaled
tannevaled deleted the wave34-docs-parser branch September 26, 2026 14:31
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