38bd492dafb5d5fd478cb2464397b5e16e2f0e4a
[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 end
822
823 private class FlatSubstringsIter
824 super Iterator[FlatText]
825
826 var tgt: nullable FlatText
827
828 init(tgt: FlatText) do self.tgt = tgt
829
830 redef fun item do
831 assert is_ok
832 return tgt.as(not null)
833 end
834
835 redef fun is_ok do return tgt != null
836
837 redef fun next do tgt = null
838 end
839
840 # Immutable strings of characters.
841 class FlatString
842 super FlatText
843 super String
844
845 # Index in _items of the start of the string
846 private var index_from: Int
847
848 # Indes in _items of the last item of the string
849 private var index_to: Int
850
851 redef var chars: SequenceRead[Char] = new FlatStringCharView(self)
852
853 redef fun [](index)
854 do
855 # Check that the index (+ index_from) is not larger than indexTo
856 # In other terms, if the index is valid
857 assert index >= 0
858 assert (index + index_from) <= index_to
859 return items[index + index_from]
860 end
861
862 ################################################
863 # AbstractString specific methods #
864 ################################################
865
866 redef fun reversed
867 do
868 var native = calloc_string(self.length + 1)
869 var length = self.length
870 var items = self.items
871 var pos = 0
872 var ipos = length-1
873 while pos < length do
874 native[pos] = items[ipos]
875 pos += 1
876 ipos -= 1
877 end
878 return native.to_s_with_length(self.length)
879 end
880
881 redef fun substring(from, count)
882 do
883 assert count >= 0
884
885 if from < 0 then
886 count += from
887 if count < 0 then count = 0
888 from = 0
889 end
890
891 var realFrom = index_from + from
892
893 if (realFrom + count) > index_to then return new FlatString.with_infos(items, index_to - realFrom + 1, realFrom, index_to)
894
895 if count == 0 then return empty
896
897 var to = realFrom + count - 1
898
899 return new FlatString.with_infos(items, to - realFrom + 1, realFrom, to)
900 end
901
902 redef fun empty do return "".as(FlatString)
903
904 redef fun to_upper
905 do
906 var outstr = calloc_string(self.length + 1)
907 var out_index = 0
908
909 var myitems = self.items
910 var index_from = self.index_from
911 var max = self.index_to
912
913 while index_from <= max do
914 outstr[out_index] = myitems[index_from].to_upper
915 out_index += 1
916 index_from += 1
917 end
918
919 outstr[self.length] = '\0'
920
921 return outstr.to_s_with_length(self.length)
922 end
923
924 redef fun to_lower
925 do
926 var outstr = calloc_string(self.length + 1)
927 var out_index = 0
928
929 var myitems = self.items
930 var index_from = self.index_from
931 var max = self.index_to
932
933 while index_from <= max do
934 outstr[out_index] = myitems[index_from].to_lower
935 out_index += 1
936 index_from += 1
937 end
938
939 outstr[self.length] = '\0'
940
941 return outstr.to_s_with_length(self.length)
942 end
943
944 redef fun output
945 do
946 var i = self.index_from
947 var imax = self.index_to
948 while i <= imax do
949 items[i].output
950 i += 1
951 end
952 end
953
954 ##################################################
955 # String Specific Methods #
956 ##################################################
957
958 private init with_infos(items: NativeString, len: Int, from: Int, to: Int)
959 do
960 self.items = items
961 length = len
962 index_from = from
963 index_to = to
964 end
965
966 redef fun to_cstring: NativeString
967 do
968 if real_items != null then return real_items.as(not null)
969 if index_from > 0 or index_to != items.cstring_length - 1 then
970 var newItems = calloc_string(length + 1)
971 self.items.copy_to(newItems, length, index_from, 0)
972 newItems[length] = '\0'
973 self.real_items = newItems
974 return newItems
975 end
976 return items
977 end
978
979 redef fun ==(other)
980 do
981 if not other isa FlatString then return super
982
983 if self.object_id == other.object_id then return true
984
985 var my_length = length
986
987 if other.length != my_length then return false
988
989 var my_index = index_from
990 var its_index = other.index_from
991
992 var last_iteration = my_index + my_length
993
994 var itsitems = other.items
995 var myitems = self.items
996
997 while my_index < last_iteration do
998 if myitems[my_index] != itsitems[its_index] then return false
999 my_index += 1
1000 its_index += 1
1001 end
1002
1003 return true
1004 end
1005
1006 redef fun <(other)
1007 do
1008 if not other isa FlatString then return super
1009
1010 if self.object_id == other.object_id then return false
1011
1012 var my_curr_char : Char
1013 var its_curr_char : Char
1014
1015 var curr_id_self = self.index_from
1016 var curr_id_other = other.index_from
1017
1018 var my_items = self.items
1019 var its_items = other.items
1020
1021 var my_length = self.length
1022 var its_length = other.length
1023
1024 var max_iterations = curr_id_self + my_length
1025
1026 while curr_id_self < max_iterations do
1027 my_curr_char = my_items[curr_id_self]
1028 its_curr_char = its_items[curr_id_other]
1029
1030 if my_curr_char != its_curr_char then
1031 if my_curr_char < its_curr_char then return true
1032 return false
1033 end
1034
1035 curr_id_self += 1
1036 curr_id_other += 1
1037 end
1038
1039 return my_length < its_length
1040 end
1041
1042 redef fun +(s)
1043 do
1044 var my_length = self.length
1045 var its_length = s.length
1046
1047 var total_length = my_length + its_length
1048
1049 var target_string = calloc_string(my_length + its_length + 1)
1050
1051 self.items.copy_to(target_string, my_length, index_from, 0)
1052 if s isa FlatString then
1053 s.items.copy_to(target_string, its_length, s.index_from, my_length)
1054 else if s isa FlatBuffer then
1055 s.items.copy_to(target_string, its_length, 0, my_length)
1056 else
1057 var curr_pos = my_length
1058 for i in [0..s.length[ do
1059 var c = s.chars[i]
1060 target_string[curr_pos] = c
1061 curr_pos += 1
1062 end
1063 end
1064
1065 target_string[total_length] = '\0'
1066
1067 return target_string.to_s_with_length(total_length)
1068 end
1069
1070 redef fun *(i)
1071 do
1072 assert i >= 0
1073
1074 var my_length = self.length
1075
1076 var final_length = my_length * i
1077
1078 var my_items = self.items
1079
1080 var target_string = calloc_string((final_length) + 1)
1081
1082 target_string[final_length] = '\0'
1083
1084 var current_last = 0
1085
1086 for iteration in [1 .. i] do
1087 my_items.copy_to(target_string, my_length, 0, current_last)
1088 current_last += my_length
1089 end
1090
1091 return target_string.to_s_with_length(final_length)
1092 end
1093
1094 redef fun hash
1095 do
1096 if hash_cache == null then
1097 # djb2 hash algorithm
1098 var h = 5381
1099 var i = index_from
1100
1101 var myitems = items
1102
1103 while i <= index_to do
1104 h = h.lshift(5) + h + myitems[i].ascii
1105 i += 1
1106 end
1107
1108 hash_cache = h
1109 end
1110
1111 return hash_cache.as(not null)
1112 end
1113
1114 redef fun substrings do return new FlatSubstringsIter(self)
1115 end
1116
1117 private class FlatStringReverseIterator
1118 super IndexedIterator[Char]
1119
1120 var target: FlatString
1121
1122 var target_items: NativeString
1123
1124 var curr_pos: Int
1125
1126 init with_pos(tgt: FlatString, pos: Int)
1127 do
1128 target = tgt
1129 target_items = tgt.items
1130 curr_pos = pos + tgt.index_from
1131 end
1132
1133 redef fun is_ok do return curr_pos >= target.index_from
1134
1135 redef fun item do return target_items[curr_pos]
1136
1137 redef fun next do curr_pos -= 1
1138
1139 redef fun index do return curr_pos - target.index_from
1140
1141 end
1142
1143 private class FlatStringIterator
1144 super IndexedIterator[Char]
1145
1146 var target: FlatString
1147
1148 var target_items: NativeString
1149
1150 var curr_pos: Int
1151
1152 init with_pos(tgt: FlatString, pos: Int)
1153 do
1154 target = tgt
1155 target_items = tgt.items
1156 curr_pos = pos + target.index_from
1157 end
1158
1159 redef fun is_ok do return curr_pos <= target.index_to
1160
1161 redef fun item do return target_items[curr_pos]
1162
1163 redef fun next do curr_pos += 1
1164
1165 redef fun index do return curr_pos - target.index_from
1166
1167 end
1168
1169 private class FlatStringCharView
1170 super StringCharView
1171
1172 redef type SELFTYPE: FlatString
1173
1174 redef fun [](index)
1175 do
1176 # Check that the index (+ index_from) is not larger than indexTo
1177 # In other terms, if the index is valid
1178 assert index >= 0
1179 var target = self.target
1180 assert (index + target.index_from) <= target.index_to
1181 return target.items[index + target.index_from]
1182 end
1183
1184 redef fun iterator_from(start) do return new FlatStringIterator.with_pos(target, start)
1185
1186 redef fun reverse_iterator_from(start) do return new FlatStringReverseIterator.with_pos(target, start)
1187
1188 end
1189
1190 abstract class Buffer
1191 super Text
1192
1193 redef type SELFTYPE: Buffer
1194
1195 # Specific implementations MUST set this to `true` in order to invalidate caches
1196 protected var is_dirty = true
1197
1198 # Modifies the char contained at pos `index`
1199 #
1200 # DEPRECATED : Use self.chars.[]= instead
1201 fun []=(index: Int, item: Char) is abstract
1202
1203 # Adds a char `c` at the end of self
1204 #
1205 # DEPRECATED : Use self.chars.add instead
1206 fun add(c: Char) is abstract
1207
1208 # Clears the buffer
1209 #
1210 # var b = new FlatBuffer
1211 # b.append "hello"
1212 # assert not b.is_empty
1213 # b.clear
1214 # assert b.is_empty
1215 fun clear is abstract
1216
1217 # Enlarges the subsequent array containing the chars of self
1218 fun enlarge(cap: Int) is abstract
1219
1220 # Adds the content of text `s` at the end of self
1221 #
1222 # var b = new FlatBuffer
1223 # b.append "hello"
1224 # b.append "world"
1225 # assert b == "helloworld"
1226 fun append(s: Text) is abstract
1227
1228 # `self` is appended in such a way that `self` is repeated `r` times
1229 #
1230 # var b = new FlatBuffer
1231 # b.append "hello"
1232 # b.times 3
1233 # assert b == "hellohellohello"
1234 fun times(r: Int) is abstract
1235
1236 # Reverses itself in-place
1237 #
1238 # var b = new FlatBuffer
1239 # b.append("hello")
1240 # b.reverse
1241 # assert b == "olleh"
1242 fun reverse is abstract
1243
1244 # Changes each lower-case char in `self` by its upper-case variant
1245 #
1246 # var b = new FlatBuffer
1247 # b.append("Hello World!")
1248 # b.upper
1249 # assert b == "HELLO WORLD!"
1250 fun upper is abstract
1251
1252 # Changes each upper-case char in `self` by its lower-case variant
1253 #
1254 # var b = new FlatBuffer
1255 # b.append("Hello World!")
1256 # b.lower
1257 # assert b == "hello world!"
1258 fun lower is abstract
1259
1260 redef fun hash
1261 do
1262 if is_dirty then hash_cache = null
1263 return super
1264 end
1265
1266 # In Buffers, the internal sequence of character is mutable
1267 # Thus, `chars` can be used to modify the buffer.
1268 redef fun chars: Sequence[Char] is abstract
1269 end
1270
1271 # Mutable strings of characters.
1272 class FlatBuffer
1273 super FlatText
1274 super Buffer
1275
1276 redef type SELFTYPE: FlatBuffer
1277
1278 redef var chars: Sequence[Char] = new FlatBufferCharView(self)
1279
1280 private var capacity: Int = 0
1281
1282 redef fun substrings do return new FlatSubstringsIter(self)
1283
1284 redef fun [](index)
1285 do
1286 assert index >= 0
1287 assert index < length
1288 return items[index]
1289 end
1290
1291 redef fun []=(index, item)
1292 do
1293 is_dirty = true
1294 if index == length then
1295 add(item)
1296 return
1297 end
1298 assert index >= 0 and index < length
1299 items[index] = item
1300 end
1301
1302 redef fun add(c)
1303 do
1304 is_dirty = true
1305 if capacity <= length then enlarge(length + 5)
1306 items[length] = c
1307 length += 1
1308 end
1309
1310 redef fun clear do
1311 is_dirty = true
1312 length = 0
1313 end
1314
1315 redef fun empty do return new FlatBuffer
1316
1317 redef fun enlarge(cap)
1318 do
1319 var c = capacity
1320 if cap <= c then return
1321 while c <= cap do c = c * 2 + 2
1322 var a = calloc_string(c+1)
1323 if length > 0 then items.copy_to(a, length, 0, 0)
1324 items = a
1325 capacity = c
1326 end
1327
1328 redef fun to_s: String
1329 do
1330 return to_cstring.to_s_with_length(length)
1331 end
1332
1333 redef fun to_cstring
1334 do
1335 if is_dirty then
1336 var new_native = calloc_string(length + 1)
1337 new_native[length] = '\0'
1338 if length > 0 then items.copy_to(new_native, length, 0, 0)
1339 real_items = new_native
1340 is_dirty = false
1341 end
1342 return real_items.as(not null)
1343 end
1344
1345 # Create a new empty string.
1346 init do end
1347
1348 init from(s: Text)
1349 do
1350 capacity = s.length + 1
1351 length = s.length
1352 items = calloc_string(capacity)
1353 if s isa FlatString then
1354 s.items.copy_to(items, length, s.index_from, 0)
1355 else if s isa FlatBuffer then
1356 s.items.copy_to(items, length, 0, 0)
1357 else
1358 var curr_pos = 0
1359 for i in [0..s.length[ do
1360 var c = s.chars[i]
1361 items[curr_pos] = c
1362 curr_pos += 1
1363 end
1364 end
1365 end
1366
1367 # Create a new empty string with a given capacity.
1368 init with_capacity(cap: Int)
1369 do
1370 assert cap >= 0
1371 # _items = new NativeString.calloc(cap)
1372 items = calloc_string(cap+1)
1373 capacity = cap
1374 length = 0
1375 end
1376
1377 redef fun append(s)
1378 do
1379 if s.is_empty then return
1380 is_dirty = true
1381 var sl = s.length
1382 if capacity < length + sl then enlarge(length + sl)
1383 if s isa FlatString then
1384 s.items.copy_to(items, sl, s.index_from, length)
1385 else if s isa FlatBuffer then
1386 s.items.copy_to(items, sl, 0, length)
1387 else
1388 var curr_pos = self.length
1389 for i in [0..s.length[ do
1390 var c = s.chars[i]
1391 items[curr_pos] = c
1392 curr_pos += 1
1393 end
1394 end
1395 length += sl
1396 end
1397
1398 # Copies the content of self in `dest`
1399 fun copy(start: Int, len: Int, dest: Buffer, new_start: Int)
1400 do
1401 var self_chars = self.chars
1402 var dest_chars = dest.chars
1403 for i in [0..len-1] do
1404 dest_chars[new_start+i] = self_chars[start+i]
1405 end
1406 end
1407
1408 redef fun substring(from, count)
1409 do
1410 assert count >= 0
1411 count += from
1412 if from < 0 then from = 0
1413 if count > length then count = length
1414 if from < count then
1415 var r = new FlatBuffer.with_capacity(count - from)
1416 while from < count do
1417 r.chars.push(items[from])
1418 from += 1
1419 end
1420 return r
1421 else
1422 return new FlatBuffer
1423 end
1424 end
1425
1426 redef fun reverse
1427 do
1428 var ns = calloc_string(capacity)
1429 var si = length - 1
1430 var ni = 0
1431 var it = items
1432 while si >= 0 do
1433 ns[ni] = it[si]
1434 ni += 1
1435 si -= 1
1436 end
1437 items = ns
1438 end
1439
1440 redef fun times(repeats)
1441 do
1442 var x = new FlatString.with_infos(items, length, 0, length - 1)
1443 for i in [1..repeats[ do
1444 append(x)
1445 end
1446 end
1447
1448 redef fun upper
1449 do
1450 var it = items
1451 var id = length - 1
1452 while id >= 0 do
1453 it[id] = it[id].to_upper
1454 id -= 1
1455 end
1456 end
1457
1458 redef fun lower
1459 do
1460 var it = items
1461 var id = length - 1
1462 while id >= 0 do
1463 it[id] = it[id].to_lower
1464 id -= 1
1465 end
1466 end
1467 end
1468
1469 private class FlatBufferReverseIterator
1470 super IndexedIterator[Char]
1471
1472 var target: FlatBuffer
1473
1474 var target_items: NativeString
1475
1476 var curr_pos: Int
1477
1478 init with_pos(tgt: FlatBuffer, pos: Int)
1479 do
1480 target = tgt
1481 if tgt.length > 0 then target_items = tgt.items
1482 curr_pos = pos
1483 end
1484
1485 redef fun index do return curr_pos
1486
1487 redef fun is_ok do return curr_pos >= 0
1488
1489 redef fun item do return target_items[curr_pos]
1490
1491 redef fun next do curr_pos -= 1
1492
1493 end
1494
1495 private class FlatBufferCharView
1496 super BufferCharView
1497 super StringCapable
1498
1499 redef type SELFTYPE: FlatBuffer
1500
1501 redef fun [](index) do return target.items[index]
1502
1503 redef fun []=(index, item)
1504 do
1505 assert index >= 0 and index <= length
1506 if index == length then
1507 add(item)
1508 return
1509 end
1510 target.items[index] = item
1511 end
1512
1513 redef fun push(c)
1514 do
1515 target.add(c)
1516 end
1517
1518 redef fun add(c)
1519 do
1520 target.add(c)
1521 end
1522
1523 fun enlarge(cap: Int)
1524 do
1525 target.enlarge(cap)
1526 end
1527
1528 redef fun append(s)
1529 do
1530 var my_items = target.items
1531 var s_length = s.length
1532 if target.capacity < s.length then enlarge(s_length + target.length)
1533 end
1534
1535 redef fun iterator_from(pos) do return new FlatBufferIterator.with_pos(target, pos)
1536
1537 redef fun reverse_iterator_from(pos) do return new FlatBufferReverseIterator.with_pos(target, pos)
1538
1539 end
1540
1541 private class FlatBufferIterator
1542 super IndexedIterator[Char]
1543
1544 var target: FlatBuffer
1545
1546 var target_items: NativeString
1547
1548 var curr_pos: Int
1549
1550 init with_pos(tgt: FlatBuffer, pos: Int)
1551 do
1552 target = tgt
1553 if tgt.length > 0 then target_items = tgt.items
1554 curr_pos = pos
1555 end
1556
1557 redef fun index do return curr_pos
1558
1559 redef fun is_ok do return curr_pos < target.length
1560
1561 redef fun item do return target_items[curr_pos]
1562
1563 redef fun next do curr_pos += 1
1564
1565 end
1566
1567 ###############################################################################
1568 # Refinement #
1569 ###############################################################################
1570
1571 redef class Object
1572 # User readable representation of `self`.
1573 fun to_s: String do return inspect
1574
1575 # The class name of the object in NativeString format.
1576 private fun native_class_name: NativeString is intern
1577
1578 # The class name of the object.
1579 #
1580 # assert 5.class_name == "Int"
1581 fun class_name: String do return native_class_name.to_s
1582
1583 # Developer readable representation of `self`.
1584 # Usually, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
1585 fun inspect: String
1586 do
1587 return "<{inspect_head}>"
1588 end
1589
1590 # Return "CLASSNAME:#OBJECTID".
1591 # This function is mainly used with the redefinition of the inspect method
1592 protected fun inspect_head: String
1593 do
1594 return "{class_name}:#{object_id.to_hex}"
1595 end
1596 end
1597
1598 redef class Bool
1599 # assert true.to_s == "true"
1600 # assert false.to_s == "false"
1601 redef fun to_s
1602 do
1603 if self then
1604 return once "true"
1605 else
1606 return once "false"
1607 end
1608 end
1609 end
1610
1611 redef class Int
1612
1613 # Wrapper of strerror C function
1614 private fun strerror_ext: NativeString is extern `{
1615 return strerror(recv);
1616 `}
1617
1618 # Returns a string describing error number
1619 fun strerror: String do return strerror_ext.to_s
1620
1621 # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
1622 # assume < to_c max const of char
1623 private fun fill_buffer(s: Buffer, base: Int, signed: Bool)
1624 do
1625 var n: Int
1626 # Sign
1627 if self < 0 then
1628 n = - self
1629 s.chars[0] = '-'
1630 else if self == 0 then
1631 s.chars[0] = '0'
1632 return
1633 else
1634 n = self
1635 end
1636 # Fill digits
1637 var pos = digit_count(base) - 1
1638 while pos >= 0 and n > 0 do
1639 s.chars[pos] = (n % base).to_c
1640 n = n / base # /
1641 pos -= 1
1642 end
1643 end
1644
1645 # C function to convert an nit Int to a NativeString (char*)
1646 private fun native_int_to_s: NativeString is extern "native_int_to_s"
1647
1648 # return displayable int in base 10 and signed
1649 #
1650 # assert 1.to_s == "1"
1651 # assert (-123).to_s == "-123"
1652 redef fun to_s do
1653 return native_int_to_s.to_s
1654 end
1655
1656 # return displayable int in hexadecimal
1657 #
1658 # assert 1.to_hex == "1"
1659 # assert (-255).to_hex == "-ff"
1660 fun to_hex: String do return to_base(16,false)
1661
1662 # return displayable int in base base and signed
1663 fun to_base(base: Int, signed: Bool): String
1664 do
1665 var l = digit_count(base)
1666 var s = new FlatBuffer.from(" " * l)
1667 fill_buffer(s, base, signed)
1668 return s.to_s
1669 end
1670 end
1671
1672 redef class Float
1673 # Pretty print self, print needoed decimals up to a max of 3.
1674 #
1675 # assert 12.34.to_s == "12.34"
1676 # assert (-0120.03450).to_s == "-120.035"
1677 #
1678 # see `to_precision` for a different precision.
1679 redef fun to_s do
1680 var str = to_precision( 3 )
1681 if is_inf != 0 or is_nan then return str
1682 var len = str.length
1683 for i in [0..len-1] do
1684 var j = len-1-i
1685 var c = str.chars[j]
1686 if c == '0' then
1687 continue
1688 else if c == '.' then
1689 return str.substring( 0, j+2 )
1690 else
1691 return str.substring( 0, j+1 )
1692 end
1693 end
1694 return str
1695 end
1696
1697 # `self` representation with `nb` digits after the '.'.
1698 #
1699 # assert 12.345.to_precision(1) == "12.3"
1700 # assert 12.345.to_precision(2) == "12.35"
1701 # assert 12.345.to_precision(3) == "12.345"
1702 # assert 12.345.to_precision(4) == "12.3450"
1703 fun to_precision(nb: Int): String
1704 do
1705 if is_nan then return "nan"
1706
1707 var isinf = self.is_inf
1708 if isinf == 1 then
1709 return "inf"
1710 else if isinf == -1 then
1711 return "-inf"
1712 end
1713
1714 if nb == 0 then return self.to_i.to_s
1715 var f = self
1716 for i in [0..nb[ do f = f * 10.0
1717 if self > 0.0 then
1718 f = f + 0.5
1719 else
1720 f = f - 0.5
1721 end
1722 var i = f.to_i
1723 if i == 0 then return "0.0"
1724 var s = i.to_s
1725 var sl = s.length
1726 if sl > nb then
1727 var p1 = s.substring(0, s.length-nb)
1728 var p2 = s.substring(s.length-nb, nb)
1729 return p1 + "." + p2
1730 else
1731 return "0." + ("0"*(nb-sl)) + s
1732 end
1733 end
1734
1735 # `self` representation with `nb` digits after the '.'.
1736 #
1737 # assert 12.345.to_precision_native(1) == "12.3"
1738 # assert 12.345.to_precision_native(2) == "12.35"
1739 # assert 12.345.to_precision_native(3) == "12.345"
1740 # assert 12.345.to_precision_native(4) == "12.3450"
1741 fun to_precision_native(nb: Int): String import NativeString.to_s `{
1742 int size;
1743 char *str;
1744
1745 size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
1746 str = malloc(size + 1);
1747 sprintf(str, "%.*f", (int)nb, recv );
1748
1749 return NativeString_to_s( str );
1750 `}
1751 end
1752
1753 redef class Char
1754 # assert 'x'.to_s == "x"
1755 redef fun to_s
1756 do
1757 var s = new FlatBuffer.with_capacity(1)
1758 s.chars[0] = self
1759 return s.to_s
1760 end
1761
1762 # Returns true if the char is a numerical digit
1763 #
1764 # assert '0'.is_numeric
1765 # assert '9'.is_numeric
1766 # assert not 'a'.is_numeric
1767 # assert not '?'.is_numeric
1768 fun is_numeric: Bool
1769 do
1770 return self >= '0' and self <= '9'
1771 end
1772
1773 # Returns true if the char is an alpha digit
1774 #
1775 # assert 'a'.is_alpha
1776 # assert 'Z'.is_alpha
1777 # assert not '0'.is_alpha
1778 # assert not '?'.is_alpha
1779 fun is_alpha: Bool
1780 do
1781 return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
1782 end
1783
1784 # Returns true if the char is an alpha or a numeric digit
1785 #
1786 # assert 'a'.is_alphanumeric
1787 # assert 'Z'.is_alphanumeric
1788 # assert '0'.is_alphanumeric
1789 # assert '9'.is_alphanumeric
1790 # assert not '?'.is_alphanumeric
1791 fun is_alphanumeric: Bool
1792 do
1793 return self.is_numeric or self.is_alpha
1794 end
1795 end
1796
1797 redef class Collection[E]
1798 # Concatenate elements.
1799 redef fun to_s
1800 do
1801 var s = new FlatBuffer
1802 for e in self do if e != null then s.append(e.to_s)
1803 return s.to_s
1804 end
1805
1806 # Concatenate and separate each elements with `sep`.
1807 #
1808 # assert [1, 2, 3].join(":") == "1:2:3"
1809 # assert [1..3].join(":") == "1:2:3"
1810 fun join(sep: Text): String
1811 do
1812 if is_empty then return ""
1813
1814 var s = new FlatBuffer # Result
1815
1816 # Concat first item
1817 var i = iterator
1818 var e = i.item
1819 if e != null then s.append(e.to_s)
1820
1821 # Concat other items
1822 i.next
1823 while i.is_ok do
1824 s.append(sep)
1825 e = i.item
1826 if e != null then s.append(e.to_s)
1827 i.next
1828 end
1829 return s.to_s
1830 end
1831 end
1832
1833 redef class Array[E]
1834
1835 # Fast implementation
1836 redef fun to_s
1837 do
1838 var l = length
1839 if l == 0 then return ""
1840 if l == 1 then if self[0] == null then return "" else return self[0].to_s
1841 var its = _items
1842 var na = new NativeArray[String](l)
1843 var i = 0
1844 var sl = 0
1845 var mypos = 0
1846 while i < l do
1847 var itsi = its[i]
1848 if itsi == null then
1849 i += 1
1850 continue
1851 end
1852 var tmp = itsi.to_s
1853 sl += tmp.length
1854 na[mypos] = tmp
1855 i += 1
1856 mypos += 1
1857 end
1858 var ns = new NativeString(sl + 1)
1859 ns[sl] = '\0'
1860 i = 0
1861 var off = 0
1862 while i < mypos do
1863 var tmp = na[i]
1864 var tpl = tmp.length
1865 if tmp isa FlatString then
1866 tmp.items.copy_to(ns, tpl, tmp.index_from, off)
1867 off += tpl
1868 else
1869 for j in tmp.substrings do
1870 var s = j.as(FlatString)
1871 var slen = s.length
1872 s.items.copy_to(ns, slen, s.index_from, off)
1873 off += slen
1874 end
1875 end
1876 i += 1
1877 end
1878 return ns.to_s_with_length(sl)
1879 end
1880 end
1881
1882 redef class Map[K,V]
1883 # Concatenate couple of 'key value'.
1884 # key and value are separated by `couple_sep`.
1885 # each couple is separated each couple with `sep`.
1886 #
1887 # var m = new ArrayMap[Int, String]
1888 # m[1] = "one"
1889 # m[10] = "ten"
1890 # assert m.join("; ", "=") == "1=one; 10=ten"
1891 fun join(sep: String, couple_sep: String): String
1892 do
1893 if is_empty then return ""
1894
1895 var s = new FlatBuffer # Result
1896
1897 # Concat first item
1898 var i = iterator
1899 var k = i.key
1900 var e = i.item
1901 s.append("{k}{couple_sep}{e or else "<null>"}")
1902
1903 # Concat other items
1904 i.next
1905 while i.is_ok do
1906 s.append(sep)
1907 k = i.key
1908 e = i.item
1909 s.append("{k}{couple_sep}{e or else "<null>"}")
1910 i.next
1911 end
1912 return s.to_s
1913 end
1914 end
1915
1916 ###############################################################################
1917 # Native classes #
1918 ###############################################################################
1919
1920 # Native strings are simple C char *
1921 extern class NativeString `{ char* `}
1922 super StringCapable
1923 # Creates a new NativeString with a capacity of `length`
1924 new(length: Int) is intern
1925 fun [](index: Int): Char is intern
1926 fun []=(index: Int, item: Char) is intern
1927 fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
1928
1929 # Position of the first nul character.
1930 fun cstring_length: Int
1931 do
1932 var l = 0
1933 while self[l] != '\0' do l += 1
1934 return l
1935 end
1936 fun atoi: Int is intern
1937 fun atof: Float is extern "atof"
1938
1939 redef fun to_s
1940 do
1941 return to_s_with_length(cstring_length)
1942 end
1943
1944 fun to_s_with_length(length: Int): FlatString
1945 do
1946 assert length >= 0
1947 return new FlatString.with_infos(self, length, 0, length - 1)
1948 end
1949
1950 fun to_s_with_copy: FlatString
1951 do
1952 var length = cstring_length
1953 var new_self = calloc_string(length + 1)
1954 copy_to(new_self, length, 0, 0)
1955 return new FlatString.with_infos(new_self, length, 0, length - 1)
1956 end
1957 end
1958
1959 # StringCapable objects can create native strings
1960 interface StringCapable
1961 protected fun calloc_string(size: Int): NativeString is intern
1962 end
1963
1964 redef class Sys
1965 private var args_cache: nullable Sequence[String]
1966
1967 # The arguments of the program as given by the OS
1968 fun program_args: Sequence[String]
1969 do
1970 if _args_cache == null then init_args
1971 return _args_cache.as(not null)
1972 end
1973
1974 # The name of the program as given by the OS
1975 fun program_name: String
1976 do
1977 return native_argv(0).to_s
1978 end
1979
1980 # Initialize `program_args` with the contents of `native_argc` and `native_argv`.
1981 private fun init_args
1982 do
1983 var argc = native_argc
1984 var args = new Array[String].with_capacity(0)
1985 var i = 1
1986 while i < argc do
1987 args[i-1] = native_argv(i).to_s
1988 i += 1
1989 end
1990 _args_cache = args
1991 end
1992
1993 # First argument of the main C function.
1994 private fun native_argc: Int is intern
1995
1996 # Second argument of the main C function.
1997 private fun native_argv(i: Int): NativeString is intern
1998 end
1999
2000 # Comparator that efficienlty use `to_s` to compare things
2001 #
2002 # The comparaison call `to_s` on object and use the result to order things.
2003 #
2004 # var a = [1, 2, 3, 10, 20]
2005 # (new CachedAlphaComparator).sort(a)
2006 # assert a == [1, 10, 2, 20, 3]
2007 #
2008 # Internally the result of `to_s` is cached in a HashMap to counter
2009 # uneficient implementation of `to_s`.
2010 #
2011 # Note: it caching is not usefull, see `alpha_comparator`
2012 class CachedAlphaComparator
2013 super Comparator[Object]
2014
2015 private var cache = new HashMap[Object, String]
2016
2017 private fun do_to_s(a: Object): String do
2018 if cache.has_key(a) then return cache[a]
2019 var res = a.to_s
2020 cache[a] = res
2021 return res
2022 end
2023
2024 redef fun compare(a, b) do
2025 return do_to_s(a) <=> do_to_s(b)
2026 end
2027 end
2028
2029 # see `alpha_comparator`
2030 private class AlphaComparator
2031 super Comparator[Object]
2032 redef fun compare(a, b) do return a.to_s <=> b.to_s
2033 end
2034
2035 # Stateless comparator that naively use `to_s` to compare things.
2036 #
2037 # Note: the result of `to_s` is not cached, thus can be invoked a lot
2038 # on a single instace. See `CachedAlphaComparator` as an alternative.
2039 #
2040 # var a = [1, 2, 3, 10, 20]
2041 # alpha_comparator.sort(a)
2042 # assert a == [1, 10, 2, 20, 3]
2043 fun alpha_comparator: Comparator[Object] do return once new AlphaComparator
2044
2045 # The arguments of the program as given by the OS
2046 fun args: Sequence[String]
2047 do
2048 return sys.program_args
2049 end