summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTomoyuki Sahara <[email protected]>2015-06-08 17:32:14 +0900
committerTomoyuki Sahara <[email protected]>2015-06-08 17:32:14 +0900
commit5dd2b8e16f33b84773cdebeec4b6a86a511e8e9c (patch)
treea0377e98eeb10f3bdd5f98759addc7cbb819d307
parenta04116267850afc5b85459940656346c7c380da5 (diff)
downloadmruby-5dd2b8e16f33b84773cdebeec4b6a86a511e8e9c.tar.gz
mruby-5dd2b8e16f33b84773cdebeec4b6a86a511e8e9c.zip
gsub/sub supports back references in substitutes. fixes #2816.
This implementation is compatible with CRuby's String#gsub/sub except \1 ... \9 and \+. They are useless without Regexp library.
-rw-r--r--mrblib/string.rb39
-rw-r--r--test/t/string.rb18
2 files changed, 55 insertions, 2 deletions
diff --git a/mrblib/string.rb b/mrblib/string.rb
index 90aad5d32..ee097ad6d 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -20,6 +20,30 @@ class String
self
end
+ # private method for gsub/sub
+ def __sub_replace(pre, m, post)
+ s = ""
+ i = 0
+ while j = index("\\", i)
+ break if j == length-1
+ t = case self[j+1]
+ when "\\"
+ "\\"
+ when "`"
+ pre
+ when "&", "0"
+ m
+ when "'"
+ post
+ else
+ self[j, 2]
+ end
+ s += self[i, j-i] + t
+ i = j + 2
+ end
+ s + self[i, length-i]
+ end
+
##
# Replace all matches of +pattern+ with +replacement+.
# Call block (if given) for each match and replace
@@ -29,7 +53,17 @@ class String
# ISO 15.2.10.5.18
def gsub(*args, &block)
if args.size == 2
- split(args[0], -1).join(args[1])
+ s = ""
+ i = 0
+ while j = index(args[0], i)
+ seplen = args[0].length
+ k = j + seplen
+ pre = self[0, j]
+ post = self[k, length-k]
+ s += self[i, j-i] + args[1].__sub_replace(pre, args[0], post)
+ i = k
+ end
+ s + self[i, length-i]
elsif args.size == 1 && block
split(args[0], -1).join(block.call(args[0]))
else
@@ -76,7 +110,8 @@ class String
# ISO 15.2.10.5.36
def sub(*args, &block)
if args.size == 2
- split(args[0], 2).join(args[1])
+ pre, post = split(args[0], 2)
+ pre + args[1].__sub_replace(pre, args[0], post) + post
elsif args.size == 1 && block
split(args[0], 2).join(block.call(args[0]))
else
diff --git a/test/t/string.rb b/test/t/string.rb
index 63e4af000..ee6fe0848 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -254,6 +254,15 @@ assert('String#gsub', '15.2.10.5.18') do
assert_equal('A', 'a'.gsub('a'){|w| w.capitalize })
end
+assert('String#gsub with backslash') do
+ s = 'abXcdXef'
+ assert_equal 'ab<\\>cd<\\>ef', s.gsub('X', '<\\\\>')
+ assert_equal 'ab<X>cd<X>ef', s.gsub('X', '<\\&>')
+ assert_equal 'ab<X>cd<X>ef', s.gsub('X', '<\\0>')
+ assert_equal 'ab<ab>cd<abXcd>ef', s.gsub('X', '<\\`>')
+ assert_equal 'ab<cdXef>cd<ef>ef', s.gsub('X', '<\\\'>')
+end
+
assert('String#gsub!', '15.2.10.5.19') do
a = 'abcabc'
a.gsub!('b', 'B')
@@ -416,6 +425,15 @@ assert('String#sub', '15.2.10.5.36') do
assert_equal 'aa$', 'aa#'.sub('#', '$')
end
+assert('String#sub with backslash') do
+ s = 'abXcdXef'
+ assert_equal 'ab<\\>cdXef', s.sub('X', '<\\\\>')
+ assert_equal 'ab<X>cdXef', s.sub('X', '<\\&>')
+ assert_equal 'ab<X>cdXef', s.sub('X', '<\\0>')
+ assert_equal 'ab<ab>cdXef', s.sub('X', '<\\`>')
+ assert_equal 'ab<cdXef>cdXef', s.sub('X', '<\\\'>')
+end
+
assert('String#sub!', '15.2.10.5.37') do
a = 'abcabc'
a.sub!('b', 'B')