X-Git-Url: http://nitlanguage.org diff --git a/lib/standard/ropes.nit b/lib/standard/ropes.nit index 940939d..33d02ee 100644 --- a/lib/standard/ropes.nit +++ b/lib/standard/ropes.nit @@ -96,6 +96,9 @@ abstract class Rope # Root node, entry point of a Rope. private var root: RopeNode + # Cached version of self as a flat String + private var str_representation: nullable NativeString = null + # Empty Rope init do from("") @@ -111,6 +114,44 @@ 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) + + # Iterator on the substrings from 0, in forward order + fun substrings: IndexedIterator[Text] do return new SubstringsIterator(self, 0) + + # Iterator on the substrings, starting at position `from`, in forward order + fun substrings_from(from: Int): IndexedIterator[Text] do return new SubstringsIterator(self, from) + + redef fun to_cstring + do + if str_representation != null then return str_representation.as(not null) + + var native_final_str = calloc_string(length + 1) + + native_final_str[length] = '\0' + + if self.length == 0 then + str_representation = native_final_str + return native_final_str + end + + var offset = 0 + + 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 + + str_representation = native_final_str + + return native_final_str + end + # Path to the Leaf for `position` private fun node_at(position: Int): Path do @@ -142,6 +183,17 @@ abstract class Rope end end + redef fun ==(o) + do + 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 + return true + end end # Rope that cannot be modified @@ -151,6 +203,46 @@ class RopeString redef fun to_s do return self + redef fun empty do return once new RopeString.from("") + + redef fun reversed + do + var ret = empty.as(RopeString) + for i in substrings do + ret = ret.prepend(i.reversed.to_s).as(RopeString) + end + return ret + end + + redef fun to_upper + do + var ret = empty + for i in substrings do + ret += i.to_upper + end + return ret + end + + redef fun to_lower + do + var ret = empty + for i in substrings do + ret += i.to_lower + end + return ret + end + + redef fun +(o) do return insert_at(o.to_s, length) + + redef fun *(n) + do + var ret = new RopeString.from("") + for i in [0..n[ do + ret = (ret + self).as(RopeString) + end + return ret + end + # Inserts a String `str` at position `pos` fun insert_at(str: String, pos: Int): RopeString do @@ -198,6 +290,9 @@ class RopeString return new RopeString.from_root(last_concat) end + # Adds `s` at the beginning of self + fun prepend(s: String): String do return insert_at(s, 0) + # Adds `s` at the end of self fun append(s: String): String do @@ -284,3 +379,228 @@ class RopeString 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 + +# Uses the leaves and calculates a new substring on each iteration +class SubstringsIterator + super IndexedIterator[Text] + + private var nodes: IndexedIterator[Leaf] + + # Current position in Rope + var pos: Int + + # Current substring, computed from the current Leaf and indexes + var substring: Text + + 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 + + # 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 + + redef fun is_ok do return nodes.is_ok + + redef fun item + do + assert is_ok + return substring + end + + redef fun index do return pos + + redef fun next + do + pos += substring.length + nodes.next + if nodes.is_ok then make_substring + end + +end + +class RopeCharIterator + super IndexedIterator[Char] + + var substrings: IndexedIterator[Text] + + var pos: Int + + var max: Int + + var substr_iter: IndexedIterator[Char] + + init(tgt: Rope, from: Int) + do + 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 substr_iter.item + + redef fun is_ok do return pos <= max + + redef fun index do return pos + + redef fun next + do + 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 + end +end +