abstract_text: Refactorisation of the to_s method
[nit.git] / lib / core / text / abstract_text.nit
index 351da66..1b4b383 100644 (file)
@@ -42,9 +42,9 @@ abstract class Text
        # Gets a view on the bytes of the Text object
        #
        # ~~~
-       # assert "hello".bytes.to_a == [104u8, 101u8, 108u8, 108u8, 111u8]
+       # assert "hello".bytes.to_a == [104, 101, 108, 108, 111]
        # ~~~
-       fun bytes: SequenceRead[Byte] is abstract
+       fun bytes: SequenceRead[Int] is abstract
 
        # Number of characters contained in self.
        #
@@ -253,6 +253,7 @@ abstract class Text
        # assert "0x64".to_i       == 100
        # assert "0b1100_0011".to_i== 195
        # assert "--12".to_i       == 12
+       # assert "+45".to_i        == 45
        # ~~~
        #
        # REQUIRE: `self`.`is_int`
@@ -957,21 +958,21 @@ abstract class Text
                        if c == '%' then
                                if i + 2 >= length then
                                        # What follows % has been cut off
-                                       buf[l] = '%'.ascii
+                                       buf[l] = u'%'
                                else
                                        i += 1
                                        var hex_s = substring(i, 2)
                                        if hex_s.is_hex then
                                                var hex_i = hex_s.to_hex
-                                               buf[l] = hex_i.to_b
+                                               buf[l] = hex_i
                                                i += 1
                                        else
                                                # What follows a % is not Hex
-                                               buf[l] = '%'.ascii
+                                               buf[l] = u'%'
                                                i -= 1
                                        end
                                end
-                       else buf[l] = c.ascii
+                       else buf[l] = c.code_point
 
                        i += 1
                        l += 1
@@ -1462,7 +1463,7 @@ end
 # Abstract class for the SequenceRead compatible
 # views on the bytes of any Text
 private abstract class StringByteView
-       super SequenceRead[Byte]
+       super SequenceRead[Int]
 
        type SELFTYPE: Text
 
@@ -1946,7 +1947,7 @@ redef class Byte
        redef fun to_s do
                var nslen = byte_to_s_len
                var ns = new CString(nslen + 1)
-               ns[nslen] = 0u8
+               ns[nslen] = 0
                native_byte_to_s(ns, nslen + 1)
                return ns.to_s_unsafe(nslen, copy=false, clean=false)
        end
@@ -2028,25 +2029,76 @@ redef class Float
        # ~~~
        # assert 12.34.to_s       == "12.34"
        # assert (-0120.030).to_s == "-120.03"
+       # assert (-inf).to_s == "-inf"
+       # assert (nan).to_s == "nan"
        # ~~~
        #
        # see `to_precision` for a custom precision.
        redef fun to_s do
-               var str = to_precision( 3 )
-               if is_inf != 0 or is_nan then return str
-               var len = str.length
+               var str = to_precision(3)
+               return adapt_number_of_decimal(str, false)
+       end
+
+       # Return the representation of `self`, with scientific notation
+       #
+       # Adpat the number of decimals as needed from 1 to a maximum of 6
+       # ~~~
+       # assert 12.34.to_sci       == "1.234e+01"
+       # assert 123.45.to_sci.to_f.to_sci  == "1.2345e+02"
+       # assert 0.001234.to_sci  == "1.234e-03"
+       # assert (inf).to_sci == "inf"
+       # assert (nan).to_sci == "nan"
+       # ~~~
+       fun to_sci: String
+       do
+               var is_inf_or_nan = check_inf_or_nan
+               if is_inf_or_nan != null then return is_inf_or_nan
+               return adapt_number_of_decimal(return_from_specific_format("%e".to_cstring), true)
+       end
+
+       # Return the `string_number` with the adapted number of decimal (i.e the fonction remove the useless `0`)
+       # `is_expo` it's here to specifi if the given `string_number` is in scientific notation
+       private fun adapt_number_of_decimal(string_number: String, is_expo: Bool): String
+       do
+               # check if `self` does not need an adaptation of the decimal
+               if is_inf != 0 or is_nan then return string_number
+               var len = string_number.length
+               var expo_value = ""
+               var numeric_value = ""
                for i in [0..len-1] do
