Skip to content

mruby-regexp: return Arrays from Regexp#named_captures - #7026

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

mruby-regexp: return Arrays from Regexp#named_captures#7026
matz merged 1 commit into
mruby:masterfrom
takumin:regexp-named-captures-array

Conversation

@takumin

@takumin takumin commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Regexp#named_captures returns Integer values where CRuby returns Arrays.

/(?<a>x)/.named_captures       # CRuby: {"a" => [1]}, mruby: {"a" => 1}

It also hands out the Hash stored in the @named_captures instance variable, so a
caller that writes into the returned Hash corrupts the Regexp for every later caller:

re = /(?<a>x)/
re.named_captures["a"] = 99
re.named_captures              # CRuby: {"a" => [1]}, mruby: {"a" => 99}

CRuby uses an Array because one name can map to several group numbers, as in
/(?<x>a)|(?<x>b)/. mruby-regexp accepts such a pattern but does not handle it
correctly yet: the later group silently overwrites the earlier one in the Hash, and a
name lookup resolves to the first entry in the C table regardless of which alternative
matched. Duplicate names therefore still resolve wrongly with this change applied:

/(?<a>x)|(?<a>b)/.named_captures  # CRuby: {"a" => [1, 2]}, mruby: {"a" => [2]}
/(?<a>x)|(?<a>b)/.match("b")[:a]  # CRuby: "b",             mruby: nil

That is a separate defect in the compiler and in name resolution, left out of this
change. The return type is aligned so that portable code can be written today.

Changes

  • Regexp#named_captures derives a fresh Hash from the @named_captures table on
    every call and wraps each group number in an Array. regexp_init() keeps filling the
    instance variable with the internal name to group-number table, and the public method
    becomes a derivation of it, which is also where the shared-Hash aliasing goes away.
  • MatchData#named_captures is unaffected. It rebuilds its result from the compiled C
    table on every call and already answers name to captured string.
  • Regexp#named_captures is added to the Ruby API block in
    mrbgems/mruby-regexp/README.md.

Tests

A new assert("Regexp#named_captures") in mrbgems/mruby-regexp/test/regexp.rb covers
the Array value type, the empty Hash for a pattern with no named group, and that
mutating the returned Hash does not affect a later call.

rake test passes.

Summary by CodeRabbit

  • New Features

    • Enhanced Regexp#named_captures to return each capture name with its corresponding group index.
    • Returns an empty hash when no named captures are present.
  • Bug Fixes

    • Ensured each call returns an independent result, preventing changes from affecting later calls.
  • Documentation

    • Added documentation describing the named_captures return format.

`Regexp#named_captures` handed out the internal name to group-number
table, so its values were Integers and every caller shared one Hash.

```ruby
/(?<a>x)/.named_captures    # CRuby: {"a" => [1]}, mruby: {"a" => 1}

re = /(?<a>x)/
re.named_captures["a"] = 99
re.named_captures           # CRuby: {"a" => [1]}, mruby: {"a" => 99}
```

CRuby uses an Array because one name can map to several group numbers,
as in `/(?<x>a)|(?<x>b)/`. mruby-regexp does not resolve duplicate names
correctly yet, which is a separate defect in the compiler and in name
resolution; this change only aligns the return type so that portable
code can be written today.

Derive a fresh Hash from the `@named_captures` table on every call and
wrap each group number in an Array. The instance variable keeps holding
the internal name to group-number table that `regexp_init()` fills.
@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: 647ad318-4b14-442f-a0d4-0e67e999aa3a

📥 Commits

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

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

📝 Walkthrough

Walkthrough

Regexp#named_captures now returns fresh hashes that map capture names to one-element group-number arrays. Tests cover mappings, empty patterns, and mutation isolation. The README documents the API result shape.

Changes

Named capture results

Layer / File(s) Summary
Named capture API behavior
mrbgems/mruby-regexp/mrblib/regexp.rb
Regexp#named_captures now builds a fresh hash with array-valued group mappings.
Named capture validation and documentation
mrbgems/mruby-regexp/test/regexp.rb, mrbgems/mruby-regexp/README.md
Tests cover mappings, empty results, and hash isolation. The README documents the return value.

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

Possibly related PRs

  • mruby/mruby#7007: Modifies named-capture handling and tests for name-length truncation.
  • mruby/mruby#7021: Modifies named-capture handling and tests for empty names.

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 describes the main change: Regexp#named_captures now returns Arrays.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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