stdlib/strings: Moved Substring to Buffer since it was in no way similar to the Strin...
[nit.git] / lib / standard / string.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
4 # Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
5 #
6 # This file is free software, which comes along with NIT. This software is
7 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
10 # is kept unaltered, and a notification of the changes is added.
11 # You are allowed to redistribute it and sell it, alone or is a part of
12 # another product.
13
14 # Basic manipulations of strings of characters
15 module string
16
17 import math
18 intrude import collection # FIXME should be collection::array
19
20 `{
21 #include <stdio.h>
22 `}
23
24 ###############################################################################
25 # String #
26 ###############################################################################
27
28 # Common subclass for String and Buffer
29 abstract class AbstractString
30 super AbstractArrayRead[Char]
31
32 readable private var _items: NativeString
33
34 fun chars: StringCharView is abstract
35
36 # Access a character at `index` in the string.
37 #
38 # assert "abcd"[2] == 'c'
39 redef fun [](index) do return _items[index]
40
41 # Create a substring.
42 #
43 # assert "abcd".substring(1, 2) == "bc"
44 # assert "abcd".substring(-1, 2) == "a"
45 # assert "abcd".substring(1, 0) == ""
46 # assert "abcd".substring(2, 5) == "cd"
47 #
48 # A `from` index < 0 will be replaced by 0.
49 # Unless a `count` value is > 0 at the same time.
50 # In this case, `from += count` and `count -= from`.
51 fun substring(from: Int, count: Int): String is abstract
52
53 # Create a substring from `self` beginning at the `from` position
54 #
55 # assert "abcd".substring_from(1) == "bcd"
56 # assert "abcd".substring_from(-1) == "abcd"
57 # assert "abcd".substring_from(2) == "cd"
58 #
59 # As with substring, a `from` index < 0 will be replaced by 0
60 fun substring_from(from: Int): String
61 do
62 assert from < length
63 return substring(from, length - from)
64 end
65
66 # Does self have a substring `str` starting from position `pos`?
67 #
68 # assert "abcd".has_substring("bc",1) == true
69 # assert "abcd".has_substring("bc",2) == false
70 fun has_substring(str: String, pos: Int): Bool
71 do
72 var itsindex = str.length - 1
73 var myindex = pos + itsindex
74 var myitems = _items
75 var itsitems = str._items
76 if myindex > length or itsindex > myindex then return false
77 var its_index_from = str._index_from
78 itsindex += its_index_from
79 while itsindex >= its_index_from do
80 if myitems[myindex] != itsitems[itsindex] then return false
81 myindex -= 1
82 itsindex -= 1
83 end
84 return true
85 end
86
87 # Is this string prefixed by `prefix`?
88 #
89 # assert "abcd".has_prefix("ab") == true
90 # assert "abcbc".has_prefix("bc") == false
91 # assert "ab".has_prefix("abcd") == false
92 fun has_prefix(prefix: String): Bool do return has_substring(prefix,0)
93
94 # Is this string suffixed by `suffix`?
95 #
96 # assert "abcd".has_suffix("abc") == false
97 # assert "abcd".has_suffix("bcd") == true
98 fun has_suffix(suffix: String): Bool do return has_substring(suffix, length - suffix.length)
99
100 # If `self` contains only digits, return the corresponding integer
101 #
102 # assert "123".to_i == 123
103 # assert "-1".to_i == -1
104 fun to_i: Int
105 do
106 # Shortcut
107 return to_s.to_cstring.atoi
108 end
109
110 # If `self` contains a float, return the corresponding float
111 #
112 # assert "123".to_f == 123.0
113 # assert "-1".to_f == -1.0
114 # assert "-1.2e-3".to_f == -0.0012
115 fun to_f: Float
116 do
117 # Shortcut
118 return to_s.to_cstring.atof
119 end
120
121 # If `self` contains only digits and alpha <= 'f', return the corresponding integer.
122 fun to_hex: Int do return a_to(16)
123
124 # If `self` contains only digits and letters, return the corresponding integer in a given base
125 #
126 # assert "120".a_to(3) == 15
127 fun a_to(base: Int) : Int
128 do
129 var i = 0
130 var neg = false
131
132 for c in self.chars
133 do
134 var v = c.to_i
135 if v > base then
136 if neg then
137 return -i
138 else
139 return i
140 end
141 else if v < 0 then
142 neg = true
143 else
144 i = i * base + v
145 end
146 end
147 if neg then
148 return -i
149 else
150 return i
151 end
152 end
153
154 # Returns `true` if the string contains only Numeric values (and one "," or one "." character)
155 #
156 # assert "123".is_numeric == true
157 # assert "1.2".is_numeric == true
158 # assert "1,2".is_numeric == true
159 # assert "1..2".is_numeric == false
160 fun is_numeric: Bool
161 do
162 var has_point_or_comma = false
163 for i in self.chars
164 do
165 if not i.is_numeric
166 then
167 if (i == '.' or i == ',') and not has_point_or_comma
168 then
169 has_point_or_comma = true
170 else
171 return false
172 end
173 end
174 end
175 return true
176 end
177
178 # A upper case version of `self`
179 #
180 # assert "Hello World!".to_upper == "HELLO WORLD!"
181 fun to_upper: String
182 do
183 var s = new Buffer.with_capacity(length)
184 for i in self.chars do s.add(i.to_upper)
185 return s.to_s
186 end
187
188 # A lower case version of `self`
189 #
190 # assert "Hello World!".to_lower == "hello world!"
191 fun to_lower : String
192 do
193 var s = new Buffer.with_capacity(length)
194 for i in self.chars do s.add(i.to_lower)
195 return s.to_s
196 end
197
198 # Trims trailing and preceding white spaces
199 # A whitespace is defined as any character which ascii value is less than or equal to 32
200 #
201 # assert " Hello World ! ".trim == "Hello World !"
202 # assert "\na\nb\tc\t".trim == "a\nb\tc"
203 fun trim: String
204 do
205 if self.length == 0 then return self.to_s
206 # find position of the first non white space char (ascii < 32) from the start of the string
207 var start_pos = 0
208 while self.chars[start_pos].ascii <= 32 do
209 start_pos += 1
210 if start_pos == length then return ""
211 end
212 # find position of the first non white space char from the end of the string
213 var end_pos = length - 1
214 while self.chars[end_pos].ascii <= 32 do
215 end_pos -= 1
216 if end_pos == start_pos then return self.chars[start_pos].to_s
217 end
218 return self.substring(start_pos, end_pos - start_pos + 1)
219 end
220
221 redef fun output
222 do
223 var i = 0
224 while i < length do
225 _items[i].output
226 i += 1
227 end
228 end
229
230 # Mangle a string to be a unique string only made of alphanumeric characters
231 fun to_cmangle: String
232 do
233 var res = new Buffer
234 var underscore = false
235 for c in self.chars do
236 if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
237 res.add(c)
238 underscore = false
239 continue
240 end
241 if underscore then
242 res.append('_'.ascii.to_s)
243 res.add('d')
244 end
245 if c >= '0' and c <= '9' then
246 res.add(c)
247 underscore = false
248 else if c == '_' then
249 res.add(c)
250 underscore = true
251 else
252 res.add('_')
253 res.append(c.ascii.to_s)
254 res.add('d')
255 underscore = false
256 end
257 end
258 return res.to_s
259 end
260
261 # Escape " \ ' and non printable characters using the rules of literal C strings and characters
262 #
263 # assert "abAB12<>&".escape_to_c == "abAB12<>&"
264 # assert "\n\"'\\".escape_to_c == "\\n\\\"\\'\\\\"
265 fun escape_to_c: String
266 do
267 var b = new Buffer
268 for c in self.chars do
269 if c == '\n' then
270 b.append("\\n")
271 else if c == '\0' then
272 b.append("\\0")
273 else if c == '"' then
274 b.append("\\\"")
275 else if c == '\'' then
276 b.append("\\\'")
277 else if c == '\\' then
278 b.append("\\\\")
279 else if c.ascii < 32 then
280 b.append("\\{c.ascii.to_base(8, false)}")
281 else
282 b.add(c)
283 end
284 end
285 return b.to_s
286 end
287
288 # Escape additionnal characters
289 # The result might no be legal in C but be used in other languages
290 #
291 # assert "ab|\{\}".escape_more_to_c("|\{\}") == "ab\\|\\\{\\\}"
292 fun escape_more_to_c(chars: String): String
293 do
294 var b = new Buffer
295 for c in escape_to_c do
296 if chars.has(c) then
297 b.add('\\')
298 end
299 b.add(c)
300 end
301 return b.to_s
302 end
303
304 # Escape to c plus braces
305 #
306 # assert "\n\"'\\\{\}".escape_to_nit == "\\n\\\"\\'\\\\\\\{\\\}"
307 fun escape_to_nit: String do return escape_more_to_c("\{\}")
308
309 # Return a string where Nit escape sequences are transformed.
310 #
311 # Example:
312 # var s = "\\n"
313 # assert s.length == 2
314 # var u = s.unescape_nit
315 # assert u.length == 1
316 # assert u[0].ascii == 10 # (the ASCII value of the "new line" character)
317 fun unescape_nit: String
318 do
319 var res = new Buffer.with_capacity(self.length)
320 var was_slash = false
321 for c in self do
322 if not was_slash then
323 if c == '\\' then
324 was_slash = true
325 else
326 res.add(c)
327 end
328 continue
329 end
330 was_slash = false
331 if c == 'n' then
332 res.add('\n')
333 else if c == 'r' then
334 res.add('\r')
335 else if c == 't' then
336 res.add('\t')
337 else if c == '0' then
338 res.add('\0')
339 else
340 res.add(c)
341 end
342 end
343 return res.to_s
344 end
345 end
346
347 # Abstract class for the SequenceRead compatible
348 # views on String and Buffer objects
349 abstract class StringCharView
350 super SequenceRead[Char]
351
352 type SELFTYPE: AbstractString
353
354 private var target: SELFTYPE
355
356 private init(tgt: SELFTYPE)
357 do
358 target = tgt
359 end
360
361 redef fun is_empty do return target.is_empty
362
363 redef fun length do return target.length
364
365 redef fun iterator: IndexedIterator[Char] do return self.iterator_from(0)
366
367 # Gets a new Iterator starting at position `pos`
368 #
369 # Ex :
370 # var iter = "abcd".iterator_from(2)
371 # while iter.is_ok do
372 # printn iter.item
373 # iter.next
374 # end
375 #
376 # Outputs : cd
377 fun iterator_from(pos: Int): IndexedIterator[Char] is abstract
378
379 # Gets an iterator starting at the end and going backwards
380 #
381 # Ex :
382 # var reviter = "now step live...".reverse_iterator
383 # while reviter.is_ok do
384 # printn reviter.item
385 # reviter.next
386 # end
387 #
388 # Outputs : ...evil pets won
389 fun reverse_iterator: IndexedIterator[Char] do return self.reverse_iterator_from(self.length - 1)
390
391 # Gets an iterator on the chars of self starting from `pos`
392 #
393 # Ex :
394 # var iter = "abcd".reverse_iterator_from(1)
395 # while iter.is_ok do
396 # printn iter.item
397 # iter.next
398 # end
399 #
400 # Outputs : ba
401 fun reverse_iterator_from(pos: Int): IndexedIterator[Char] is abstract
402
403 redef fun has(c: Char): Bool
404 do
405 for i in self do
406 if i == c then return true
407 end
408 return false
409 end
410
411 end
412
413 # View on Buffer objects, extends Sequence
414 # for mutation operations
415 abstract class BufferCharView
416 super StringCharView
417 super Sequence[Char]
418
419 redef type SELFTYPE: Buffer
420
421 end
422
423 # Immutable strings of characters.
424 class String
425 super Comparable
426 super AbstractString
427 super StringCapable
428
429 redef type OTHER: String
430
431 # Index in _items of the start of the string
432 readable var _index_from: Int
433
434 # Indes in _items of the last item of the string
435 readable var _index_to: Int
436
437 redef var chars: StringCharView = new FlatStringCharView(self)
438
439 ################################################
440 # AbstractString specific methods #
441 ################################################
442
443 redef fun [](index) do
444 assert index >= 0
445 # Check that the index (+ index_from) is not larger than indexTo
446 # In other terms, if the index is valid
447 assert (index + _index_from) <= _index_to
448 return _items[index + _index_from]
449 end
450
451 redef fun substring(from: Int, count: Int): String
452 do
453 assert count >= 0
454
455 if from < 0 then
456 count += from
457 if count < 0 then count = 0
458 from = 0
459 end
460
461 var realFrom = _index_from + from
462
463 if (realFrom + count) > _index_to then return new String.with_infos(_items, _index_to - realFrom + 1, realFrom, _index_to)
464
465 if count == 0 then return ""
466
467 var to = realFrom + count - 1
468
469 return new String.with_infos(_items, to - realFrom + 1, realFrom, to)
470 end
471
472 redef fun substring_from(from: Int): String
473 do
474 if from > _length then return ""
475 if from < 0 then from = 0
476 return substring(from, _length)
477 end
478
479 redef fun has_substring(str: String, pos: Int): Bool
480 do
481 var itsindex = str._length - 1
482
483 var myindex = pos + itsindex
484 var myitems = _items
485
486 var itsitems = str._items
487
488 if myindex > _length or itsindex > myindex then return false
489
490 var itsindexfrom = str.index_from
491 itsindex += itsindexfrom
492 myindex += index_from
493
494 while itsindex >= itsindexfrom do
495 if myitems[myindex] != itsitems[itsindex] then return false
496 myindex -= 1
497 itsindex -= 1
498 end
499
500 return true
501 end
502
503 redef fun to_upper: String
504 do
505 var outstr = calloc_string(self._length + 1)
506 var out_index = 0
507
508 var myitems = self._items
509 var index_from = self._index_from
510 var max = self._index_to
511
512 while index_from <= max do
513 outstr[out_index] = myitems[index_from].to_upper
514 out_index += 1
515 index_from += 1
516 end
517
518 outstr[self.length] = '\0'
519
520 return outstr.to_s_with_length(self._length)
521 end
522
523 redef fun to_lower : String
524 do
525 var outstr = calloc_string(self._length + 1)
526 var out_index = 0
527
528 var myitems = self._items
529 var index_from = self._index_from
530 var max = self._index_to
531
532 while index_from <= max do
533 outstr[out_index] = myitems[index_from].to_lower
534 out_index += 1
535 index_from += 1
536 end
537
538 outstr[self.length] = '\0'
539
540 return outstr.to_s_with_length(self._length)
541 end
542
543 redef fun trim: String
544 do
545 if self._length == 0 then return self
546 # find position of the first non white space char (ascii < 32) from the start of the string
547 var start_pos = self._index_from
548 while _items[start_pos].ascii <= 32 do
549 start_pos += 1
550 if start_pos == _index_to + 1 then return ""
551 end
552 # find position of the first non white space char from the end of the string
553 var end_pos = _index_to
554 while _items[end_pos].ascii <= 32 do
555 end_pos -= 1
556 if end_pos == start_pos then return _items[start_pos].to_s
557 end
558 start_pos -= index_from
559 end_pos -= index_from
560 return self.substring(start_pos, end_pos - start_pos + 1)
561 end
562
563 redef fun output
564 do
565 var i = self._index_from
566 var imax = self._index_to
567 while i <= imax do
568 _items[i].output
569 i += 1
570 end
571 end
572
573 ##################################################
574 # String Specific Methods #
575 ##################################################
576
577 private init with_infos(items: NativeString, len: Int, from: Int, to: Int)
578 do
579 self._items = items
580 _length = len
581 _index_from = from
582 _index_to = to
583 end
584
585 # Return a null terminated char *
586 fun to_cstring: NativeString
587 do
588 if _index_from > 0 or _index_to != items.cstring_length - 1 then
589 var newItems = calloc_string(_length + 1)
590 self.items.copy_to(newItems, _length, _index_from, 0)
591 newItems[length] = '\0'
592 return newItems
593 end
594 return _items
595 end
596
597 redef fun ==(other)
598 do
599 if not other isa String then return false
600
601 if self.object_id == other.object_id then return true
602
603 var my_length = _length
604
605 if other._length != my_length then return false
606
607 var my_index = _index_from
608 var its_index = other._index_from
609
610 var last_iteration = my_index + my_length
611
612 var itsitems = other._items
613 var myitems = self._items
614
615 while my_index < last_iteration do
616 if myitems[my_index] != itsitems[its_index] then return false
617 my_index += 1
618 its_index += 1
619 end
620
621 return true
622 end
623
624 # The comparison between two strings is done on a lexicographical basis
625 #
626 # assert ("aa" < "b") == true
627 redef fun <(other)
628 do
629 if self.object_id == other.object_id then return false
630
631 var my_curr_char : Char
632 var its_curr_char : Char
633
634 var curr_id_self = self._index_from
635 var curr_id_other = other._index_from
636
637 var my_items = self._items
638 var its_items = other._items
639
640 var my_length = self._length
641 var its_length = other._length
642
643 var max_iterations = curr_id_self + my_length
644
645 while curr_id_self < max_iterations do
646 my_curr_char = my_items[curr_id_self]
647 its_curr_char = its_items[curr_id_other]
648
649 if my_curr_char != its_curr_char then
650 if my_curr_char < its_curr_char then return true
651 return false
652 end
653
654 curr_id_self += 1
655 curr_id_other += 1
656 end
657
658 return my_length < its_length
659 end
660
661 # The concatenation of `self` with `s`
662 #
663 # assert "hello " + "world!" == "hello world!"
664 fun +(s: String): String
665 do
666 var my_length = self._length
667 var its_length = s._length
668
669 var total_length = my_length + its_length
670
671 var target_string = calloc_string(my_length + its_length + 1)
672
673 self._items.copy_to(target_string, my_length, _index_from, 0)
674 s._items.copy_to(target_string, its_length, s._index_from, my_length)
675
676 target_string[total_length] = '\0'
677
678 return target_string.to_s_with_length(total_length)
679 end
680
681 # `i` repetitions of `self`
682 #
683 # assert "abc"*3 == "abcabcabc"
684 # assert "abc"*1 == "abc"
685 # assert "abc"*0 == ""
686 fun *(i: Int): String
687 do
688 assert i >= 0
689
690 var my_length = self._length
691
692 var final_length = my_length * i
693
694 var my_items = self._items
695
696 var target_string = calloc_string((final_length) + 1)
697
698 target_string[final_length] = '\0'
699
700 var current_last = 0
701
702 for iteration in [1 .. i] do
703 my_items.copy_to(target_string, my_length, 0, current_last)
704 current_last += my_length
705 end
706
707 return target_string.to_s_with_length(final_length)
708 end
709
710 redef fun to_s do return self
711
712 redef fun hash
713 do
714 # djb2 hash algorythm
715 var h = 5381
716 var i = _length - 1
717
718 var myitems = _items
719 var strStart = _index_from
720
721 i += strStart
722
723 while i >= strStart do
724 h = (h * 32) + h + self._items[i].ascii
725 i -= 1
726 end
727
728 return h
729 end
730 end
731
732 private class FlatStringReverseIterator
733 super IndexedIterator[Char]
734
735 var target: String
736
737 var target_items: NativeString
738
739 var curr_pos: Int
740
741 init with_pos(tgt: String, pos: Int)
742 do
743 target = tgt
744 target_items = tgt.items
745 curr_pos = pos + tgt.index_from
746 end
747
748 redef fun is_ok do return curr_pos >= 0
749
750 redef fun item do return target_items[curr_pos]
751
752 redef fun next do curr_pos -= 1
753
754 redef fun index do return curr_pos - target.index_from
755
756 end
757
758 private class FlatStringIterator
759 super IndexedIterator[Char]
760
761 var target: String
762
763 var target_items: NativeString
764
765 var curr_pos: Int
766
767 init with_pos(tgt: String, pos: Int)
768 do
769 target = tgt
770 target_items = tgt.items
771 curr_pos = pos + target.index_from
772 end
773
774 redef fun is_ok do return curr_pos <= target.index_to
775
776 redef fun item do return target_items[curr_pos]
777
778 redef fun next do curr_pos += 1
779
780 redef fun index do return curr_pos - target.index_from
781
782 end
783
784 private class FlatStringCharView
785 super StringCharView
786
787 redef type SELFTYPE: String
788
789 redef fun [](index)
790 do
791 # Check that the index (+ index_from) is not larger than indexTo
792 # In other terms, if the index is valid
793 assert index >= 0
794 assert (index + target._index_from) <= target._index_to
795 return target._items[index + target._index_from]
796 end
797
798 redef fun iterator_from(start) do return new FlatStringIterator.with_pos(target, start)
799
800 redef fun reverse_iterator_from(start) do return new FlatStringReverseIterator.with_pos(target, start)
801
802 end
803
804 # Mutable strings of characters.
805 class Buffer
806 super AbstractString
807 super Comparable
808 super StringCapable
809 super AbstractArray[Char]
810
811 redef type OTHER: String
812
813 redef var chars: BufferCharView = new FlatBufferCharView(self)
814
815 redef fun []=(index, item)
816 do
817 if index == length then
818 add(item)
819 return
820 end
821 assert index >= 0 and index < length
822 _items[index] = item
823 end
824
825 redef fun add(c)
826 do
827 if _capacity <= length then enlarge(length + 5)
828 _items[length] = c
829 _length += 1
830 end
831
832 redef fun enlarge(cap)
833 do
834 var c = _capacity
835 if cap <= c then return
836 while c <= cap do c = c * 2 + 2
837 var a = calloc_string(c+1)
838 _items.copy_to(a, length, 0, 0)
839 _items = a
840 _capacity = c
841 end
842
843 redef fun append(s)
844 do
845 if s isa String then
846 var sl = s.length
847 if _capacity < _length + sl then enlarge(_length + sl)
848 s.items.copy_to(_items, sl, s._index_from, _length)
849 _length += sl
850 else
851 super
852 end
853 end
854
855 redef fun to_s: String
856 do
857 var l = length
858 var a = calloc_string(l+1)
859 _items.copy_to(a, l, 0, 0)
860
861 # Ensure the afterlast byte is '\0' to nul-terminated char *
862 a[length] = '\0'
863
864 return a.to_s_with_length(length)
865 end
866
867 redef fun <(s)
868 do
869 var i = 0
870 var l1 = length
871 var l2 = s.length
872 while i < l1 and i < l2 do
873 var c1 = self.chars[i].ascii
874 var c2 = s.chars[i].ascii
875 if c1 < c2 then
876 return true
877 else if c2 < c1 then
878 return false
879 end
880 i += 1
881 end
882 if l1 < l2 then
883 return true
884 else
885 return false
886 end
887 end
888
889 # Create a new empty string.
890 init
891 do
892 with_capacity(5)
893 end
894
895 init from(s: String)
896 do
897 _capacity = s.length + 1
898 _length = s.length
899 _items = calloc_string(_capacity)
900 s.items.copy_to(_items, _length, s._index_from, 0)
901 end
902
903 # Create a new empty string with a given capacity.
904 init with_capacity(cap: Int)
905 do
906 assert cap >= 0
907 # _items = new NativeString.calloc(cap)
908 _items = calloc_string(cap+1)
909 _capacity = cap
910 _length = 0
911 end
912
913 redef fun ==(o)
914 do
915 if not o isa Buffer then return false
916 var l = length
917 if o.length != l then return false
918 var i = 0
919 var it = _items
920 var oit = o._items
921 while i < l do
922 if it[i] != oit[i] then return false
923 i += 1
924 end
925 return true
926 end
927
928 readable private var _capacity: Int
929
930 redef fun substring(from, count)
931 do
932 assert count >= 0
933 count += from
934 if from < 0 then from = 0
935 if count > length then count = length
936 if from < count then
937 var r = new Buffer.with_capacity(count - from)
938 while from < count do
939 r.chars.push(_items[from])
940 from += 1
941 end
942 return r.to_s
943 else
944 return ""
945 end
946 end
947
948 end
949
950 private class FlatBufferReverseIterator
951 super IndexedIterator[Char]
952
953 var target: Buffer
954
955 var target_items: NativeString
956
957 var curr_pos: Int
958
959 init with_pos(tgt: Buffer, pos: Int)
960 do
961 target = tgt
962 target_items = tgt.items
963 curr_pos = pos
964 end
965
966 redef fun index do return curr_pos
967
968 redef fun is_ok do return curr_pos >= 0
969
970 redef fun item do return target_items[curr_pos]
971
972 redef fun next do curr_pos -= 1
973
974 end
975
976 private class FlatBufferCharView
977 super BufferCharView
978 super StringCapable
979
980 redef type SELFTYPE: Buffer
981
982 redef fun [](index) do return target._items[index]
983
984 redef fun []=(index, item)
985 do
986 assert index >= 0 and index <= length
987 if index == length then
988 add(item)
989 return
990 end
991 target._items[index] = item
992 end
993
994 redef fun push(c)
995 do
996 target.add(c)
997 end
998
999 redef fun add(c)
1000 do
1001 target.add(c)
1002 end
1003
1004 fun enlarge(cap: Int)
1005 do
1006 target.enlarge(cap)
1007 end
1008
1009 redef fun append(s)
1010 do
1011 var my_items = target.items
1012 var s_length = s.length
1013 if target.capacity < s.length then enlarge(s_length + target.length)
1014 end
1015
1016 redef fun iterator_from(pos) do return new FlatBufferIterator.with_pos(target, pos)
1017
1018 redef fun reverse_iterator_from(pos) do return new FlatBufferReverseIterator.with_pos(target, pos)
1019
1020 end
1021
1022 private class FlatBufferIterator
1023 super IndexedIterator[Char]
1024
1025 var target: Buffer
1026
1027 var target_items: NativeString
1028
1029 var curr_pos: Int
1030
1031 init with_pos(tgt: Buffer, pos: Int)
1032 do
1033 target = tgt
1034 target_items = tgt.items
1035 curr_pos = pos
1036 end
1037
1038 redef fun index do return curr_pos
1039
1040 redef fun is_ok do return curr_pos < target.length
1041
1042 redef fun item do return target_items[curr_pos]
1043
1044 redef fun next do curr_pos += 1
1045
1046 end
1047
1048 ###############################################################################
1049 # Refinement #
1050 ###############################################################################
1051
1052 redef class Object
1053 # User readable representation of `self`.
1054 fun to_s: String do return inspect
1055
1056 # The class name of the object in NativeString format.
1057 private fun native_class_name: NativeString is intern
1058
1059 # The class name of the object.
1060 #
1061 # assert 5.class_name == "Int"
1062 fun class_name: String do return native_class_name.to_s
1063
1064 # Developer readable representation of `self`.
1065 # Usually, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
1066 fun inspect: String
1067 do
1068 return "<{inspect_head}>"
1069 end
1070
1071 # Return "CLASSNAME:#OBJECTID".
1072 # This function is mainly used with the redefinition of the inspect method
1073 protected fun inspect_head: String
1074 do
1075 return "{class_name}:#{object_id.to_hex}"
1076 end
1077
1078 protected fun args: Sequence[String]
1079 do
1080 return sys.args
1081 end
1082 end
1083
1084 redef class Bool
1085 # assert true.to_s == "true"
1086 # assert false.to_s == "false"
1087 redef fun to_s
1088 do
1089 if self then
1090 return once "true"
1091 else
1092 return once "false"
1093 end
1094 end
1095 end
1096
1097 redef class Int
1098 # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
1099 # assume < to_c max const of char
1100 fun fill_buffer(s: Buffer, base: Int, signed: Bool)
1101 do
1102 var n: Int
1103 # Sign
1104 if self < 0 then
1105 n = - self
1106 s.chars[0] = '-'
1107 else if self == 0 then
1108 s.chars[0] = '0'
1109 return
1110 else
1111 n = self
1112 end
1113 # Fill digits
1114 var pos = digit_count(base) - 1
1115 while pos >= 0 and n > 0 do
1116 s.chars[pos] = (n % base).to_c
1117 n = n / base # /
1118 pos -= 1
1119 end
1120 end
1121
1122 # C function to convert an nit Int to a NativeString (char*)
1123 private fun native_int_to_s(len: Int): NativeString is extern "native_int_to_s"
1124
1125 # return displayable int in base 10 and signed
1126 #
1127 # assert 1.to_s == "1"
1128 # assert (-123).to_s == "-123"
1129 redef fun to_s do
1130 var len = digit_count(10)
1131 return native_int_to_s(len).to_s_with_length(len)
1132 end
1133
1134 # return displayable int in hexadecimal (unsigned (not now))
1135 fun to_hex: String do return to_base(16,false)
1136
1137 # return displayable int in base base and signed
1138 fun to_base(base: Int, signed: Bool): String
1139 do
1140 var l = digit_count(base)
1141 var s = new Buffer.from(" " * l)
1142 fill_buffer(s, base, signed)
1143 return s.to_s
1144 end
1145 end
1146
1147 redef class Float
1148 # Pretty print self, print needoed decimals up to a max of 3.
1149 redef fun to_s do
1150 var str = to_precision( 3 )
1151 if is_inf != 0 or is_nan then return str
1152 var len = str.length
1153 for i in [0..len-1] do
1154 var j = len-1-i
1155 var c = str.chars[j]
1156 if c == '0' then
1157 continue
1158 else if c == '.' then
1159 return str.substring( 0, j+2 )
1160 else
1161 return str.substring( 0, j+1 )
1162 end
1163 end
1164 return str
1165 end
1166
1167 # `self` representation with `nb` digits after the '.'.
1168 fun to_precision(nb: Int): String
1169 do
1170 if is_nan then return "nan"
1171
1172 var isinf = self.is_inf
1173 if isinf == 1 then
1174 return "inf"
1175 else if isinf == -1 then
1176 return "-inf"
1177 end
1178
1179 if nb == 0 then return self.to_i.to_s
1180 var f = self
1181 for i in [0..nb[ do f = f * 10.0
1182 if self > 0.0 then
1183 f = f + 0.5
1184 else
1185 f = f - 0.5
1186 end
1187 var i = f.to_i
1188 if i == 0 then return "0.0"
1189 var s = i.to_s
1190 var sl = s.length
1191 if sl > nb then
1192 var p1 = s.substring(0, s.length-nb)
1193 var p2 = s.substring(s.length-nb, nb)
1194 return p1 + "." + p2
1195 else
1196 return "0." + ("0"*(nb-sl)) + s
1197 end
1198 end
1199
1200 fun to_precision_native(nb: Int): String import NativeString.to_s `{
1201 int size;
1202 char *str;
1203
1204 size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
1205 str = malloc(size + 1);
1206 sprintf(str, "%.*f", (int)nb, recv );
1207
1208 return NativeString_to_s( str );
1209 `}
1210 end
1211
1212 redef class Char
1213 # assert 'x'.to_s == "x"
1214 redef fun to_s
1215 do
1216 var s = new Buffer.with_capacity(1)
1217 s.chars[0] = self
1218 return s.to_s
1219 end
1220
1221 # Returns true if the char is a numerical digit
1222 fun is_numeric: Bool
1223 do
1224 if self >= '0' and self <= '9'
1225 then
1226 return true
1227 end
1228 return false
1229 end
1230
1231 # Returns true if the char is an alpha digit
1232 fun is_alpha: Bool
1233 do
1234 if (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z') then return true
1235 return false
1236 end
1237
1238 # Returns true if the char is an alpha or a numeric digit
1239 fun is_alphanumeric: Bool
1240 do
1241 if self.is_numeric or self.is_alpha then return true
1242 return false
1243 end
1244 end
1245
1246 redef class Collection[E]
1247 # Concatenate elements.
1248 redef fun to_s
1249 do
1250 var s = new Buffer
1251 for e in self do if e != null then s.append(e.to_s)
1252 return s.to_s
1253 end
1254
1255 # Concatenate and separate each elements with `sep`.
1256 #
1257 # assert [1, 2, 3].join(":") == "1:2:3"
1258 # assert [1..3].join(":") == "1:2:3"
1259 fun join(sep: String): String
1260 do
1261 if is_empty then return ""
1262
1263 var s = new Buffer # Result
1264
1265 # Concat first item
1266 var i = iterator
1267 var e = i.item
1268 if e != null then s.append(e.to_s)
1269
1270 # Concat other items
1271 i.next
1272 while i.is_ok do
1273 s.append(sep)
1274 e = i.item
1275 if e != null then s.append(e.to_s)
1276 i.next
1277 end
1278 return s.to_s
1279 end
1280 end
1281
1282 redef class Array[E]
1283 # Fast implementation
1284 redef fun to_s
1285 do
1286 var s = new Buffer
1287 var i = 0
1288 var l = length
1289 while i < l do
1290 var e = self[i]
1291 if e != null then s.append(e.to_s)
1292 i += 1
1293 end
1294 return s.to_s
1295 end
1296 end
1297
1298 redef class Map[K,V]
1299 # Concatenate couple of 'key value'.
1300 # key and value are separated by `couple_sep`.
1301 # each couple is separated each couple with `sep`.
1302 #
1303 # var m = new ArrayMap[Int, String]
1304 # m[1] = "one"
1305 # m[10] = "ten"
1306 # assert m.join("; ", "=") == "1=one; 10=ten"
1307 fun join(sep: String, couple_sep: String): String
1308 do
1309 if is_empty then return ""
1310
1311 var s = new Buffer # Result
1312
1313 # Concat first item
1314 var i = iterator
1315 var k = i.key
1316 var e = i.item
1317 s.append("{k}{couple_sep}{e or else "<null>"}")
1318
1319 # Concat other items
1320 i.next
1321 while i.is_ok do
1322 s.append(sep)
1323 k = i.key
1324 e = i.item
1325 s.append("{k}{couple_sep}{e or else "<null>"}")
1326 i.next
1327 end
1328 return s.to_s
1329 end
1330 end
1331
1332 ###############################################################################
1333 # Native classes #
1334 ###############################################################################
1335
1336 # Native strings are simple C char *
1337 class NativeString
1338 super StringCapable
1339
1340 fun [](index: Int): Char is intern
1341 fun []=(index: Int, item: Char) is intern
1342 fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
1343
1344 # Position of the first nul character.
1345 fun cstring_length: Int
1346 do
1347 var l = 0
1348 while self[l] != '\0' do l += 1
1349 return l
1350 end
1351 fun atoi: Int is intern
1352 fun atof: Float is extern "atof"
1353
1354 redef fun to_s
1355 do
1356 return to_s_with_length(cstring_length)
1357 end
1358
1359 fun to_s_with_length(length: Int): String
1360 do
1361 assert length >= 0
1362 return new String.with_infos(self, length, 0, length - 1)
1363 end
1364
1365 fun to_s_with_copy: String
1366 do
1367 var length = cstring_length
1368 var new_self = calloc_string(length + 1)
1369 copy_to(new_self, length, 0, 0)
1370 return new String.with_infos(new_self, length, 0, length - 1)
1371 end
1372
1373 end
1374
1375 # StringCapable objects can create native strings
1376 interface StringCapable
1377 protected fun calloc_string(size: Int): NativeString is intern
1378 end
1379
1380 redef class Sys
1381 var _args_cache: nullable Sequence[String]
1382
1383 redef fun args: Sequence[String]
1384 do
1385 if _args_cache == null then init_args
1386 return _args_cache.as(not null)
1387 end
1388
1389 # The name of the program as given by the OS
1390 fun program_name: String
1391 do
1392 return native_argv(0).to_s
1393 end
1394
1395 # Initialize `args` with the contents of `native_argc` and `native_argv`.
1396 private fun init_args
1397 do
1398 var argc = native_argc
1399 var args = new Array[String].with_capacity(0)
1400 var i = 1
1401 while i < argc do
1402 args[i-1] = native_argv(i).to_s
1403 i += 1
1404 end
1405 _args_cache = args
1406 end
1407
1408 # First argument of the main C function.
1409 private fun native_argc: Int is intern
1410
1411 # Second argument of the main C function.
1412 private fun native_argv(i: Int): NativeString is intern
1413 end
1414