X-Git-Url: http://nitlanguage.org diff --git a/lib/standard/ropes.nit b/lib/standard/ropes.nit index b5697bd..b493de9 100644 --- a/lib/standard/ropes.nit +++ b/lib/standard/ropes.nit @@ -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 +