Skip to content

mruby-regexp: accept a Symbol on the Regexp side of a match - #6995

Merged
matz merged 3 commits into
mruby:masterfrom
takumin:regexp-symbol-argument
Aug 3, 2026
Merged

mruby-regexp: accept a Symbol on the Regexp side of a match#6995
matz merged 3 commits into
mruby:masterfrom
takumin:regexp-symbol-argument

Conversation

@takumin

@takumin takumin commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

CRuby matches a Symbol against its name wherever a Regexp is given a subject,
through reg_operand(). mruby-regexp took strings only, so all four entry
points refused a symbol:

/a/ =~ :ab       # CRuby: 0,         mruby: TypeError
/a/.match(:ab)   # CRuby: MatchData, mruby: TypeError
/a/.match?(:ab)  # CRuby: true,      mruby: TypeError
/a/ === :ab      # CRuby: true,      mruby: false

#=== is the one that matters most. It answers false instead of raising, so a
case over symbols quietly falls through to else and Enumerable#grep
returns an empty array, with nothing to show that the pattern was never given
a chance to run:

%i[to_s to_i size].grep(/^to_/)   # CRuby: [:to_s, :to_i], mruby: []

Convert the operand with mrb_sym_str() in one helper and call it from
regexp_match(), regexp_match_p() and regexp_match_op() in place of
mrb_ensure_string_type(). regexp_case_match() uses the same helper behind
a type test, because #=== has to keep answering false for a type it cannot
match rather than start raising. __byte_match is left alone: it is internal,
is only reached from mrblib with self as the subject, and its argument spec
already pins the type.

For a symbol too long for the inline representation, mrb_sym_str() returns
an mrb_str_new_static() string sharing the symbol table's buffer. Mutating
the MatchData#string that comes out of one is safe, because str_modify()
copies an RSTR_NOFREE buffer before writing, but a test pins that.

Relation to #6993

This is the Regexp-side half of the symbol support #6993 adds on the Symbol
side, and neither closes the other. #6993 delegates Symbol#match and friends
through to_s, so it never reaches the C code changed here; the two are
independent and can land in either order.

One merge-order detail: if #6993 lands first, its "Symbols only on the left of
a match" entry in the gem README becomes stale and should be dropped as part of
merging this one.

Updated the README and comments following the merge of #6993.

sym[/re/] stays a third gap, waiting on the regexp form of String#[]. None
of the three closes the others.

Testing

Seven assert blocks in mrbgems/mruby-regexp/test/regexp.rb, one per entry
point plus the cases the conversion is responsible for:

  • #match: MatchData, captures, #string, #pre_match, $~, named captures,
    the block form, and nil on no match
  • #match with pos, so the offset argument is reached through a Symbol
  • a multibyte symbol name, which never fits the inline representation and so
    always takes the shared-buffer path into the offset conversion
  • mutating MatchData#string leaves the symbol table intact
  • #match? with and without pos, both polarities
  • #=~, including $~ cleared on no match
  • #=== via Enumerable#grep and via case/when
  • the operand types that are still refused, and that #=== answers false for
    them rather than raising

Every expected value was taken from CRuby 4.0.6. A 23-case comparison against
it leaves four differences, all of them pre-existing and outside this change:
MatchData#inspect, the wording of mruby's TypeError messages, MatchData#string
not being frozen, and String#match with a Symbol pattern (#6994).

rake test is green in both string modes:

build total OK KO
default (ASCII-8BIT) 1919 1902 0
MRB_UTF8_STRING 1939 1929 0

No existing test changed.

Summary by CodeRabbit

  • New Features

    • Regular expression matching now accepts symbols and matches against their names across supported matching operators.
    • Symbol operands support offsets, captures, blocks, multibyte characters, and case statements.
    • Matching preserves isolation between symbol and string data.
  • Bug Fixes

    • Preserved appropriate handling for unsupported operand types, including errors or false results depending on the operator.
  • Documentation

    • Updated regular expression documentation to describe symbol matching support.

