feat(parser): accept parenthesised singleton def receiver - #14
Merged
Conversation
MRI's `def` grammar allows the singleton receiver to be a parenthesised arbitrary expression: `def (expr).method(...) ... end`. The parser only accepted an unparenthesised name (`def self.m`, `def Const.m`, `def obj.m`, ivar/cvar/gvar), rejecting the parenthesised form with "expected method name after def". Add the `def ( <expr> ) . <method>` production. A `(` right after `def` can only open this form (a method name is never a paren group), so it is parsed as the receiver: a single expression (local, constant, method call, `self`, ternary, and/or, assignment) evaluating to the receiver object, exactly as MRI does. A compound group (`def (a; b).m`) is a syntax error in MRI and is rejected here too. The production emits the identical MethodDef shape as the bare `def obj.m` form — Singleton=false with the parsed receiver in Recv — so the compiler lowers it with no special case (rbgo compileSingletonReceiver already handles an arbitrary-expression receiver). When the receiver is a known local or a constant, the node is byte-identical to the bare form's. Tests: paren-local/const/method-call/self receivers, positional/keyword/ paren-less params, endless `= expr` body, and compound-receiver rejection; a table test asserts the parenthesised node equals the bare form's. 100% statement coverage; builds on 6 64-bit arches + wasm. Co-Authored-By: Claude Opus 4.8 <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.
Gap
MRI's `def` grammar allows the singleton receiver to be a parenthesised arbitrary expression: `def (expr).method(...) ... end`. The parser only accepted an unparenthesised name (`def self.m`, `def Const.m`, `def obj.m`, ivar/cvar/gvar), rejecting the parenthesised form with
expected method name after def.```ruby
obj = Object.new
def (obj).kw(a:); a; end # MRI defines singleton kw on obj; parser errored
def (String).foo; "hi"; end # parenthesised const receiver
```
Change
Add the `def ( ) . ` production. A `(` right after `def` can only open this form (a method name is never a paren group), so it is parsed as the receiver: a single expression (local, constant, method call, `self`, ternary, `and`/`or`, assignment) evaluating to the receiver object, exactly as MRI. A compound group (`def (a; b).m`) is a syntax error in MRI and is rejected here too.
Same AST as the bare form
The production emits the identical `MethodDef` shape as the bare `def obj.m` form — `Singleton=false` with the parsed receiver in `Recv` — so the compiler lowers it with no special case (rbgo `compileSingletonReceiver` already handles an arbitrary-expression receiver). When the receiver is a known local or a constant, the node is byte-identical to the bare form's; a table test asserts this.
Verification