-                       var j = len-1-i
-                       var c = str.chars[j]
-                       if c == '0' then
-                               continue
-                       else if c == '.' then
-                               return str.substring( 0, j+2 )
-                       else
-                               return str.substring( 0, j+1 )
+                       var j = len - 1 - i
+                       var c = string_number.chars[j]
+                       if not is_expo then
+                               if c == '0' then
+                                       continue
+                               else if c == '.' then
+                                       numeric_value = string_number.substring( 0, j + 2)
+                                       break
+                               else
+                                       numeric_value = string_number.substring( 0, j + 1)
+                                       break
+                               end
+                       else if c == 'e' then
+                               expo_value = string_number.substring( j, len - 1 )
+                               is_expo = false
                        end
                end
-               return str
+               return numeric_value + expo_value
+       end
+
+       # Return a string representation of `self` in fonction if it is not a number or infinity.
+       # Return `null` if `self` is not a not a number or an infinity
+       private fun check_inf_or_nan: nullable String
+       do
+               if is_nan then return "nan"
+
+               var isinf = self.is_inf
+               if isinf == 1 then
+                       return "inf"
+               else if isinf == -1 then
+                       return  "-inf"
+               end
+               return null
        end
 
        # `String` representation of `self` with the given number of `decimals`
@@ -2061,31 +2113,39 @@ redef class Float
        # ~~~
        fun to_precision(decimals: Int): String
        do
-               if is_nan then return "nan"
+               var is_inf_or_nan = check_inf_or_nan
+               if is_inf_or_nan != null then return is_inf_or_nan
+               return return_from_specific_format("%.{decimals}f".to_cstring)
+       end
 
-               var isinf = self.is_inf
-               if isinf == 1 then
-                       return "inf"
-               else if isinf == -1 then
-                       return  "-inf"
-               end
+       # Returns the hexadecimal (`String`) representation of `self` in exponential notation
+       #
+       # ~~~
+       # assert 12.345.to_hexa_exponential_notation    == "0x1.8b0a3d70a3d71p+3"
+       # assert 12.345.to_hexa_exponential_notation.to_f == 12.345
+       # ~~~
+       fun to_hexa_exponential_notation: String
+       do
+               return return_from_specific_format("%a".to_cstring)
+       end
 
-               var size = to_precision_size(decimals)
-               var cstr = new CString(size+1)
-               to_precision_fill(decimals, size+1, cstr)
-               return cstr.to_s_unsafe(byte_length=size, copy=false)
+       # Return the representation of `self`, with the specific given c `format`.
+       private fun return_from_specific_format(format: CString): String
+       do
+               var size = to_precision_size_with_format(format)
+               var cstr = new CString(size + 1)
+               to_precision_fill_with_format(format, size + 1, cstr)
+               return cstr.to_s_unsafe(byte_length = size, copy = false)
        end
 
-       # Required string length to hold `self` with `nb` decimals
-       #
-       # The length does not include the terminating null byte.
-       private fun to_precision_size(nb: Int): Int `{
-               return snprintf(NULL, 0, "%.*f", (int)nb, self);
+       # The lenght of `self` in the specific given c `format`
+       private fun to_precision_size_with_format(format: CString): Int`{
+               return snprintf(NULL, 0, format, self);
        `}
 
-       # Fill `cstr` with `self` and `nb` decimals
-       private fun to_precision_fill(nb, size: Int, cstr: CString) `{
-               snprintf(cstr, size, "%.*f", (int)nb, self);
+       # Fill `cstr` with `self` in the specific given c `format`
+       private fun to_precision_fill_with_format(format: CString, size: Int, cstr: CString) `{
+               snprintf(cstr, size, format, self);
        `}
 end
 
@@ -2094,10 +2154,10 @@ redef class Char
        # Returns a sequence with the UTF-8 bytes of `self`
        #
        # ~~~
-       # assert 'a'.bytes == [0x61u8]
-       # assert 'ま'.bytes == [0xE3u8, 0x81u8, 0xBEu8]
+       # assert 'a'.bytes == [0x61]
+       # assert 'ま'.bytes == [0xE3, 0x81, 0xBE]
        # ~~~
-       fun bytes: SequenceRead[Byte] do return to_s.bytes
+       fun bytes: SequenceRead[Int] do return to_s.bytes
 
        # Is `self` an UTF-16 surrogate pair ?
        fun is_surrogate: Bool do