@takumin
takumin requested a review from matz as a code owner August 2, 2026 16:54
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Regexp matching methods now accept Symbols by matching their names. Tests cover captures, offsets, blocks, case dispatch, buffer isolation, and unsupported operands. Documentation describes the new behavior and remaining limitations.

Changes

Regexp Symbol operands

Layer / File(s) Summary
Operand normalization and method integration
mrbgems/mruby-regexp/src/regexp.c
match_operand converts Symbols to their names and enforces String conversion. match, match?, =~, and === use the updated behavior.
Symbol matching behavior and documentation
mrbgems/mruby-regexp/test/regexp.rb, mrbgems/mruby-regexp/README.md, mrbgems/mruby-regexp/mrblib/symbol_regexp.rb
Tests cover Symbol matching, captures, offsets, blocks, case dispatch, buffer isolation, and type handling. Documentation describes Symbol operands and remaining symbol-left limitations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant RegexpMethods
  participant match_operand
  participant Symbol
  Caller->>RegexpMethods: pass Symbol operand
  RegexpMethods->>match_operand: normalize operand
  match_operand->>Symbol: obtain symbol name
  Symbol-->>match_operand: return name string
  match_operand-->>RegexpMethods: return matchable string
  RegexpMethods-->>Caller: return match result
Loading

Possibly related PRs

Suggested reviewers: matz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
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 matching now accepts a Symbol operand.
✨ 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.

takumin added 3 commits August 3, 2026 07:56
CRuby matches a Symbol against its name wherever a Regexp is given a subject,
through `reg_operand()`. mruby-regexp took strings only, so all four entry
points refused a symbol:

```ruby
/a/ =~ :ab       # CRuby: 0,         mruby: TypeError
/a/.match(:ab)   # CRuby: MatchData, mruby: TypeError
/a/.match?(:ab)  # CRuby: true,      mruby: TypeError
/a/ === :ab      # CRuby: true,      mruby: false
```

`#===` is the one that matters most. It answers false instead of raising, so a
`case` over symbols quietly falls through to `else` and `Enumerable#grep`
returns an empty array, with nothing to show that the pattern was never given
a chance to run.

Convert the operand with `mrb_sym_str()` in one helper and call it from
`regexp_match()`, `regexp_match_p()` and `regexp_match_op()` in place of
`mrb_ensure_string_type()`. `regexp_case_match()` uses the same helper behind
a type test, because `#===` has to keep answering false for a type it cannot
match rather than start raising. `__byte_match` is left alone: it is internal,
is only reached from mrblib with `self` as the subject, and its argument spec
already pins the type.

For a symbol too long for the inline representation, `mrb_sym_str()` returns
an `mrb_str_new_static()` string sharing the symbol table's buffer. Mutating
the `MatchData#string` that comes out of one is safe, because `str_modify()`
copies an `RSTR_NOFREE` buffer before writing, but a test pins that.

This is the Regexp-side half of the symbol support mruby#6993 adds on the Symbol
side, and neither closes the other. mruby#6993 delegates `Symbol#match` and friends
through `to_s`, so it never reaches the C code changed here; the two are
independent and can land in either order. If mruby#6993 lands first, its
"Symbols only on the left of a match" entry in the gem README becomes stale
and should be dropped as part of the merge. `sym[/re/]` stays a third gap,
waiting on the regexp form of `String#[]`.
`match_operand()` converts a Symbol on the Regexp side, so `re =~ sym`,
`re.match(sym)`, `re.match?(sym)` and `re === sym` all match against the
symbol's name. The "Symbols only on the left of a match" entry still described
the behaviour from before that, down to `syms.grep(re)` answering `[]`.

`sym[/re/]` stays unsupported, and the entry on the regexp form of `String#[]`
already covers it.
The comment on the delegating Symbol methods claimed the Regexp side rejects
symbols, with `syms.grep(/^to_/)` as the example that does not work. It does
now: `match_operand()` converts the operand, and none of that reaches the
mrblib file the comment sits in.

`sym[/re/]` is the direction that really is still missing, so keep it and drop
the rest.
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