Skip to content

mruby-regexp: add Regexp#names and MatchData#names - #7027

Merged
matz merged 1 commit into
mruby:masterfrom
takumin:regexp-names
Aug 9, 2026
Merged

mruby-regexp: add Regexp#names and MatchData#names#7027
matz merged 1 commit into
mruby:masterfrom
takumin:regexp-names

Conversation

@takumin

@takumin takumin commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Neither Regexp nor MatchData answers names, so code that asks a pattern for its
capture names raises NoMethodError.

/(?<a>x)(?<b>y)/.names         # CRuby: ["a", "b"], mruby: NoMethodError
/(?<x>a)/.match("a").names     # CRuby: ["x"],      mruby: NoMethodError

Everything both methods need is already reachable from Ruby. regexp_init() stores a
name to group-number table in the @named_captures instance variable when the pattern
has a named group, and MatchData#regexp is already a C binding.

Changes

  • Regexp#names returns the keys of the @named_captures table, or [] when the
    pattern has no named group and the instance variable was never set. regexp_init()
    inserts in the order the compiler registered the groups, which is ascending group
    number, so the key order already matches CRuby's.
  • MatchData#names is regexp.names. On an uninitialized receiver such as
    MatchData.allocate it raises the same TypeError that MatchData#named_captures
    already raises, because MatchData#regexp reads DATA_PTR through DATA_GET_PTR().
  • Both live in mrblib; no new C binding is required.
  • Both are added to the Ruby API block in mrbgems/mruby-regexp/README.md.

A pattern with duplicate names registers one table entry per group and the Hash keeps
only the last of them, so names reports the name once, which is what CRuby answers
as well:

/(?<a>x)|(?<a>b)/.names        # CRuby: ["a"], mruby: ["a"]

Tests

New assert("Regexp#names") and assert("MatchData#names") in
mrbgems/mruby-regexp/test/regexp.rb cover a pattern with named groups and a pattern
with none.

rake test passes.

Summary by CodeRabbit

  • New Features

    • Added Regexp#names to return named capture names in group order.
    • Added MatchData#names to return the named captures associated with a match.
    • Methods return an empty array when no named captures are present and handle repeated names consistently.
  • Documentation

    • Documented both methods and their return values.
  • Tests

    • Added coverage for ordering, empty results, and duplicate capture names.

@takumin
takumin requested a review from matz as a code owner August 9, 2026 11:11
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c2e14a9-672b-49ef-b16f-66dab4d93372

📥 Commits

Reviewing files that changed from the base of the PR and between aba90a0 and 6731013.

📒 Files selected for processing (3)
  • mrbgems/mruby-regexp/README.md
  • mrbgems/mruby-regexp/mrblib/regexp.rb
  • mrbgems/mruby-regexp/test/regexp.rb
🚧 Files skipped from review as they are similar to previous changes (3)
  • mrbgems/mruby-regexp/README.md
  • mrbgems/mruby-regexp/mrblib/regexp.rb
  • mrbgems/mruby-regexp/test/regexp.rb

📝 Walkthrough

Walkthrough

Added Regexp#names and MatchData#names. Both return named capture names, with empty arrays for patterns without named captures. Documentation and tests cover both APIs.

Changes

Named capture names

Layer / File(s) Summary
Regexp names API
mrbgems/mruby-regexp/mrblib/regexp.rb, mrbgems/mruby-regexp/test/regexp.rb, mrbgems/mruby-regexp/README.md
Regexp#names returns named capture names in group order or an empty array. Tests cover ordering, duplicate names, and unnamed patterns.
MatchData names delegation
mrbgems/mruby-regexp/mrblib/regexp.rb, mrbgems/mruby-regexp/test/regexp.rb, mrbgems/mruby-regexp/README.md
MatchData#names delegates to regexp.names. Tests and documentation cover named and unnamed patterns.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • mruby/mruby#7007: Changes named-capture handling in mruby-regexp and its tests.
  • mruby/mruby#7021: Changes related named-capture parser and API functionality in mruby-regexp.

Suggested reviewers: matz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the two methods added by the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
mrbgems/mruby-regexp/test/regexp.rb (1)

1200-1204: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression test for duplicate capture names.

The PR contract requires duplicate names to appear once. These tests cover distinct names and unnamed patterns, but not duplicates.

