Merge: introduce plain_to_s
[nit.git] / lib / standard / string.nit
index 409b4ed..ff46e46 100644 (file)
@@ -60,7 +60,7 @@ abstract class Text
        fun substring(from: Int, count: Int): SELFTYPE is abstract
 
        # Iterates on the substrings of self if any
-       fun substrings: Iterator[Text] is abstract
+       fun substrings: Iterator[FlatText] is abstract
 
        # Is the current Text empty (== "")
        #
@@ -249,6 +249,16 @@ abstract class Text
        #     assert "ff".to_hex == 255
        fun to_hex: Int do return a_to(16)
 
+       # If `self` contains only digits <= '7', return the corresponding integer.
+       #
+       #     assert "714".to_oct == 460
+       fun to_oct: Int do return a_to(8)
+
+       # If `self` contains only '0' et '1', return the corresponding integer.
+       #
+       #     assert "101101".to_bin == 45
+       fun to_bin: Int do return a_to(2)
+
        # If `self` contains only digits and letters, return the corresponding integer in a given base
        #
        #     assert "120".a_to(3)     == 15
@@ -345,7 +355,7 @@ abstract class Text
                end
                return true
        end
-                       
+
        # Removes the whitespaces at the beginning of self
        #
        #     assert " \n\thello \n\t".l_trim == "hello \n\t"
@@ -849,6 +859,60 @@ abstract class Text
                return hash_cache.as(not null)
        end
 
+       # Gives the formatted string back as a Nit string with `args` in place
+       #
+       #    assert "This %1 is a %2.".format("String", "formatted String") == "This String is a formatted String."
+       #    assert "\\%1 This string".format("String") == "\\%1 This string"
+       fun format(args: Object...): String do
+               var s = new Array[Text]
+               var curr_st = 0
+               var i = 0
+               while i < length do
+                       # Skip escaped characters
+                       if self[i] == '\\' then
+                               i += 1
+                       # In case of format
+                       else if self[i] == '%' then
+                               var fmt_st = i
+                               i += 1
+                               var ciph_st = i
+                               while i < length and self[i].is_numeric do
+                                       i += 1
+                               end
+                               i -= 1
+                               var fmt_end = i
+                               var ciph_len = fmt_end - ciph_st + 1
+                               s.push substring(curr_st, fmt_st - curr_st)
+                               s.push args[substring(ciph_st, ciph_len).to_i - 1].to_s
+                               curr_st = i + 1
+                       end
+                       i += 1
+               end
+               s.push substring(curr_st, length - curr_st)
+               return s.to_s
+       end
+
+       # Copies `n` bytes from `self` at `src_offset` into `dest` starting at `dest_offset`
+       #
+       # Basically a high-level synonym of NativeString::copy_to
+       #
+       # REQUIRE: `n` must be large enough to contain `len` bytes
+       #
+       #       var ns = new NativeString(8)
+       #       "Text is String".copy_to_native(ns, 8, 2, 0)
+       #       assert ns.to_s_with_length(8) == "xt is St"
+       #
+       fun copy_to_native(dest: NativeString, n, src_offset, dest_offset: Int) do
+               var mypos = src_offset
+               var itspos = dest_offset
+               while n > 0 do
+                       dest[itspos] = self.chars[mypos]
+                       itspos += 1
+                       mypos += 1
+                       n -= 1
+               end
+       end
+
 end
 
 # All kinds of array-based text representations.
@@ -881,7 +945,7 @@ abstract class FlatText
        # copy locally the char* as Nit Strings are immutable.
        private fun fast_cstring: NativeString is abstract
 
-       redef var length: Int = 0
+       redef var length = 0
 
        redef fun output
        do
@@ -893,6 +957,10 @@ abstract class FlatText
        end
 
        redef fun flatten do return self
+
+       redef fun copy_to_native(dest, n, src_offset, dest_offset) do
+               items.copy_to(dest, n, src_offset, dest_offset)
+       end
 end
 
 # Abstract class for the SequenceRead compatible
@@ -953,7 +1021,7 @@ abstract class String
        #     assert "helloworld".insert_at(" ", 5)     == "hello world"
        fun insert_at(s: String, pos: Int): SELFTYPE is abstract
 
-       redef fun substrings: Iterator[String] is abstract
+       redef fun substrings is abstract
 
        # Returns a reversed version of self
        #
@@ -972,41 +1040,61 @@ abstract class String
        #     assert "Hello World!".to_lower     == "hello world!"
        fun to_lower : SELFTYPE is abstract
 
