Merge: App n tiles
authorJean Privat <jean@pryen.org>
Tue, 10 Jun 2014 19:10:46 +0000 (15:10 -0400)
committerJean Privat <jean@pryen.org>
Tue, 10 Jun 2014 19:10:46 +0000 (15:10 -0400)
Introduce tileset, monospace bitmap fonts for mnit

Also some bugfixes

Pull-Request: #484
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

17 files changed:
lib/ropes_debug.nit [new file with mode: 0644]
lib/standard/file.nit
lib/standard/ropes.nit
lib/standard/standard.nit
lib/standard/stream.nit
lib/standard/string.nit
src/nitni/nitni_base.nit
tests/sav/test_ffi_c_operators.res
tests/sav/test_flatrope.res [new file with mode: 0644]
tests/sav/test_ropes.res [new file with mode: 0644]
tests/sav/test_text.res [new file with mode: 0644]
tests/sav/test_text_alt1.res [new file with mode: 0644]
tests/sav/test_text_alt2.res [new file with mode: 0644]
tests/test_ffi_c_operators.nit
tests/test_flatrope.nit [new file with mode: 0644]
tests/test_ropes.nit [new file with mode: 0644]
tests/test_text.nit [new file with mode: 0644]

diff --git a/lib/ropes_debug.nit b/lib/ropes_debug.nit
new file mode 100644 (file)
index 0000000..b9ba5ab
--- /dev/null
@@ -0,0 +1,76 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+# Exposes methods for debugging ropes when needed.
+module ropes_debug
+
+intrude import ::standard::ropes
+import ::standard
+
+redef class Rope
+       # Writes self as a dot file on the hard drive
+       fun to_dot(filepath: String): String is abstract
+end
+
+redef class RopeNode
+       # Generates a dot string
+       fun to_dot(s: String): String is abstract
+end
+
+redef class Leaf
+       redef fun to_dot(s): String
+       do
+               s += "n{object_id} [label = \"{str}\" shape = rect];\n"
+               s += "n{str.object_id} -> n{object_id} [label = \"contains\"];\n"
+               s = str.to_dot(s)
+               return s
+       end
+end
+
+redef class Concat
+       redef fun to_dot(s): String
+       do
+               s += "n{object_id} [label = {length}];\n"
+               if left != null then
+                       s += "n{object_id} -> n{left.object_id} [label = \"left\"];\n"
+                       s = left.to_dot(s)
+               end
+               if right != null then
+                       s += "n{object_id} -> n{right.object_id} [label = \"right\"];\n"
+                       s = right.to_dot(s)
+               end
+               return s
+       end
+end
+
+redef class FlatString
+       fun to_dot(s: String): String
+       do
+               return s + "n{object_id} [label=\"FlatString\\nindex_from = {index_from}\\nindex_to = {index_to}\\nNativeString = {items.to_s_with_length(items.cstring_length)}\"];\n"
+       end
+end
+
+redef class RopeString
+       redef fun to_dot(filepath: String)
+       do
+               var of = new OFStream.open(filepath)
+               var ret: String = new RopeString.from("digraph g \{\n")
+               ret = root.to_dot(ret).as(RopeString)
+               ret += "\}\n"
+               ret.write_to(of)
+               of.close
+               return ret
+       end
+end
+
+
index 27b2626..f82a1fd 100644 (file)
@@ -16,7 +16,7 @@
 module file
 
 intrude import stream
-intrude import string
+intrude import ropes
 import string_search
 import time
 
@@ -134,7 +134,11 @@ class OFStream
        redef fun write(s)
        do
                assert _writable
-               write_native(s.to_cstring, s.length)
+               if s isa FlatText then
+                       write_native(s.to_cstring, s.length)
+               else
+                       for i in s.substrings do write_native(i.to_cstring, i.length)
+               end
        end
 
        redef fun is_writable do return _writable
index 5758afd..45c1cbd 100644 (file)
 # A rope is a kind of string but instead of being flat, it relies on a binary tree structure to store data.
 module ropes
 
-import file
 intrude import string
 
-# Structure, tuple containing a LeafNode and an Int
-# Used for methods like [] or has/has_only
-private class TupleLeafNodePos
-       private var curr_node: LeafNode
-       private var corrected_pos: Int
-       private var visit_stack: List[TupleVisitNode]
+# Used when searching for a particular node
+# Returns the path to the node from the root of the rope
+# Also, the node and the offset for seeked position in the rope
+private class Path
+       # Leaf found
+       var leaf: Leaf
+       # Offset in leaf
+       var offset: Int
+       # Stack of the nodes traversed, and the path used
+       var stack: List[PathElement]
 end
 
-# Abstract class, represents all the services offered by both mutable and immutable ropes
-abstract class Rope
-       super Comparable
-       super StringCapable
-
-       # Cached version of self as a flat String
-       private var str_representation: nullable String = null
-
-       redef type OTHER: Rope
+# An element for a Path, has the concat node and whether or not
+# left or right child was visited.
+private class PathElement
+       # Visited node
+       var node: Concat
+       # Was the left child visited ?
+       var left = false
+       # Was the right child visited ?
+       var right = false
+end
 
-       # The first node of the hierarchy
-       private var parent_node: RopeNode
+# A node for a Rope
+private abstract class RopeNode
+       # Length of the node
+       var length = 0
+end
 
-       # Needed by the compiler to avoid producing an error with constructors in subclasses
-       init do
-               self.parent_node = new ConcatNode
-       end
+# Node that represents a concatenation between two nodes (of any RopeNode type)
+private class Concat
+       super RopeNode
 
-       # Initializes a new Rope with a text embedded in directly
-       init with_string(str: String) do
-               self.parent_node = new ConcatNode
-               parent_node.as(ConcatNode).right_child = new LeafNode(str)
-               parent_node.as(ConcatNode).update_data
-       end
+       # Left child of the node
+       var _left: nullable RopeNode = null
+       # Right child of the node
+       var _right: nullable RopeNode = null
 
-       # Returns a view on the rope
-       fun chars: SequenceRead[Char]
-       do
-               return new CharRopeView(self)
-       end
+       fun left: nullable RopeNode do return _left
+       fun right: nullable RopeNode do return _right
 
-       # Gets the total length of the Rope
-       fun length: Int
+       fun left=(l: RopeNode)
        do
-               return parent_node.length
+               _left = l
+               length = l.length
+               if _right != null then length += _right.length
        end
 
-       # Returns a flat version of self
-       redef fun to_s
+       fun right=(r: RopeNode)
        do
-               if self.str_representation == null then flatten
-               return str_representation.as(not null)
+               _right = r
+               length = r.length
+               if _left != null then length += _left.length
        end
+end
 
-       # Stores a flat version of self in cache
-       private fun flatten: FlatString
-       do
-               var native_final_str = calloc_string(length + 1)
-
-               native_final_str[length] = '\0'
-
-               var offset = 0
-
-               var iter = new DFSRopeLeafIterator(self)
-
-               while iter.is_ok do
-                       iter.item.value.as(FlatString).items.copy_to(native_final_str, iter.item.value.length, 0, offset)
-                       offset += iter.item.value.length
-                       iter.next
-               end
+# Leaf of a Rope, contains a FlatString
+private class Leaf
+       super RopeNode
 
-               return native_final_str.to_s_with_length(length)
-       end
+       # Encapsulated FlatString in the leaf node
+       var str: FlatString
 
-       # Gets a node containing the substring to seek the char at the require position
-       private fun get_node_for_pos(position: Int): TupleLeafNodePos
-       do
-               assert position >= 0 and position < self.length
-
-               var curr_node: nullable RopeNode = parent_node
-
-               var visit_stack = new List[TupleVisitNode]
-
-               var curr_visit_tuple: TupleVisitNode
-
-               loop
-                       if curr_node isa ConcatNode then
-                               curr_visit_tuple = new TupleVisitNode(curr_node)
-                               if curr_node.left_child != null and position < curr_node.left_child.length then
-                                       curr_visit_tuple.left_visited = true
-                                       curr_node = curr_node.left_child
-                               else if curr_node.right_child != null then
-                                       curr_visit_tuple.left_visited = true
-                                       curr_visit_tuple.right_visited = true
-                                       if curr_node.left_child != null then position -= curr_node.left_child.length
-                                       curr_node = curr_node.right_child
-                               else
-                                       print "Fatal Error"
-                                       abort
-                               end
-                               visit_stack.push(curr_visit_tuple)
-                       else if curr_node isa LeafNode then
-                               return new TupleLeafNodePos(curr_node, position, visit_stack)
-                       end
-               end
+       init(val: FlatString) do
+               self.str = val
+               length = str.length
        end
 
