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