-       # Takes a camel case `self` and converts it to snake case 
+       # Takes a camel case `self` and converts it to snake case
        #
        #     assert "randomMethodId".to_snake_case == "random_method_id"
        #
-       # If `self` is upper, it is returned unchanged
+       # The rules are the following:
        #
-       #     assert "RANDOM_METHOD_ID".to_snake_case == "RANDOM_METHOD_ID"
+       # An uppercase is always converted to a lowercase
        #
-       # If the identifier is prefixed by an underscore, the underscore is ignored
+       #     assert "HELLO_WORLD".to_snake_case == "hello_world"
+       #
+       # An uppercase that follows a lowercase is prefixed with an underscore
        #
-       #     assert "_privateField".to_snake_case == "_private_field"
+       #     assert "HelloTheWORLD".to_snake_case == "hello_the_world"
+       #
+       # An uppercase that follows an uppercase and is followed by a lowercase, is prefixed with an underscore
+       #
+       #     assert "HelloTHEWorld".to_snake_case == "hello_the_world"
+       #
+       # All other characters are kept as is; `self` does not need to be a proper CamelCased string.
+       #
+       #     assert "=-_H3ll0Th3W0rld_-=".to_snake_case == "=-_h3ll0th3w0rld_-="
        fun to_snake_case: SELFTYPE
        do
-               if self.is_upper then return self
+               if self.is_lower then return self
 
                var new_str = new FlatBuffer.with_capacity(self.length)
-               var is_first_char = true
+               var prev_is_lower = false
+               var prev_is_upper = false
 
                for i in [0..length[ do
                        var char = chars[i]
-                       if is_first_char then 
-                               new_str.add(char.to_lower)
-                               is_first_char = false
+                       if char.is_lower then
+                               new_str.add(char)
+                               prev_is_lower = true
+                               prev_is_upper = false
                        else if char.is_upper then
-                               new_str.add('_')
+                               if prev_is_lower then
+                                       new_str.add('_')
+                               else if prev_is_upper and i+1 < length and chars[i+1].is_lower then
+                                       new_str.add('_')
+                               end
                                new_str.add(char.to_lower)
+                               prev_is_lower = false
+                               prev_is_upper = true
                        else
                                new_str.add(char)
+                               prev_is_lower = false
+                               prev_is_upper = false
                        end
                end
-               
+
                return new_str.to_s
        end
 
-       # Takes a snake case `self` and converts it to camel case 
+       # Takes a snake case `self` and converts it to camel case
        #
        #     assert "random_method_id".to_camel_case == "randomMethodId"
        #
@@ -1106,7 +1194,7 @@ class FlatString
        # Indes in _items of the last item of the string
        private var index_to: Int is noinit
 
-       redef var chars: SequenceRead[Char] = new FlatStringCharView(self) is lazy
+       redef var chars = new FlatStringCharView(self) is lazy
 
        redef fun [](index)
        do
@@ -1231,8 +1319,7 @@ class FlatString
                index_to = to
        end
 
-       redef fun to_cstring: NativeString
-       do
+       redef fun to_cstring do
                if real_items != null then
                        return real_items.as(not null)
                else
@@ -1650,8 +1737,7 @@ class FlatBuffer
                capacity = c
        end
 
-       redef fun to_s: String
-       do
+       redef fun to_s do
                written = true
                if length == 0 then items = new NativeString(1)
                return new FlatString.with_infos(items, length, 0, length - 1)
@@ -1952,9 +2038,7 @@ end
 redef class Int
 
        # Wrapper of strerror C function
-       private fun strerror_ext: NativeString is extern `{
-               return strerror(recv);
-       `}
+       private fun strerror_ext: NativeString is extern "strerror"
 
        # Returns a string describing error number
        fun strerror: String do return strerror_ext.to_s
@@ -2144,6 +2228,12 @@ redef class Collection[E]
        # Concatenate elements.
        redef fun to_s
        do
+               return plain_to_s
+       end
+
+       # Concatenate element without separators
+       fun plain_to_s: String
+       do
                var s = new FlatBuffer
                for e in self do if e != null then s.append(e.to_s)
                return s.to_s
@@ -2179,7 +2269,7 @@ end
 redef class Array[E]
 
        # Fast implementation
-       redef fun to_s
+       redef fun plain_to_s
        do
                var l = length
                if l == 0 then return ""