lib/standard/ropes: Added append method.
[nit.git] / lib / standard / ropes.nit
index b5697bd..b493de9 100644 (file)
@@ -17,6 +17,29 @@ module ropes
 
 intrude import string
 
+# 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
+
+# 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
+
 # A node for a Rope
 private abstract class RopeNode
        # Length of the node
@@ -64,3 +87,95 @@ private class Leaf
 
 end
 
+# Basic structure, binary tree with a root node.
+#
+# Also shared services by subsequent implementations.
+abstract class Rope
+       super Text
+
+       # Root node, entry point of a Rope.
+       private var root: RopeNode
+
+       # Empty Rope
+       init do from("")
+
+       # 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
+
+       private init from_root(r: RopeNode)
+       do
+               root = r
+       end
+
+       redef fun length do return root.length
+
+       # 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])
+       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)
+               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
+       end
+
+end
+
+# Rope that cannot be modified
+class RopeString
+       super Rope
+       super String
+
+       redef fun to_s do return self
+
+       # Adds `s` at the end of self
+       fun append(s: String): String
+       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 = 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 cct
+       end
+
+end
+