nitgs: opt_direct_call_monomorph call `before_send` to deal with special cases
[nit.git] / lib / standard / ropes.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it if you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or as a part of
9 # another product.
10
11 # Nit implementation of the Ropes (see Ropes : An Alternative to Strings,
12 # SOFTWARE - PRACTICE AND EXPERIENCE, VOL. 25(12), 1315 - 1330 (DECEMBER 1995)
13 # Hans. J Boehm, Russ Atkinson and Michael Plass)
14 #
15 # A rope is a kind of string but instead of being flat, it relies on a binary tree structure to store data.
16 module ropes
17
18 import file
19 intrude import string
20
21 # Structure, tuple containing a LeafNode and an Int
22 # Used for methods like [] or has/has_only
23 private class TupleLeafNodePos
24 private var curr_node: LeafNode
25 private var corrected_pos: Int
26 private var visit_stack: List[TupleVisitNode]
27 end
28
29 # Abstract class, represents all the services offered by both mutable and immutable ropes
30 abstract class Rope
31 super Comparable
32 super StringCapable
33
34 # Cached version of self as a flat String
35 private var str_representation: nullable String = null
36
37 redef type OTHER: Rope
38
39 # The first node of the hierarchy
40 private var parent_node: RopeNode
41
42 # Needed by the compiler to avoid producing an error with constructors in subclasses
43 init do
44 self.parent_node = new ConcatNode
45 end
46
47 # Initializes a new Rope with a text embedded in directly
48 init with_string(str: AbstractString) do
49 self.parent_node = new ConcatNode
50 parent_node.as(ConcatNode).right_child = new LeafNode(str)
51 parent_node.as(ConcatNode).update_data
52 end
53
54 # Returns a view on the rope
55 fun chars: SequenceRead[Char]
56 do
57 return new CharRopeView(self)
58 end
59
60 # Gets the total length of the Rope
61 fun length: Int
62 do
63 return parent_node.length
64 end
65
66 # Returns a flat version of self
67 redef fun to_s
68 do
69 if self.str_representation == null then flatten
70 return str_representation.as(not null)
71 end
72
73 # Stores a flat version of self in cache
74 private fun flatten: String
75 do
76 var native_final_str = calloc_string(length + 1)
77
78 native_final_str[length] = '\0'
79
80 var offset = 0
81
82 var iter = new DFSRopeLeafIterator(self)
83
84 while iter.is_ok do
85 iter.item.value._items.copy_to(native_final_str, iter.item.value.length, 0, offset)
86 offset += iter.item.value.length
87 iter.next
88 end
89
90 return native_final_str.to_s_with_length(length)
91 end
92
93 # Gets a node containing the substring to seek the char at the require position
94 private fun get_node_for_pos(position: Int): TupleLeafNodePos
95 do
96 assert position >= 0 and position < self.length
97
98 var curr_node: nullable RopeNode = parent_node
99
100 var visit_stack = new List[TupleVisitNode]
101
102 var curr_visit_tuple: TupleVisitNode
103
104 loop
105 if curr_node isa ConcatNode then
106 curr_visit_tuple = new TupleVisitNode(curr_node)
107 if curr_node.left_child != null and position < curr_node.left_child.length then
108 curr_visit_tuple.left_visited = true
109 curr_node = curr_node.left_child
110 else if curr_node.right_child != null then
111 curr_visit_tuple.left_visited = true
112 curr_visit_tuple.right_visited = true
113 if curr_node.left_child != null then position -= curr_node.left_child.length
114 curr_node = curr_node.right_child
115 else
116 print "Fatal Error"
117 abort
118 end
119 visit_stack.push(curr_visit_tuple)
120 else if curr_node isa LeafNode then
121 return new TupleLeafNodePos(curr_node, position, visit_stack)
122 end
123 end
124 end
125
126 # Concats two ropes and returns a new one
127 fun +(other: Rope): Rope do
128 var new_rope = new BufferRope
129
130 var first_iter = new DFSRopeLeafIterator(self)
131
132 while first_iter.is_ok do
133 new_rope.append(first_iter.item.value)
134 first_iter.next
135 end
136
137 var second_iter = new DFSRopeLeafIterator(other)
138
139 while second_iter.is_ok do
140 new_rope.append(second_iter.item.value)
141 second_iter.next
142 end
143
144 return new_rope
145 end
146
147 # Returns a copy of several ropes concatenated
148 #
149 # Is equivalent to a chain of + operations
150 # Except this one is optimized for performance
151 fun multi_concat(ropes: Rope...): Rope
152 do
153 var new_rope = new BufferRope
154
155 var self_iter = self.iterator
156 while self_iter.is_ok do
157 new_rope.append(self_iter.item.value)
158 self_iter.next
159 end
160
161 for i in ropes do
162 var iter = i.iterator
163 while iter.is_ok do
164 new_rope.append(iter.item.value)
165 iter.next
166 end
167 end
168
169 return new_rope
170 end
171
172 # Appends the content of self multiple times in a new Rope object
173 fun *(repeats: Int): Rope do
174
175 var new_rope = new BufferRope
176
177 var str = self.to_s
178
179 for i in [1 .. repeats] do new_rope.append(str)
180
181 return new_rope
182 end
183
184 # Returns an iterator on self
185 #
186 # Unsafe modifications on a MutableRope
187 #
188 private fun iterator: Iterator[LeafNode] do return new DFSRopeLeafIterator(self)
189
190 # Creates a subrope.
191 #
192 # var rope = (new BufferRope).append("abcd")
193 #
194 # assert rope.subrope(1, 2) == "bc"
195 # assert rope.subrope(-1, ) == "a"
196 # assert rope.subrope(1, 0) == ""
197 # assert rope.subrope(2, 5) == "cd"
198 #
199 # A `index_from` index < 0 will be replaced by 0.
200 # Unless a `count` value is > 0 at the same time.
201 # In this case, `index_from += count` and `count -= index_from`.
202 #
203 fun subrope(index_from: Int, count: Int): Rope
204 do
205 assert count >= 0
206
207 if index_from < 0 then
208 count += index_from
209 if count < 0 then count = 0
210 index_from = 0
211 end
212
213 if count - index_from >= self.length then count = length - index_from
214
215 var buffer = new BufferRope
216
217 var iter = new DFSRopeLeafIterator.with_index(self, index_from)
218
219 var curr_subrope_index = index_from - iter.pos
220
221 while iter.is_ok do
222 if count == 0 then break
223 if curr_subrope_index > 0 then
224 if count >= iter.item.value.length then
225 buffer.append(iter.item.value.substring(curr_subrope_index, count))
226 count -= iter.item.value.length - curr_subrope_index
227 curr_subrope_index = 0
228 else
229 buffer.append(iter.item.value.substring(curr_subrope_index, count))
230 break
231 end
232 else
233 if count >= iter.item.value.length then
234 buffer.append(iter.item.value)
235 count -= iter.item.value.length
236 else
237 buffer.append(iter.item.value.substring(0, count))
238 break
239 end
240 end
241
242 iter.next
243 end
244
245 return buffer
246 end
247
248 # Returns an upper (capitalized) version of self
249 fun to_upper: Rope
250 do
251 var new_rope = new BufferRope
252 var iter = new DFSRopeLeafIterator(self)
253 while iter.is_ok do
254 new_rope.append(iter.item.value.to_upper)
255 iter.next
256 end
257 return new_rope
258 end
259
260 # Returns a lower (minuscule) version of self
261 fun to_lower: Rope
262 do
263 var new_rope = new BufferRope
264 var iter = new DFSRopeLeafIterator(self)
265 while iter.is_ok do
266 new_rope.append(iter.item.value.to_lower)
267 iter.next
268 end
269 return new_rope
270 end
271
272 ############################################################################
273 # Comparable Refined Methods #
274 ############################################################################
275
276 # Compares the current Rope to another object (either another rope or a String)
277 redef fun == (other)
278 do
279 if other == null or not (other isa Rope or other isa AbstractString) then return false
280 var self_iter = new RopeCharIterator(self)
281 if other isa Rope then
282 if self.length != other.length then return false
283 var other_iterator = new RopeCharIterator(other)
284 while self_iter.is_ok do
285 if self_iter.item != other_iterator.item then return false
286 self_iter.next
287 other_iterator.next
288 end
289 else if other isa AbstractString then
290 var pos = 0
291 if self.length != other.length then return false
292 while self_iter.is_ok do
293 if self_iter.item != other[pos] then return false
294 pos += 1
295 self_iter.next
296 end
297 end
298 return true
299 end
300
301 # Checks if self is lesser than other
302 #
303 # Comparison done in lexicographical order
304 # i.e. 'aa' < 'b'
305 #
306 redef fun <(other)
307 do
308 var other_iter = other.chars.iterator
309 for i in self.chars do
310 if not other_iter.is_ok then return false
311 if i < other_iter.item then return true
312 if i > other_iter.item then return false
313 other_iter.next
314 end
315 if other_iter.is_ok then return true
316 return false
317 end
318
319 end
320
321 # Rope that can be modified
322 #
323 # /!\ Non Thread-safe /!\
324 #
325 class BufferRope
326 super Rope
327
328 var is_dirty: Bool = false
329
330 init
331 do
332 super
333 end
334
335 init with_string(str)
336 do
337 super
338 end
339
340 ############################################################################
341 # Tree Balancing Methods #
342 ############################################################################
343
344 # Performs a right rotation on a node of the Rope
345 #
346 # Root Pivot
347 # / \ / \
348 # Pivot Leaf3 => Leaf1 Root
349 # / \ / \
350 # Leaf1 Leaf2 Leaf2 Leaf3
351 private fun rotate_right(root: ConcatNode)
352 do
353 assert root.left_child != null
354 var pivot = root.left_child.as(ConcatNode)
355 var root_new_left = pivot.right_child
356 var root_parent = root.parent
357
358 root.left_child = root_new_left
359 pivot.right_child = root
360 if root_parent == null then
361 self.parent_node = pivot
362 pivot.parent = null
363 return
364 end
365
366 if root_parent.left_child == root then
367 root_parent.left_child = pivot
368 else
369 root_parent.right_child = pivot
370 end
371
372 root.update_data
373 pivot.update_data
374 root_parent.update_data
375 end
376
377 # Performs a left rotation on a node of the Rope
378 #
379 # Root Pivot
380 # / \ / \
381 # Leaf1 Pivot => Root Leaf3
382 # / \ / \
383 # Leaf2 Leaf3 Leaf1 Leaf2
384 private fun rotate_left(root: ConcatNode)
385 do
386 assert root.right_child != null
387 var pivot = root.right_child.as(ConcatNode)
388 var root_new_right = pivot.left_child
389 var root_parent = root.parent
390
391 root.right_child = root_new_right
392 pivot.left_child = root
393 if root_parent == null then
394 self.parent_node = pivot
395 pivot.parent = null
396 return
397 end
398
399 if root_parent.left_child == root then
400 root_parent.left_child = pivot
401 else
402 root_parent.right_child = pivot
403 end
404
405 root.update_data
406 pivot.update_data
407 root_parent.update_data
408 end
409
410 # Shortcut method to balance a node and its parents
411 # based on the rules of the AVL Tree
412 private fun balance_from_node(parent_node: nullable ConcatNode)
413 do
414 while parent_node != null do
415 parent_node.update_data
416 var node_balance = parent_node.balance_factor
417 if node_balance < -1 or node_balance > 1 then
418 balance_node(parent_node)
419 end
420 parent_node = parent_node.parent
421 end
422 end
423
424 # Performs rotations to balance a node according to AVL Tree rules
425 private fun balance_node(node: ConcatNode)
426 do
427 var balance_factor = node.balance_factor
428 if balance_factor < -1 then
429 var right_balance = node.right_child.balance_factor
430 if right_balance < 0 then
431 rotate_left(node)
432 else
433 rotate_right(node.right_child.as(ConcatNode))
434 rotate_left(node)
435 end
436 else
437 var left_balance = node.left_child.balance_factor
438 if left_balance > 0 then
439 rotate_right(node)
440 else
441 rotate_left(node.left_child.as(ConcatNode))
442 rotate_right(node)
443 end
444 end
445 end
446
447 ############################################################################
448 # BufferRope exclusive Methods #
449 ############################################################################
450
451 # Appends a new Collection[Char] at the end of the current rope
452 fun append(str: Collection[Char]): BufferRope
453 do
454 if str isa AbstractString then
455 var last_node = parent_node
456
457 while last_node isa ConcatNode and last_node.right_child != null do
458 last_node = last_node.right_child.as(not null)
459 end
460
461 if last_node isa ConcatNode then
462 last_node.right_child = new LeafNode(str.to_s)
463 else if last_node isa LeafNode then
464 var last_node_parent = last_node.parent
465 var new_concat = new ConcatNode
466 last_node_parent.right_child = new_concat
467 new_concat.left_child = last_node
468 new_concat.right_child = new LeafNode(str.to_s)
469 last_node = new_concat
470 else
471 print "Fatal Error, please report to the developers for more insight."
472 abort
473 end
474
475 balance_from_node(last_node)
476 else
477 var buf = new Buffer.with_capacity(str.length)
478 for i in str do
479 buf.add(i)
480 end
481 append(buf)
482 end
483
484 if not is_dirty then is_dirty = true
485
486 return self
487 end
488
489 # Variatic function to append several collections of Chars
490 fun append_multi(strs: Collection[Char]...): BufferRope
491 do
492 for i in strs do
493 append(i)
494 end
495 return self
496 end
497
498 # Adds a new Collection[Char] at the beginning of the rope
499 fun prepend(str: Collection[Char]): BufferRope
500 do
501 if str isa AbstractString then
502 var curr_node = parent_node
503
504 while curr_node isa ConcatNode and curr_node.left_child != null do
505 curr_node = curr_node.left_child.as(not null)
506 end
507
508 if curr_node isa ConcatNode then
509 curr_node.left_child = new LeafNode(str.to_s)
510 else if curr_node isa LeafNode then
511 var parent = curr_node.parent
512 var new_concat = new ConcatNode
513 var new_leaf = new LeafNode(str.to_s)
514 new_concat.left_child = new_leaf
515 new_concat.right_child = curr_node
516 parent.left_child = new_concat
517 curr_node = new_concat
518 else
519 print "Fatal Error"
520 abort
521 end
522
523 balance_from_node(curr_node)
524 else
525 var buf = new Buffer.with_capacity(str.length)
526 for i in str do
527 buf.add(i)
528 end
529 prepend(buf.to_s)
530 end
531
532 if not is_dirty then is_dirty = true
533
534 return self
535 end
536
537 # Variatic function to prepend several collections of Chars
538 fun prepend_multi(strs: Collection[Char]...): BufferRope
539 do
540 for i in strs do
541 prepend(i)
542 end
543 return self
544 end
545
546 # Adds the content of `str` after self, does not create a new rope object
547 fun concat(str: Rope): Rope
548 do
549 var other_iter = new DFSRopeLeafIterator(str)
550
551 var modif_list = new List[String]
552
553 while other_iter.is_ok do
554 modif_list.push(other_iter.item.value)
555 other_iter.next
556 end
557
558 while modif_list.length > 0 do
559 self.append(modif_list.shift)
560 end
561
562 if not is_dirty then is_dirty = true
563
564 return self
565 end
566
567 # Returns the content of the current BufferRope object as an ImmutableRope
568 fun freeze: ImmutableRope
569 do
570 var buffer_rope = new BufferRope
571 var new_rope = new ImmutableRope
572
573 var iter = new DFSRopeLeafIterator(self)
574
575 while iter.is_ok do
576 buffer_rope.append(iter.item.value)
577 iter.next
578 end
579
580 new_rope.parent_node = buffer_rope.parent_node
581
582 if not is_dirty then new_rope.str_representation = self.str_representation
583
584 return new_rope
585 end
586
587 # Unsafe method to convert self as an ImmutableRope
588 #
589 # To be used internally only
590 private fun to_immutable: ImmutableRope
591 do
592 var immutable_self = new ImmutableRope
593 immutable_self.parent_node = self.parent_node
594 return immutable_self
595 end
596
597 ############################################################################
598 # Rope refined Methods #
599 ############################################################################
600
601 redef fun subrope(index_from: Int, count: Int): BufferRope
602 do
603 return super.as(BufferRope)
604 end
605
606 redef fun *(repeats: Int): BufferRope
607 do
608 return super.as(BufferRope)
609 end
610
611 redef fun +(other: Rope): BufferRope
612 do
613 return super.as(BufferRope)
614 end
615
616 redef fun multi_concat(ropes: Rope...): BufferRope
617 do
618 return super.as(BufferRope)
619 end
620
621 # Refines to add a cache method, calculates only once for every modification
622 # the string representation for self
623 redef fun to_s
624 do
625 if self.str_representation == null or is_dirty then
626 self.str_representation = flatten
627 is_dirty = false
628 end
629 return self.str_representation.as(not null)
630 end
631
632 end
633
634 # Rope that cannot be modified
635 class ImmutableRope
636 super Rope
637
638 init
639 do
640 super
641 end
642
643 init with_string(str)
644 do
645 super
646 end
647
648 ############################################################################
649 # Rope refined Methods #
650 ############################################################################
651
652 redef fun subrope(index_from: Int, count: Int): ImmutableRope
653 do
654 return (super.as(BufferRope)).to_immutable
655 end
656
657 redef fun *(repeats: Int): ImmutableRope
658 do
659 return (super.as(BufferRope)).to_immutable
660 end
661
662 redef fun +(other: Rope): ImmutableRope
663 do
664 return (super.as(BufferRope)).to_immutable
665 end
666
667 redef fun multi_concat(ropes: Rope...): ImmutableRope
668 do
669 return (super.as(BufferRope)).to_immutable
670 end
671
672 end
673
674 ############################################
675 # Rope view classes #
676 ############################################
677
678 class CharRopeView
679 super SequenceRead[Char]
680
681 # Targeted Rope for the view
682 private var target: Rope
683
684 init(tgt: Rope)
685 do
686 self.target = tgt
687 end
688
689 redef fun [](position)
690 do
691 var tuple = target.get_node_for_pos(position)
692 return tuple.curr_node.value[tuple.corrected_pos]
693 end
694
695 redef fun first do return self[0]
696
697 redef fun index_of(char)
698 do
699 var intern_iter = new RopeCharIterator(target)
700 while intern_iter.is_ok do
701 if intern_iter.item == char then return intern_iter.index
702 intern_iter.next
703 end
704 return -1
705 end
706
707 redef fun iterator do
708 return new RopeCharIterator(target)
709 end
710
711 redef fun last do return self[self.length-1]
712
713 redef fun length do return target.length
714
715 redef fun count(item)
716 do
717 var count = 0
718 var iter = self.iterator
719
720 for i in self do
721 if i == item then count += 1
722 end
723
724 return count
725 end
726
727 redef fun has_only(item)
728 do
729 for i in self do
730 if i != item then return false
731 end
732 return true
733 end
734
735 redef fun is_empty do return length == 0
736
737 redef fun to_a do
738 return to_s.to_a
739 end
740
741 redef fun to_s do
742 return target.to_s
743 end
744
745 redef fun ==(other)
746 do
747 if not other isa SequenceRead[Char] then return false
748
749 if self.length != other then return false
750
751 var iter = other.iterator
752
753 for i in self do
754 if i != iter.item then return false
755 iter.next
756 end
757
758 return true
759 end
760
761 end
762
763 ###########################################
764 # Iterator classes #
765 ###########################################
766
767 # A tuple representing the state of a node for a tree parsing algorithm
768 private class TupleVisitNode
769
770 init(tgt: ConcatNode)
771 do
772 self.node = tgt
773 end
774
775 private var node: ConcatNode
776
777 private var left_visited = false
778 private var right_visited = false
779
780 end
781
782 # Any kind of iterator parsing a Rope for LeafNodes
783 private abstract class RopeIterator
784 super IndexedIterator[LeafNode]
785
786 # Rope meant to be visited
787 private var _target: Rope
788
789 private fun target: Rope do return self._target
790
791 # Position in target
792 private var pos = 0
793
794 init(tgt: Rope)
795 do
796 self._target = tgt
797 end
798
799 init with_index(tgt: Rope, index: Int)
800 do
801 self._target = tgt
802 end
803
804 end
805
806 # Iterator returning the content of a rope one char at a time
807 class RopeCharIterator
808 super IndexedIterator[Char]
809
810 # The iterator used to visit the rope
811 private var sub_str_iter: DFSRopeLeafIterator
812
813 # The current position in the rope
814 private var abs_pos = 0
815
816 # The position in the current substring
817 private var sub_pos: Int = 0
818
819 # The substring contained in the current node visited by `sub_str_iter`
820 private var curr_substring: nullable String
821
822 init(tgt: Rope)
823 do
824 sub_str_iter = new DFSRopeLeafIterator(tgt)
825 if sub_str_iter.is_ok then curr_substring = sub_str_iter.item.value
826 end
827
828 redef fun item do return curr_substring[sub_pos]
829
830 redef fun is_ok
831 do
832 if sub_str_iter.is_ok then return true
833 if not sub_str_iter.is_ok and curr_substring != null and sub_pos < curr_substring.length then return true
834 return false
835 end
836
837 redef fun next
838 do
839 assert is_ok
840 if sub_pos < curr_substring.length - 1 then
841 sub_pos += 1
842 else
843 sub_str_iter.next
844 if sub_str_iter.is_ok then
845 curr_substring = sub_str_iter.item.value
846 sub_pos = 0
847 else
848 sub_pos = curr_substring.length
849 end
850 end
851 abs_pos += 1
852 end
853
854 redef fun index do return abs_pos
855
856 end
857
858 # Special kind of iterator
859 #
860 # Performs a Depth-First Search on RopeLeaf items
861 #
862 private class DFSRopeLeafIterator
863 super RopeIterator
864
865 # Stack of the visited nodes in the rope
866 private var visit_stack = new List[TupleVisitNode]
867
868 # The leaf node being visited by the iterator
869 private var curr_leaf: nullable LeafNode
870
871 init(tgt: Rope)
872 do
873 super
874
875 var first_node = target.parent_node
876
877 if first_node isa ConcatNode then
878 visit_stack.push(new TupleVisitNode(first_node))
879 else if first_node isa LeafNode then
880 curr_leaf = first_node
881 return
882 end
883
884 next_body
885 end
886
887 # Creates a new iterator on `tgt` starting at `index`
888 init with_index(tgt: Rope, index: Int)
889 do
890 super
891
892 var returned_tuple = target.get_node_for_pos(index)
893 curr_leaf = returned_tuple.curr_node
894 visit_stack = returned_tuple.visit_stack
895 pos = index - returned_tuple.corrected_pos
896 end
897
898 redef fun is_ok do return curr_leaf != null
899
900 redef fun next
901 do
902 assert is_ok
903 pos += curr_leaf.value.length
904 next_body
905 end
906
907 private fun next_body
908 do
909 var next_node: nullable RopeNode
910 while not visit_stack.is_empty do
911 var curr_concat_tuple = visit_stack.last
912 if not curr_concat_tuple.left_visited then
913
914 curr_concat_tuple.left_visited = true
915
916 next_node = curr_concat_tuple.node.left_child
917
918 if next_node == null then continue
919
920 if next_node isa ConcatNode then
921 visit_stack.push(new TupleVisitNode(next_node))
922 else if next_node isa LeafNode then
923 curr_leaf = next_node
924 return
925 end
926
927 else if not curr_concat_tuple.right_visited then
928
929 curr_concat_tuple.right_visited = true
930
931 next_node = curr_concat_tuple.node.right_child
932
933 if next_node == null then continue
934
935 if next_node isa ConcatNode then
936 visit_stack.push(new TupleVisitNode(next_node))
937 else if next_node isa LeafNode then
938 curr_leaf = next_node
939 return
940 end
941
942 else
943 visit_stack.pop
944 end
945 end
946 self.curr_leaf = null
947 end
948
949 redef fun item
950 do
951 assert is_ok
952 return curr_leaf.as(not null)
953 end
954
955 end
956
957 ###########################################
958 # Node classes #
959 ###########################################
960
961 # A node for a Rope
962 private abstract class RopeNode
963
964 private var _length = 0
965
966 private var parent: nullable ConcatNode = null
967
968 private var height = 0
969
970 # The balance factor of a node, if it is a Leaf, it equals its length
971 # Else, it will be equal to the difference between the height on the left and on the right
972 private fun balance_factor: Int do return height end
973
974 fun length: Int do return _length
975
976 private fun length=(len: Int)
977 do
978 _length = len
979 end
980 end
981
982 # Node that represents a concatenation between two nodes (of any RopeNode type)
983 private class ConcatNode
984 super RopeNode
985
986 private var _left_child: nullable RopeNode
987 private var _right_child: nullable RopeNode
988
989 private fun left_child: nullable RopeNode
990 do
991 if _left_child != null then
992 return _left_child
993 else
994 return null
995 end
996 end
997
998 private fun right_child: nullable RopeNode
999 do
1000 if _right_child != null then
1001 return _right_child
1002 else
1003 return null
1004 end
1005 end
1006
1007 private fun left_child=(new_node: nullable RopeNode)
1008 do
1009 self._left_child = new_node
1010 new_node.parent = self
1011 update_data
1012 end
1013
1014 private fun right_child=(new_node: nullable RopeNode)
1015 do
1016 self._right_child = new_node
1017 new_node.parent = self
1018 update_data
1019 end
1020
1021 # Updates the internal data of the current node
1022 #
1023 # Concretely, updates the length and the height of the node
1024 private fun update_data
1025 do
1026 self.length = 0
1027 self.height = 1
1028 if left_child != null then
1029 self.length += left_child.length
1030 if left_child.height + 1 > self.height then self.height = left_child.height + 1
1031 end
1032 if right_child != null then
1033 self.length += right_child.length
1034 if right_child.height + 1 > self.height then self.height = right_child.height + 1
1035 end
1036 end
1037
1038 # Computes and returns the balance factor (used for AVL trees)
1039 #
1040 # Formula : left.height - right.height
1041 redef private fun balance_factor
1042 do
1043 var left_height = 0
1044 var right_height = 0
1045 if left_child != null then left_height = left_child.height
1046 if right_child != null then right_height = right_child.height
1047 return left_height - right_height
1048 end
1049 end
1050
1051 # A leaf node contains a substring of some sort
1052 private class LeafNode
1053 super RopeNode
1054
1055 # Encapsulated string in the leaf node
1056 private var _value: String
1057
1058 init(val: AbstractString)
1059 do
1060 self._value = val.to_s
1061 self.length = val.length
1062 end
1063
1064 private fun value: String do return self._value
1065
1066 private fun value= (val: String)
1067 do
1068 _value = val
1069 end
1070 end
1071
1072 #####################################################
1073 # Foreign classes refinement #
1074 #####################################################
1075
1076 redef class String
1077 redef fun ==(other)
1078 do
1079 if other isa Rope then
1080 return other == self
1081 else
1082 return super
1083 end
1084 end
1085 end
1086
1087 redef class Buffer
1088 redef fun ==(other)
1089 do
1090 if other isa Rope then
1091 return other == self
1092 else
1093 return super
1094 end
1095 end
1096 end