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