lib/standard/ropes: Added balancing method to Concat
[nit.git] / lib / standard / text / abstract_text.nit
index e0f0f96..97191d2 100644 (file)
@@ -45,12 +45,14 @@ abstract class Text
        #
        #     assert "12345".length == 5
        #     assert "".length == 0
+       #     assert "あいうえお".length == 5
        fun length: Int is abstract
 
        # Number of bytes in `self`
        #
-       # TODO: Implement correctly once UTF-8 is supported
-       fun bytelen: Int do return length
+       #     assert "12345".bytelen == 5
+       #     assert "あいうえお".bytelen == 15
+       fun bytelen: Int is abstract
 
        # Create a substring.
        #
@@ -58,6 +60,7 @@ abstract class Text
        #     assert "abcd".substring(-1, 2)     ==  "a"
        #     assert "abcd".substring(1, 0)      ==  ""
        #     assert "abcd".substring(2, 5)      ==  "cd"
+       #     assert "あいうえお".substring(1,3) ==  "いうえ"
        #
        # A `from` index < 0 will be replaced by 0.
        # Unless a `count` value is > 0 at the same time.
@@ -912,7 +915,7 @@ abstract class Text
                var mypos = src_offset
                var itspos = dest_offset
                while n > 0 do
-                       dest[itspos] = self.chars[mypos]
+                       dest[itspos] = self.bytes[mypos]
                        itspos += 1
                        mypos += 1
                        n -= 1
@@ -934,7 +937,7 @@ abstract class FlatText
        # Real items, used as cache for to_cstring is called
        private var real_items: nullable NativeString = null
 
-       # Returns a char* starting at position `index_from`
+       # Returns a char* starting at position `first_byte`
        #
        # WARNING: If you choose to use this service, be careful of the following.
        #
@@ -953,6 +956,8 @@ abstract class FlatText
 
        redef var length = 0
 
+       redef var bytelen = 0
+
        redef fun output
        do
                var i = 0
@@ -1000,7 +1005,7 @@ private abstract class StringByteView
 
        redef fun iterator do return self.iterator_from(0)
 
-       redef fun reverse_iterator do return self.reverse_iterator_from(self.length - 1)
+       redef fun reverse_iterator do return self.reverse_iterator_from(target.bytelen - 1)
 end
 
 # Immutable sequence of characters.
@@ -1329,7 +1334,6 @@ private abstract class BufferByteView
        super Sequence[Byte]
 
        redef type SELFTYPE: Buffer
-
 end
 
 redef class Object
@@ -1390,7 +1394,7 @@ redef class Byte
        redef fun to_s do
                var nslen = byte_to_s_len
                var ns = new NativeString(nslen + 1)
-               ns[nslen] = '\0'
+               ns[nslen] = 0u8
                native_byte_to_s(ns, nslen + 1)
                return ns.to_s_with_length(nslen)
        end
@@ -1524,20 +1528,58 @@ redef class Float
 end
 
 redef class Char
+
+       # Length of `self` in a UTF-8 String
+       private fun u8char_len: Int do
+               var c = self.ascii
+               if c < 0x80 then return 1
+               if c <= 0x7FF then return 2
+               if c <= 0xFFFF then return 3
+               if c <= 0x10FFFF then return 4
+               # Bad character format
+               return 1
+       end
+
        #     assert 'x'.to_s    == "x"
-       redef fun to_s
-       do
-               var s = new Buffer.with_cap(1)
-               s.chars[0] = self
-               return s.to_s
+       redef fun to_s do
+               var ln = u8char_len
+               var ns = new NativeString(ln + 1)
+               u8char_tos(ns, ln)
+               return ns.to_s_with_length(ln)
        end
 
+       private fun u8char_tos(r: NativeString, len: Int) `{
+               r[len] = '\0';
+               switch(len){
+                       case 1:
+                               r[0] = self;
+                               break;
+                       case 2:
+                               r[0] = 0xC0 | ((self & 0x7C0) >> 6);
+                               r[1] = 0x80 | (self & 0x3F);
+                               break;
+                       case 3:
+                               r[0] = 0xE0 | ((self & 0xF000) >> 12);
+                               r[1] = 0x80 | ((self & 0xFC0) >> 6);
+                               r[2] = 0x80 | (self & 0x3F);
+                               break;
+                       case 4:
+                               r[0] = 0xF0 | ((self & 0x1C0000) >> 18);
+                               r[1] = 0x80 | ((self & 0x3F000) >> 12);
+                               r[2] = 0x80 | ((self & 0xFC0) >> 6);
+                               r[3] = 0x80 | (self & 0x3F);
+                               break;
+               }
+       `}
+
        # Returns true if the char is a numerical digit
        #
        #     assert '0'.is_numeric
        #     assert '9'.is_numeric
        #     assert not 'a'.is_numeric
        #     assert not '?'.is_numeric
+       #
+       # FIXME: Works on ASCII-range only
        fun is_numeric: Bool
        do
                return self >= '0' and self <= '9'
@@ -1549,6 +1591,8 @@ redef class Char
        #     assert 'Z'.is_alpha
        #     assert not '0'.is_alpha
        #     assert not '?'.is_alpha
+       #
+       # FIXME: Works on ASCII-range only
        fun is_alpha: Bool
        do
                return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
@@ -1561,6 +1605,8 @@ redef class Char
        #     assert '0'.is_alphanumeric
        #     assert '9'.is_alphanumeric
        #     assert not '?'.is_alphanumeric
+       #
+       # FIXME: Works on ASCII-range only
        fun is_alphanumeric: Bool
        do
                return self.is_numeric or self.is_alpha
@@ -1598,11 +1644,14 @@ redef class Collection[E]
                return s.to_s
        end
 
-       # Concatenate and separate each elements with `sep`.
+       # Concatenate and separate each elements with `separator`.
+       #
+       # Only concatenate if `separator == null`.
        #
-       #     assert [1, 2, 3].join(":")         == "1:2:3"
-       #     assert [1..3].join(":")            == "1:2:3"
-       fun join(sep: Text): String
+       #     assert [1, 2, 3].join(":")    == "1:2:3"
+       #     assert [1..3].join(":")       == "1:2:3"
+       #     assert [1..3].join            == "123"
+       fun join(separator: nullable Text): String
        do
                if is_empty then return ""
 
@@ -1616,7 +1665,7 @@ redef class Collection[E]
                # Concat other items
                i.next
                while i.is_ok do
-                       s.append(sep)
+                       if separator != null then s.append(separator)
                        e = i.item
                        if e != null then s.append(e.to_s)
                        i.next
@@ -1626,15 +1675,15 @@ redef class Collection[E]
 end
 
 redef class Map[K,V]
-       # Concatenate couple of 'key value'.
-       # key and value are separated by `couple_sep`.
-       # each couple is separated each couple with `sep`.
+       # Concatenate couples of key value.
+       # Key and value are separated by `couple_sep`.
+       # Couples are separated by `sep`.
        #
-       #     var m = new ArrayMap[Int, String]
+       #     var m = new HashMap[Int, String]
        #     m[1] = "one"
        #     m[10] = "ten"
        #     assert m.join("; ", "=") == "1=one; 10=ten"
-       fun join(sep: String, couple_sep: String): String is abstract
+       fun join(sep, couple_sep: String): String is abstract
 end
 
 redef class Sys