mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
9d8d41006b
`class String; def split` in mruby-regexp/mrblib/string_regexp.rb replaced the C-defined String#split rather than overriding it, so the in-Ruby `return super if pattern.nil?` paths raised NoMethodError for any `"x".split(...)` call once mruby-regexp was loaded (the default full-core production binary). The regression wasn't caught by the test suite because per-gem tests run under mrb_open_core() with only the gem's dep_list, so the broken override is never visible from test/t/string.rb (mruby-test, not mruby-regexp). Add `alias __split split` at the top of the override class body, which captures the C-defined method, and change the Ruby override to delegate via `__split(pattern, limit)` for non-regexp fallback paths. Add a bintest under mruby-bin-mruby that runs through bin/mruby (full gem load) to catch this regression class. Co-authored-by: Claude <noreply@anthropic.com>
110 lines
2.9 KiB
Ruby
110 lines
2.9 KiB
Ruby
class String
|
|
# Capture the C-defined String#split under `__split` before the override
|
|
# below replaces it, so the override can delegate non-regexp patterns
|
|
# back to the core implementation.
|
|
alias __split split
|
|
|
|
def match(re, pos = 0)
|
|
re = Regexp.new(re) if re.is_a?(String)
|
|
re.match(self, pos)
|
|
end
|
|
|
|
def match?(re, pos = 0)
|
|
re = Regexp.new(re) if re.is_a?(String)
|
|
re.match?(self, pos)
|
|
end
|
|
|
|
def =~(re)
|
|
re =~ self
|
|
end
|
|
|
|
def sub(pattern, replacement = nil, &block)
|
|
pattern = Regexp.new(Regexp.escape(pattern)) if pattern.is_a?(String)
|
|
unless block
|
|
return pattern.__sub_str(self, replacement.to_s)
|
|
end
|
|
md = pattern.match(self)
|
|
return self.dup unless md
|
|
md.pre_match + block.call(md[0]).to_s + md.post_match
|
|
end
|
|
|
|
def gsub(pattern, replacement = nil, &block)
|
|
pattern = Regexp.new(Regexp.escape(pattern)) if pattern.is_a?(String)
|
|
unless block
|
|
return pattern.__gsub_str(self, replacement.to_s)
|
|
end
|
|
# block case: keep in Ruby to avoid VM callback from C
|
|
parts = []
|
|
rest = self
|
|
while rest.length > 0
|
|
md = pattern.match(rest)
|
|
break unless md
|
|
parts << md.pre_match
|
|
parts << block.call(md[0]).to_s
|
|
matched_len = md[0].length
|
|
if matched_len == 0
|
|
parts << rest[0] if rest.length > 0
|
|
rest = rest[1..-1] || ""
|
|
else
|
|
rest = md.post_match
|
|
end
|
|
end
|
|
parts << rest
|
|
parts.join
|
|
end
|
|
|
|
def scan(pattern)
|
|
pattern = Regexp.new(Regexp.escape(pattern)) if pattern.is_a?(String)
|
|
result = pattern.__scan(self)
|
|
if block_given?
|
|
result.each { |m| yield m }
|
|
self
|
|
else
|
|
result
|
|
end
|
|
end
|
|
|
|
# Regexp-aware split. Falls back to the C-defined split (aliased as
|
|
# `__split` in mrb_mruby_regexp_gem_init before this override loads) for
|
|
# nil or simple-string patterns; converts string-with-backslash to a
|
|
# Regexp and handles regexp patterns in Ruby.
|
|
def split(pattern = nil, limit = -1)
|
|
return __split(pattern, limit) if pattern.nil?
|
|
if pattern.is_a?(String)
|
|
return __split(pattern, limit) if pattern.length == 1 || !pattern.include?('\\')
|
|
pattern = Regexp.new(Regexp.escape(pattern))
|
|
end
|
|
result = []
|
|
rest = self
|
|
count = 0
|
|
while rest.length > 0
|
|
if limit > 0 && count >= limit - 1
|
|
result << rest
|
|
return result
|
|
end
|
|
md = pattern.match(rest)
|
|
break unless md
|
|
result << md.pre_match
|
|
rest = md.post_match
|
|
count += 1
|
|
# skip zero-length match at beginning
|
|
if md[0].length == 0
|
|
if rest.length > 0
|
|
result[-1] = result[-1] + rest[0]
|
|
rest = rest[1..-1] || ""
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
result << rest
|
|
# remove trailing empty strings if no limit
|
|
if limit < 0
|
|
while result.length > 0 && result[-1] == ""
|
|
result.pop
|
|
end
|
|
end
|
|
result
|
|
end
|
|
end
|