diff --git a/mrbgems/mruby-regexp/mrblib/string_regexp.rb b/mrbgems/mruby-regexp/mrblib/string_regexp.rb index e15e507a3a..c63e6e3b01 100644 --- a/mrbgems/mruby-regexp/mrblib/string_regexp.rb +++ b/mrbgems/mruby-regexp/mrblib/string_regexp.rb @@ -35,21 +35,29 @@ def gsub(pattern, replacement = nil, &block) end # block case: keep in Ruby to avoid VM callback from C parts = [] - rest = self - while rest.length > 0 - md = pattern.match(rest) + pos = 0 + len = self.bytesize + while pos <= len + md = pattern.match(self, pos) break unless md - parts << md.pre_match + match_start = md.begin(0) + match_end = md.end(0) + parts << self.byteslice(pos, match_start - pos) 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] || "" + if match_start == match_end + rest = self.byteslice(match_end..-1) + if rest && rest.bytesize > 0 + char = rest[0] + parts << char + pos = match_end + char.bytesize + else + pos = match_end + 1 + end else - rest = md.post_match + pos = match_end end end - parts << rest + parts << self.byteslice(pos..-1) parts.join end diff --git a/mrbgems/mruby-regexp/src/re_exec.c b/mrbgems/mruby-regexp/src/re_exec.c index 8f6e047319..39dd39165a 100644 --- a/mrbgems/mruby-regexp/src/re_exec.c +++ b/mrbgems/mruby-regexp/src/re_exec.c @@ -145,7 +145,7 @@ add_thread(pike_state *s, re_threadlist *list, continue; case RE_BOL: - if (sp == s->str || ((s->pat->flags & RE_FLAG_MULTILINE) && sp > s->str && sp[-1] == '\n')) { + if (sp == s->str || ((s->pat->flags & RE_FLAG_MULTILINE) && sp > s->str && sp < s->str_end && sp[-1] == '\n')) { pc++; continue; } return; @@ -452,7 +452,7 @@ bt_match(const mrb_regexp_pattern *pat, const char *str, const char *str_end, } case RE_BOL: - if (sp != str && !(pat->flags & RE_FLAG_MULTILINE && sp > str && sp[-1] == '\n')) return FALSE; + if (sp != str && !(pat->flags & RE_FLAG_MULTILINE && sp > str && sp < str_end && sp[-1] == '\n')) return FALSE; pc++; break; diff --git a/mrbgems/mruby-regexp/test/regexp.rb b/mrbgems/mruby-regexp/test/regexp.rb index e84ead98d4..5c20700a00 100644 --- a/mrbgems/mruby-regexp/test/regexp.rb +++ b/mrbgems/mruby-regexp/test/regexp.rb @@ -349,6 +349,20 @@ assert_equal "HELLO WORLD", "hello world".gsub(/\w+/) { |m| m.upcase } end +assert("String#gsub with block and zero-width match") do + assert_equal "!abc", "abc".gsub(/^/) { "!" } + assert_equal "a!bc", "abc".gsub(/(?=b)/) { "!" } + assert_equal "!a!b!c!", "abc".gsub(//) { "!" } + assert_equal "!\n", "\n".gsub(/^/m) { "!" } + assert_equal "!a\n", "a\n".gsub(/^/m) { "!" } + assert_equal "!a\n!b", "a\nb".gsub(/^/m) { "!" } + if __ENCODING__ == "UTF-8" + assert_equal "!いろは", "いろは".gsub(/^/) { "!" } + assert_equal "い!ろは", "いろは".gsub(/(?=ろ)/) { "!" } + assert_equal "!い!ろ!は!", "いろは".gsub(//) { "!" } + end +end + assert("String#gsub date reformat") do result = "2026-03-21".gsub(/(\d+)-(\d+)-(\d+)/) { "#{$~[3]}/#{$~[2]}/#{$~[1]}" } assert_equal "21/03/2026", result