Skip to content

Fix Ruby grammar conformance gaps - #4

Merged
tarebyte merged 4 commits into
masterfrom
fix/ruby-spec-conformance
Aug 24, 2026
Merged

Fix Ruby grammar conformance gaps#4
tarebyte merged 4 commits into
masterfrom
fix/ruby-spec-conformance

Conversation

@tarebyte

@tarebyte tarebyte commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix scanner ambiguity around heredocs, operators, percent literals, regexes, Unicode symbols, and comments.
  • Fix lambda boundaries, ranges, parenthesized returns, endless method command bodies, and declaration comment ownership.
  • Parse CI examples recursively and clear the Prism conformance allowlist.
  • Pin the CI parser test ABI version to 15.

References

Validation

  • 337 corpus and highlight tests
  • 4,431 Prism-valid ruby/spec files with zero mismatches
  • 4,433 recursively parsed example files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d800609e-6d1c-43d3-8811-d59ad9495420
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d800609e-6d1c-43d3-8811-d59ad9495420

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new comment scanner and endless-method command rules still mishandle valid Ruby inputs.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Improves Ruby grammar conformance across literals, operators, declarations, control flow, and CI validation.

Changes:

  • Resolves scanner ambiguities involving heredocs, comments, regexes, percent literals, and operators.
  • Expands grammar handling for lambdas, ranges, returns, and endless methods.
  • Strengthens corpus, ruby/spec, and recursive example validation.
File summaries
File Description
grammar.js Updates Ruby grammar rules.
src/scanner.c Extends external token scanning.
src/grammar.json Regenerates grammar metadata.
src/node-types.json Regenerates node definitions.
test/corpus/literals.txt Adds literal and heredoc coverage.
test/corpus/expressions.txt Adds expression and lambda coverage.
test/corpus/declarations.txt Covers declarations and endless methods.
test/corpus/control-flow.txt Covers returns and rescue boundaries.
test/corpus/comments.txt Covers comment scanning behavior.
test/prism/ruby-spec-allowlist.txt Clears resolved conformance exceptions.
.github/workflows/ci.yml Pins ABI 14 and recursively parses examples.
package.json Adds the grammar lint command.
Review details
  • Files reviewed: 12/13 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/scanner.c
Comment thread grammar.js Outdated
Comment thread grammar.js Outdated
MRI's grammar defines an endless method body as `endless_command : command`,
reusing the same `command` nonterminal (and therefore the same `command_args`)
as any other command call. The previous rule instead enumerated an allowlist of
argument types, which is wrong by construction: it both rejects valid programs
and, worse, silently misparses them.

The silent misparses are the more serious failure. When the allowlist runs out
of usable tokens, the command call is truncated there and the surrounding `arg`
grammar reabsorbs the remainder, yielding an error-free but incorrect tree. For
example `def f = puts 1..2` grouped as `(def f = puts 1)..2`, and
`def f = puts %w[a]` parsed the literal as a binary modulo. Because nothing in
the tree is an ERROR or MISSING node, the conformance suite reports no problem.

`command_args : call_args`, and the elements of `call_args` are `arg`s rather
than `expr`s, so the argument rule is anchored at `_arg`. That is both the
correct boundary (an endless body cannot contain a bare `and`/`or`/`in`) and the
one that resolves the ambiguity with `def` appearing as a call receiver, since
`method` is reachable from `_primary`.

Also fixed alongside:

- Lambdas accept block-local declarations. `f_larglist` is
  `'(' f_largs opt_bv_decl ')'`, so `->(a; b) { }` is valid even though a
  method's parameter list has no such clause.
- `!@` is a valid operator method name in `op`, next to `+@`, `-@` and `~@`.
- `=begin` only opens a block comment when the keyword ends the token;
  `=beginning` was being swallowed as a comment through end of line.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b691b14c-5c15-4011-97d3-721070793751

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes are comprehensively tested, internally consistent, and all parser, conformance, fuzz, and lint checks pass.

Review details
  • Files reviewed: 12/13 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

A heredoc opened inside a running heredoc body suspends that body: Ruby
reads the new body at the next line break and resumes the enclosing one
once the nested terminator is reached. `open_heredocs` modelled this with
a single cursor at index 0, which had to serve both "next body to start"
and "body currently being scanned". Those diverge as soon as heredocs
nest, so the enclosing body was matched against the nested word and its
own terminator was left over as a stray expression.

Split the two roles. Started heredocs are kept as a prefix, innermost
last, followed by the pending ones in the order they were opened. Content
scanning uses the innermost started heredoc, while a pending heredoc
starts at the next line break. Each pending heredoc records how many
bodies were active when it was opened so that it suspends only the body
it was opened inside of, rather than interrupting a sibling that is
already running.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7c65d880-b26f-4f6a-9862-8d7c1a9ff0c7
@tarebyte
tarebyte merged commit adec52e into master Aug 24, 2026
6 checks passed
@tarebyte
tarebyte deleted the fix/ruby-spec-conformance branch August 24, 2026 03:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants