lib/string: redefine [] for FlatString and FlatBuffer without using chars
[nit.git] / lib / standard / string.nit
index 7e42fb6..e4423d3 100644 (file)
@@ -15,7 +15,7 @@
 module string
 
 import math
-intrude import collection # FIXME should be collection::array
+import collection
 
 `{
 #include <stdio.h>
@@ -247,8 +247,8 @@ abstract class Text
                var i = 0
                var neg = false
 
-               for c in self.chars
-               do
+               for j in [0..length[ do
+                       var c = chars[j]
                        var v = c.to_i
                        if v > base then
                                if neg then
@@ -278,12 +278,10 @@ abstract class Text
        fun is_numeric: Bool
        do
                var has_point_or_comma = false
-               for i in self.chars
-               do
-                       if not i.is_numeric
-                       then
-                               if (i == '.' or i == ',') and not has_point_or_comma
-                               then
+               for i in [0..length[ do
+                       var c = chars[i]
+                       if not c.is_numeric then
+                               if (c == '.' or c == ',') and not has_point_or_comma then
                                        has_point_or_comma = true
                                else
                                        return false
@@ -293,6 +291,51 @@ abstract class Text
                return true
        end
 
+       # Returns `true` if the string contains only Hex chars
+       #
+       #     assert "048bf".is_hex  == true
+       #     assert "ABCDEF".is_hex  == true
+       #     assert "0G".is_hex == false
+       fun is_hex: Bool
+       do
+               for i in [0..length[ do
+                       var c = chars[i]
+                       if not (c >= 'a' and c <= 'f') and
+                          not (c >= 'A' and c <= 'F') and
+                          not (c >= '0' and c <= '9') then return false
+               end
+               return true
+       end
+
+       # Are all letters in `self` upper-case ?
+       #
+       #     assert "HELLO WORLD".is_upper == true
+       #     assert "%$&%!".is_upper       == true
+       #     assert "hello world".is_upper == false
+       #     assert "Hello World".is_upper == false
+       fun is_upper: Bool
+       do
+               for i in [0..length[ do
+                       var char = chars[i]
+                       if char.is_lower then return false
+               end
+               return true
+       end
+
+       # Are all letters in `self` lower-case ?
+       #
+       #     assert "hello world".is_lower == true
+       #     assert "%$&%!".is_lower       == true
+       #     assert "Hello World".is_lower == false
+       fun is_lower: Bool
+       do
+               for i in [0..length[ do
+                       var char = chars[i]
+                       if char.is_upper then return false
+               end
+               return true
+       end
+                       
        # Removes the whitespaces at the beginning of self
        #
        #     assert " \n\thello \n\t".l_trim == "hello \n\t"
@@ -337,7 +380,8 @@ abstract class Text
        do
                var res = new FlatBuffer
                var underscore = false
-               for c in self.chars do
+               for i in [0..length[ do
+                       var c = chars[i]
                        if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
                                res.add(c)
                                underscore = false
@@ -370,7 +414,8 @@ abstract class Text
        fun escape_to_c: String
        do
                var b = new FlatBuffer
-               for c in self.chars do
+               for i in [0..length[ do
+                       var c = chars[i]
                        if c == '\n' then
                                b.append("\\n")
                        else if c == '\0' then
@@ -422,7 +467,8 @@ abstract class Text
        do
                var res = new FlatBuffer.with_capacity(self.length)
                var was_slash = false
-               for c in chars do
+               for i in [0..length[ do
+                       var c = chars[i]
                        if not was_slash then
                                if c == '\\' then
                                        was_slash = true
@@ -447,6 +493,95 @@ abstract class Text
                return res.to_s
        end
 
+       # Encode `self` to percent (or URL) encoding
+       #
+       #     assert "aBc09-._~".to_percent_encoding == "aBc09-._~"
+       #     assert "%()< >".to_percent_encoding == "%25%28%29%3c%20%3e"
+       #     assert ".com/post?e=asdf&f=123".to_percent_encoding == ".com%2fpost%3fe%3dasdf%26f%3d123"
+       fun to_percent_encoding: String
+       do
+               var buf = new FlatBuffer
+
+               for i in [0..length[ do
+                       var c = chars[i]
+                       if (c >= '0' and c <= '9') or
+                          (c >= 'a' and c <= 'z') or
+                          (c >= 'A' and c <= 'Z') or
+                          c == '-' or c == '.' or
+                          c == '_' or c == '~'
+                       then
+                               buf.add c
+                       else buf.append "%{c.ascii.to_hex}"
+               end
+
+               return buf.to_s
+       end
+
+       # Decode `self` from percent (or URL) encoding to a clear string
+       #
+       # Replace invalid use of '%' with '?'.
+       #
+       #     assert "aBc09-._~".from_percent_encoding == "aBc09-._~"
+       #     assert "%25%28%29%3c%20%3e".from_percent_encoding == "%()< >"
+       #     assert ".com%2fpost%3fe%3dasdf%26f%3d123".from_percent_encoding == ".com/post?e=asdf&f=123"
+       #     assert "%25%28%29%3C%20%3E".from_percent_encoding == "%()< >"
+       #     assert "incomplete %".from_percent_encoding == "incomplete ?"
+       #     assert "invalid % usage".from_percent_encoding == "invalid ? usage"
+       fun from_percent_encoding: String
+       do
+               var buf = new FlatBuffer
+
+               var i = 0
+               while i < length do
+                       var c = chars[i]
+                       if c == '%' then
+                               if i + 2 >= length then
+                                       # What follows % has been cut off
+                                       buf.add '?'
+                               else
+                                       i += 1
+                                       var hex_s = substring(i, 2)
+                                       if hex_s.is_hex then
+                                               var hex_i = hex_s.to_hex
+                                               buf.add hex_i.ascii
+                                               i += 1
+                                       else
+                                               # What follows a % is not Hex
+                                               buf.add '?'
+                                               i -= 1
+                                       end
+                               end
+                       else buf.add c
+
+                       i += 1
+               end
+
+               return buf.to_s
+       end
+
+       # Escape the four characters `<`, `>`, `&`, and `"` with their html counterpart
+       #
+       #     assert "a&b->\"x\"".html_escape      ==  "a&amp;b-&gt;&quot;x&quot;"
+       fun html_escape: SELFTYPE
+       do
+               var buf = new FlatBuffer
+
+               for i in [0..length[ do
+                       var c = chars[i]
+                       if c == '&' then
+                               buf.append "&amp;"
+                       else if c == '<' then
+                               buf.append "&lt;"
+                       else if c == '>' then
+                               buf.append "&gt;"
+                       else if c == '"' then
+                               buf.append "&quot;"
+                       else buf.add c
+               end
+
+               return buf.to_s
+       end
+
        # Equality of text
        # Two pieces of text are equals if thez have the same characters in the same order.
        #
@@ -506,7 +641,8 @@ abstract class Text
                        # djb2 hash algorithm
                        var h = 5381
 
-                       for char in self.chars do
+                       for i in [0..length[ do
+                               var char = chars[i]
                                h = h.lshift(5) + h + char.ascii
                        end
 
@@ -613,6 +749,81 @@ 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 
+       #
+       #     assert "randomMethodId".to_snake_case == "random_method_id"
+       #
+       # If `self` is upper, it is returned unchanged
+       #
+       #     assert "RANDOM_METHOD_ID".to_snake_case == "RANDOM_METHOD_ID"
+       #
+       # If the identifier is prefixed by an underscore, the underscore is ignored
+       #
+       #     assert "_privateField".to_snake_case == "_private_field"
+       fun to_snake_case: SELFTYPE
+       do
+               if self.is_upper then return self
+
+               var new_str = new FlatBuffer.with_capacity(self.length)
+               var is_first_char = true
+
+               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
+                       else if char.is_upper then
+                               new_str.add('_')
+                               new_str.add(char.to_lower)
+                       else
+                               new_str.add(char)
+                       end
+               end
+               
+               return new_str.to_s
+       end
+
+       # Takes a snake case `self` and converts it to camel case 
+       #
+       #     assert "random_method_id".to_camel_case == "randomMethodId"
+       #
+       # If the identifier is prefixed by an underscore, the underscore is ignored
+       #
+       #     assert "_private_field".to_camel_case == "_privateField"
+       #
+       # If `self` is upper, it is returned unchanged
+       #
+       #     assert "RANDOM_ID".to_camel_case == "RANDOM_ID"
+       #
+       # If there are several consecutive underscores, they are considered as a single one
+       #
+       #     assert "random__method_id".to_camel_case == "randomMethodId"
+       fun to_camel_case: SELFTYPE
+       do
+               if self.is_upper then return self
+
+               var new_str = new FlatBuffer
+               var is_first_char = true
+               var follows_us = false
+
+               for i in [0..length[ do
+                       var char = chars[i]
+                       if is_first_char then
+                               new_str.add(char)
+                               is_first_char = false
+                       else if char == '_' then
+                               follows_us = true
+                       else if follows_us then
+                               new_str.add(char.to_upper)
+                               follows_us = false
+                       else
+                               new_str.add(char)
+                       end
+               end
+
+               return new_str.to_s
+       end
 end
 
 private class FlatSubstringsIter
@@ -645,6 +856,15 @@ class FlatString
 
        redef var chars: SequenceRead[Char] = new FlatStringCharView(self)
 
+       redef fun [](index)
+       do
+               # Check that the index (+ index_from) is not larger than indexTo
+               # In other terms, if the index is valid
+               assert index >= 0
+               assert (index + index_from) <= index_to
+               return items[index + index_from]
+       end
+
        ################################################
        #       AbstractString specific methods        #
        ################################################
@@ -841,8 +1061,9 @@ class FlatString
                        s.items.copy_to(target_string, its_length, 0, my_length)
                else
                        var curr_pos = my_length
-                       for i in s.chars do
-                               target_string[curr_pos] = i
+                       for i in [0..s.length[ do
+                               var c = s.chars[i]
+                               target_string[curr_pos] = c
                                curr_pos += 1
                        end
                end
@@ -1066,6 +1287,13 @@ class FlatBuffer
 
        redef fun substrings do return new FlatSubstringsIter(self)
 
+       redef fun [](index)
+       do
+               assert index >= 0
+               assert index  < length
+               return items[index]
+       end
+
        redef fun []=(index, item)
        do
                is_dirty = true
@@ -1134,8 +1362,9 @@ class FlatBuffer
                        s.items.copy_to(items, length, 0, 0)
                else
                        var curr_pos = 0
-                       for i in s.chars do
-                               items[curr_pos] = i
+                       for i in [0..s.length[ do
+                               var c = s.chars[i]
+                               items[curr_pos] = c
                                curr_pos += 1
                        end
                end
@@ -1163,8 +1392,9 @@ class FlatBuffer
                        s.items.copy_to(items, sl, 0, length)
                else
                        var curr_pos = self.length
-                       for i in s.chars do
-                               items[curr_pos] = i
+                       for i in [0..s.length[ do
+                               var c = s.chars[i]
+                               items[curr_pos] = c
                                curr_pos += 1
                        end
                end
@@ -1369,11 +1599,6 @@ redef class Object
        do
                return "{class_name}:#{object_id.to_hex}"
        end
-
-       protected fun args: Sequence[String]
-       do
-               return sys.args
-       end
 end
 
 redef class Bool
@@ -1424,15 +1649,14 @@ redef class Int
        end
 
        # C function to convert an nit Int to a NativeString (char*)
-       private fun native_int_to_s(len: Int): NativeString is extern "native_int_to_s"
+       private fun native_int_to_s: NativeString is extern "native_int_to_s"
 
        # return displayable int in base 10 and signed
        #
        #     assert 1.to_s            == "1"
        #     assert (-123).to_s       == "-123"
        redef fun to_s do
-               var len = digit_count(10)
-               return native_int_to_s(len).to_s_with_length(len)
+               return native_int_to_s.to_s
        end
 
        # return displayable int in hexadecimal
@@ -1712,7 +1936,8 @@ end
 redef class Sys
        var _args_cache: nullable Sequence[String]
 
-       redef fun args: Sequence[String]
+       # The arguments of the program as given by the OS
+       fun program_args: Sequence[String]
        do
                if _args_cache == null then init_args
                return _args_cache.as(not null)
@@ -1724,7 +1949,7 @@ redef class Sys
                return native_argv(0).to_s
        end
 
-       # Initialize `args` with the contents of `native_argc` and `native_argv`.
+       # Initialize `program_args` with the contents of `native_argc` and `native_argv`.
        private fun init_args
        do
                var argc = native_argc
@@ -1788,3 +2013,9 @@ end
 #     alpha_comparator.sort(a)
 #     assert a == [1, 10, 2, 20, 3]
 fun alpha_comparator: Comparator[Object] do return once new AlphaComparator
+
+# The arguments of the program as given by the OS
+fun args: Sequence[String]
+do
+       return sys.program_args
+end