lib/standard/string: Moved escape_to_dot from nitcc to standard/string.nit
[nit.git] / lib / standard / string.nit
index 07a7e1f..011cfa1 100644 (file)
@@ -16,6 +16,7 @@ module string
 
 import math
 import collection
+intrude import collection::array
 
 `{
 #include <stdio.h>
@@ -156,13 +157,6 @@ abstract class Text
                return self.chars.iterator
        end
 
-       # Is 'c' contained in self ?
-       #
-       # DEPRECATED : Use self.chars.has instead
-       fun has(c: Char): Bool
-       do
-               return self.chars.has(c)
-       end
 
        # Gets an Array containing the chars of self
        #
@@ -178,7 +172,7 @@ abstract class Text
        # As with substring, a `from` index < 0 will be replaced by 0
        fun substring_from(from: Int): SELFTYPE
        do
-               if from > self.length then return empty
+               if from >= self.length then return empty
                if from < 0 then from = 0
                return substring(from, length - from)
        end
@@ -247,8 +241,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 +272,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
@@ -300,7 +292,8 @@ abstract class Text
        #     assert "0G".is_hex == false
        fun is_hex: Bool
        do
-               for c in self.chars 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
@@ -316,7 +309,8 @@ abstract class Text
        #     assert "Hello World".is_upper == false
        fun is_upper: Bool
        do
-               for char in self.chars do 
+               for i in [0..length[ do
+                       var char = chars[i]
                        if char.is_lower then return false
                end
                return true
@@ -329,7 +323,8 @@ abstract class Text
        #     assert "Hello World".is_lower == false
        fun is_lower: Bool
        do
-               for char in self.chars do 
+               for i in [0..length[ do
+                       var char = chars[i]
                        if char.is_upper then return false
                end
                return true
@@ -363,7 +358,7 @@ abstract class Text
                        if iter.item.ascii > 32 then break
                        iter.next
                end
-               if iter.index == length then return self.empty
+               if iter.index < 0 then return self.empty
                return self.substring(0, iter.index + 1)
        end
 
@@ -379,7 +374,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
@@ -412,7 +408,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
@@ -464,7 +461,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
@@ -498,7 +496,8 @@ abstract class Text
        do
                var buf = new FlatBuffer
 
-               for c in self.chars do
+               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
@@ -554,6 +553,29 @@ abstract class Text
                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.
        #
@@ -602,6 +624,14 @@ abstract class Text
                end
        end
 
+       # Escape string used in labels for graphviz
+       #
+       #   assert ">><<".escape_to_dot == "\\>\\>\\<\\<"
+       fun escape_to_dot: String
+       do
+               return escape_more_to_c("|\{\}<>")
+       end
+
        # Flat representation of self
        fun flatten: FlatText is abstract
 
@@ -613,7 +643,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
 
@@ -739,7 +770,8 @@ abstract class String
                var new_str = new FlatBuffer.with_capacity(self.length)
                var is_first_char = true
 
-               for char in self.chars do
+               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
@@ -777,7 +809,8 @@ abstract class String
                var is_first_char = true
                var follows_us = false
 
-               for char in self.chars do
+               for i in [0..length[ do
+                       var char = chars[i]
                        if is_first_char then
                                new_str.add(char)
                                is_first_char = false
@@ -825,6 +858,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        #
        ################################################
@@ -1021,8 +1063,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
@@ -1095,7 +1138,7 @@ private class FlatStringReverseIterator
                curr_pos = pos + tgt.index_from
        end
 
-       redef fun is_ok do return curr_pos >= 0
+       redef fun is_ok do return curr_pos >= target.index_from
 
        redef fun item do return target_items[curr_pos]
 
@@ -1246,6 +1289,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
@@ -1314,8 +1364,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
@@ -1343,8 +1394,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
@@ -1787,18 +1839,51 @@ redef class Collection[E]
 end
 
 redef class Array[E]
+
        # Fast implementation
        redef fun to_s
        do
-               var s = new FlatBuffer
-               var i = 0
                var l = length
+               if l == 0 then return ""
+               if l == 1 then if self[0] == null then return "" else return self[0].to_s
+               var its = _items
+               var na = new NativeArray[String](l)
+               var i = 0
+               var sl = 0
+               var mypos = 0
                while i < l do
-                       var e = self[i]
-                       if e != null then s.append(e.to_s)
+                       var itsi = its[i]
+                       if itsi == null then
+                               i += 1
+                               continue
+                       end
+                       var tmp = itsi.to_s
+                       sl += tmp.length
+                       na[mypos] = tmp
                        i += 1
+                       mypos += 1
                end
-               return s.to_s
+               var ns = new NativeString(sl + 1)
+               ns[sl] = '\0'
+               i = 0
+               var off = 0
+               while i < mypos do
+                       var tmp = na[i]
+                       var tpl = tmp.length
+                       if tmp isa FlatString then
+                               tmp.items.copy_to(ns, tpl, tmp.index_from, off)
+                               off += tpl
+                       else
+                               for j in tmp.substrings do
+                                       var s = j.as(FlatString)
+                                       var slen = s.length
+                                       s.items.copy_to(ns, slen, s.index_from, off)
+                                       off += slen
+                               end
+                       end
+                       i += 1
+               end
+               return ns.to_s_with_length(sl)
        end
 end
 
@@ -1843,7 +1928,8 @@ end
 # Native strings are simple C char *
 extern class NativeString `{ char* `}
        super StringCapable
-
+       # Creates a new NativeString with a capacity of `length`
+       new(length: Int) is intern
        fun [](index: Int): Char is intern
        fun []=(index: Int, item: Char) is intern
        fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
@@ -1884,7 +1970,7 @@ interface StringCapable
 end
 
 redef class Sys
-       var _args_cache: nullable Sequence[String]
+       private var args_cache: nullable Sequence[String]
 
        # The arguments of the program as given by the OS
        fun program_args: Sequence[String]