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