lib/standard/ropes: Added balancing method to Concat
[nit.git] / lib / standard / text / abstract_text.nit
index 37ae8a7..97191d2 100644 (file)
@@ -16,6 +16,12 @@ import math
 import collection
 intrude import collection::array
 
+in "C" `{
+       #include <stdio.h>
+       #include <stdlib.h>
+       #include <string.h>
+`}
+
 # High-level abstraction for all text representations
 abstract class Text
        super Comparable
@@ -30,18 +36,31 @@ abstract class Text
        #     assert "hello".chars.to_a == ['h', 'e', 'l', 'l', 'o']
        fun chars: SequenceRead[Char] is abstract
 
+       # Gets a view on the bytes of the Text object
+       #
+       #     assert "hello".bytes.to_a == [104u8, 101u8, 108u8, 108u8, 111u8]
+       fun bytes: SequenceRead[Byte] is abstract
+
        # Number of characters contained in self.
        #
        #     assert "12345".length == 5
        #     assert "".length == 0
+       #     assert "あいうえお".length == 5
        fun length: Int is abstract
 
+       # Number of bytes in `self`
+       #
+       #     assert "12345".bytelen == 5
+       #     assert "あいうえお".bytelen == 15
+       fun bytelen: Int is abstract
+
        # Create a substring.
        #
        #     assert "abcd".substring(1, 2)      ==  "bc"
        #     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.
@@ -896,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
@@ -918,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.
        #
@@ -937,6 +956,8 @@ abstract class FlatText
 
        redef var length = 0
 
+       redef var bytelen = 0
+
        redef fun output
        do
                var i = 0
@@ -952,7 +973,7 @@ abstract class FlatText
 end
 
 # Abstract class for the SequenceRead compatible
-# views on String and Buffer objects
+# views on the chars of any Text
 private abstract class StringCharView
        super SequenceRead[Char]
 
@@ -969,6 +990,24 @@ private abstract class StringCharView
        redef fun reverse_iterator do return self.reverse_iterator_from(self.length - 1)
 end
 
+# Abstract class for the SequenceRead compatible
+# views on the bytes of any Text
+private abstract class StringByteView
+       super SequenceRead[Byte]
+
+       type SELFTYPE: Text
+
+       var target: SELFTYPE
+
+       redef fun is_empty do return target.is_empty
+
+       redef fun length do return target.length
+
+       redef fun iterator do return self.iterator_from(0)
+
+       redef fun reverse_iterator do return self.reverse_iterator_from(target.bytelen - 1)
+end
+
 # Immutable sequence of characters.
 #
 # String objects may be created using literals.
@@ -1272,9 +1311,13 @@ abstract class Buffer
        # In Buffers, the internal sequence of character is mutable
        # Thus, `chars` can be used to modify the buffer.
        redef fun chars: Sequence[Char] is abstract
+
+       # In Buffers, the internal sequence of bytes is mutable
+       # Thus, `bytes` can be used to modify the buffer.
+       redef fun bytes: Sequence[Byte] is abstract
 end
 
-# View on Buffer objects, extends Sequence
+# View for chars on Buffer objects, extends Sequence
 # for mutation operations
 private abstract class BufferCharView
        super StringCharView
@@ -1284,6 +1327,15 @@ private abstract class BufferCharView
 
 end
 
+# View for bytes on Buffer objects, extends Sequence
+# for mutation operations
+private abstract class BufferByteView
+       super StringByteView
+       super Sequence[Byte]
+
+       redef type SELFTYPE: Buffer
+end
+
 redef class Object
        # User readable representation of `self`.
        fun to_s: String do return inspect
@@ -1326,10 +1378,14 @@ end
 
 redef class Byte
        # C function to calculate the length of the `NativeString` to receive `self`
-       private fun byte_to_s_len: Int is extern "native_byte_length_str"
+       private fun byte_to_s_len: Int `{
+               return snprintf(NULL, 0, "0x%02x", self);
+       `}
 
        # C function to convert an nit Int to a NativeString (char*)
-       private fun native_byte_to_s(nstr: NativeString, strlen: Int) is extern "native_byte_to_s"
+       private fun native_byte_to_s(nstr: NativeString, strlen: Int) `{
+               snprintf(nstr, strlen, "0x%02x", self);
+       `}
 
        # Displayable byte in its hexadecimal form (0x..)
        #
@@ -1338,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
@@ -1347,7 +1403,7 @@ end
 redef class Int
 
        # Wrapper of strerror C function
-       private fun strerror_ext: NativeString is extern "strerror"
+       private fun strerror_ext: NativeString `{ return strerror(self); `}
 
        # Returns a string describing error number
        fun strerror: String do return strerror_ext.to_s
@@ -1377,10 +1433,14 @@ redef class Int
        end
 
        # C function to calculate the length of the `NativeString` to receive `self`
-       private fun int_to_s_len: Int is extern "native_int_length_str"
+       private fun int_to_s_len: Int `{
+               return snprintf(NULL, 0, "%ld", self);
+       `}
 
        # C function to convert an nit Int to a NativeString (char*)
-       private fun native_int_to_s(nstr: NativeString, strlen: Int) is extern "native_int_to_s"
+       private fun native_int_to_s(nstr: NativeString, strlen: Int) `{
+               snprintf(nstr, strlen, "%ld", self);
+       `}
 
        # return displayable int in base base and signed
        fun to_base(base: Int, signed: Bool): String is abstract
@@ -1468,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'
@@ -1493,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')
@@ -1505,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
@@ -1542,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 ""
 
@@ -1560,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
@@ -1570,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