-       # Concats two ropes and returns a new one
-       fun +(other: Rope): Rope do
-               var new_rope = new BufferRope
+end
 
-               var first_iter = new DFSRopeLeafIterator(self)
+# Basic structure, binary tree with a root node.
+#
+# Also shared services by subsequent implementations.
+abstract class Rope
+       super Text
 
-               while first_iter.is_ok do
-                       new_rope.append(first_iter.item.value)
-                       first_iter.next
-               end
+       # Root node, entry point of a Rope.
+       private var root: RopeNode
 
-               var second_iter = new DFSRopeLeafIterator(other)
+       # Cached version of self as a flat String
+       private var str_representation: nullable NativeString = null
 
-               while second_iter.is_ok do
-                       new_rope.append(second_iter.item.value)
-                       second_iter.next
-               end
+       # Empty Rope
+       init do from("")
 
-               return new_rope
+       # Creates a new Rope with `s` as root
+       init from(s: String) do
+               if s isa RopeString then root = s.root else root = new Leaf(s.as(FlatString))
        end
 
-       # Returns a copy of several ropes concatenated
-       #
-       # Is equivalent to a chain of + operations
-       # Except this one is optimized for performance
-       fun multi_concat(ropes: Rope...): Rope
+       private init from_root(r: RopeNode)
        do
-               var new_rope = new BufferRope
+               root = r
+       end
 
-               var self_iter = self.iterator
-               while self_iter.is_ok do
-                       new_rope.append(self_iter.item.value)
-                       self_iter.next
-               end
+       redef fun length do return root.length
 
-               for i in ropes do
-                       var iter = i.iterator
-                       while iter.is_ok do
-                               new_rope.append(iter.item.value)
-                               iter.next
-                       end
-               end
+       # Iterator on the nodes of the rope, in forward postfix order
+       private fun postfix(from: Int): Postfix do return new Postfix.from(self, from)
 
-               return new_rope
-       end
+       # Iterator on the leaves of the rope, forward order
+       private fun leaves(from: Int): LeavesIterator do return new LeavesIterator(self, from)
 
-       # Appends the content of self multiple times in a new Rope object
-       fun *(repeats: Int): Rope do
+       # Iterator on the substrings from 0, in forward order
+       redef fun substrings do return new SubstringsIterator(self, 0)
 
-               var new_rope = new BufferRope
+       # Iterator on the substrings, starting at position `from`, in forward order
+       fun substrings_from(from: Int): IndexedIterator[Text] do return new SubstringsIterator(self, from)
 
-               var str = self.to_s
+       # Iterator on the nodes of the rope, in backwards postfix order
+       private fun reverse_postfix(from: Int): ReversePostfix do return new ReversePostfix.from(self, from)
 
-               for i in [1 .. repeats] do new_rope.append(str)
+       # Iterator on the leaves of the rope, backwards order
+       private fun reverse_leaves(from: Int): ReverseLeavesIterator do return new ReverseLeavesIterator(self,from)
 
-               return new_rope
-       end
+       # Iterator on the substrings, in reverse order
+       fun reverse_substrings: IndexedIterator[Text] do return new ReverseSubstringsIterator(self, length-1)
 
-       # Returns an iterator on self
-       #
-       # Unsafe modifications on a MutableRope
-       #
-       private fun iterator: Iterator[LeafNode] do return new DFSRopeLeafIterator(self)
+       # Iterator on the substrings, in reverse order, starting iteration at position `from`
+       fun reverse_substrings_from(from: Int): IndexedIterator[Text] do return new ReverseSubstringsIterator(self, from)
 
-       # Creates a subrope.
-       #
-       # var rope = (new BufferRope).append("abcd")
-       #
-       #       assert rope.subrope(1, 2)         ==  "bc"
-       #       assert rope.subrope(-1, )         ==  "a"
-       #       assert rope.subrope(1, 0)         ==  ""
-       #       assert rope.subrope(2, 5)         ==  "cd"
-       #
-       # A `index_from` index < 0 will be replaced by 0.
-       # Unless a `count` value is > 0 at the same time.
-       # In this case, `index_from += count` and `count -= index_from`.
-       #
-       fun subrope(index_from: Int, count: Int): Rope
+       redef fun output
        do
-               assert count >= 0
-
-               if index_from < 0 then
-                       count += index_from
-                       if count < 0 then count = 0
-                       index_from = 0
+               for i in substrings do
+                       i.output
                end
+       end
 
-               if count - index_from >= self.length then count = length - index_from
+       redef fun to_cstring
+       do
+               if str_representation != null then return str_representation.as(not null)
 
-               var buffer = new BufferRope
+               var native_final_str = calloc_string(length + 1)
 
-               var iter = new DFSRopeLeafIterator.with_index(self, index_from)
+               native_final_str[length] = '\0'
 
-               var curr_subrope_index = index_from - iter.pos
+               if self.length == 0 then
+                       str_representation = native_final_str
+                       return native_final_str
+               end
 
-               while iter.is_ok do
-                       if count == 0 then break
-                       if curr_subrope_index > 0 then
-                               if count >= iter.item.value.length then
-                                       buffer.append(iter.item.value.substring(curr_subrope_index, count))
-                                       count -= iter.item.value.length - curr_subrope_index
-                                       curr_subrope_index = 0
-                               else
-                                       buffer.append(iter.item.value.substring(curr_subrope_index, count))
-                                       break
-                               end
-                       else
-                               if count >= iter.item.value.length then
-                                       buffer.append(iter.item.value)
-                                       count -= iter.item.value.length
-                               else
-                                       buffer.append(iter.item.value.substring(0, count))
-                                       break
-                               end
-                       end
+               var offset = 0
 
-                       iter.next
+               for i in substrings do
+                       var str = i.flatten
+                       if str isa FlatString then str.items.copy_to(native_final_str, str.length, str.index_from, offset)
+                       offset += i.length
                end
 
-               return buffer
-       end
+               str_representation = native_final_str
 
-       # Returns an upper (capitalized) version of self
-       fun to_upper: Rope
-       do
-               var new_rope = new BufferRope
-               var iter = new DFSRopeLeafIterator(self)
-               while iter.is_ok do
-                       new_rope.append(iter.item.value.to_upper)
-                       iter.next
-               end
-               return new_rope
+               return native_final_str
        end
 
-       # Returns a lower (minuscule) version of self
-       fun to_lower: Rope
+       # Path to the Leaf for `position`
+       private fun node_at(position: Int): Path
        do
-               var new_rope = new BufferRope
-               var iter = new DFSRopeLeafIterator(self)
-               while iter.is_ok do
-                       new_rope.append(iter.item.value.to_lower)
-                       iter.next
-               end
-               return new_rope
+               assert position >= 0 and position < length
+               return get_node_from(root.as(not null), 0, position, new List[PathElement])
        end
 
-       ############################################################################
-       #                       Comparable Refined Methods                         #
-       ############################################################################
-
-       # Compares the current Rope to another object (either another rope or a String)
-       redef fun == (other)
+       # Builds the path to Leaf at position `seek_pos`
+       private fun get_node_from(node: RopeNode, curr_pos: Int, seek_pos: Int, stack: List[PathElement]): Path
        do
-               if other == null or not (other isa Rope or other isa FlatText) then return false
-               var self_iter = new RopeCharIterator(self)
-               if other isa Rope then
-                       if self.length != other.length then return false
-                       var other_iterator = new RopeCharIterator(other)
-                       while self_iter.is_ok do
-                               if self_iter.item != other_iterator.item then return false
-                               self_iter.next
-                               other_iterator.next
-                       end
-               else if other isa FlatText then
-                       var pos = 0
-                       if self.length != other.length then return false
-                       while self_iter.is_ok do
-                               if self_iter.item != other[pos] then return false
-                               pos += 1
-                               self_iter.next
+               assert curr_pos >= 0
+               if node isa Leaf then return new Path(node, seek_pos - curr_pos, stack)
+               node = node.as(Concat)
+
+               if node.left != null then
+                       var next_pos = curr_pos + node.left.length
+                       stack.add(new PathElement(node))
+                       if next_pos > seek_pos then
+                               stack.last.left = true
+                               return get_node_from(node.left.as(not null), curr_pos, seek_pos, stack)
                        end
+                       stack.last.right = true
+                       return get_node_from(node.right.as(not null), next_pos, seek_pos, stack)
+               else
+                       var vis = new PathElement(node)
+                       vis.right = true
+                       stack.add(vis)
+                       return get_node_from(node.right.as(not null), curr_pos, seek_pos, stack)
                end
-               return true
        end
 
-       # Checks if self is lesser than other
-       #
-       # Comparison done in lexicographical order
-       # i.e. 'aa' < 'b'
-       #
-       redef fun <(other)
+       redef fun ==(o)
        do
-               var other_iter = other.chars.iterator
-               for i in self.chars do
-                       if not other_iter.is_ok then return false
-                       if i < other_iter.item then return true
-                       if i > other_iter.item then return false
-                       other_iter.next
+               if not o isa Text then return false
+               if o.length != self.length then return false
+               var oit = o.chars.iterator
+               for i in self.chars.iterator do
+                       if i != oit.item then return false
+                       oit.next
                end
-               if other_iter.is_ok then return true
-               return false
+               return true
        end
-
 end
 
-# Rope that can be modified
-#
-# /!\ Non Thread-safe /!\
-#
-class BufferRope
+# Rope that cannot be modified
+class RopeString
        super Rope
+       super String
 
-       var is_dirty: Bool = false
+       redef fun to_s do return self
 
-       init
-       do
-               super
-       end
+       redef fun empty do return once new RopeString.from("")
 
-       redef init with_string(str)
-       do
-               super
-       end
-
-       ############################################################################
-       #                         Tree Balancing Methods                           #
-       ############################################################################
+       redef var chars: SequenceRead[Char] = new RopeStringChars(self)
 
-       # Performs a right rotation on a node of the Rope
-       #
-       #              Root                Pivot
-       #             /    \              /     \
-       #           Pivot Leaf3     =>  Leaf1  Root
-       #          /     \                    /   \
-       #       Leaf1   Leaf2              Leaf2  Leaf3
-       private fun rotate_right(root: ConcatNode)
+       redef fun reversed
        do
-               assert root.left_child != null
-               var pivot = root.left_child.as(ConcatNode)
-               var root_new_left = pivot.right_child
-               var root_parent = root.parent
-
-               root.left_child = root_new_left
-               pivot.right_child = root
-               if root_parent == null then
-                       self.parent_node = pivot
-                       pivot.parent = null
-                       return
+               var ret = empty.as(RopeString)
+               for i in substrings do
+                       ret = ret.prepend(i.reversed.to_s).as(RopeString)
                end
-
-               if root_parent.left_child == root then
-                       root_parent.left_child = pivot
-               else
-                       root_parent.right_child = pivot
-               end
-
-               root.update_data
-               pivot.update_data
-               root_parent.update_data
+               return ret
        end
 
-       # Performs a left rotation on a node of the Rope
-       #
-       #      Root                    Pivot
-       #     /    \                  /     \
-       #  Leaf1  Pivot        =>   Root   Leaf3
-       #        /     \           /    \
-       #      Leaf2  Leaf3      Leaf1 Leaf2
-       private fun rotate_left(root: ConcatNode)
+       redef fun to_upper
        do
-               assert root.right_child != null
-               var pivot = root.right_child.as(ConcatNode)
-               var root_new_right = pivot.left_child
-               var root_parent = root.parent
-
-               root.right_child = root_new_right
-               pivot.left_child = root
-               if root_parent == null then
-                       self.parent_node = pivot
-                       pivot.parent = null
-                       return
+               var ret = empty
+               for i in substrings do
+                       ret += i.to_upper
                end
-
-               if root_parent.left_child == root then
-                       root_parent.left_child = pivot
-               else
-                       root_parent.right_child = pivot
-               end
-
-               root.update_data
-               pivot.update_data
-               root_parent.update_data
+               return ret
        end
 
-       # Shortcut method to balance a node and its parents
-       # based on the rules of the AVL Tree
-       private fun balance_from_node(parent_node: nullable ConcatNode)
+       redef fun to_lower
        do
-               while parent_node != null do
-                       parent_node.update_data
-                       var node_balance = parent_node.balance_factor
-                       if node_balance < -1 or node_balance > 1 then
-                               balance_node(parent_node)
-                       end
-                       parent_node = parent_node.parent
+               var ret = empty
+               for i in substrings do
+                       ret += i.to_lower
                end
+               return ret
        end
 
-       # Performs rotations to balance a node according to AVL Tree rules
-       private fun balance_node(node: ConcatNode)
+       redef fun +(o) do return insert_at(o.to_s, length)
+
+       redef fun *(n)
        do
-               var balance_factor = node.balance_factor
-               if balance_factor < -1 then
-                       var right_balance = node.right_child.balance_factor
-                       if right_balance < 0 then
-                               rotate_left(node)
-                       else
-                               rotate_right(node.right_child.as(ConcatNode))
-                               rotate_left(node)
-                       end
-               else
-                       var left_balance = node.left_child.balance_factor
-                       if left_balance > 0 then
-                               rotate_right(node)
-                       else
-                               rotate_left(node.left_child.as(ConcatNode))
-                               rotate_right(node)
-                       end
+               var ret = new RopeString.from("")
+               for i in [0..n[ do
+                       ret = (ret + self).as(RopeString)
                end
+               return ret
        end
 
-       ############################################################################
-       #                      BufferRope exclusive Methods                        #
-       ############################################################################
-
-       # Appends a new Collection[Char] at the end of the current rope
-       fun append(str: String): BufferRope
+       # Inserts a String `str` at position `pos`
+       redef fun insert_at(str, pos)
        do
-               var last_node = parent_node
-
-               while last_node isa ConcatNode and last_node.right_child != null do
-                       last_node = last_node.right_child.as(not null)
-               end
-
-               if last_node isa ConcatNode then
-                       last_node.right_child = new LeafNode(str.to_s)
-               else if last_node isa LeafNode then
-                       var last_node_parent = last_node.parent
-                       var new_concat = new ConcatNode
-                       last_node_parent.right_child = new_concat
-                       new_concat.left_child = last_node
-                       new_concat.right_child = new LeafNode(str.to_s)
-                       last_node = new_concat
-               else
-                       print "Fatal Error, please report to the developers for more insight."
-                       abort
-               end
+               if str.length == 0 then return self
+               if self.length == 0 then return new RopeString.from(str)
 
-               balance_from_node(last_node)
+               assert pos >= 0 and pos <= length
 
-               is_dirty = true
+               if pos == length then return append(str).as(RopeString)
 
-               return self
-       end
+               var path = node_at(pos)
 
-       # Variatic function to append several collections of Chars
-       fun append_multi(strs: String...): BufferRope
-       do
-               for i in strs do
-                       append(i)
-               end
-               return self
-       end
+               var last_concat = new Concat
 
-       # Adds a new Collection[Char] at the beginning of the rope
-       fun prepend(str: String): BufferRope
-       do
-               var curr_node = parent_node
-
-               while curr_node isa ConcatNode and curr_node.left_child != null do
-                       curr_node = curr_node.left_child.as(not null)
-               end
-
-               if curr_node isa ConcatNode then
-                       curr_node.left_child = new LeafNode(str.to_s)
-               else if curr_node isa LeafNode then
-                       var parent = curr_node.parent
-                       var new_concat = new ConcatNode
-                       var new_leaf = new LeafNode(str.to_s)
-                       new_concat.left_child = new_leaf
-                       new_concat.right_child = curr_node
-                       parent.left_child = new_concat
-                       curr_node = new_concat
+               if path.offset == 0 then
+                       last_concat.right = path.leaf
+                       if str isa FlatString then last_concat.left = new Leaf(str) else last_concat.left = str.as(RopeString).root
+               else if path.offset == path.leaf.length then
+                       if str isa FlatString then last_concat.right = new Leaf(str) else last_concat.right = str.as(RopeString).root
+                       last_concat.left = path.leaf
                else
-                       print "Fatal Error"
-                       abort
+                       var s = path.leaf.str
+                       var l_half = s.substring(0, s.length - path.offset)
+                       var r_half = s.substring_from(s.length - path.offset)
+                       var cct = new Concat
+                       cct.right = new Leaf(r_half.as(FlatString))
+                       last_concat.left = new Leaf(l_half.as(FlatString))
+                       if str isa FlatString then last_concat.right = new Leaf(str) else last_concat.right = str.as(RopeString).root
+                       cct.left = last_concat
+                       last_concat = cct
+               end
+
+               for i in path.stack.reverse_iterator do
+                       var nod = new Concat
+                       if i.left then
+                               nod.right = i.node.right.as(not null)
+                               nod.left = last_concat
+                       else
+                               nod.left = i.node.left.as(not null)
+                               nod.right = last_concat
+                       end
+                       last_concat = nod
                end
 
-               balance_from_node(curr_node)
+               return new RopeString.from_root(last_concat)
+       end
 
-               is_dirty = true
+       # Adds `s` at the beginning of self
+       redef fun prepend(s) do return insert_at(s, 0)
 
-               return self
+       # Adds `s` at the end of self
+       redef fun append(s)
+       do
+               if self.is_empty then return s
+               return new RopeString.from_root(append_to_path(root,s))
        end
 
-       # Variatic function to prepend several collections of Chars
-       fun prepend_multi(strs: String...): BufferRope
+       # Builds a new path from root to the rightmost node with s appended
+       private fun append_to_path(node: RopeNode, s: String): RopeNode
        do
-               for i in strs do
-                       prepend(i)
+               var cct = new Concat
+               if node isa Leaf then
+                       cct.left = node
+                       if s isa FlatString then cct.right = new Leaf(s) else cct.right = s.as(RopeString).root
+               else if node isa Concat then
+                       var right = node.right
+                       if node.left != null then cct.left = node.left.as(not null)
+                       if right == null then
+                               if s isa FlatString then cct.right = new Leaf(s) else cct.right = s.as(RopeString).root
+                       else
+                               cct.right = append_to_path(right, s)
+                       end
                end
-               return self
+               return cct
        end
 
-       # Adds the content of `str` after self, does not create a new rope object
-       fun concat(str: Rope): Rope
+       # O(log(n))
+       #
+       #     var rope = new RopeString.from("abcd")
+       #     assert rope.substring(1, 2)         ==  "bc"
+       #     assert rope.substring(-1, 2)         ==  "a"
+       #     assert rope.substring(1, 0)         ==  ""
+       #     assert rope.substring(2, 5)         ==  "cd"
+       #
+       redef fun substring(pos, len)
        do
-               var other_iter = new DFSRopeLeafIterator(str)
-
-               var modif_list = new List[String]
-
-               while other_iter.is_ok do
-                       modif_list.push(other_iter.item.value)
-                       other_iter.next
+               if pos < 0 then
+                       len += pos
+                       pos = 0
                end
 
-               while modif_list.length > 0 do
-                       self.append(modif_list.shift)
-               end
+               if pos + len > length then len = length - pos
 
-               if not is_dirty then is_dirty = true
+               if len <= 0 then return new RopeString.from("")
 
-               return self
-       end
+               var path = node_at(pos)
 
-       # Returns the content of the current BufferRope object as an ImmutableRope
-       fun freeze: ImmutableRope
-       do
-               var buffer_rope = new BufferRope
-               var new_rope = new ImmutableRope
+               var lf = path.leaf
+               var offset = path.offset
+
+               if path.leaf.str.length - offset > len then lf = new Leaf(lf.str.substring(offset,len).as(FlatString)) else lf = new Leaf(lf.str.substring_from(offset).as(FlatString))
 
-               var iter = new DFSRopeLeafIterator(self)
+               var nod: RopeNode = lf
 
-               while iter.is_ok do
-                       buffer_rope.append(iter.item.value)
-                       iter.next
+               for i in path.stack.reverse_iterator do
+                       if i.right then continue
+                       var tmp = new Concat
+                       tmp.left = nod
+                       var r = i.node.right
+                       if r != null then tmp.right = r
+                       nod = tmp
                end
 
-               new_rope.parent_node = buffer_rope.parent_node
+               var ret = new RopeString
+               ret.root = nod
 
-               if not is_dirty then new_rope.str_representation = self.str_representation
+               path = ret.node_at(len-1)
 
-               return new_rope
-       end
+               offset = path.offset
+               nod = new Leaf(path.leaf.str.substring(0, offset+1).as(FlatString))
 
-       # Unsafe method to convert self as an ImmutableRope
-       #
-       # To be used internally only
-       private fun to_immutable: ImmutableRope
-       do
-               var immutable_self = new ImmutableRope
-               immutable_self.parent_node = self.parent_node
-               return immutable_self
-       end
+               for i in path.stack.reverse_iterator do
+                       if i.left then continue
+                       var tmp = new Concat
+                       tmp.right = nod
+                       var l = i.node.left
+                       if l != null then tmp.left = l
+                       nod = tmp
+               end
 
-       ############################################################################
-       #                          Rope refined Methods                            #
-       ############################################################################
+               ret.root = nod
 
-       redef fun subrope(index_from: Int, count: Int): BufferRope
-       do
-               return super.as(BufferRope)
+               return ret
        end
+end
 
-       redef fun *(repeats: Int): BufferRope
-       do
-               return super.as(BufferRope)
-       end
+redef class FlatString
 
-       redef fun +(other: Rope): BufferRope
-       do
-               return super.as(BufferRope)
-       end
+       redef fun append(s) do return (new RopeString.from(self)) + s
 
-       redef fun multi_concat(ropes: Rope...): BufferRope
-       do
-               return super.as(BufferRope)
-       end
+       redef fun prepend(s) do return (new RopeString.from(self)).prepend(s)
 
-       # Refines to add a cache method, calculates only once for every modification
-       # the string representation for self
-       redef fun to_s
+       redef fun insert_at(s, pos)
        do
-               if self.str_representation == null or is_dirty then
-                       self.str_representation = flatten
-                       is_dirty = false
-               end
-               return self.str_representation.as(not null)
+               if pos == 0 then return prepend(s)
+               if pos == length then return append(s)
+
+               var l = substring(0,pos)
+               var r = substring_from(pos)
+               var ret: String = new RopeString.from(l)
+               ret += s
+               return ret + r
        end
 
 end
 
-# Rope that cannot be modified
-class ImmutableRope
-       super Rope
+private class RopeStringChars
+       super SequenceRead[Char]
 
-       init
-       do
-               super
-       end
+       var tgt: Rope
 
-       redef init with_string(str)
+       redef fun [](pos)
        do
-               super
+               assert pos < tgt.length
+               var path = tgt.node_at(pos)
+               return path.leaf.str.chars[path.offset]
        end
 
-       ############################################################################
-       #                          Rope refined Methods                            #
-       ############################################################################
+       redef fun iterator do return iterator_from(0)
 
-       redef fun subrope(index_from: Int, count: Int): ImmutableRope
-       do
-               return (super.as(BufferRope)).to_immutable
-       end
+       redef fun iterator_from(pos) do return new RopeCharIterator(tgt, pos)
 
-       redef fun *(repeats: Int): ImmutableRope
-       do
-               return (super.as(BufferRope)).to_immutable
-       end
+       redef fun reverse_iterator do return reverse_iterator_from(tgt.length-1)
 
-       redef fun +(other: Rope): ImmutableRope
-       do
-               return (super.as(BufferRope)).to_immutable
-       end
+       redef fun reverse_iterator_from(pos) do return new ReverseRopeCharIterator(tgt, pos)
+end
 
-       redef fun multi_concat(ropes: Rope...): ImmutableRope
+# Used to iterate on a Rope
+private class IteratorElement
+
+       init(e: RopeNode)
        do
-               return (super.as(BufferRope)).to_immutable
+               if e isa Leaf then
+                       left = true
+                       right = true
+               end
+               node = e
        end
 
+       # The node being visited
+       var node: RopeNode
+       # If the node has a left child, was it visited ?
+       var left = false
+       # If the node has a right child, was it visited ?
+       var right = false
+       # Was the current node visited ?
+       var done = false
 end
 
-############################################
-#            Rope view classes             #
-############################################
+# Simple Postfix iterator on the nodes of a Rope
+private class Postfix
+       super IndexedIterator[RopeNode]
 
-class CharRopeView
-       super SequenceRead[Char]
+       # Target Rope to iterate on
+       var target: Rope
+
+       # Current position in Rope
+       var pos: Int
 
-       # Targeted Rope for the view
-       private var target: Rope
+       # Visited nodes
+       var stack = new List[IteratorElement]
 
-       init(tgt: Rope)
+       init from(tgt: Rope, pos: Int)
        do
                self.target = tgt
+               self.pos = pos
+               if pos < 0 or pos >= tgt.length then return
+               var path = tgt.node_at(pos)
+               self.pos -= path.offset
+               for i in path.stack do
+                       var item = new IteratorElement(i.node)
+                       item.left = true
+                       if i.right then item.right = true
+                       stack.push item
+               end
+               var item = new IteratorElement(path.leaf)
+               item.done = true
+               stack.push item
        end
 
-       redef fun [](position)
+       redef fun item
        do
-               var tuple = target.get_node_for_pos(position)
-               return tuple.curr_node.value[tuple.corrected_pos]
+               assert is_ok
+               return stack.last.node
        end
 
-       redef fun first do return self[0]
+       redef fun is_ok do return not stack.is_empty
 
-       redef fun index_of(char)
-       do
-               var intern_iter = new RopeCharIterator(target)
-               while intern_iter.is_ok do
-                       if intern_iter.item == char then return intern_iter.index
-                       intern_iter.next
-               end
-               return -1
-       end
+       redef fun index do return pos
 
-       redef fun iterator do
-               return new RopeCharIterator(target)
+       redef fun next do
+               if stack.is_empty then return
+               if pos > target.length-1 then
+                       stack.clear
+                       return
+               end
+               var lst = stack.last
+               if lst.done then
+                       if lst.node isa Leaf then
+                               pos += lst.node.length
+                       end
+                       stack.pop
+                       next
+                       return
+               end
+               if not lst.left then
+                       lst.left = true
+                       var nod = lst.node
+                       if nod isa Concat and nod.left != null then
+                               stack.push(new IteratorElement(nod.left.as(not null)))
+                               next
+                               return
+                       end
+               end
+               if not lst.right then
+                       lst.right = true
+                       var nod = lst.node
+                       if nod isa Concat and nod.right != null then
+                               stack.push(new IteratorElement(nod.right.as(not null)))
+                               next
+                               return
+                       end
+               end
+               lst.done = true
        end
+end
 
-       redef fun last do return self[self.length-1]
+# Iterates on the leaves (substrings) of the Rope
+class LeavesIterator
+       super IndexedIterator[Leaf]
 
-       redef fun length do return target.length
+       private var nodes: Postfix
 
-       redef fun count(item)
+       init(tgt: Rope, pos: Int)
        do
-               var count = 0
-               var iter = self.iterator
-
-               for i in self do
-                       if i == item then count += 1
-               end
-
-               return count
+               nodes = tgt.postfix(pos)
        end
 
-       redef fun has_only(item)
-       do
-               for i in self do
-                       if i != item then return false
-               end
-               return true
-       end
-
-       redef fun is_empty do return length == 0
+       redef fun is_ok do return nodes.is_ok
 
-       redef fun to_a do
-               return to_s.to_a
+       redef fun item
+       do
+               assert is_ok
+               return nodes.item.as(Leaf)
        end
 
-       redef fun to_s do
-               return target.to_s
-       end
+       redef fun index do return nodes.index
 
-       redef fun ==(other)
+       redef fun next
        do
-               if not other isa SequenceRead[Char] then return false
-
-               if self.length != other then return false
-
-               var iter = other.iterator
-
-               for i in self do
-                       if i != iter.item then return false
-                       iter.next
+               while nodes.is_ok do
+                       nodes.next
+                       if nodes.is_ok and nodes.item isa Leaf then break
                end
-
-               return true
        end
-
 end
 
-###########################################
-#            Iterator classes             #
-###########################################
-
-# A tuple representing the state of a node for a tree parsing algorithm
-private class TupleVisitNode
+# Uses the leaves and calculates a new substring on each iteration
+class SubstringsIterator
+       super IndexedIterator[Text]
 
-       init(tgt: ConcatNode)
-       do
-               self.node = tgt
-       end
-
-       private var node: ConcatNode
+       private var nodes: IndexedIterator[Leaf]
 
-       private var left_visited = false
-       private var right_visited = false
-
-end
+       # Current position in Rope
+       var pos: Int
 
-# Any kind of iterator parsing a Rope for LeafNodes
-private abstract class RopeIterator
-       super IndexedIterator[LeafNode]
+       # Current substring, computed from the current Leaf and indexes
+       var substring: Text
 
-       # Rope meant to be visited
-       private var _target: Rope
+       init(tgt: Rope, pos: Int)
+       do
+               nodes = tgt.leaves(pos)
+               self.pos = pos
+               if pos < 0 or pos >= tgt.length then return
+               make_substring
+       end
 
-       private fun target: Rope do return self._target
+       # Compute the bounds of the current substring and makes the substring
+       private fun make_substring
+       do
+               substring = nodes.item.str
+               var min = 0
+               var length = substring.length
+               if nodes.index < pos then
+                       min = pos - nodes.index
+               end
+               substring = substring.substring(min, length)
+       end
 
-       # Position in target
-       private var pos = 0
+       redef fun is_ok do return nodes.is_ok
 
-       init(tgt: Rope)
+       redef fun item
        do
-               self._target = tgt
+               assert is_ok
+               return substring
        end
 
-       init with_index(tgt: Rope, index: Int)
+       redef fun index do return pos
+
+       redef fun next
        do
-               self._target = tgt
+               pos += substring.length
+               nodes.next
+               if nodes.is_ok then make_substring
        end
 
 end
 
-# Iterator returning the content of a rope one char at a time
 class RopeCharIterator
        super IndexedIterator[Char]
 
-       # The iterator used to visit the rope
-       private var sub_str_iter: DFSRopeLeafIterator
+       var substrings: IndexedIterator[Text]
 
-       # The current position in the rope
-       private var abs_pos = 0
+       var pos: Int
 
-       # The position in the current substring
-       private var sub_pos: Int = 0
+       var max: Int
 
-       # The substring contained in the current node visited by `sub_str_iter`
-       private var curr_substring: nullable String
+       var substr_iter: IndexedIterator[Char]
 
-       init(tgt: Rope)
+       init(tgt: Rope, from: Int)
        do
-               sub_str_iter = new DFSRopeLeafIterator(tgt)
-               if sub_str_iter.is_ok then curr_substring = sub_str_iter.item.value
+               substrings = tgt.substrings_from(from)
+               max = tgt.length - 1
+               if not substrings.is_ok then
+                       pos = tgt.length
+                       return
+               end
+               pos = from
+               substr_iter = substrings.item.chars.iterator
        end
 
-       redef fun item do return curr_substring[sub_pos]
+       redef fun item do return substr_iter.item
 
-       redef fun is_ok
-       do
-               if sub_str_iter.is_ok then return true
-               if not sub_str_iter.is_ok and curr_substring != null and sub_pos < curr_substring.length then return true
-               return false
-       end
+       redef fun is_ok do return pos <= max
+
+       redef fun index do return pos
 
        redef fun next
        do
-               assert is_ok
-               if sub_pos < curr_substring.length - 1 then
-                       sub_pos += 1
-               else
-                       sub_str_iter.next
-                       if sub_str_iter.is_ok then
-                               curr_substring = sub_str_iter.item.value
-                               sub_pos = 0
-                       else
-                               sub_pos = curr_substring.length
+               pos += 1
+               if substr_iter.is_ok then
+                       substr_iter.next
+               end
+               if not substr_iter.is_ok then
+                       substrings.next
+                       if substrings.is_ok then
+                               substr_iter = substrings.item.chars.iterator
                        end
                end
-               abs_pos += 1
        end
-
-       redef fun index do return abs_pos
-
 end
 
-# Special kind of iterator
-#
-# Performs a Depth-First Search on RopeLeaf items
-#
-private class DFSRopeLeafIterator
-       super RopeIterator
+private class ReversePostfix
+       super IndexedIterator[RopeNode]
 
-       # Stack of the visited nodes in the rope
-       private var visit_stack = new List[TupleVisitNode]
+       var target: Rope
 
-       # The leaf node being visited by the iterator
-       private var curr_leaf: nullable LeafNode
+       var pos: Int
 
-       init(tgt: Rope)
-       do
-               super
+       var min = 0
 
-               var first_node = target.parent_node
+       var stack = new List[IteratorElement]
 
-               if first_node isa ConcatNode then
-                       visit_stack.push(new TupleVisitNode(first_node))
-               else if first_node isa LeafNode then
-                       curr_leaf = first_node
-                       return
+       init from(tgt: Rope, pos: Int)
+       do
+               self.pos = pos
+               target = tgt
+               if pos < 0 or pos >= tgt.length then return
+               var path = tgt.node_at(pos)
+               self.pos -= path.offset
+               for i in path.stack do
+                       var elemt = new IteratorElement(i.node)
+                       elemt.right = true
+                       if i.left then
+                               elemt.left = true
+                       end
+                       stack.push elemt
                end
-
-               next_body
+               stack.push(new IteratorElement(path.leaf))
+               stack.last.done = true
        end
 
-       # Creates a new iterator on `tgt` starting at `index`
-       redef init with_index(tgt: Rope, index: Int)
-       do
-               super
-
-               var returned_tuple = target.get_node_for_pos(index)
-               curr_leaf = returned_tuple.curr_node
-               visit_stack = returned_tuple.visit_stack
-               pos = index - returned_tuple.corrected_pos
+       redef fun item do
+               assert is_ok
+               return stack.last.node
        end
 
-       redef fun is_ok do return curr_leaf != null
+       redef fun is_ok do return not stack.is_empty
+
+       redef fun index do return pos
 
        redef fun next
        do
-               assert is_ok
-               pos += curr_leaf.value.length
-               next_body
+               if stack.is_empty then return
+               if pos < min then
+                       stack.clear
+                       return
+               end
+               var lst = stack.last
+               if lst.done then
+                       stack.pop
+                       next
+                       return
+               end
+               if not lst.right then
+                       var nod = lst.node.as(Concat)
+                       var rgt = nod.right
+                       lst.right = true
+                       if rgt != null then
+                               stack.push(new IteratorElement(rgt))
+                               next
+                               return
+                       end
+               end
+               if not lst.left then
+                       var nod = lst.node.as(Concat)
+                       var lft = nod.left
+                       lst.left = true
+                       if lft != null then
+                               stack.push(new IteratorElement(lft))
+                               next
+                               return
+                       end
+               end
+               if lst.node isa Leaf then pos -= lst.node.length
+               lst.done = true
        end
+end
 
-       private fun next_body
-       do
-               var next_node: nullable RopeNode
-               while not visit_stack.is_empty do
-                       var curr_concat_tuple = visit_stack.last
-                       if not curr_concat_tuple.left_visited then
-
-                               curr_concat_tuple.left_visited = true
-
-                               next_node = curr_concat_tuple.node.left_child
-
-                               if next_node == null then continue
-
-                               if next_node isa ConcatNode then
-                                       visit_stack.push(new TupleVisitNode(next_node))
-                               else if next_node isa LeafNode then
-                                       curr_leaf = next_node
-                                       return
-                               end
-
-                       else if not curr_concat_tuple.right_visited then
+private class ReverseLeavesIterator
+       super IndexedIterator[Leaf]
 
-                               curr_concat_tuple.right_visited = true
+       var nodes: ReversePostfix
 
-                               next_node = curr_concat_tuple.node.right_child
+       init(tgt: Rope, from: Int)
+       do
+               nodes = tgt.reverse_postfix(from)
+       end
 
-                               if next_node == null then continue
+       redef fun is_ok do return nodes.is_ok
 
-                               if next_node isa ConcatNode then
-                                       visit_stack.push(new TupleVisitNode(next_node))
-                               else if next_node isa LeafNode then
-                                       curr_leaf = next_node
-                                       return
-                               end
+       redef fun item do
+               assert is_ok
+               return nodes.item.as(Leaf)
+       end
 
-                       else
-                               visit_stack.pop
-                       end
+       redef fun next do
+               while nodes.is_ok do
+                       nodes.next
+                       if nodes.is_ok then if nodes.item isa Leaf then break
                end
-               self.curr_leaf = null
        end
 
-       redef fun item
-       do
-               assert is_ok
-               return curr_leaf.as(not null)
-       end
+       redef fun index do return nodes.index
 
 end
 
-###########################################
-#              Node classes               #
-###########################################
-
-# A node for a Rope
-private abstract class RopeNode
-
-       private var _length = 0
-
-       private var parent: nullable ConcatNode = null
-
-       private var height = 0
-
-       # The balance factor of a node, if it is a Leaf, it equals its length
-       # Else, it will be equal to the difference between the height on the left and on the right
-       private fun balance_factor: Int do return height end
+private class ReverseSubstringsIterator
+       super IndexedIterator[Text]
 
-       fun length: Int do return _length
+       var leaves: ReverseLeavesIterator
 
-       private fun length=(len: Int)
-       do
-               _length = len
-       end
-end
-
-# Node that represents a concatenation between two nodes (of any RopeNode type)
-private class ConcatNode
-       super RopeNode
+       var pos: Int
 
-       private var _left_child: nullable RopeNode = null
-       private var _right_child: nullable RopeNode = null
+       var str: Text
 
-       private fun left_child: nullable RopeNode
+       init(tgt: Rope, from: Int)
        do
-               if _left_child != null then
-                       return _left_child
-               else
-                       return null
-               end
+               leaves = tgt.reverse_leaves(from)
+               pos = from
+               if not leaves.is_ok then return
+               str = leaves.item.str
+               make_substring
        end
 
-       private fun right_child: nullable RopeNode
+       fun make_substring
        do
-               if _right_child != null then
-                       return _right_child
-               else
-                       return null
-               end
+               if pos >= (leaves.index + str.length - 1) then return
+               str = str.substring(0, (pos - leaves.index + 1))
        end
 
-       private fun left_child=(new_node: nullable RopeNode)
-       do
-               self._left_child = new_node
-               new_node.parent = self
-               update_data
-       end
+       redef fun is_ok do return leaves.is_ok
 
-       private fun right_child=(new_node: nullable RopeNode)
-       do
-               self._right_child = new_node
-               new_node.parent = self
-               update_data
+       redef fun item do
+               assert is_ok
+               return str
        end
 
-       # Updates the internal data of the current node
-       #
-       # Concretely, updates the length and the height of the node
-       private fun update_data
-       do
-               self.length = 0
-               self.height = 1
-               if left_child != null then
-                       self.length += left_child.length
-                       if left_child.height + 1 > self.height then self.height = left_child.height + 1
-               end
-               if right_child != null then
-                       self.length += right_child.length
-                       if right_child.height + 1 > self.height then self.height = right_child.height + 1
-               end
-       end
+       redef fun index do return pos
 
-       # Computes and returns the balance factor (used for AVL trees)
-       #
-       # Formula : left.height - right.height
-       redef private fun balance_factor
-       do
-               var left_height = 0
-               var right_height = 0
-               if left_child != null then left_height = left_child.height
-               if right_child != null then right_height = right_child.height
-               return left_height - right_height
+       redef fun next do
+               pos -= str.length
+               leaves.next
+               if not leaves.is_ok then return
+               str = leaves.item.str
+               make_substring
        end
 end
 
-# A leaf node contains a substring of some sort
-private class LeafNode
-       super RopeNode
+private class ReverseRopeCharIterator
+       super IndexedIterator[Char]
 
-       # Encapsulated string in the leaf node
-       private var _value: String
+       var substrs: IndexedIterator[Text]
 
-       init(val: String)
-       do
-               self._value = val.to_s
-               self.length = val.length
-       end
+       var pos: Int
 
-       private fun value: String do return self._value
+       var subiter: IndexedIterator[Char]
 
-       private fun value= (val: String)
+       init(tgt: Rope, from: Int)
        do
-               _value = val
+               substrs = tgt.reverse_substrings_from(from)
+               if not substrs.is_ok then
+                       pos = -1
+                       return
+               end
+               subiter = substrs.item.chars.reverse_iterator
+               pos = from
        end
-end
 
-#####################################################
-#            Foreign classes refinement             #
-#####################################################
+       redef fun is_ok do return pos >= 0
 
-redef class String
-       redef fun ==(other)
-       do
-               if other isa Rope then
-                       return other == self
-               else
-                       return super
-               end
+       redef fun item do
+               assert is_ok
+               return subiter.item
        end
-end
 
-redef class Buffer
-       redef fun ==(other)
-       do
-               if other isa Rope then
-                       return other == self
-               else
-                       return super
+       redef fun index do return pos
+
+       redef fun next do
+               pos -= 1
+               if subiter.is_ok then subiter.next
+               if not subiter.is_ok then
+                       if substrs.is_ok then substrs.next
+                       if substrs.is_ok then subiter = substrs.item.chars.reverse_iterator
                end
        end
 end
+
index 6297038..85d28c9 100644 (file)
@@ -22,7 +22,7 @@ import string_search
 import file
 import exec
 import stream
-import string
+import ropes
 import collection
 import math
 import kernel
index 507ba8d..504396a 100644 (file)
@@ -13,7 +13,7 @@
 # Input and output streams of characters
 module stream
 
-import string
+intrude import ropes
 
 in "C" `{
        #include <unistd.h>
@@ -136,6 +136,29 @@ redef class Text
        redef fun write_to(stream) do stream.write(self)
 end
 
+redef class RopeNode
+       super Streamable
+end
+
+redef class Leaf
+
+       redef fun write_to(s) do s.write(str)
+end
+
+redef class Concat
+
+       redef fun write_to(s)
+       do
+               if left != null then left.write_to(s)
+               if right != null then right.write_to(s)
+       end
+end
+
+redef class RopeString
+
+       redef fun write_to(s) do root.write_to(s)
+end
+
 # Input streams with a buffer
 abstract class BufferedIStream
        super IStream
index 59a286d..fcc1f1b 100644 (file)
@@ -59,6 +59,9 @@ abstract class Text
        # In this case, `from += count` and `count -= from`.
        fun substring(from: Int, count: Int): SELFTYPE is abstract
 
+       # Iterates on the substrings of self if any
+       fun substrings: Iterator[Text] is abstract
+
        # Concatenates `o` to `self`
        #
        #     assert "hello" + "world"  == "helloworld"
@@ -609,6 +612,28 @@ abstract class String
 
        redef fun to_s do return self
 
+       fun append(s: String): SELFTYPE is abstract
+
+       fun prepend(s: String): SELFTYPE is abstract
+
+       fun insert_at(s: String, pos: Int): SELFTYPE is abstract
+end
+
+private class FlatSubstringsIter
+       super Iterator[FlatText]
+
+       var tgt: nullable FlatText
+
+       init(tgt: FlatText) do self.tgt = tgt
+
+       redef fun item do
+               assert is_ok
+               return tgt.as(not null)
+       end
+
+       redef fun is_ok do return tgt != null
+
+       redef fun next do tgt = null
 end
 
 # Immutable strings of characters.
@@ -616,8 +641,6 @@ class FlatString
        super FlatText
        super String
 
-       redef type SELFTYPE: FlatString
-
        # Index in _items of the start of the string
        private var index_from: Int
 
@@ -876,6 +899,8 @@ class FlatString
 
                return hash_cache.as(not null)
        end
+
+       redef fun substrings do return new FlatSubstringsIter(self)
 end
 
 private class FlatStringReverseIterator
@@ -1011,6 +1036,8 @@ class FlatBuffer
 
        private var capacity: Int = 0
 
+       redef fun substrings do return new FlatSubstringsIter(self)
+
        redef fun []=(index, item)
        do
                is_dirty = true
index 391a2b0..dd2fe10 100644 (file)
@@ -30,6 +30,7 @@ redef class MMethod
 
                if nit_name == "+" then return "_plus"
                if nit_name == "-" then return "_minus"
+               if nit_name == "unary -" then return "_unary_minus"
                if nit_name == "*" then return "_star"
                if nit_name == "/" then return "_slash"
                if nit_name == "%" then return "_percent"
diff --git a/tests/sav/test_flatrope.res b/tests/sav/test_flatrope.res
new file mode 100644 (file)
index 0000000..5ec30ac
--- /dev/null
@@ -0,0 +1,5 @@
+quick brown fox over the lazy dog
+The quick brown fox over the lazy dog
+quick brown fox jumps over the lazy dog
+quick brown fox over the lazy dog.
+The quick brown fox jumps over the lazy dog.
diff --git a/tests/sav/test_ropes.res b/tests/sav/test_ropes.res
new file mode 100644 (file)
index 0000000..7e4b7c1
--- /dev/null
@@ -0,0 +1,26 @@
+NODEATTEST
+INZZ
+INDDZZ
+EEINDDZZ
+EEINDDZZFF
+eeinddzzff
+EEINDDZZFF
+FFZZDDNIEE
+hello_world.types.1.o
+now step live...
+...evil pets won
+now step live...
+now step live...
+ live...
+.
+ live stepnow
+...evil pets won
+n
+now step live... step live...
+w s
+ZZ
+ZZZZZZZZZZ
+ZZAAZZZZZZZZ
+NNZZAAZZZZZZZZ
+NIINZZAAZZZZZZZZ
+NINIINZZAAZZZZZZZZINZZAAZZZZZZZZ
diff --git a/tests/sav/test_text.res b/tests/sav/test_text.res
new file mode 100644 (file)
index 0000000..fc9283b
--- /dev/null
@@ -0,0 +1,15 @@
+WOE TO YOU, OH EARTH AND SEA FOR THE DEVIL SENDS THE BEAST WITH WRATH BECAUSE HE KNOWS THE TIME IS SHORT. LET HIM WHO HATH UNDERSTANDING RECKON THE NUMBER OF THE BEAST, FOR IT IS A HUMAN NUMBER, ITS NUMBER IS SIX HUNDRED AND SIXTY-SIX.
+woe to you, oh earth and sea for the devil sends the beast with wrath because he knows the time is short. let him who hath understanding reckon the number of the beast, for it is a human number, its number is six hundred and sixty-six.
+Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short.
+Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six.
+.xiS-ytxiS dna derdnuH xiS si rebmun sti ,rebmun namuh a si ti rof ,tsaeb eht fo rebmun eht nokcer gnidnatsrednu htah ohw mih teL .trohs si emit eht swonk eh esuaceb htarw htiw tsaeb eht sdnes liveD eht rof aes dna htrae ho ,uoy ot eoW
+235
+13
+110
+-1
+106
+37
+true
+true
+Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short. Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six.
+W o e   t o   y o u ,   o h   e a r t h   a n d   s e a   f o r   t h e   D e v i l   s e n d s   t h e   b e a s t   w i t h   w r a t h   b e c a u s e   h e   k n o w s   t h e   t i m e   i s   s h o r t .   L e t   h i m   w h o   h a t h   u n d e r s t a n d i n g   r e c k o n   t h e   n u m b e r   o f   t h e   b e a s t ,   f o r   i t   i s   a   h u m a n   n u m b e r ,   i t s   n u m b e r   i s   S i x   H u n d r e d   a n d   S i x t y - S i x .
diff --git a/tests/sav/test_text_alt1.res b/tests/sav/test_text_alt1.res
new file mode 100644 (file)
index 0000000..fc9283b
--- /dev/null
@@ -0,0 +1,15 @@
+WOE TO YOU, OH EARTH AND SEA FOR THE DEVIL SENDS THE BEAST WITH WRATH BECAUSE HE KNOWS THE TIME IS SHORT. LET HIM WHO HATH UNDERSTANDING RECKON THE NUMBER OF THE BEAST, FOR IT IS A HUMAN NUMBER, ITS NUMBER IS SIX HUNDRED AND SIXTY-SIX.
+woe to you, oh earth and sea for the devil sends the beast with wrath because he knows the time is short. let him who hath understanding reckon the number of the beast, for it is a human number, its number is six hundred and sixty-six.
+Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short.
+Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six.
+.xiS-ytxiS dna derdnuH xiS si rebmun sti ,rebmun namuh a si ti rof ,tsaeb eht fo rebmun eht nokcer gnidnatsrednu htah ohw mih teL .trohs si emit eht swonk eh esuaceb htarw htiw tsaeb eht sdnes liveD eht rof aes dna htrae ho ,uoy ot eoW
+235
+13
+110
+-1
+106
+37
+true
+true
+Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short. Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six.
+W o e   t o   y o u ,   o h   e a r t h   a n d   s e a   f o r   t h e   D e v i l   s e n d s   t h e   b e a s t   w i t h   w r a t h   b e c a u s e   h e   k n o w s   t h e   t i m e   i s   s h o r t .   L e t   h i m   w h o   h a t h   u n d e r s t a n d i n g   r e c k o n   t h e   n u m b e r   o f   t h e   b e a s t ,   f o r   i t   i s   a   h u m a n   n u m b e r ,   i t s   n u m b e r   i s   S i x   H u n d r e d   a n d   S i x t y - S i x .
diff --git a/tests/sav/test_text_alt2.res b/tests/sav/test_text_alt2.res
new file mode 100644 (file)
index 0000000..fc9283b
--- /dev/null
@@ -0,0 +1,15 @@
+WOE TO YOU, OH EARTH AND SEA FOR THE DEVIL SENDS THE BEAST WITH WRATH BECAUSE HE KNOWS THE TIME IS SHORT. LET HIM WHO HATH UNDERSTANDING RECKON THE NUMBER OF THE BEAST, FOR IT IS A HUMAN NUMBER, ITS NUMBER IS SIX HUNDRED AND SIXTY-SIX.
+woe to you, oh earth and sea for the devil sends the beast with wrath because he knows the time is short. let him who hath understanding reckon the number of the beast, for it is a human number, its number is six hundred and sixty-six.
+Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short.
+Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six.
+.xiS-ytxiS dna derdnuH xiS si rebmun sti ,rebmun namuh a si ti rof ,tsaeb eht fo rebmun eht nokcer gnidnatsrednu htah ohw mih teL .trohs si emit eht swonk eh esuaceb htarw htiw tsaeb eht sdnes liveD eht rof aes dna htrae ho ,uoy ot eoW
+235
+13
+110
+-1
+106
+37
+true
+true
+Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short. Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six.
+W o e   t o   y o u ,   o h   e a r t h   a n d   s e a   f o r   t h e   D e v i l   s e n d s   t h e   b e a s t   w i t h   w r a t h   b e c a u s e   h e   k n o w s   t h e   t i m e   i s   s h o r t .   L e t   h i m   w h o   h a t h   u n d e r s t a n d i n g   r e c k o n   t h e   n u m b e r   o f   t h e   b e a s t ,   f o r   i t   i s   a   h u m a n   n u m b e r ,   i t s   n u m b e r   i s   S i x   H u n d r e d   a n d   S i x t y - S i x .
index 3174cb7..ecc608f 100644 (file)
@@ -1,6 +1,6 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2011-2013 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2011-2014 Alexis Laferrière <alexis.laf@xymus.net>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -33,6 +33,11 @@ class A
                return new_A( s - o );
        `}
 
+       fun -: A import value, A `{
+               int s = A_value(recv);
+               return new_A(-s);
+       `}
+
        fun *( by : Int ) : A import value, A `{
                int s = A_value( recv );
 
@@ -158,3 +163,4 @@ print a[ 52 ] # 52
 a[ 74 ] = new A( 96 )
 print a # 96
 
+print(-(new A(123)))
diff --git a/tests/test_flatrope.nit b/tests/test_flatrope.nit
new file mode 100644 (file)
index 0000000..79b719f
--- /dev/null
@@ -0,0 +1,31 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var st = "quick brown fox over the lazy dog"
+
+print st
+
+var pr = st.prepend("The ")
+
+print pr
+
+pr = st.insert_at(" jumps", 15)
+
+print pr
+
+pr = st.append(".")
+
+print pr
+
+print st.insert_at(" jumps", 15). prepend("The ").append(".")
diff --git a/tests/test_ropes.nit b/tests/test_ropes.nit
new file mode 100644 (file)
index 0000000..3040d12
--- /dev/null
@@ -0,0 +1,118 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var x :String = new RopeString
+
+x = x + "NODE"
+x = x + "AT"
+x = x + "TEST"
+
+print x
+
+var lst = new List[String]
+
+lst.push(new RopeString.from("ZZ"))
+
+lst.push((lst.last * 5))
+
+lst.push(lst.last.insert_at("AA", 2))
+
+lst.push(lst.last.insert_at("NN", 0))
+
+lst.push(lst.last.insert_at("II", 1))
+
+lst.push(lst.last.insert_at(lst.last, 2))
+
+var ss = lst.last.substring(4,4)
+
+print ss
+
+ss = ss.as(RopeString).insert_at("DD", 2)
+
+print ss
+
+ss = ss.insert_at("EE", 0)
+
+print ss
+
+ss = ss.insert_at("FF", ss.length)
+
+print ss
+
+ss = ss.to_lower
+
+print ss
+
+ss = ss.to_upper
+
+print ss
+
+ss = ss.reversed
+
+print ss
+
+var atb = new Array[String]
+
+var s = new RopeString
+s = s.prepend(".types").as(RopeString)
+s = s.prepend("./examples/hello_world.nit".substring(11,11)).as(RopeString)
+s = s.append(".").as(RopeString)
+s = s.append("1").as(RopeString)
+s = s.append(".o").as(RopeString)
+
+print s
+
+var str = new RopeString.from("now") + " step" + " live..."
+
+print str
+
+print str.reversed
+
+for i in str.chars do printn i
+printn "\n"
+
+for i in [0..str.length[ do printn str.chars[i]
+printn "\n"
+
+var iter = str.chars.iterator
+for i in [0..str.length[ do
+       assert str.chars[i] == iter.item
+       iter.next
+end
+
+assert "now step live...".hash == str.hash
+
+for i in str.chars.iterator_from(8) do printn i
+printn "\n"
+
+for i in str.chars.iterator_from(str.length-1) do printn i
+printn "\n"
+
+for i in str.as(RopeString).reverse_substrings_from(12) do printn i
+printn "\n"
+
+for i in str.chars.reverse_iterator do printn i
+printn "\n"
+
+for i in str.chars.reverse_iterator_from(0) do printn i
+printn "\n"
+
+var str2 = str.as(RopeString).insert_at(str.substring_from(3), 3)
+
+print str2
+
+print str2.substring(2,3)
+
+for i in lst do print i
+
diff --git a/tests/test_text.nit b/tests/test_text.nit
new file mode 100644 (file)
index 0000000..4a27158
--- /dev/null
@@ -0,0 +1,99 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var str = "Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short. Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six."
+var spaces = "           "
+var numstr = "1001"
+
+var txt: Text
+var trimable: Text
+var num: Text
+
+txt = str
+trimable = spaces + str + spaces
+num = numstr
+
+#alt1 txt = new FlatBuffer.from(str)
+#alt1 trimable = new FlatBuffer.from(spaces)
+#alt1 trimable.append(str)
+#alt1 trimable.append(spaces)
+#alt1 num = new FlatBuffer.from(numstr)
+
+#alt2 txt = new RopeString.from(str)
+#alt2 trimable = new RopeString.from(spaces)
+#alt2 trimable = trimable + str
+#alt2 trimable = trimable + spaces
+#alt2 num = new RopeString.from(numstr)
+
+# Test Text methods on all types of receivers
+
+print txt.to_upper
+print txt.to_lower
+assert txt * 2 == txt + txt
+print txt.substring(0, 105)
+print txt.substring_from(106)
+print txt.reversed
+print txt.length
+assert not txt.is_empty
+print txt.index_of('h')
+print txt.index_of_from('h', 105)
+print txt.index_of('Z')
+print txt.last_index_of('L')
+print txt.last_index_of_from('D', 105)
+print txt.has_substring("Woe", 0)
+print txt.has_substring("Let", 106)
+assert trimable != txt
+assert trimable.trim == txt
+assert num.is_numeric
+assert txt.has_prefix("Woe")
+assert txt.has_suffix("Six.")
+assert txt.hash == trimable.trim.hash
+
+# Test Text.chars (SequenceRead[Char]) methods on all receivers
+
+var chars = txt.chars
+
+assert chars != txt.substring_from(106).chars
+assert (txt.substring(0,105) + txt.substring_from(105)).chars == txt.chars
+assert chars[0] == 'W'
+assert chars.count('o') == 11
+assert chars.first == chars[0]
+assert chars.has('L')
+assert spaces.chars.has_only(' ')
+assert chars.index_of('D') == 37
+assert not chars.is_empty
+var count = 0
+for i in chars do
+       assert i != '!'
+       printn i
+       count += 1
+end
+printn "\n"
+assert count == txt.length
+var iter_from = txt.substring_from(105).chars.iterator
+var txt_iter_from = chars.iterator_from(105)
+while iter_from.is_ok do
+       assert iter_from.item == txt_iter_from.item
+       iter_from.next
+       txt_iter_from.next
+end
+assert not iter_from.is_ok and not txt_iter_from.is_ok
+var txt_reviter = chars.reverse_iterator_from(104)
+var sub_reviter = txt.substring(0,105).chars.reverse_iterator
+while txt_reviter.is_ok do
+       assert txt_reviter.item == sub_reviter.item
+       txt_reviter.next
+       sub_reviter.next
+end
+print chars.join(" ")