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