summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-string-ext
diff options
context:
space:
mode:
Diffstat (limited to 'mrbgems/mruby-string-ext')
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb100
-rw-r--r--mrbgems/mruby-string-ext/src/string.c136
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb204
3 files changed, 436 insertions, 4 deletions
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index b8cb93199..2b61fdb48 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -181,6 +181,7 @@ class String
ed = arg1.end
beg += self.size if beg < 0
ed += self.size if ed < 0
+ ed -= 1 if arg1.exclude_end?
validated = true
elsif arg1.kind_of?(String)
validated = true
@@ -198,22 +199,115 @@ class String
unless str == nil || str == ""
if arg1 != nil && arg2 !=nil
idx = arg1 >= 0 ? arg1 : self.size+arg1
- str2 = self[0...idx] + self[idx+arg2..-1]
+ str2 = self[0...idx] + self[idx+arg2..-1].to_s
else
if arg1.kind_of?(Range)
idx = beg >= 0 ? beg : self.size+beg
idx2 = ed>= 0 ? ed : self.size+ed
- str2 = self[0...idx] + self[idx2+1..-1]
+ str2 = self[0...idx] + self[idx2+1..-1].to_s
elsif arg1.kind_of?(String)
idx = self.index(arg1)
str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx == nil
else
idx = arg1 >= 0 ? arg1 : self.size+arg1
- str2 = self[0...idx] + self[idx+1..-1]
+ str2 = self[0...idx] + self[idx+1..-1].to_s
end
end
self.replace(str2) unless str2 == nil
end
str
end
+
+ ##
+ # call-seq:
+ # str.insert(index, other_str) -> str
+ #
+ # Inserts <i>other_str</i> before the character at the given
+ # <i>index</i>, modifying <i>str</i>. Negative indices count from the
+ # end of the string, and insert <em>after</em> the given character.
+ # The intent is insert <i>aString</i> so that it starts at the given
+ # <i>index</i>.
+ #
+ # "abcd".insert(0, 'X') #=> "Xabcd"
+ # "abcd".insert(3, 'X') #=> "abcXd"
+ # "abcd".insert(4, 'X') #=> "abcdX"
+ # "abcd".insert(-3, 'X') #=> "abXcd"
+ # "abcd".insert(-1, 'X') #=> "abcdX"
+ #
+ def insert(idx, str)
+ pos = idx.to_i
+ pos += self.size + 1 if pos < 0
+
+ raise IndexError, "index #{idx.to_i} out of string" if pos < 0 || pos > self.size
+
+ return self + str if pos == -1
+ return str + self if pos == 0
+ return self[0..pos - 1] + str + self[pos..-1]
+ end
+
+ ##
+ # call-seq:
+ # str.ljust(integer, padstr=' ') -> new_str
+ #
+ # If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ # <code>String</code> of length <i>integer</i> with <i>str</i> left justified
+ # and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ #
+ # "hello".ljust(4) #=> "hello"
+ # "hello".ljust(20) #=> "hello "
+ # "hello".ljust(20, '1234') #=> "hello123412341234123"
+ def ljust(idx, padstr = ' ')
+ if idx <= self.size
+ return self
+ end
+ newstr = self.dup
+ newstr << padstr
+ while newstr.size <= idx
+ newstr << padstr
+ end
+ return newstr.slice(0,idx)
+ end
+
+ # str.upto(other_str, exclusive=false) {|s| block } -> str
+ # str.upto(other_str, exclusive=false) -> an_enumerator
+ #
+ # Iterates through successive values, starting at <i>str</i> and
+ # ending at <i>other_str</i> inclusive, passing each value in turn to
+ # the block. The <code>String#succ</code> method is used to generate
+ # each value. If optional second argument exclusive is omitted or is false,
+ # the last value will be included; otherwise it will be excluded.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # "a8".upto("b6") {|s| print s, ' ' }
+ # for s in "a8".."b6"
+ # print s, ' '
+ # end
+ #
+ # <em>produces:</em>
+ #
+ # a8 a9 b0 b1 b2 b3 b4 b5 b6
+ # a8 a9 b0 b1 b2 b3 b4 b5 b6
+ #
+ # If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
+ # both are recognized as decimal numbers. In addition, the width of
+ # string (e.g. leading zeros) is handled appropriately.
+ #
+ # "9".upto("11").to_a #=> ["9", "10", "11"]
+ # "25".upto("5").to_a #=> []
+ # "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"]
+ #
+ def upto(other_str, excl=false, &block)
+ return to_enum :upto, other_str, excl unless block
+
+ str = self
+ n = self.<=>other_str
+ return self if n > 0 || (self == other_str && excl)
+ while true
+ block.call(str)
+ return self if !excl && str == other_str
+ str = str.succ
+ return self if excl && str == other_str
+ end
+ end
end
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index f04f12c4b..e925a82a7 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -1,4 +1,3 @@
-#include <ctype.h>
#include <string.h>
#include "mruby.h"
#include "mruby/array.h"
@@ -239,6 +238,136 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
return result;
}
+/*
+ * call-seq:
+ * string.succ -> string
+ *
+ * Returns next sequence of the string;
+ *
+ * a = "abc"
+ * a.succ #=> "abd"
+ */
+static mrb_value
+mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
+{
+ mrb_value result;
+ unsigned char *p, *e, *b, *t;
+ const char *prepend;
+ struct RString *s = mrb_str_ptr(self);
+ size_t l;
+
+ if (RSTRING_LEN(self) == 0)
+ return self;
+
+ mrb_str_modify(mrb, s);
+ l = RSTRING_LEN(self);
+ b = p = (unsigned char*) RSTRING_PTR(self);
+ t = e = p + l;
+ *(e--) = 0;
+
+ // find trailing ascii/number
+ while (e >= b) {
+ if (ISALNUM(*e))
+ break;
+ e--;
+ }
+ if (e < b) {
+ e = p + l - 1;
+ result = mrb_str_new_lit(mrb, "");
+ } else {
+ // find leading letter of the ascii/number
+ b = e;
+ while (b > p) {
+ if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z'))
+ break;
+ b--;
+ }
+ if (!ISALNUM(*b))
+ b++;
+ result = mrb_str_new(mrb, (char*) p, b - p);
+ }
+
+ while (e >= b) {
+ if (!ISALNUM(*e)) {
+ if (*e == 0xff) {
+ mrb_str_cat_lit(mrb, result, "\x01");
+ (*e) = 0;
+ } else
+ (*e)++;
+ break;
+ }
+ prepend = NULL;
+ if (*e == '9') {
+ if (e == b) prepend = "1";
+ *e = '0';
+ } else if (*e == 'z') {
+ if (e == b) prepend = "a";
+ *e = 'a';
+ } else if (*e == 'Z') {
+ if (e == b) prepend = "A";
+ *e = 'A';
+ } else {
+ (*e)++;
+ break;
+ }
+ if (prepend) mrb_str_cat_cstr(mrb, result, prepend);
+ e--;
+ }
+ result = mrb_str_cat(mrb, result, (char*) b, t - b);
+ l = RSTRING_LEN(result);
+ mrb_str_resize(mrb, self, l);
+ memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l);
+ return self;
+}
+
+static mrb_value
+mrb_str_succ(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+
+ str = mrb_str_dup(mrb, self);
+ mrb_str_succ_bang(mrb, str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * str.prepend(other_str) -> str
+ *
+ * Prepend---Prepend the given string to <i>str</i>.
+ *
+ * a = "world"
+ * a.prepend("hello ") #=> "hello world"
+ * a #=> "hello world"
+ */
+static mrb_value
+mrb_str_prepend(mrb_state *mrb, mrb_value self)
+{
+ struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s;
+ mrb_int len;
+ mrb_value other, temp_str;
+
+ mrb_get_args(mrb, "S", &other);
+
+ mrb_str_modify(mrb, s1);
+ if (!mrb_string_p(other)) {
+ other = mrb_str_to_str(mrb, other);
+ }
+ s2 = mrb_str_ptr(other);
+ len = RSTR_LEN(s1) + RSTR_LEN(s2);
+ temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1));
+ temp_s = mrb_str_ptr(temp_str);
+ memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1));
+ if (RSTRING_CAPA(self) < len) {
+ mrb_str_resize(mrb, self, len);
+ }
+ memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2));
+ memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s));
+ RSTR_SET_LEN(s1, len);
+ RSTR_PTR(s1)[len] = '\0';
+ return self;
+}
+
void
mrb_mruby_string_ext_gem_init(mrb_state* mrb)
{
@@ -256,6 +385,11 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1));
+ mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
+ mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
}
void
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index 2a8dd0cca..14e00428e 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -251,3 +251,207 @@ assert('String#slice!') do
assert_raise(ArgumentError) { "foo".slice! }
end
+
+assert('String#succ') do
+ assert_equal "", "".succ
+ assert_equal "1", "0".succ
+ assert_equal "10", "9".succ
+ assert_equal "01", "00".succ
+ assert_equal "a1", "a0".succ
+ assert_equal "A1", "A0".succ
+ assert_equal "10", "09".succ
+ assert_equal "b0", "a9".succ
+ assert_equal "B0", "A9".succ
+
+ assert_equal "b", "a".succ
+ assert_equal "aa", "z".succ
+ assert_equal "ab", "aa".succ
+ assert_equal "Ab", "Aa".succ
+ assert_equal "0b", "0a".succ
+ assert_equal "ba", "az".succ
+ assert_equal "Ba", "Az".succ
+ assert_equal "1a", "0z".succ
+
+ assert_equal "B", "A".succ
+ assert_equal "AA", "Z".succ
+ assert_equal "AB", "AA".succ
+ assert_equal "aB", "aA".succ
+ assert_equal "0B", "0A".succ
+ assert_equal "BA", "AZ".succ
+ assert_equal "bA", "aZ".succ
+ assert_equal "1A", "0Z".succ
+
+ assert_equal ".", "-".succ
+ assert_equal "\x01\x00", "\xff".succ
+ assert_equal "-b", "-a".succ
+ assert_equal "-aa", "-z".succ
+ assert_equal "-a-b-", "-a-a-".succ
+ assert_equal "-b-", "-a-".succ
+ assert_equal "-aa-", "-z-".succ
+ assert_equal "あb", "あa".succ
+ assert_equal "あba", "あaz".succ
+
+ a = ""; a.succ!
+ assert_equal "", a
+ a = "0"; a.succ!
+ assert_equal "1", a
+ a = "9"; a.succ!
+ assert_equal "10", a
+ a = "00"; a.succ!
+ assert_equal "01", a
+ a = "a0"; a.succ!
+ assert_equal "a1", a
+ a = "A0"; a.succ!
+ assert_equal "A1", a
+ a = "09"; a.succ!
+ assert_equal "10", a
+ a = "a9"; a.succ!
+ assert_equal "b0", a
+ a = "A9"; a.succ!
+ assert_equal "B0", a
+
+ a = "a"; a.succ!
+ assert_equal "b", a
+ a = "z"; a.succ!
+ assert_equal "aa", a
+ a = "aa"; a.succ!
+ assert_equal "ab", a
+ a = "Aa"; a.succ!
+ assert_equal "Ab", a
+ a = "0a"; a.succ!
+ assert_equal "0b", a
+ a = "az"; a.succ!
+ assert_equal "ba", a
+ a = "Az"; a.succ!
+ assert_equal "Ba", a
+ a = "0z"; a.succ!
+ assert_equal "1a", a
+
+ a = "A"; a.succ!
+ assert_equal "B", a
+ a = "Z"; a.succ!
+ assert_equal "AA", a
+ a = "AA"; a.succ!
+ assert_equal "AB", a
+ a = "aA"; a.succ!
+ assert_equal "aB", a
+ a = "0A"; a.succ!
+ assert_equal "0B", a
+ a = "AZ"; a.succ!
+ assert_equal "BA", a
+ a = "aZ"; a.succ!
+ assert_equal "bA", a
+ a = "0Z"; a.succ!
+ assert_equal "1A", a
+
+ a = "-"; a.succ!
+ assert_equal ".", a
+ a = "\xff"; a.succ!
+ assert_equal "\x01\x00", a
+ a = "-a"; a.succ!
+ assert_equal "-b", a
+ a = "-z"; a.succ!
+ assert_equal "-aa", a
+ a = "-a-a-"; a.succ!
+ assert_equal "-a-b-", a
+ a = "-a-"; a.succ!
+ assert_equal "-b-", a
+ a = "-z-"; a.succ!
+ assert_equal "-aa-", a
+ a = "あa"; a.succ!
+ assert_equal "あb", a
+ a = "あaz"; a.succ!
+ assert_equal "あba", a
+end
+
+assert('String#next') do
+ assert_equal "01", "00".next
+
+ a = "00"; a.next!
+ assert_equal "01", a
+end
+
+assert('String#insert') do
+ assert_equal "Xabcd", "abcd".insert(0, 'X')
+ assert_equal "abcXd", "abcd".insert(3, 'X')
+ assert_equal "abcdX", "abcd".insert(4, 'X')
+ assert_equal "abXcd", "abcd".insert(-3, 'X')
+ assert_equal "abcdX", "abcd".insert(-1, 'X')
+ assert_raise(IndexError) { "abcd".insert(5, 'X') }
+ assert_raise(IndexError) { "abcd".insert(-6, 'X') }
+end
+
+assert('String#prepend') do
+ a = "world"
+ assert_equal "hello world", a.prepend("hello ")
+ assert_equal "hello world", a
+end
+
+assert('String#ljust') do
+ assert_equal "hello", "hello".ljust(4)
+ assert_equal "hello ", "hello".ljust(20)
+ assert_equal "hello123412341234123", "hello".ljust(20, '1234')
+ assert_equal "hello", "hello".ljust(-3)
+end
+
+assert('String#upto') do
+ a = "aa"
+ start = "aa"
+ count = 0
+ assert_equal("aa", a.upto("zz") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(676, count)
+
+ a = "a"
+ start = "a"
+ count = 0
+ assert_equal("a", a.upto("a") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "a"
+ start = "a"
+ count = 0
+ assert_equal("a", a.upto("b", true) {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "0"
+ start = "0"
+ count = 0
+ assert_equal("0", a.upto("0") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "0"
+ start = "0"
+ count = 0
+ assert_equal("0", a.upto("-1") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(0, count)
+
+ a = "-1"
+ start = "-1"
+ count = 0
+ assert_equal("-1", a.upto("-2") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(2, count)
+end