X-Git-Url: http://nitlanguage.org diff --git a/lib/standard/ropes.nit b/lib/standard/ropes.nit index f992f66..b76784f 100644 --- a/lib/standard/ropes.nit +++ b/lib/standard/ropes.nit @@ -111,6 +111,12 @@ abstract class Rope redef fun length do return root.length + # Iterator on the nodes of the rope, in forward postfix order + private fun postfix(from: Int): Postfix do return new Postfix.from(self, from) + + # Iterator on the leaves of the rope, forward order + private fun leaves(from: Int): LeavesIterator do return new LeavesIterator(self, from) + # Path to the Leaf for `position` private fun node_at(position: Int): Path do @@ -224,5 +230,193 @@ class RopeString return cct end + # 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 + if pos < 0 then + len += pos + pos = 0 + end + + if pos + len > length then len = length - pos + + if len <= 0 then return new RopeString.from("") + + var path = node_at(pos) + + var lf = path.leaf + var offset = path.offset + + if path.leaf.str.length - offset > len then lf = new Leaf(lf.str.substring(offset,len)) else lf = new Leaf(lf.str.substring_from(offset)) + + var nod: RopeNode = lf + + 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 + + var ret = new RopeString + ret.root = nod + + path = ret.node_at(len-1) + + offset = path.offset + nod = new Leaf(path.leaf.str.substring(0, offset+1)) + + 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 + + ret.root = nod + + return ret + end +end + +# Used to iterate on a Rope +private class IteratorElement + + init(e: RopeNode) + do + 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 + +# Simple Postfix iterator on the nodes of a Rope +private class Postfix + super IndexedIterator[RopeNode] + + # Target Rope to iterate on + var target: Rope + + # Current position in Rope + var pos: Int + + # Visited nodes + var stack = new List[IteratorElement] + + 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 item + do + assert is_ok + return stack.last.node + end + + redef fun is_ok do return not stack.is_empty + + redef fun index do return pos + + 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 + +# Iterates on the leaves (substrings) of the Rope +class LeavesIterator + super IndexedIterator[Leaf] + + private var nodes: Postfix + + init(tgt: Rope, pos: Int) + do + nodes = tgt.postfix(pos) + end + + redef fun is_ok do return nodes.is_ok + + redef fun item + do + assert is_ok + return nodes.item.as(Leaf) + end + + redef fun index do return nodes.index + + redef fun next + do + while nodes.is_ok do + nodes.next + if nodes.is_ok and nodes.item isa Leaf then break + end + end end