Suggested test
   assert_equal ["year", "month", "day"],
                /(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/.names
+  assert_equal ["tag"], /(?<tag>\w+)-(?<tag>\w+)/.names
   assert_equal [], /\d+/.names
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mrbgems/mruby-regexp/test/regexp.rb` around lines 1200 - 1204, Extend the
Regexp#names assertions to cover a pattern with duplicate named captures, such
as repeated year groups, and verify the returned array contains that name only
once. Keep the existing distinct-name and unnamed-pattern assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mrbgems/mruby-regexp/README.md`:
- Line 57: Correct the documented results for the Regexp#names and
MatchData#names examples: either update both patterns to contain named captures
and retain the named-result examples, or change both expected results to [].
Keep each example internally consistent with its pattern.

---

Nitpick comments:
In `@mrbgems/mruby-regexp/test/regexp.rb`:
- Around line 1200-1204: Extend the Regexp#names assertions to cover a pattern
with duplicate named captures, such as repeated year groups, and verify the
returned array contains that name only once. Keep the existing distinct-name and
unnamed-pattern assertions unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 787202f7-a4b0-4009-ac5d-856a7bca93bd

📥 Commits

Reviewing files that changed from the base of the PR and between 4461d87 and aa463d9.

📒 Files selected for processing (3)
  • mrbgems/mruby-regexp/README.md
  • mrbgems/mruby-regexp/mrblib/regexp.rb
  • mrbgems/mruby-regexp/test/regexp.rb

Comment thread mrbgems/mruby-regexp/README.md
@matz

matz commented Aug 9, 2026

Copy link
Copy Markdown
Member

This needs a rebase: #7026 landed first as cfc496a.

I merged that one first only because it changes behaviour that already existed, where this adds methods that did not. Both are wanted, and I verified them together before merging anything.

The conflict is not in the implementation. Both changes add a line to the same two places:

  • mrbgems/mruby-regexp/README.md, the Ruby API block: re.named_captures gained a value shape, and this adds re.names next to it
  • mrbgems/mruby-regexp/test/regexp.rb, adjacent assertions

Keeping both sides is the whole resolution. Applied that way on top of #7022 and #7026, the suite passes (2133 OK, 0 KO, 78 bintests) with no sanitizer report, and the eight rows I compared against CRuby all agree:

/(?<a>x)/.named_captures            {"a" => [1]}
/(?<a>x)(?<b>y)/.named_captures     {"a" => [1], "b" => [2]}
re.named_captures["a"] = 99 ; re.named_captures   {"a" => [1]}
/(x)/.named_captures                {}
/(?<a>x)(?<b>y)/.names              ["a", "b"]
/(x)/.names                         []
/(?<x>a)/.match("a").names          ["x"]
/(?<x>a)/.match("a").named_captures {"x" => "a"}

I checked your claim about key order rather than taking it on trust: regexp_init() inserts into @named_captures in the order the compiler registered the groups, which is ascending group number, so names comes out in pattern order without sorting.

One note for whoever picks up duplicate names later, since both of you flagged it and neither change touches it: /(?<a>x)|(?<a>b)/ still answers {"a" => [2]} and match("b")[:a] still answers nil. names reporting the name once is right either way, so nothing here has to change when that is fixed.

Neither class answered `names`, so code asking a pattern for its capture
names raised `NoMethodError`.

```ruby
/(?<a>x)(?<b>y)/.names      # CRuby: ["a", "b"], mruby: NoMethodError
/(?<x>a)/.match("a").names  # CRuby: ["x"],      mruby: NoMethodError
```

`Regexp#names` reads the keys of the `@named_captures` table that
`regexp_init()` fills. The compiler registers named groups in ascending
group number, so the key order already matches CRuby's.
`MatchData#names` composes that with the existing `MatchData#regexp`
binding.

Both live in mrblib because they need nothing that is not already
exposed to Ruby, so no new C binding is required.
@takumin

takumin commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto cfc496ace. Resolved exactly as you described, keeping both sides in each place:

  • mrbgems/mruby-regexp/README.md: re.names now sits right after re.named_captures, matching the md.named_captures / md.names order in the MatchData block below.
  • mrbgems/mruby-regexp/test/regexp.rb: assert("Regexp#named_captures") and assert("Regexp#names") are two separate blocks.

mrbgems/mruby-regexp/mrblib/regexp.rb merged cleanly, so the implementation is unchanged from what you reviewed. The tree is the same 3 files and 26 added lines as before.

rake test passes here (1940 OK, 0 KO, 105 bintests), and all eight rows you compared against CRuby reproduce on the rebased branch.

Thanks for checking the key order claim instead of taking it on trust. Agreed on duplicate names: names reporting the name once holds either way, so that fix can land independently of this.

@matz
matz merged commit 66d3f0a into mruby:master Aug 9, 2026
20 of 21 checks passed
@takumin
takumin deleted the regexp-names branch August 9, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants