lib: prepare for new constructors
[nit.git] / lib / standard / ropes.nit
index 7bc4154..843096f 100644 (file)
@@ -99,6 +99,14 @@ private class StringLeaf
        redef fun to_leaf do return self
 end
 
+# Used as a cache when using indexed access to a substring in the Rope
+private class LeafCache
+       # Cached leaf
+       var leaf: Leaf
+       # Position in Rope
+       var pos: Int
+end
+
 # Basic structure, binary tree with a root node.
 #
 # Also shared services by subsequent implementations.
@@ -111,8 +119,10 @@ abstract class Rope
        # Cached version of self as a flat String
        private var str_representation: nullable NativeString = null
 
+       private var leaf_cache: nullable LeafCache = null
+
        # Empty Rope
-       init do from("")
+       init do root = new StringLeaf("".as(FlatString))
 
        # Creates a new Rope with `s` as root
        init from(s: String) do
@@ -186,15 +196,49 @@ abstract class Rope
        # Path to the Leaf for `position`
        private fun node_at(position: Int): Path
        do
-               assert position >= 0 and position < length
-               return get_node_from(root.as(not null), 0, position, new List[PathElement])
+               assert position >= 0 and position <= length
+               if position == length then
+                       var st = new List[PathElement]
+                       stack_to_end(root,st)
+                       if not st.is_empty then
+                               var lst = st.last
+                               var lf = lst.node.right
+                               if lf != null then
+                                       return new Path(lf.as(Leaf), lf.length, st)
+                               else
+                                       lf = lst.node.left
+                                       return new Path(lf.as(Leaf), lf.length, st)
+                               end
+                       else
+                               return new Path(root.as(Leaf), length, st)
+                       end
+               end
+               return get_node_from(root, 0, position, new List[PathElement])
+       end
+
+       # Special case for when the required pos is length
+       private fun stack_to_end(nod: RopeNode, st: List[PathElement])
+       do
+               if nod isa Leaf then return
+               var n = nod.as(Concat)
+               var r = n.right
+               var ele = new PathElement(n)
+               ele.right = true
+               st.push(ele)
+               if r != null then
+                       stack_to_end(r, st)
+               end
+               return
        end
 
        # 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
                assert curr_pos >= 0
-               if node isa Leaf then return new Path(node, seek_pos - curr_pos, stack)
+               if node isa Leaf then
+                       self.leaf_cache = new LeafCache(node, curr_pos)
+                       return new Path(node, seek_pos - curr_pos, stack)
+               end
                node = node.as(Concat)
 
                if node.left != null then
@@ -240,9 +284,9 @@ class RopeString
 
        redef fun reversed
        do
-               var ret = empty.as(RopeString)
+               var ret = empty
                for i in substrings do
-                       ret = ret.prepend(i.reversed.to_s).as(RopeString)
+                       ret = i.as(String).reversed + ret
                end
                return ret
        end
@@ -251,7 +295,7 @@ class RopeString
        do
                var ret = empty
                for i in substrings do
-                       ret += i.to_upper
+                       ret += i.as(String).to_upper
                end
                return ret
        end
@@ -260,12 +304,23 @@ class RopeString
        do
                var ret = empty
                for i in substrings do
-                       ret += i.to_lower
+                       ret += i.as(String).to_lower
                end
                return ret
        end
 
-       redef fun +(o) do return insert_at(o.to_s, length)
+       redef fun +(o) do
+               if self.length == 0 then return o.to_s
+               if o.length == 0 then return self
+               var str = o.to_s
+               if str isa FlatString then
+                       return new RopeString.from_root(new Concat(root, new StringLeaf(str)))
+               else if str isa RopeString then
+                       return new RopeString.from_root(new Concat(root, str.root))
+               else
+                       abort
+               end
+       end
 
        redef fun *(n)
        do
@@ -284,8 +339,6 @@ class RopeString
 
                assert pos >= 0 and pos <= length
 
-               if pos == length then return append(str).as(RopeString)
-
                var path = node_at(pos)
 
                var last_concat: Concat
@@ -327,43 +380,6 @@ class RopeString
                return new RopeString.from_root(last_concat)
        end
 
-       # Adds `s` at the beginning of self
-       redef fun prepend(s) do return insert_at(s, 0)
-
-       # 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
-
-       # Builds a new path from root to the rightmost node with s appended
-       private fun append_to_path(node: RopeNode, s: String): RopeNode
-       do
-               var cct: Concat
-               if node isa Leaf then
-                       if s isa FlatString then
-                               cct = new Concat(node, new StringLeaf(s))
-                       else
-                               cct = new Concat(node, s.as(RopeString).root)
-                       end
-               else
-                       var n = node.as(Concat)
-                       var right = n.right
-                       var lft = n.left.as(not null)
-                       if right == null then
-                               if s isa FlatString then
-                                       cct = new Concat(lft, new StringLeaf(s))
-                               else
-                                       cct = new Concat(lft, s.as(RopeString).root)
-                               end
-                       else
-                               cct = new Concat(lft, append_to_path(right, s))
-                       end
-               end
-               return cct
-       end
-
        # O(log(n))
        #
        #     var rope = new RopeString.from("abcd")
@@ -418,14 +434,17 @@ end
 
 redef class FlatString
 
-       redef fun append(s) do return (new RopeString.from(self)) + s
-
-       redef fun prepend(s) do return (new RopeString.from(self)).prepend(s)
-
        redef fun insert_at(s, pos)
        do
-               if pos == 0 then return prepend(s)
-               if pos == length then return append(s)
+
+               if pos == 0 then
+                       var r = new RopeString.from(s)
+                       return r + self
+               end
+               if pos == length then
+                       var r = new RopeString.from(self)
+                       return r + s
+               end
 
                var l = substring(0,pos)
                var r = substring_from(pos)
@@ -444,6 +463,7 @@ private class RopeStringChars
        redef fun [](pos)
        do
                assert pos < tgt.length
+               if tgt.leaf_cache != null and pos >= tgt.leaf_cache.pos and (tgt.leaf_cache.pos + tgt.leaf_cache.leaf.length) > pos then return tgt.leaf_cache.leaf.str.chars[pos - tgt.leaf_cache.pos]
                var path = tgt.node_at(pos)
                return path.leaf.str.chars[path.offset]
        end