# 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("")
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
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
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
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
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
+