Makefile: Document deeply-nested libraries.
[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 import collection
19 intrude import collection::array
20
21 `{
22 #include <stdio.h>
23 #include <string.h>
24 `}
25
26 ###############################################################################
27 # String #
28 ###############################################################################
29
30 # High-level abstraction for all text representations
31 abstract class Text
32 super Comparable
33 super StringCapable
34
35 redef type OTHER: Text
36
37 # Type of self (used for factorization of several methods, ex : substring_from, empty...)
38 type SELFTYPE: Text
39
40 # Gets a view on the chars of the Text object
41 #
42 # assert "hello".chars.to_a == ['h', 'e', 'l', 'l', 'o']
43 fun chars: SequenceRead[Char] is abstract
44
45 # Number of characters contained in self.
46 #
47 # assert "12345".length == 5
48 # assert "".length == 0
49 fun length: Int is abstract
50
51 # Create a substring.
52 #
53 # assert "abcd".substring(1, 2) == "bc"
54 # assert "abcd".substring(-1, 2) == "a"
55 # assert "abcd".substring(1, 0) == ""
56 # assert "abcd".substring(2, 5) == "cd"
57 #
58 # A `from` index < 0 will be replaced by 0.
59 # Unless a `count` value is > 0 at the same time.
60 # In this case, `from += count` and `count -= from`.
61 fun substring(from: Int, count: Int): SELFTYPE is abstract
62
63 # Iterates on the substrings of self if any
64 fun substrings: Iterator[Text] is abstract
65
66 # Is the current Text empty (== "")
67 #
68 # assert "".is_empty
69 # assert not "foo".is_empty
70 fun is_empty: Bool do return self.length == 0
71
72 # Returns an empty Text of the right type
73 #
74 # This method is used internally to get the right
75 # implementation of an empty string.
76 protected fun empty: SELFTYPE is abstract
77
78 # Gets the first char of the Text
79 #
80 # DEPRECATED : Use self.chars.first instead
81 fun first: Char do return self.chars[0]
82
83 # Access a character at `index` in the string.
84 #
85 # assert "abcd"[2] == 'c'
86 #
87 # DEPRECATED : Use self.chars.[] instead
88 fun [](index: Int): Char do return self.chars[index]
89
90 # Gets the index of the first occurence of 'c'
91 #
92 # Returns -1 if not found
93 #
94 # DEPRECATED : Use self.chars.index_of instead
95 fun index_of(c: Char): Int
96 do
97 return index_of_from(c, 0)
98 end
99
100 # Gets the last char of self
101 #
102 # DEPRECATED : Use self.chars.last instead
103 fun last: Char do return self.chars[length-1]
104
105 # Gets the index of the first occurence of ´c´ starting from ´pos´
106 #
107 # Returns -1 if not found
108 #
109 # DEPRECATED : Use self.chars.index_of_from instead
110 fun index_of_from(c: Char, pos: Int): Int
111 do
112 var iter = self.chars.iterator_from(pos)
113 while iter.is_ok do
114 if iter.item == c then return iter.index
115 iter.next
116 end
117 return -1
118 end
119
120 # Gets the last index of char ´c´
121 #
122 # Returns -1 if not found
123 #
124 # DEPRECATED : Use self.chars.last_index_of instead
125 fun last_index_of(c: Char): Int
126 do
127 return last_index_of_from(c, length - 1)
128 end
129
130 # Return a null terminated char *
131 fun to_cstring: NativeString do return flatten.to_cstring
132
133 # The index of the last occurrence of an element starting from pos (in reverse order).
134 #
135 # var s = "/etc/bin/test/test.nit"
136 # assert s.last_index_of_from('/', s.length-1) == 13
137 # assert s.last_index_of_from('/', 12) == 8
138 #
139 # Returns -1 if not found
140 #
141 # DEPRECATED : Use self.chars.last_index_of_from instead
142 fun last_index_of_from(item: Char, pos: Int): Int
143 do
144 var iter = self.chars.reverse_iterator_from(pos)
145 while iter.is_ok do
146 if iter.item == item then return iter.index
147 iter.next
148 end
149 return -1
150 end
151
152 # Gets an iterator on the chars of self
153 #
154 # DEPRECATED : Use self.chars.iterator instead
155 fun iterator: Iterator[Char]
156 do
157 return self.chars.iterator
158 end
159
160
161 # Gets an Array containing the chars of self
162 #
163 # DEPRECATED : Use self.chars.to_a instead
164 fun to_a: Array[Char] do return chars.to_a
165
166 # Create a substring from `self` beginning at the `from` position
167 #
168 # assert "abcd".substring_from(1) == "bcd"
169 # assert "abcd".substring_from(-1) == "abcd"
170 # assert "abcd".substring_from(2) == "cd"
171 #
172 # As with substring, a `from` index < 0 will be replaced by 0
173 fun substring_from(from: Int): SELFTYPE
174 do
175 if from >= self.length then return empty
176 if from < 0 then from = 0
177 return substring(from, length - from)
178 end
179
180 # Does self have a substring `str` starting from position `pos`?
181 #
182 # assert "abcd".has_substring("bc",1) == true
183 # assert "abcd".has_substring("bc",2) == false
184 fun has_substring(str: String, pos: Int): Bool
185 do
186 var myiter = self.chars.iterator_from(pos)
187 var itsiter = str.chars.iterator
188 while myiter.is_ok and itsiter.is_ok do
189 if myiter.item != itsiter.item then return false
190 myiter.next
191 itsiter.next
192 end
193 if itsiter.is_ok then return false
194 return true
195 end
196
197 # Is this string prefixed by `prefix`?
198 #
199 # assert "abcd".has_prefix("ab") == true
200 # assert "abcbc".has_prefix("bc") == false
201 # assert "ab".has_prefix("abcd") == false
202 fun has_prefix(prefix: String): Bool do return has_substring(prefix,0)
203
204 # Is this string suffixed by `suffix`?
205 #
206 # assert "abcd".has_suffix("abc") == false
207 # assert "abcd".has_suffix("bcd") == true
208 fun has_suffix(suffix: String): Bool do return has_substring(suffix, length - suffix.length)
209
210 # If `self` contains only digits, return the corresponding integer
211 #
212 # assert "123".to_i == 123
213 # assert "-1".to_i == -1
214 fun to_i: Int
215 do
216 # Shortcut
217 return to_s.to_cstring.atoi
218 end
219
220 # If `self` contains a float, return the corresponding float
221 #
222 # assert "123".to_f == 123.0
223 # assert "-1".to_f == -1.0
224 # assert "-1.2e-3".to_f == -0.0012
225 fun to_f: Float
226 do
227 # Shortcut
228 return to_s.to_cstring.atof
229 end
230
231 # If `self` contains only digits and alpha <= 'f', return the corresponding integer.
232 #
233 # assert "ff".to_hex == 255
234 fun to_hex: Int do return a_to(16)
235
236 # If `self` contains only digits and letters, return the corresponding integer in a given base
237 #
238 # assert "120".a_to(3) == 15
239 fun a_to(base: Int) : Int
240 do
241 var i = 0
242 var neg = false
243
244 for j in [0..length[ do
245 var c = chars[j]
246 var v = c.to_i
247 if v > base then
248 if neg then
249 return -i
250 else
251 return i
252 end
253 else if v < 0 then
254 neg = true
255 else
256 i = i * base + v
257 end
258 end
259 if neg then
260 return -i
261 else
262 return i
263 end
264 end
265
266 # Returns `true` if the string contains only Numeric values (and one "," or one "." character)
267 #
268 # assert "123".is_numeric == true
269 # assert "1.2".is_numeric == true
270 # assert "1,2".is_numeric == true
271 # assert "1..2".is_numeric == false
272 fun is_numeric: Bool
273 do
274 var has_point_or_comma = false
275 for i in [0..length[ do
276 var c = chars[i]
277 if not c.is_numeric then
278 if (c == '.' or c == ',') and not has_point_or_comma then
279 has_point_or_comma = true
280 else
281 return false
282 end
283 end
284 end
285 return true
286 end
287
288 # Returns `true` if the string contains only Hex chars
289 #
290 # assert "048bf".is_hex == true
291 # assert "ABCDEF".is_hex == true
292 # assert "0G".is_hex == false
293 fun is_hex: Bool
294 do
295 for i in [0..length[ do
296 var c = chars[i]
297 if not (c >= 'a' and c <= 'f') and
298 not (c >= 'A' and c <= 'F') and
299 not (c >= '0' and c <= '9') then return false
300 end
301 return true
302 end
303
304 # Are all letters in `self` upper-case ?
305 #
306 # assert "HELLO WORLD".is_upper == true
307 # assert "%$&%!".is_upper == true
308 # assert "hello world".is_upper == false
309 # assert "Hello World".is_upper == false
310 fun is_upper: Bool
311 do
312 for i in [0..length[ do
313 var char = chars[i]
314 if char.is_lower then return false
315 end
316 return true
317 end
318
319 # Are all letters in `self` lower-case ?
320 #
321 # assert "hello world".is_lower == true
322 # assert "%$&%!".is_lower == true
323 # assert "Hello World".is_lower == false
324 fun is_lower: Bool
325 do
326 for i in [0..length[ do
327 var char = chars[i]
328 if char.is_upper then return false
329 end
330 return true
331 end
332
333 # Removes the whitespaces at the beginning of self
334 #
335 # assert " \n\thello \n\t".l_trim == "hello \n\t"
336 #
337 # A whitespace is defined as any character which ascii value is less than or equal to 32
338 fun l_trim: SELFTYPE
339 do
340 var iter = self.chars.iterator
341 while iter.is_ok do
342 if iter.item.ascii > 32 then break
343 iter.next
344 end
345 if iter.index == length then return self.empty
346 return self.substring_from(iter.index)
347 end
348
349 # Removes the whitespaces at the end of self
350 #
351 # assert " \n\thello \n\t".r_trim == " \n\thello"
352 #
353 # A whitespace is defined as any character which ascii value is less than or equal to 32
354 fun r_trim: SELFTYPE
355 do
356 var iter = self.chars.reverse_iterator
357 while iter.is_ok do
358 if iter.item.ascii > 32 then break
359 iter.next
360 end
361 if iter.index < 0 then return self.empty
362 return self.substring(0, iter.index + 1)
363 end
364
365 # Trims trailing and preceding white spaces
366 # A whitespace is defined as any character which ascii value is less than or equal to 32
367 #
368 # assert " Hello World ! ".trim == "Hello World !"
369 # assert "\na\nb\tc\t".trim == "a\nb\tc"
370 fun trim: SELFTYPE do return (self.l_trim).r_trim
371
372 # Mangle a string to be a unique string only made of alphanumeric characters
373 fun to_cmangle: String
374 do
375 var res = new FlatBuffer
376 var underscore = false
377 for i in [0..length[ do
378 var c = chars[i]
379 if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
380 res.add(c)
381 underscore = false
382 continue
383 end
384 if underscore then
385 res.append('_'.ascii.to_s)
386 res.add('d')
387 end
388 if c >= '0' and c <= '9' then
389 res.add(c)
390 underscore = false
391 else if c == '_' then
392 res.add(c)
393 underscore = true
394 else
395 res.add('_')
396 res.append(c.ascii.to_s)
397 res.add('d')
398 underscore = false
399 end
400 end
401 return res.to_s
402 end
403
404 # Escape " \ ' and non printable characters using the rules of literal C strings and characters
405 #
406 # assert "abAB12<>&".escape_to_c == "abAB12<>&"
407 # assert "\n\"'\\".escape_to_c == "\\n\\\"\\'\\\\"
408 fun escape_to_c: String
409 do
410 var b = new FlatBuffer
411 for i in [0..length[ do
412 var c = chars[i]
413 if c == '\n' then
414 b.append("\\n")
415 else if c == '\0' then
416 b.append("\\0")
417 else if c == '"' then
418 b.append("\\\"")
419 else if c == '\'' then
420 b.append("\\\'")
421 else if c == '\\' then
422 b.append("\\\\")
423 else if c.ascii < 32 then
424 b.append("\\{c.ascii.to_base(8, false)}")
425 else
426 b.add(c)
427 end
428 end
429 return b.to_s
430 end
431
432 # Escape additionnal characters
433 # The result might no be legal in C but be used in other languages
434 #
435 # assert "ab|\{\}".escape_more_to_c("|\{\}") == "ab\\|\\\{\\\}"
436 fun escape_more_to_c(chars: String): String
437 do
438 var b = new FlatBuffer
439 for c in escape_to_c.chars do
440 if chars.chars.has(c) then
441 b.add('\\')
442 end
443 b.add(c)
444 end
445 return b.to_s
446 end
447
448 # Escape to C plus braces
449 #
450 # assert "\n\"'\\\{\}".escape_to_nit == "\\n\\\"\\'\\\\\\\{\\\}"
451 fun escape_to_nit: String do return escape_more_to_c("\{\}")
452
453 # Return a string where Nit escape sequences are transformed.
454 #
455 # var s = "\\n"
456 # assert s.length == 2
457 # var u = s.unescape_nit
458 # assert u.length == 1
459 # assert u.chars[0].ascii == 10 # (the ASCII value of the "new line" character)
460 fun unescape_nit: String
461 do
462 var res = new FlatBuffer.with_capacity(self.length)
463 var was_slash = false
464 for i in [0..length[ do
465 var c = chars[i]
466 if not was_slash then
467 if c == '\\' then
468 was_slash = true
469 else
470 res.add(c)
471 end
472 continue
473 end
474 was_slash = false
475 if c == 'n' then
476 res.add('\n')
477 else if c == 'r' then
478 res.add('\r')
479 else if c == 't' then
480 res.add('\t')
481 else if c == '0' then
482 res.add('\0')
483 else
484 res.add(c)
485 end
486 end
487 return res.to_s
488 end
489
490 # Encode `self` to percent (or URL) encoding
491 #
492 # assert "aBc09-._~".to_percent_encoding == "aBc09-._~"
493 # assert "%()< >".to_percent_encoding == "%25%28%29%3c%20%3e"
494 # assert ".com/post?e=asdf&f=123".to_percent_encoding == ".com%2fpost%3fe%3dasdf%26f%3d123"
495 fun to_percent_encoding: String
496 do
497 var buf = new FlatBuffer
498
499 for i in [0..length[ do
500 var c = chars[i]
501 if (c >= '0' and c <= '9') or
502 (c >= 'a' and c <= 'z') or
503 (c >= 'A' and c <= 'Z') or
504 c == '-' or c == '.' or
505 c == '_' or c == '~'
506 then
507 buf.add c
508 else buf.append "%{c.ascii.to_hex}"
509 end
510
511 return buf.to_s
512 end
513
514 # Decode `self` from percent (or URL) encoding to a clear string
515 #
516 # Replace invalid use of '%' with '?'.
517 #
518 # assert "aBc09-._~".from_percent_encoding == "aBc09-._~"
519 # assert "%25%28%29%3c%20%3e".from_percent_encoding == "%()< >"
520 # assert ".com%2fpost%3fe%3dasdf%26f%3d123".from_percent_encoding == ".com/post?e=asdf&f=123"
521 # assert "%25%28%29%3C%20%3E".from_percent_encoding == "%()< >"
522 # assert "incomplete %".from_percent_encoding == "incomplete ?"
523 # assert "invalid % usage".from_percent_encoding == "invalid ? usage"
524 fun from_percent_encoding: String
525 do
526 var buf = new FlatBuffer
527
528 var i = 0
529 while i < length do
530 var c = chars[i]
531 if c == '%' then
532 if i + 2 >= length then
533 # What follows % has been cut off
534 buf.add '?'
535 else
536 i += 1
537 var hex_s = substring(i, 2)
538 if hex_s.is_hex then
539 var hex_i = hex_s.to_hex
540 buf.add hex_i.ascii
541 i += 1
542 else
543 # What follows a % is not Hex
544 buf.add '?'
545 i -= 1
546 end
547 end
548 else buf.add c
549
550 i += 1
551 end
552
553 return buf.to_s
554 end
555
556 # Escape the four characters `<`, `>`, `&`, and `"` with their html counterpart
557 #
558 # assert "a&b->\"x\"".html_escape == "a&amp;b-&gt;&quot;x&quot;"
559 fun html_escape: SELFTYPE
560 do
561 var buf = new FlatBuffer
562
563 for i in [0..length[ do
564 var c = chars[i]
565 if c == '&' then
566 buf.append "&amp;"
567 else if c == '<' then
568 buf.append "&lt;"
569 else if c == '>' then
570 buf.append "&gt;"
571 else if c == '"' then
572 buf.append "&quot;"
573 else buf.add c
574 end
575
576 return buf.to_s
577 end
578
579 # Equality of text
580 # Two pieces of text are equals if thez have the same characters in the same order.
581 #
582 # assert "hello" == "hello"
583 # assert "hello" != "HELLO"
584 # assert "hello" == "hel"+"lo"
585 #
586 # Things that are not Text are not equal.
587 #
588 # assert "9" != '9'
589 # assert "9" != ['9']
590 # assert "9" != 9
591 #
592 # assert "9".chars.first == '9' # equality of Char
593 # assert "9".chars == ['9'] # equality of Sequence
594 # assert "9".to_i == 9 # equality of Int
595 redef fun ==(o)
596 do
597 if o == null then return false
598 if not o isa Text then return false
599 if self.is_same_instance(o) then return true
600 if self.length != o.length then return false
601 return self.chars == o.chars
602 end
603
604 # Lexicographical comparaison
605 #
606 # assert "abc" < "xy"
607 # assert "ABC" < "abc"
608 redef fun <(other)
609 do
610 var self_chars = self.chars.iterator
611 var other_chars = other.chars.iterator
612
613 while self_chars.is_ok and other_chars.is_ok do
614 if self_chars.item < other_chars.item then return true
615 if self_chars.item > other_chars.item then return false
616 self_chars.next
617 other_chars.next
618 end
619
620 if self_chars.is_ok then
621 return false
622 else
623 return true
624 end
625 end
626
627 # Flat representation of self
628 fun flatten: FlatText is abstract
629
630 private var hash_cache: nullable Int = null
631
632 redef fun hash
633 do
634 if hash_cache == null then
635 # djb2 hash algorithm
636 var h = 5381
637
638 for i in [0..length[ do
639 var char = chars[i]
640 h = h.lshift(5) + h + char.ascii
641 end
642
643 hash_cache = h
644 end
645 return hash_cache.as(not null)
646 end
647
648 end
649
650 # All kinds of array-based text representations.
651 abstract class FlatText
652 super Text
653
654 private var items: NativeString
655
656 # Real items, used as cache for to_cstring is called
657 private var real_items: nullable NativeString = null
658
659 redef var length: Int = 0
660
661 init do end
662
663 redef fun output
664 do
665 var i = 0
666 while i < length do
667 items[i].output
668 i += 1
669 end
670 end
671
672 redef fun flatten do return self
673 end
674
675 # Abstract class for the SequenceRead compatible
676 # views on String and Buffer objects
677 private abstract class StringCharView
678 super SequenceRead[Char]
679
680 type SELFTYPE: Text
681
682 private var target: SELFTYPE
683
684 private init(tgt: SELFTYPE)
685 do
686 target = tgt
687 end
688
689 redef fun is_empty do return target.is_empty
690
691 redef fun length do return target.length
692
693 redef fun iterator: IndexedIterator[Char] do return self.iterator_from(0)
694
695 redef fun reverse_iterator do return self.reverse_iterator_from(self.length - 1)
696 end
697
698 # View on Buffer objects, extends Sequence
699 # for mutation operations
700 private abstract class BufferCharView
701 super StringCharView
702 super Sequence[Char]
703
704 redef type SELFTYPE: Buffer
705
706 end
707
708 abstract class String
709 super Text
710
711 redef type SELFTYPE: String
712
713 redef fun to_s do return self
714
715 # Concatenates `o` to `self`
716 #
717 # assert "hello" + "world" == "helloworld"
718 # assert "" + "hello" + "" == "hello"
719 fun +(o: Text): SELFTYPE is abstract
720
721 # Concatenates self `i` times
722 #
723 # assert "abc" * 4 == "abcabcabcabc"
724 # assert "abc" * 1 == "abc"
725 # assert "abc" * 0 == ""
726 fun *(i: Int): SELFTYPE is abstract
727
728 fun insert_at(s: String, pos: Int): SELFTYPE is abstract
729
730 # Returns a reversed version of self
731 #
732 # assert "hello".reversed == "olleh"
733 # assert "bob".reversed == "bob"
734 # assert "".reversed == ""
735 fun reversed: SELFTYPE is abstract
736
737 # A upper case version of `self`
738 #
739 # assert "Hello World!".to_upper == "HELLO WORLD!"
740 fun to_upper: SELFTYPE is abstract
741
742 # A lower case version of `self`
743 #
744 # assert "Hello World!".to_lower == "hello world!"
745 fun to_lower : SELFTYPE is abstract
746
747 # Takes a camel case `self` and converts it to snake case
748 #
749 # assert "randomMethodId".to_snake_case == "random_method_id"
750 #
751 # If `self` is upper, it is returned unchanged
752 #
753 # assert "RANDOM_METHOD_ID".to_snake_case == "RANDOM_METHOD_ID"
754 #
755 # If the identifier is prefixed by an underscore, the underscore is ignored
756 #
757 # assert "_privateField".to_snake_case == "_private_field"
758 fun to_snake_case: SELFTYPE
759 do
760 if self.is_upper then return self
761
762 var new_str = new FlatBuffer.with_capacity(self.length)
763 var is_first_char = true
764
765 for i in [0..length[ do
766 var char = chars[i]
767 if is_first_char then
768 new_str.add(char.to_lower)
769 is_first_char = false
770 else if char.is_upper then
771 new_str.add('_')
772 new_str.add(char.to_lower)
773 else
774 new_str.add(char)
775 end
776 end
777
778 return new_str.to_s
779 end
780
781 # Takes a snake case `self` and converts it to camel case
782 #
783 # assert "random_method_id".to_camel_case == "randomMethodId"
784 #
785 # If the identifier is prefixed by an underscore, the underscore is ignored
786 #
787 # assert "_private_field".to_camel_case == "_privateField"
788 #
789 # If `self` is upper, it is returned unchanged
790 #
791 # assert "RANDOM_ID".to_camel_case == "RANDOM_ID"
792 #
793 # If there are several consecutive underscores, they are considered as a single one
794 #
795 # assert "random__method_id".to_camel_case == "randomMethodId"
796 fun to_camel_case: SELFTYPE
797 do
798 if self.is_upper then return self
799
800 var new_str = new FlatBuffer
801 var is_first_char = true
802 var follows_us = false
803
804 for i in [0..length[ do
805 var char = chars[i]
806 if is_first_char then
807 new_str.add(char)
808 is_first_char = false
809 else if char == '_' then
810 follows_us = true
811 else if follows_us then
812 new_str.add(char.to_upper)
813 follows_us = false
814 else
815 new_str.add(char)
816 end
817 end
818
819 return new_str.to_s
820 end
821
822 # Returns a capitalized `self`
823 #
824 # Letters that follow a letter are lowercased
825 # Letters that follow a non-letter are upcased.
826 #
827 # SEE : `Char::is_letter` for the definition of letter.
828 #
829 # assert "jAVASCRIPT".capitalized == "Javascript"
830 # assert "i am root".capitalized == "I Am Root"
831 # assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
832 fun capitalized: SELFTYPE do
833 if length == 0 then return self
834
835 var buf = new FlatBuffer.with_capacity(length)
836
837 var curr = chars[0].to_upper
838 var prev = curr
839 buf[0] = curr
840
841 for i in [1 .. length[ do
842 prev = curr
843 curr = self[i]
844 if prev.is_letter then
845 buf[i] = curr.to_lower
846 else
847 buf[i] = curr.to_upper
848 end
849 end
850
851 return buf.to_s
852 end
853 end
854
855 private class FlatSubstringsIter
856 super Iterator[FlatText]
857
858 var tgt: nullable FlatText
859
860 init(tgt: FlatText) do self.tgt = tgt
861
862 redef fun item do
863 assert is_ok
864 return tgt.as(not null)
865 end
866
867 redef fun is_ok do return tgt != null
868
869 redef fun next do tgt = null
870 end
871
872 # Immutable strings of characters.
873 class FlatString
874 super FlatText
875 super String
876
877 # Index in _items of the start of the string
878 private var index_from: Int
879
880 # Indes in _items of the last item of the string
881 private var index_to: Int
882
883 redef var chars: SequenceRead[Char] = new FlatStringCharView(self)
884
885 redef fun [](index)
886 do
887 # Check that the index (+ index_from) is not larger than indexTo
888 # In other terms, if the index is valid
889 assert index >= 0
890 assert (index + index_from) <= index_to
891 return items[index + index_from]
892 end
893
894 ################################################
895 # AbstractString specific methods #
896 ################################################
897
898 redef fun reversed
899 do
900 var native = calloc_string(self.length + 1)
901 var length = self.length
902 var items = self.items
903 var pos = 0
904 var ipos = length-1
905 while pos < length do
906 native[pos] = items[ipos]
907 pos += 1
908 ipos -= 1
909 end
910 return native.to_s_with_length(self.length)
911 end
912
913 redef fun substring(from, count)
914 do
915 assert count >= 0
916
917 if from < 0 then
918 count += from
919 if count < 0 then count = 0
920 from = 0
921 end
922
923 var realFrom = index_from + from
924
925 if (realFrom + count) > index_to then return new FlatString.with_infos(items, index_to - realFrom + 1, realFrom, index_to)
926
927 if count == 0 then return empty
928
929 var to = realFrom + count - 1
930
931 return new FlatString.with_infos(items, to - realFrom + 1, realFrom, to)
932 end
933
934 redef fun empty do return "".as(FlatString)
935
936 redef fun to_upper
937 do
938 var outstr = calloc_string(self.length + 1)
939 var out_index = 0
940
941 var myitems = self.items
942 var index_from = self.index_from
943 var max = self.index_to
944
945 while index_from <= max do
946 outstr[out_index] = myitems[index_from].to_upper
947 out_index += 1
948 index_from += 1
949 end
950
951 outstr[self.length] = '\0'
952
953 return outstr.to_s_with_length(self.length)
954 end
955
956 redef fun to_lower
957 do
958 var outstr = calloc_string(self.length + 1)
959 var out_index = 0
960
961 var myitems = self.items
962 var index_from = self.index_from
963 var max = self.index_to
964
965 while index_from <= max do
966 outstr[out_index] = myitems[index_from].to_lower
967 out_index += 1
968 index_from += 1
969 end
970
971 outstr[self.length] = '\0'
972
973 return outstr.to_s_with_length(self.length)
974 end
975
976 redef fun output
977 do
978 var i = self.index_from
979 var imax = self.index_to
980 while i <= imax do
981 items[i].output
982 i += 1
983 end
984 end
985
986 ##################################################
987 # String Specific Methods #
988 ##################################################
989
990 private init with_infos(items: NativeString, len: Int, from: Int, to: Int)
991 do
992 self.items = items
993 length = len
994 index_from = from
995 index_to = to
996 end
997
998 redef fun to_cstring: NativeString
999 do
1000 if real_items != null then return real_items.as(not null)
1001 if index_from > 0 or index_to != items.cstring_length - 1 then
1002 var newItems = calloc_string(length + 1)
1003 self.items.copy_to(newItems, length, index_from, 0)
1004 newItems[length] = '\0'
1005 self.real_items = newItems
1006 return newItems
1007 end
1008 return items
1009 end
1010
1011 redef fun ==(other)
1012 do
1013 if not other isa FlatString then return super
1014
1015 if self.object_id == other.object_id then return true
1016
1017 var my_length = length
1018
1019 if other.length != my_length then return false
1020
1021 var my_index = index_from
1022 var its_index = other.index_from
1023
1024 var last_iteration = my_index + my_length
1025
1026 var itsitems = other.items
1027 var myitems = self.items
1028
1029 while my_index < last_iteration do
1030 if myitems[my_index] != itsitems[its_index] then return false
1031 my_index += 1
1032 its_index += 1
1033 end
1034
1035 return true
1036 end
1037
1038 redef fun <(other)
1039 do
1040 if not other isa FlatString then return super
1041
1042 if self.object_id == other.object_id then return false
1043
1044 var my_curr_char : Char
1045 var its_curr_char : Char
1046
1047 var curr_id_self = self.index_from
1048 var curr_id_other = other.index_from
1049
1050 var my_items = self.items
1051 var its_items = other.items
1052
1053 var my_length = self.length
1054 var its_length = other.length
1055
1056 var max_iterations = curr_id_self + my_length
1057
1058 while curr_id_self < max_iterations do
1059 my_curr_char = my_items[curr_id_self]
1060 its_curr_char = its_items[curr_id_other]
1061
1062 if my_curr_char != its_curr_char then
1063 if my_curr_char < its_curr_char then return true
1064 return false
1065 end
1066
1067 curr_id_self += 1
1068 curr_id_other += 1
1069 end
1070
1071 return my_length < its_length
1072 end
1073
1074 redef fun +(s)
1075 do
1076 var my_length = self.length
1077 var its_length = s.length
1078
1079 var total_length = my_length + its_length
1080
1081 var target_string = calloc_string(my_length + its_length + 1)
1082
1083 self.items.copy_to(target_string, my_length, index_from, 0)
1084 if s isa FlatString then
1085 s.items.copy_to(target_string, its_length, s.index_from, my_length)
1086 else if s isa FlatBuffer then
1087 s.items.copy_to(target_string, its_length, 0, my_length)
1088 else
1089 var curr_pos = my_length
1090 for i in [0..s.length[ do
1091 var c = s.chars[i]
1092 target_string[curr_pos] = c
1093 curr_pos += 1
1094 end
1095 end
1096
1097 target_string[total_length] = '\0'
1098
1099 return target_string.to_s_with_length(total_length)
1100 end
1101
1102 redef fun *(i)
1103 do
1104 assert i >= 0
1105
1106 var my_length = self.length
1107
1108 var final_length = my_length * i
1109
1110 var my_items = self.items
1111
1112 var target_string = calloc_string((final_length) + 1)
1113
1114 target_string[final_length] = '\0'
1115
1116 var current_last = 0
1117
1118 for iteration in [1 .. i] do
1119 my_items.copy_to(target_string, my_length, 0, current_last)
1120 current_last += my_length
1121 end
1122
1123 return target_string.to_s_with_length(final_length)
1124 end
1125
1126 redef fun hash
1127 do
1128 if hash_cache == null then
1129 # djb2 hash algorithm
1130 var h = 5381
1131 var i = index_from
1132
1133 var myitems = items
1134
1135 while i <= index_to do
1136 h = h.lshift(5) + h + myitems[i].ascii
1137 i += 1
1138 end
1139
1140 hash_cache = h
1141 end
1142
1143 return hash_cache.as(not null)
1144 end
1145
1146 redef fun substrings do return new FlatSubstringsIter(self)
1147 end
1148
1149 private class FlatStringReverseIterator
1150 super IndexedIterator[Char]
1151
1152 var target: FlatString
1153
1154 var target_items: NativeString
1155
1156 var curr_pos: Int
1157
1158 init with_pos(tgt: FlatString, pos: Int)
1159 do
1160 target = tgt
1161 target_items = tgt.items
1162 curr_pos = pos + tgt.index_from
1163 end
1164
1165 redef fun is_ok do return curr_pos >= target.index_from
1166
1167 redef fun item do return target_items[curr_pos]
1168
1169 redef fun next do curr_pos -= 1
1170
1171 redef fun index do return curr_pos - target.index_from
1172
1173 end
1174
1175 private class FlatStringIterator
1176 super IndexedIterator[Char]
1177
1178 var target: FlatString
1179
1180 var target_items: NativeString
1181
1182 var curr_pos: Int
1183
1184 init with_pos(tgt: FlatString, pos: Int)
1185 do
1186 target = tgt
1187 target_items = tgt.items
1188 curr_pos = pos + target.index_from
1189 end
1190
1191 redef fun is_ok do return curr_pos <= target.index_to
1192
1193 redef fun item do return target_items[curr_pos]
1194
1195 redef fun next do curr_pos += 1
1196
1197 redef fun index do return curr_pos - target.index_from
1198
1199 end
1200
1201 private class FlatStringCharView
1202 super StringCharView
1203
1204 redef type SELFTYPE: FlatString
1205
1206 redef fun [](index)
1207 do
1208 # Check that the index (+ index_from) is not larger than indexTo
1209 # In other terms, if the index is valid
1210 assert index >= 0
1211 var target = self.target
1212 assert (index + target.index_from) <= target.index_to
1213 return target.items[index + target.index_from]
1214 end
1215
1216 redef fun iterator_from(start) do return new FlatStringIterator.with_pos(target, start)
1217
1218 redef fun reverse_iterator_from(start) do return new FlatStringReverseIterator.with_pos(target, start)
1219
1220 end
1221
1222 abstract class Buffer
1223 super Text
1224
1225 redef type SELFTYPE: Buffer
1226
1227 # Specific implementations MUST set this to `true` in order to invalidate caches
1228 protected var is_dirty = true
1229
1230 # Modifies the char contained at pos `index`
1231 #
1232 # DEPRECATED : Use self.chars.[]= instead
1233 fun []=(index: Int, item: Char) is abstract
1234
1235 # Adds a char `c` at the end of self
1236 #
1237 # DEPRECATED : Use self.chars.add instead
1238 fun add(c: Char) is abstract
1239
1240 # Clears the buffer
1241 #
1242 # var b = new FlatBuffer
1243 # b.append "hello"
1244 # assert not b.is_empty
1245 # b.clear
1246 # assert b.is_empty
1247 fun clear is abstract
1248
1249 # Enlarges the subsequent array containing the chars of self
1250 fun enlarge(cap: Int) is abstract
1251
1252 # Adds the content of text `s` at the end of self
1253 #
1254 # var b = new FlatBuffer
1255 # b.append "hello"
1256 # b.append "world"
1257 # assert b == "helloworld"
1258 fun append(s: Text) is abstract
1259
1260 # `self` is appended in such a way that `self` is repeated `r` times
1261 #
1262 # var b = new FlatBuffer
1263 # b.append "hello"
1264 # b.times 3
1265 # assert b == "hellohellohello"
1266 fun times(r: Int) is abstract
1267
1268 # Reverses itself in-place
1269 #
1270 # var b = new FlatBuffer
1271 # b.append("hello")
1272 # b.reverse
1273 # assert b == "olleh"
1274 fun reverse is abstract
1275
1276 # Changes each lower-case char in `self` by its upper-case variant
1277 #
1278 # var b = new FlatBuffer
1279 # b.append("Hello World!")
1280 # b.upper
1281 # assert b == "HELLO WORLD!"
1282 fun upper is abstract
1283
1284 # Changes each upper-case char in `self` by its lower-case variant
1285 #
1286 # var b = new FlatBuffer
1287 # b.append("Hello World!")
1288 # b.lower
1289 # assert b == "hello world!"
1290 fun lower is abstract
1291
1292 # Capitalizes each word in `self`
1293 #
1294 # Letters that follow a letter are lowercased
1295 # Letters that follow a non-letter are upcased.
1296 #
1297 # SEE: `Char::is_letter` for the definition of a letter.
1298 #
1299 # var b = new FlatBuffer.from("jAVAsCriPt")"
1300 # b.capitalize
1301 # assert b == "Javascript"
1302 # b = new FlatBuffer.from("i am root")
1303 # b.capitalize
1304 # assert b == "I Am Root"
1305 # b = new FlatBuffer.from("ab_c -ab0c ab\nc")
1306 # b.capitalize
1307 # assert b == "Ab_C -Ab0C Ab\nC"
1308 fun capitalize do
1309 if length == 0 then return
1310 var c = self[0].to_upper
1311 self[0] = c
1312 var prev: Char = c
1313 for i in [1 .. length[ do
1314 prev = c
1315 c = self[i]
1316 if prev.is_letter then
1317 self[i] = c.to_lower
1318 else
1319 self[i] = c.to_upper
1320 end
1321 end
1322 end
1323
1324 redef fun hash
1325 do
1326 if is_dirty then hash_cache = null
1327 return super
1328 end
1329
1330 # In Buffers, the internal sequence of character is mutable
1331 # Thus, `chars` can be used to modify the buffer.
1332 redef fun chars: Sequence[Char] is abstract
1333 end
1334
1335 # Mutable strings of characters.
1336 class FlatBuffer
1337 super FlatText
1338 super Buffer
1339
1340 redef type SELFTYPE: FlatBuffer
1341
1342 redef var chars: Sequence[Char] = new FlatBufferCharView(self)
1343
1344 private var capacity: Int = 0
1345
1346 redef fun substrings do return new FlatSubstringsIter(self)
1347
1348 redef fun [](index)
1349 do
1350 assert index >= 0
1351 assert index < length
1352 return items[index]
1353 end
1354
1355 redef fun []=(index, item)
1356 do
1357 is_dirty = true
1358 if index == length then
1359 add(item)
1360 return
1361 end
1362 assert index >= 0 and index < length
1363 items[index] = item
1364 end
1365
1366 redef fun add(c)
1367 do
1368 is_dirty = true
1369 if capacity <= length then enlarge(length + 5)
1370 items[length] = c
1371 length += 1
1372 end
1373
1374 redef fun clear do
1375 is_dirty = true
1376 length = 0
1377 end
1378
1379 redef fun empty do return new FlatBuffer
1380
1381 redef fun enlarge(cap)
1382 do
1383 var c = capacity
1384 if cap <= c then return
1385 while c <= cap do c = c * 2 + 2
1386 var a = calloc_string(c+1)
1387 if length > 0 then items.copy_to(a, length, 0, 0)
1388 items = a
1389 capacity = c
1390 end
1391
1392 redef fun to_s: String
1393 do
1394 return to_cstring.to_s_with_length(length)
1395 end
1396
1397 redef fun to_cstring
1398 do
1399 if is_dirty then
1400 var new_native = calloc_string(length + 1)
1401 new_native[length] = '\0'
1402 if length > 0 then items.copy_to(new_native, length, 0, 0)
1403 real_items = new_native
1404 is_dirty = false
1405 end
1406 return real_items.as(not null)
1407 end
1408
1409 # Create a new empty string.
1410 init do end
1411
1412 init from(s: Text)
1413 do
1414 capacity = s.length + 1
1415 length = s.length
1416 items = calloc_string(capacity)
1417 if s isa FlatString then
1418 s.items.copy_to(items, length, s.index_from, 0)
1419 else if s isa FlatBuffer then
1420 s.items.copy_to(items, length, 0, 0)
1421 else
1422 var curr_pos = 0
1423 for i in [0..s.length[ do
1424 var c = s.chars[i]
1425 items[curr_pos] = c
1426 curr_pos += 1
1427 end
1428 end
1429 end
1430
1431 # Create a new empty string with a given capacity.
1432 init with_capacity(cap: Int)
1433 do
1434 assert cap >= 0
1435 # _items = new NativeString.calloc(cap)
1436 items = calloc_string(cap+1)
1437 capacity = cap
1438 length = 0
1439 end
1440
1441 redef fun append(s)
1442 do
1443 if s.is_empty then return
1444 is_dirty = true
1445 var sl = s.length
1446 if capacity < length + sl then enlarge(length + sl)
1447 if s isa FlatString then
1448 s.items.copy_to(items, sl, s.index_from, length)
1449 else if s isa FlatBuffer then
1450 s.items.copy_to(items, sl, 0, length)
1451 else
1452 var curr_pos = self.length
1453 for i in [0..s.length[ do
1454 var c = s.chars[i]
1455 items[curr_pos] = c
1456 curr_pos += 1
1457 end
1458 end
1459 length += sl
1460 end
1461
1462 # Copies the content of self in `dest`
1463 fun copy(start: Int, len: Int, dest: Buffer, new_start: Int)
1464 do
1465 var self_chars = self.chars
1466 var dest_chars = dest.chars
1467 for i in [0..len-1] do
1468 dest_chars[new_start+i] = self_chars[start+i]
1469 end
1470 end
1471
1472 redef fun substring(from, count)
1473 do
1474 assert count >= 0
1475 count += from
1476 if from < 0 then from = 0
1477 if count > length then count = length
1478 if from < count then
1479 var r = new FlatBuffer.with_capacity(count - from)
1480 while from < count do
1481 r.chars.push(items[from])
1482 from += 1
1483 end
1484 return r
1485 else
1486 return new FlatBuffer
1487 end
1488 end
1489
1490 redef fun reverse
1491 do
1492 var ns = calloc_string(capacity)
1493 var si = length - 1
1494 var ni = 0
1495 var it = items
1496 while si >= 0 do
1497 ns[ni] = it[si]
1498 ni += 1
1499 si -= 1
1500 end
1501 items = ns
1502 end
1503
1504 redef fun times(repeats)
1505 do
1506 var x = new FlatString.with_infos(items, length, 0, length - 1)
1507 for i in [1..repeats[ do
1508 append(x)
1509 end
1510 end
1511
1512 redef fun upper
1513 do
1514 var it = items
1515 var id = length - 1
1516 while id >= 0 do
1517 it[id] = it[id].to_upper
1518 id -= 1
1519 end
1520 end
1521
1522 redef fun lower
1523 do
1524 var it = items
1525 var id = length - 1
1526 while id >= 0 do
1527 it[id] = it[id].to_lower
1528 id -= 1
1529 end
1530 end
1531 end
1532
1533 private class FlatBufferReverseIterator
1534 super IndexedIterator[Char]
1535
1536 var target: FlatBuffer
1537
1538 var target_items: NativeString
1539
1540 var curr_pos: Int
1541
1542 init with_pos(tgt: FlatBuffer, pos: Int)
1543 do
1544 target = tgt
1545 if tgt.length > 0 then target_items = tgt.items
1546 curr_pos = pos
1547 end
1548
1549 redef fun index do return curr_pos
1550
1551 redef fun is_ok do return curr_pos >= 0
1552
1553 redef fun item do return target_items[curr_pos]
1554
1555 redef fun next do curr_pos -= 1
1556
1557 end
1558
1559 private class FlatBufferCharView
1560 super BufferCharView
1561 super StringCapable
1562
1563 redef type SELFTYPE: FlatBuffer
1564
1565 redef fun [](index) do return target.items[index]
1566
1567 redef fun []=(index, item)
1568 do
1569 assert index >= 0 and index <= length
1570 if index == length then
1571 add(item)
1572 return
1573 end
1574 target.items[index] = item
1575 end
1576
1577 redef fun push(c)
1578 do
1579 target.add(c)
1580 end
1581
1582 redef fun add(c)
1583 do
1584 target.add(c)
1585 end
1586
1587 fun enlarge(cap: Int)
1588 do
1589 target.enlarge(cap)
1590 end
1591
1592 redef fun append(s)
1593 do
1594 var my_items = target.items
1595 var s_length = s.length
1596 if target.capacity < s.length then enlarge(s_length + target.length)
1597 end
1598
1599 redef fun iterator_from(pos) do return new FlatBufferIterator.with_pos(target, pos)
1600
1601 redef fun reverse_iterator_from(pos) do return new FlatBufferReverseIterator.with_pos(target, pos)
1602
1603 end
1604
1605 private class FlatBufferIterator
1606 super IndexedIterator[Char]
1607
1608 var target: FlatBuffer
1609
1610 var target_items: NativeString
1611
1612 var curr_pos: Int
1613
1614 init with_pos(tgt: FlatBuffer, pos: Int)
1615 do
1616 target = tgt
1617 if tgt.length > 0 then target_items = tgt.items
1618 curr_pos = pos
1619 end
1620
1621 redef fun index do return curr_pos
1622
1623 redef fun is_ok do return curr_pos < target.length
1624
1625 redef fun item do return target_items[curr_pos]
1626
1627 redef fun next do curr_pos += 1
1628
1629 end
1630
1631 ###############################################################################
1632 # Refinement #
1633 ###############################################################################
1634
1635 redef class Object
1636 # User readable representation of `self`.
1637 fun to_s: String do return inspect
1638
1639 # The class name of the object in NativeString format.
1640 private fun native_class_name: NativeString is intern
1641
1642 # The class name of the object.
1643 #
1644 # assert 5.class_name == "Int"
1645 fun class_name: String do return native_class_name.to_s
1646
1647 # Developer readable representation of `self`.
1648 # Usually, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
1649 fun inspect: String
1650 do
1651 return "<{inspect_head}>"
1652 end
1653
1654 # Return "CLASSNAME:#OBJECTID".
1655 # This function is mainly used with the redefinition of the inspect method
1656 protected fun inspect_head: String
1657 do
1658 return "{class_name}:#{object_id.to_hex}"
1659 end
1660 end
1661
1662 redef class Bool
1663 # assert true.to_s == "true"
1664 # assert false.to_s == "false"
1665 redef fun to_s
1666 do
1667 if self then
1668 return once "true"
1669 else
1670 return once "false"
1671 end
1672 end
1673 end
1674
1675 redef class Int
1676
1677 # Wrapper of strerror C function
1678 private fun strerror_ext: NativeString is extern `{
1679 return strerror(recv);
1680 `}
1681
1682 # Returns a string describing error number
1683 fun strerror: String do return strerror_ext.to_s
1684
1685 # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
1686 # assume < to_c max const of char
1687 private fun fill_buffer(s: Buffer, base: Int, signed: Bool)
1688 do
1689 var n: Int
1690 # Sign
1691 if self < 0 then
1692 n = - self
1693 s.chars[0] = '-'
1694 else if self == 0 then
1695 s.chars[0] = '0'
1696 return
1697 else
1698 n = self
1699 end
1700 # Fill digits
1701 var pos = digit_count(base) - 1
1702 while pos >= 0 and n > 0 do
1703 s.chars[pos] = (n % base).to_c
1704 n = n / base # /
1705 pos -= 1
1706 end
1707 end
1708
1709 # C function to convert an nit Int to a NativeString (char*)
1710 private fun native_int_to_s: NativeString is extern "native_int_to_s"
1711
1712 # return displayable int in base 10 and signed
1713 #
1714 # assert 1.to_s == "1"
1715 # assert (-123).to_s == "-123"
1716 redef fun to_s do
1717 return native_int_to_s.to_s
1718 end
1719
1720 # return displayable int in hexadecimal
1721 #
1722 # assert 1.to_hex == "1"
1723 # assert (-255).to_hex == "-ff"
1724 fun to_hex: String do return to_base(16,false)
1725
1726 # return displayable int in base base and signed
1727 fun to_base(base: Int, signed: Bool): String
1728 do
1729 var l = digit_count(base)
1730 var s = new FlatBuffer.from(" " * l)
1731 fill_buffer(s, base, signed)
1732 return s.to_s
1733 end
1734 end
1735
1736 redef class Float
1737 # Pretty print self, print needoed decimals up to a max of 3.
1738 #
1739 # assert 12.34.to_s == "12.34"
1740 # assert (-0120.03450).to_s == "-120.035"
1741 #
1742 # see `to_precision` for a different precision.
1743 redef fun to_s do
1744 var str = to_precision( 3 )
1745 if is_inf != 0 or is_nan then return str
1746 var len = str.length
1747 for i in [0..len-1] do
1748 var j = len-1-i
1749 var c = str.chars[j]
1750 if c == '0' then
1751 continue
1752 else if c == '.' then
1753 return str.substring( 0, j+2 )
1754 else
1755 return str.substring( 0, j+1 )
1756 end
1757 end
1758 return str
1759 end
1760
1761 # `self` representation with `nb` digits after the '.'.
1762 #
1763 # assert 12.345.to_precision(1) == "12.3"
1764 # assert 12.345.to_precision(2) == "12.35"
1765 # assert 12.345.to_precision(3) == "12.345"
1766 # assert 12.345.to_precision(4) == "12.3450"
1767 fun to_precision(nb: Int): String
1768 do
1769 if is_nan then return "nan"
1770
1771 var isinf = self.is_inf
1772 if isinf == 1 then
1773 return "inf"
1774 else if isinf == -1 then
1775 return "-inf"
1776 end
1777
1778 if nb == 0 then return self.to_i.to_s
1779 var f = self
1780 for i in [0..nb[ do f = f * 10.0
1781 if self > 0.0 then
1782 f = f + 0.5
1783 else
1784 f = f - 0.5
1785 end
1786 var i = f.to_i
1787 if i == 0 then return "0.0"
1788 var s = i.to_s
1789 var sl = s.length
1790 if sl > nb then
1791 var p1 = s.substring(0, s.length-nb)
1792 var p2 = s.substring(s.length-nb, nb)
1793 return p1 + "." + p2
1794 else
1795 return "0." + ("0"*(nb-sl)) + s
1796 end
1797 end
1798
1799 # `self` representation with `nb` digits after the '.'.
1800 #
1801 # assert 12.345.to_precision_native(1) == "12.3"
1802 # assert 12.345.to_precision_native(2) == "12.35"
1803 # assert 12.345.to_precision_native(3) == "12.345"
1804 # assert 12.345.to_precision_native(4) == "12.3450"
1805 fun to_precision_native(nb: Int): String import NativeString.to_s `{
1806 int size;
1807 char *str;
1808
1809 size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
1810 str = malloc(size + 1);
1811 sprintf(str, "%.*f", (int)nb, recv );
1812
1813 return NativeString_to_s( str );
1814 `}
1815 end
1816
1817 redef class Char
1818 # assert 'x'.to_s == "x"
1819 redef fun to_s
1820 do
1821 var s = new FlatBuffer.with_capacity(1)
1822 s.chars[0] = self
1823 return s.to_s
1824 end
1825
1826 # Returns true if the char is a numerical digit
1827 #
1828 # assert '0'.is_numeric
1829 # assert '9'.is_numeric
1830 # assert not 'a'.is_numeric
1831 # assert not '?'.is_numeric
1832 fun is_numeric: Bool
1833 do
1834 return self >= '0' and self <= '9'
1835 end
1836
1837 # Returns true if the char is an alpha digit
1838 #
1839 # assert 'a'.is_alpha
1840 # assert 'Z'.is_alpha
1841 # assert not '0'.is_alpha
1842 # assert not '?'.is_alpha
1843 fun is_alpha: Bool
1844 do
1845 return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
1846 end
1847
1848 # Returns true if the char is an alpha or a numeric digit
1849 #
1850 # assert 'a'.is_alphanumeric
1851 # assert 'Z'.is_alphanumeric
1852 # assert '0'.is_alphanumeric
1853 # assert '9'.is_alphanumeric
1854 # assert not '?'.is_alphanumeric
1855 fun is_alphanumeric: Bool
1856 do
1857 return self.is_numeric or self.is_alpha
1858 end
1859 end
1860
1861 redef class Collection[E]
1862 # Concatenate elements.
1863 redef fun to_s
1864 do
1865 var s = new FlatBuffer
1866 for e in self do if e != null then s.append(e.to_s)
1867 return s.to_s
1868 end
1869
1870 # Concatenate and separate each elements with `sep`.
1871 #
1872 # assert [1, 2, 3].join(":") == "1:2:3"
1873 # assert [1..3].join(":") == "1:2:3"
1874 fun join(sep: Text): String
1875 do
1876 if is_empty then return ""
1877
1878 var s = new FlatBuffer # Result
1879
1880 # Concat first item
1881 var i = iterator
1882 var e = i.item
1883 if e != null then s.append(e.to_s)
1884
1885 # Concat other items
1886 i.next
1887 while i.is_ok do
1888 s.append(sep)
1889 e = i.item
1890 if e != null then s.append(e.to_s)
1891 i.next
1892 end
1893 return s.to_s
1894 end
1895 end
1896
1897 redef class Array[E]
1898
1899 # Fast implementation
1900 redef fun to_s
1901 do
1902 var l = length
1903 if l == 0 then return ""
1904 if l == 1 then if self[0] == null then return "" else return self[0].to_s
1905 var its = _items
1906 var na = new NativeArray[String](l)
1907 var i = 0
1908 var sl = 0
1909 var mypos = 0
1910 while i < l do
1911 var itsi = its[i]
1912 if itsi == null then
1913 i += 1
1914 continue
1915 end
1916 var tmp = itsi.to_s
1917 sl += tmp.length
1918 na[mypos] = tmp
1919 i += 1
1920 mypos += 1
1921 end
1922 var ns = new NativeString(sl + 1)
1923 ns[sl] = '\0'
1924 i = 0
1925 var off = 0
1926 while i < mypos do
1927 var tmp = na[i]
1928 var tpl = tmp.length
1929 if tmp isa FlatString then
1930 tmp.items.copy_to(ns, tpl, tmp.index_from, off)
1931 off += tpl
1932 else
1933 for j in tmp.substrings do
1934 var s = j.as(FlatString)
1935 var slen = s.length
1936 s.items.copy_to(ns, slen, s.index_from, off)
1937 off += slen
1938 end
1939 end
1940 i += 1
1941 end
1942 return ns.to_s_with_length(sl)
1943 end
1944 end
1945
1946 redef class Map[K,V]
1947 # Concatenate couple of 'key value'.
1948 # key and value are separated by `couple_sep`.
1949 # each couple is separated each couple with `sep`.
1950 #
1951 # var m = new ArrayMap[Int, String]
1952 # m[1] = "one"
1953 # m[10] = "ten"
1954 # assert m.join("; ", "=") == "1=one; 10=ten"
1955 fun join(sep: String, couple_sep: String): String
1956 do
1957 if is_empty then return ""
1958
1959 var s = new FlatBuffer # Result
1960
1961 # Concat first item
1962 var i = iterator
1963 var k = i.key
1964 var e = i.item
1965 s.append("{k}{couple_sep}{e or else "<null>"}")
1966
1967 # Concat other items
1968 i.next
1969 while i.is_ok do
1970 s.append(sep)
1971 k = i.key
1972 e = i.item
1973 s.append("{k}{couple_sep}{e or else "<null>"}")
1974 i.next
1975 end
1976 return s.to_s
1977 end
1978 end
1979
1980 ###############################################################################
1981 # Native classes #
1982 ###############################################################################
1983
1984 # Native strings are simple C char *
1985 extern class NativeString `{ char* `}
1986 super StringCapable
1987 # Creates a new NativeString with a capacity of `length`
1988 new(length: Int) is intern
1989 fun [](index: Int): Char is intern
1990 fun []=(index: Int, item: Char) is intern
1991 fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
1992
1993 # Position of the first nul character.
1994 fun cstring_length: Int
1995 do
1996 var l = 0
1997 while self[l] != '\0' do l += 1
1998 return l
1999 end
2000 fun atoi: Int is intern
2001 fun atof: Float is extern "atof"
2002
2003 redef fun to_s
2004 do
2005 return to_s_with_length(cstring_length)
2006 end
2007
2008 fun to_s_with_length(length: Int): FlatString
2009 do
2010 assert length >= 0
2011 return new FlatString.with_infos(self, length, 0, length - 1)
2012 end
2013
2014 fun to_s_with_copy: FlatString
2015 do
2016 var length = cstring_length
2017 var new_self = calloc_string(length + 1)
2018 copy_to(new_self, length, 0, 0)
2019 return new FlatString.with_infos(new_self, length, 0, length - 1)
2020 end
2021 end
2022
2023 # StringCapable objects can create native strings
2024 interface StringCapable
2025 protected fun calloc_string(size: Int): NativeString is intern
2026 end
2027
2028 redef class Sys
2029 private var args_cache: nullable Sequence[String]
2030
2031 # The arguments of the program as given by the OS
2032 fun program_args: Sequence[String]
2033 do
2034 if _args_cache == null then init_args
2035 return _args_cache.as(not null)
2036 end
2037
2038 # The name of the program as given by the OS
2039 fun program_name: String
2040 do
2041 return native_argv(0).to_s
2042 end
2043
2044 # Initialize `program_args` with the contents of `native_argc` and `native_argv`.
2045 private fun init_args
2046 do
2047 var argc = native_argc
2048 var args = new Array[String].with_capacity(0)
2049 var i = 1
2050 while i < argc do
2051 args[i-1] = native_argv(i).to_s
2052 i += 1
2053 end
2054 _args_cache = args
2055 end
2056
2057 # First argument of the main C function.
2058 private fun native_argc: Int is intern
2059
2060 # Second argument of the main C function.
2061 private fun native_argv(i: Int): NativeString is intern
2062 end
2063
2064 # Comparator that efficienlty use `to_s` to compare things
2065 #
2066 # The comparaison call `to_s` on object and use the result to order things.
2067 #
2068 # var a = [1, 2, 3, 10, 20]
2069 # (new CachedAlphaComparator).sort(a)
2070 # assert a == [1, 10, 2, 20, 3]
2071 #
2072 # Internally the result of `to_s` is cached in a HashMap to counter
2073 # uneficient implementation of `to_s`.
2074 #
2075 # Note: it caching is not usefull, see `alpha_comparator`
2076 class CachedAlphaComparator
2077 super Comparator[Object]
2078
2079 private var cache = new HashMap[Object, String]
2080
2081 private fun do_to_s(a: Object): String do
2082 if cache.has_key(a) then return cache[a]
2083 var res = a.to_s
2084 cache[a] = res
2085 return res
2086 end
2087
2088 redef fun compare(a, b) do
2089 return do_to_s(a) <=> do_to_s(b)
2090 end
2091 end
2092
2093 # see `alpha_comparator`
2094 private class AlphaComparator
2095 super Comparator[Object]
2096 redef fun compare(a, b) do return a.to_s <=> b.to_s
2097 end
2098
2099 # Stateless comparator that naively use `to_s` to compare things.
2100 #
2101 # Note: the result of `to_s` is not cached, thus can be invoked a lot
2102 # on a single instace. See `CachedAlphaComparator` as an alternative.
2103 #
2104 # var a = [1, 2, 3, 10, 20]
2105 # alpha_comparator.sort(a)
2106 # assert a == [1, 10, 2, 20, 3]
2107 fun alpha_comparator: Comparator[Object] do return once new AlphaComparator
2108
2109 # The arguments of the program as given by the OS
2110 fun args: Sequence[String]
2111 do
2112 return sys.program_args
2113 end