X-Git-Url: http://nitlanguage.org diff --git a/lib/standard/ropes.nit b/lib/standard/ropes.nit index 40d531b..7bc4154 100644 --- a/lib/standard/ropes.nit +++ b/lib/standard/ropes.nit @@ -44,6 +44,11 @@ end private abstract class RopeNode # Length of the node var length = 0 + + # Transforms the current node to a Leaf. + # This might be costly to invoke since this produces a FlatString concatenation. + # Can be used internally to limit the growth of the Rope when working with small leaves. + fun to_leaf: Leaf is abstract end # Node that represents a concatenation between two nodes (of any RopeNode type) @@ -51,40 +56,47 @@ private class Concat super RopeNode # Left child of the node - var _left: nullable RopeNode = null + var left: nullable RopeNode # Right child of the node - var _right: nullable RopeNode = null - - fun left: nullable RopeNode do return _left - fun right: nullable RopeNode do return _right + var right: nullable RopeNode - fun left=(l: RopeNode) + init(l: nullable RopeNode, r: nullable RopeNode) do - _left = l - length = l.length - if _right != null then length += _right.length + left = l + right = r + if l != null then length += l.length + if r != null then length += r.length end - fun right=(r: RopeNode) + redef fun to_leaf do - _right = r - length = r.length - if _left != null then length += _left.length + if left == null then + if right == null then return new StringLeaf("".as(FlatString)) + return right.to_leaf + end + if right == null then return left.as(not null).to_leaf + return new StringLeaf((left.to_leaf.str.as(FlatString) + right.to_leaf.str.as(FlatString)).as(FlatString)) end end # Leaf of a Rope, contains a FlatString -private class Leaf +private abstract class Leaf super RopeNode # Encapsulated FlatString in the leaf node - var str: FlatString + var str: FlatText + +end + +private class StringLeaf + super Leaf init(val: FlatString) do self.str = val length = str.length end + redef fun to_leaf do return self end # Basic structure, binary tree with a root node. @@ -96,12 +108,15 @@ 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("") # 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)) + if s isa RopeString then root = s.root else root = new StringLeaf(s.as(FlatString)) end private init from_root(r: RopeNode) @@ -118,11 +133,56 @@ abstract class Rope 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) + redef fun substrings 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) + # Iterator on the nodes of the rope, in backwards postfix order + private fun reverse_postfix(from: Int): ReversePostfix do return new ReversePostfix.from(self, from) + + # Iterator on the leaves of the rope, backwards order + private fun reverse_leaves(from: Int): ReverseLeavesIterator do return new ReverseLeavesIterator(self,from) + + # Iterator on the substrings, in reverse order + fun reverse_substrings: IndexedIterator[Text] do return new ReverseSubstringsIterator(self, length-1) + + # Iterator on the substrings, in reverse order, starting iteration at position `from` + fun reverse_substrings_from(from: Int): IndexedIterator[Text] do return new ReverseSubstringsIterator(self, from) + + redef fun output + do + for i in substrings do + i.output + end + end + + 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 @@ -154,6 +214,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 @@ -165,6 +236,8 @@ class RopeString redef fun empty do return once new RopeString.from("") + redef var chars: SequenceRead[Char] = new RopeStringChars(self) + redef fun reversed do var ret = empty.as(RopeString) @@ -204,7 +277,7 @@ class RopeString end # Inserts a String `str` at position `pos` - fun insert_at(str: String, pos: Int): RopeString + redef fun insert_at(str, pos) do if str.length == 0 then return self if self.length == 0 then return new RopeString.from(str) @@ -215,46 +288,50 @@ class RopeString var path = node_at(pos) - var last_concat = new Concat + var last_concat: Concat if path.offset == 0 then - last_concat.right = path.leaf - if str isa FlatString then last_concat.left = new Leaf(str) else last_concat.left = str.as(RopeString).root + if str isa FlatString then + last_concat = new Concat(new StringLeaf(str), path.leaf) + else + last_concat = new Concat(str.as(RopeString).root, path.leaf) + end else if path.offset == path.leaf.length then - if str isa FlatString then last_concat.right = new Leaf(str) else last_concat.right = str.as(RopeString).root - last_concat.left = path.leaf + if str isa FlatString then + last_concat = new Concat(path.leaf, new StringLeaf(str)) + else + last_concat = new Concat(path.leaf, str.as(RopeString).root) + end else var s = path.leaf.str var l_half = s.substring(0, s.length - path.offset) var r_half = s.substring_from(s.length - path.offset) - var cct = new Concat - cct.right = new Leaf(r_half) - last_concat.left = new Leaf(l_half) - if str isa FlatString then last_concat.right = new Leaf(str) else last_concat.right = str.as(RopeString).root - cct.left = last_concat - last_concat = cct + var cct: Concat + var ll = new StringLeaf(l_half.as(FlatString)) + if str isa FlatString then + cct = new Concat(ll, new StringLeaf(str)) + else + cct = new Concat(ll, str.as(RopeString).root) + end + last_concat = new Concat(cct, new StringLeaf(r_half.as(FlatString))) end for i in path.stack.reverse_iterator do - var nod = new Concat if i.left then - nod.right = i.node.right.as(not null) - nod.left = last_concat + last_concat = new Concat(last_concat, i.node.right) else - nod.left = i.node.left.as(not null) - nod.right = last_concat + last_concat = new Concat(i.node.left, last_concat) end - last_concat = nod end 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) + redef fun prepend(s) do return insert_at(s, 0) # Adds `s` at the end of self - fun append(s: String): String + redef fun append(s) do if self.is_empty then return s return new RopeString.from_root(append_to_path(root,s)) @@ -263,17 +340,25 @@ class RopeString # 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 + var cct: 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 s isa FlatString then + cct = new Concat(node, new StringLeaf(s)) + else + cct = new Concat(node, s.as(RopeString).root) + end + else + var n = node.as(Concat) + var right = n.right + var lft = n.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 + if s isa FlatString then + cct = new Concat(lft, new StringLeaf(s)) + else + cct = new Concat(lft, s.as(RopeString).root) + end else - cct.right = append_to_path(right, s) + cct = new Concat(lft, append_to_path(right, s)) end end return cct @@ -303,17 +388,13 @@ class RopeString 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)) + if path.leaf.str.length - offset > len then lf = new StringLeaf(lf.str.substring(offset,len).as(FlatString)) else lf = new StringLeaf(lf.str.substring_from(offset).as(FlatString)) 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 + nod = new Concat(nod, i.node.right) end var ret = new RopeString @@ -322,15 +403,11 @@ class RopeString path = ret.node_at(len-1) offset = path.offset - nod = new Leaf(path.leaf.str.substring(0, offset+1)) + nod = new StringLeaf(path.leaf.str.substring(0, offset+1).as(FlatString)) 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 + nod = new Concat(i.node.left, nod) end ret.root = nod @@ -339,6 +416,47 @@ class RopeString end end +redef class FlatString + + redef fun append(s) do return (new RopeString.from(self)) + s + + redef fun prepend(s) do return (new RopeString.from(self)).prepend(s) + + redef fun insert_at(s, pos) + do + if pos == 0 then return prepend(s) + if pos == length then return append(s) + + var l = substring(0,pos) + var r = substring_from(pos) + var ret: String = new RopeString.from(l) + ret += s + return ret + r + end + +end + +private class RopeStringChars + super SequenceRead[Char] + + var tgt: Rope + + redef fun [](pos) + do + assert pos < tgt.length + var path = tgt.node_at(pos) + return path.leaf.str.chars[path.offset] + end + + redef fun iterator do return iterator_from(0) + + redef fun iterator_from(pos) do return new RopeCharIterator(tgt, pos) + + redef fun reverse_iterator do return reverse_iterator_from(tgt.length-1) + + redef fun reverse_iterator_from(pos) do return new ReverseRopeCharIterator(tgt, pos) +end + # Used to iterate on a Rope private class IteratorElement @@ -564,3 +682,189 @@ class RopeCharIterator end end +private class ReversePostfix + super IndexedIterator[RopeNode] + + var target: Rope + + var pos: Int + + var min = 0 + + var stack = new List[IteratorElement] + + init from(tgt: Rope, pos: Int) + do + self.pos = pos + target = tgt + 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 elemt = new IteratorElement(i.node) + elemt.right = true + if i.left then + elemt.left = true + end + stack.push elemt + end + stack.push(new IteratorElement(path.leaf)) + stack.last.done = true + 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 < min then + stack.clear + return + end + var lst = stack.last + if lst.done then + stack.pop + next + return + end + if not lst.right then + var nod = lst.node.as(Concat) + var rgt = nod.right + lst.right = true + if rgt != null then + stack.push(new IteratorElement(rgt)) + next + return + end + end + if not lst.left then + var nod = lst.node.as(Concat) + var lft = nod.left + lst.left = true + if lft != null then + stack.push(new IteratorElement(lft)) + next + return + end + end + if lst.node isa Leaf then pos -= lst.node.length + lst.done = true + end +end + +private class ReverseLeavesIterator + super IndexedIterator[Leaf] + + var nodes: ReversePostfix + + init(tgt: Rope, from: Int) + do + nodes = tgt.reverse_postfix(from) + 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 next do + while nodes.is_ok do + nodes.next + if nodes.is_ok then if nodes.item isa Leaf then break + end + end + + redef fun index do return nodes.index + +end + +private class ReverseSubstringsIterator + super IndexedIterator[Text] + + var leaves: ReverseLeavesIterator + + var pos: Int + + var str: Text + + init(tgt: Rope, from: Int) + do + leaves = tgt.reverse_leaves(from) + pos = from + if not leaves.is_ok then return + str = leaves.item.str + make_substring + end + + fun make_substring + do + if pos >= (leaves.index + str.length - 1) then return + str = str.substring(0, (pos - leaves.index + 1)) + end + + redef fun is_ok do return leaves.is_ok + + redef fun item do + assert is_ok + return str + end + + redef fun index do return pos + + redef fun next do + pos -= str.length + leaves.next + if not leaves.is_ok then return + str = leaves.item.str + make_substring + end +end + +private class ReverseRopeCharIterator + super IndexedIterator[Char] + + var substrs: IndexedIterator[Text] + + var pos: Int + + var subiter: IndexedIterator[Char] + + init(tgt: Rope, from: Int) + do + substrs = tgt.reverse_substrings_from(from) + if not substrs.is_ok then + pos = -1 + return + end + subiter = substrs.item.chars.reverse_iterator + pos = from + end + + redef fun is_ok do return pos >= 0 + + redef fun item do + assert is_ok + return subiter.item + end + + redef fun index do return pos + + redef fun next do + pos -= 1 + if subiter.is_ok then subiter.next + if not subiter.is_ok then + if substrs.is_ok then substrs.next + if substrs.is_ok then subiter = substrs.item.chars.reverse_iterator + end + end +end +