cfe5673dfa48e1c6cdd29d161519972632f5d2d0
[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 package string
16
17 intrude import collection # FIXME should be collection::array
18 import hash
19
20 `{
21 #include <stdio.h>
22 `}
23
24 ###############################################################################
25 # String #
26 ###############################################################################
27
28 # Common subclass for String and Buffer
29 abstract class AbstractString
30 super AbstractArrayRead[Char]
31
32 readable private var _items: NativeString
33
34 # Access a character at `index` in the string.
35 #
36 # assert "abcd"[2] == 'c'
37 redef fun [](index) do return _items[index]
38
39 # Create a substring.
40 #
41 # assert "abcd".substring(1, 2) == "bc"
42 # assert "abcd".substring(-1, 2) == "a"
43 # assert "abcd".substring(1, 0) == ""
44 # assert "abcd".substring(2, 5) == "cd"
45 #
46 # A `from` index < 0 will be replaced by 0.
47 # Unless a `count` value is > 0 at the same time.
48 # In this case, `from += count` and `count -= from`.
49 fun substring(from: Int, count: Int): String
50 do
51 assert count >= 0
52 count += from
53 if from < 0 then from = 0
54 if count > length then count = length
55 if from < count then
56 var r = new Buffer.with_capacity(count - from)
57 while from < count do
58 r.push(_items[from])
59 from += 1
60 end
61 return r.to_s
62 else
63 return ""
64 end
65 end
66
67 # Create a substring from `self` beginning at the `from` position
68 #
69 # assert "abcd".substring_from(1) == "bcd"
70 # assert "abcd".substring_from(-1) == "abcd"
71 # assert "abcd".substring_from(2) == "cd"
72 #
73 # As with substring, a `from` index < 0 will be replaced by 0
74 fun substring_from(from: Int): String
75 do
76 assert from < length
77 return substring(from, length - from)
78 end
79
80 # Does self have a substring `str` starting from position `pos`?
81 #
82 # assert "abcd".has_substring("bc",1) == true
83 # assert "abcd".has_substring("bc",2) == false
84 fun has_substring(str: String, pos: Int): Bool
85 do
86 var itsindex = str.length - 1
87 var myindex = pos + itsindex
88 var myitems = _items
89 var itsitems = str._items
90 if myindex > length or itsindex > myindex then return false
91 var its_index_from = str._index_from
92 itsindex += its_index_from
93 while itsindex >= its_index_from do
94 if myitems[myindex] != itsitems[itsindex] then return false
95 myindex -= 1
96 itsindex -= 1
97 end
98 return true
99 end
100
101 # Is this string prefixed by `prefix`?
102 #
103 # assert "abcd".has_prefix("ab") == true
104 # assert "abcbc".has_prefix("bc") == false
105 fun has_prefix(prefix: String): Bool do return has_substring(prefix,0)
106
107 # Is this string suffixed by `suffix`?
108 #
109 # assert "abcd".has_suffix("abc") == false
110 # assert "abcd".has_suffix("bcd") == true
111 fun has_suffix(suffix: String): Bool do return has_substring(suffix, length - suffix.length)
112
113 # If `self` contains only digits, return the corresponding integer
114 #
115 # assert "123".to_i == 123
116 # assert "-1".to_i == -1
117 fun to_i: Int
118 do
119 # Shortcut
120 return to_s.to_cstring.atoi
121 end
122
123 # If `self` contains a float, return the corresponding float
124 #
125 # assert "123".to_f == 123.0
126 # assert "-1".to_f == -1.0
127 # assert "-1.2e-3".to_f == -0.0012
128 fun to_f: Float
129 do
130 # Shortcut
131 return to_s.to_cstring.atof
132 end
133
134 # If `self` contains only digits and alpha <= 'f', return the corresponding integer.
135 fun to_hex: Int do return a_to(16)
136
137 # If `self` contains only digits and letters, return the corresponding integer in a given base
138 #
139 # assert "120".a_to(3) == 15
140 fun a_to(base: Int) : Int
141 do
142 var i = 0
143 var neg = false
144
145 for c in self
146 do
147 var v = c.to_i
148 if v > base then
149 if neg then
150 return -i
151 else
152 return i
153 end
154 else if v < 0 then
155 neg = true
156 else
157 i = i * base + v
158 end
159 end
160 if neg then
161 return -i
162 else
163 return i
164 end
165 end
166
167 # Returns `true` if the string contains only Numeric values (and one "," or one "." character)
168 #
169 # assert "123".is_numeric == true
170 # assert "1.2".is_numeric == true
171 # assert "1,2".is_numeric == true
172 # assert "1..2".is_numeric == false
173 fun is_numeric: Bool
174 do
175 var has_point_or_comma = false
176 for i in self
177 do
178 if not i.is_numeric
179 then
180 if (i == '.' or i == ',') and not has_point_or_comma
181 then
182 has_point_or_comma = true
183 else
184 return false
185 end
186 end
187 end
188 return true
189 end
190
191 # A upper case version of `self`
192 #
193 # assert "Hello World!".to_upper == "HELLO WORLD!"
194 fun to_upper: String
195 do
196 var s = new Buffer.with_capacity(length)
197 for i in self do s.add(i.to_upper)
198 return s.to_s
199 end
200
201 # A lower case version of `self`
202 #
203 # assert "Hello World!".to_lower == "hello world!"
204 fun to_lower : String
205 do
206 var s = new Buffer.with_capacity(length)
207 for i in self do s.add(i.to_lower)
208 return s.to_s
209 end
210
211 # Trims trailing and preceding white spaces
212 # A whitespace is defined as any character which ascii value is less than or equal to 32
213 #
214 # assert " Hello World ! ".trim == "Hello World !"
215 # assert "\na\nb\tc\t".trim == "a\nb\tc"
216 fun trim: String
217 do
218 if self._length == 0 then return self.to_s
219 # find position of the first non white space char (ascii < 32) from the start of the string
220 var start_pos = 0
221 while self[start_pos].ascii <= 32 do
222 start_pos += 1
223 if start_pos == _length then return ""
224 end
225 # find position of the first non white space char from the end of the string
226 var end_pos = length - 1
227 while self[end_pos].ascii <= 32 do
228 end_pos -= 1
229 if end_pos == start_pos then return self[start_pos].to_s
230 end
231 return self.substring(start_pos, end_pos - start_pos + 1)
232 end
233
234 redef fun output
235 do
236 var i = 0
237 while i < length do
238 _items[i].output
239 i += 1
240 end
241 end
242
243 # Mangle a string to be a unique string only made of alphanumeric characters
244 fun to_cmangle: String
245 do
246 var res = new Buffer
247 var underscore = false
248 for c in self do
249 if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then
250 res.add(c)
251 underscore = false
252 continue
253 end
254 if underscore then
255 res.append('_'.ascii.to_s)
256 res.add('d')
257 end
258 if c >= '0' and c <= '9' then
259 res.add(c)
260 underscore = false
261 else if c == '_' then
262 res.add(c)
263 underscore = true
264 else
265 res.add('_')
266 res.append(c.ascii.to_s)
267 res.add('d')
268 underscore = false
269 end
270 end
271 return res.to_s
272 end
273
274 # Escape " \ ' and non printable characters using the rules of literal C strings and characters
275 #
276 # assert "abAB12<>&".escape_to_c == "abAB12<>&"
277 # assert "\n\"'\\".escape_to_c == "\\n\\\"\\'\\\\"
278 fun escape_to_c: String
279 do
280 var b = new Buffer
281 for c in self do
282 if c == '\n' then
283 b.append("\\n")
284 else if c == '\0' then
285 b.append("\\0")
286 else if c == '"' then
287 b.append("\\\"")
288 else if c == '\'' then
289 b.append("\\\'")
290 else if c == '\\' then
291 b.append("\\\\")
292 else if c.ascii < 32 then
293 b.append("\\{c.ascii.to_base(8, false)}")
294 else
295 b.add(c)
296 end
297 end
298 return b.to_s
299 end
300 end
301
302 # Immutable strings of characters.
303 class String
304 super Comparable
305 super AbstractString
306 super StringCapable
307
308 redef type OTHER: String
309
310 # Index in _items of the start of the string
311 readable var _index_from: Int
312
313 # Indes in _items of the last item of the string
314 readable var _index_to: Int
315
316 ################################################
317 # AbstractString specific methods #
318 ################################################
319
320 redef fun [](index) do
321 assert index >= 0
322 # Check that the index (+ index_from) is not larger than indexTo
323 # In other terms, if the index is valid
324 assert (index + _index_from) <= _index_to
325 return _items[index + _index_from]
326 end
327
328 redef fun substring(from: Int, count: Int): String
329 do
330 assert count >= 0
331
332 if from < 0 then
333 count += from
334 if count < 0 then count = 0
335 from = 0
336 end
337
338 var realFrom = _index_from + from
339
340 if (realFrom + count) > _index_to then return new String.from_substring(realFrom, _index_to, _items)
341
342 if count == 0 then return ""
343
344 return new String.from_substring(realFrom, realFrom + count - 1, _items)
345 end
346
347 redef fun substring_from(from: Int): String
348 do
349 if from > _length then return ""
350 if from < 0 then from = 0
351 return substring(from, _length)
352 end
353
354 redef fun has_substring(str: String, pos: Int): Bool
355 do
356 var itsindex = str._length - 1
357
358 var myindex = pos + itsindex
359 var myitems = _items
360
361 var itsitems = str._items
362
363 if myindex > _length or itsindex > myindex then return false
364
365 var itsindexfrom = str.index_from
366 itsindex += itsindexfrom
367 myindex += index_from
368
369 while itsindex >= itsindexfrom do
370 if myitems[myindex] != itsitems[itsindex] then return false
371 myindex -= 1
372 itsindex -= 1
373 end
374
375 return true
376 end
377
378 redef fun to_upper: String
379 do
380 var outstr = calloc_string(self._length + 1)
381 var out_index = 0
382
383 var myitems = self._items
384 var index_from = self._index_from
385 var max = self._index_to
386
387 while index_from <= max do
388 outstr[out_index] = myitems[index_from].to_upper
389 out_index += 1
390 index_from += 1
391 end
392
393 outstr[self.length] = '\0'
394
395 return outstr.to_s_with_length(self._length)
396 end
397
398 redef fun to_lower : String
399 do
400 var outstr = calloc_string(self._length + 1)
401 var out_index = 0
402
403 var myitems = self._items
404 var index_from = self._index_from
405 var max = self._index_to
406
407 while index_from <= max do
408 outstr[out_index] = myitems[index_from].to_lower
409 out_index += 1
410 index_from += 1
411 end
412
413 outstr[self.length] = '\0'
414
415 return outstr.to_s_with_length(self._length)
416 end
417
418 redef fun trim: String
419 do
420 if self._length == 0 then return self
421 # find position of the first non white space char (ascii < 32) from the start of the string
422 var start_pos = self._index_from
423 while _items[start_pos].ascii <= 32 do
424 start_pos += 1
425 if start_pos == _index_to + 1 then return ""
426 end
427 # find position of the first non white space char from the end of the string
428 var end_pos = _index_to
429 while _items[end_pos].ascii <= 32 do
430 end_pos -= 1
431 if end_pos == start_pos then return _items[start_pos].to_s
432 end
433 start_pos -= index_from
434 end_pos -= index_from
435 return self.substring(start_pos, end_pos - start_pos + 1)
436 end
437
438 redef fun output
439 do
440 var i = self._index_from
441 var imax = self._index_to
442 while i <= imax do
443 _items[i].output
444 i += 1
445 end
446 end
447
448 ##################################################
449 # String Specific Methods #
450 ##################################################
451
452 # Creates a String object as a substring of another String
453 #
454 # From : index to start at
455 #
456 # To : Index to stop at (from + count -1)
457 #
458 private init from_substring(from: Int, to: Int, internalString: NativeString)
459 do
460 _items = internalString
461 _index_from = from
462 _index_to = to
463 _length = to - from + 1
464 end
465
466 private init with_infos(items: NativeString, len: Int, from: Int, to: Int)
467 do
468 self._items = items
469 _length = len
470 _index_from = from
471 _index_to = to
472 end
473
474 # Return a null terminated char *
475 fun to_cstring: NativeString
476 do
477 if _index_from > 0 or _index_to != items.cstring_length - 1 then
478 var newItems = calloc_string(_length + 1)
479 self.items.copy_to(newItems, _length, _index_from, 0)
480 newItems[length] = '\0'
481 return newItems
482 end
483 return _items
484 end
485
486 redef fun ==(other)
487 do
488 if not other isa String or other is null then return false
489
490 if self.object_id == other.object_id then return true
491
492 var my_length = _length
493
494 if other._length != my_length then return false
495
496 var my_index = _index_from
497 var its_index = other._index_from
498
499 var last_iteration = my_index + my_length
500
501 var itsitems = other._items
502 var myitems = self._items
503
504 while my_index < last_iteration do
505 if myitems[my_index] != itsitems[its_index] then return false
506 my_index += 1
507 its_index += 1
508 end
509
510 return true
511 end
512
513 # The comparison between two strings is done on a lexicographical basis
514 #
515 # assert ("aa" < "b") == true
516 redef fun <(other)
517 do
518 if self.object_id == other.object_id then return false
519
520 var my_curr_char : Char
521 var its_curr_char : Char
522
523 var curr_id_self = self._index_from
524 var curr_id_other = other._index_from
525
526 var my_items = self._items
527 var its_items = other._items
528
529 var my_length = self._length
530 var its_length = other._length
531
532 var max_iterations = curr_id_self + my_length
533
534 while curr_id_self < max_iterations do
535 my_curr_char = my_items[curr_id_self]
536 its_curr_char = its_items[curr_id_other]
537
538 if my_curr_char != its_curr_char then
539 if my_curr_char < its_curr_char then return true
540 return false
541 end
542
543 curr_id_self += 1
544 curr_id_other += 1
545 end
546
547 return my_length < its_length
548 end
549
550 # The concatenation of `self` with `s`
551 #
552 # assert "hello " + "world!" == "hello world!"
553 fun +(s: String): String
554 do
555 var my_length = self._length
556 var its_length = s._length
557
558 var total_length = my_length + its_length
559
560 var target_string = calloc_string(my_length + its_length + 1)
561
562 self._items.copy_to(target_string, my_length, _index_from, 0)
563 s._items.copy_to(target_string, its_length, s._index_from, my_length)
564
565 target_string[total_length] = '\0'
566
567 return target_string.to_s_with_length(total_length)
568 end
569
570 # `i` repetitions of `self`
571 #
572 # assert "abc"*3 == "abcabcabc"
573 # assert "abc"*1 == "abc"
574 # assert "abc"*0 == ""
575 fun *(i: Int): String
576 do
577 assert i >= 0
578
579 var my_length = self._length
580
581 var final_length = my_length * i
582
583 var my_items = self._items
584
585 var target_string = calloc_string((final_length) + 1)
586
587 target_string[final_length] = '\0'
588
589 var current_last = 0
590
591 for iteration in [1 .. i] do
592 my_items.copy_to(target_string, my_length, 0, current_last)
593 current_last += my_length
594 end
595
596 return target_string.to_s_with_length(final_length)
597 end
598
599 redef fun to_s do return self
600
601 redef fun hash
602 do
603 # djb2 hash algorythm
604 var h = 5381
605 var i = _length - 1
606
607 var myitems = _items
608 var strStart = _index_from
609
610 i += strStart
611
612 while i >= strStart do
613 h = (h * 32) + h + self._items[i].ascii
614 i -= 1
615 end
616
617 return h
618 end
619 end
620
621 # Mutable strings of characters.
622 class Buffer
623 super AbstractString
624 super Comparable
625 super StringCapable
626 super AbstractArray[Char]
627
628 redef type OTHER: String
629
630 redef fun []=(index, item)
631 do
632 if index == length then
633 add(item)
634 return
635 end
636 assert index >= 0 and index < length
637 _items[index] = item
638 end
639
640 redef fun add(c)
641 do
642 if _capacity <= length then enlarge(length + 5)
643 _items[length] = c
644 _length += 1
645 end
646
647 redef fun enlarge(cap)
648 do
649 var c = _capacity
650 if cap <= c then return
651 while c <= cap do c = c * 2 + 2
652 var a = calloc_string(c+1)
653 _items.copy_to(a, length, 0, 0)
654 _items = a
655 _capacity = c
656 end
657
658 redef fun append(s)
659 do
660 if s isa String then
661 var sl = s.length
662 if _capacity < _length + sl then enlarge(_length + sl)
663 s.items.copy_to(_items, sl, s._index_from, _length)
664 _length += sl
665 else
666 super
667 end
668 end
669
670 redef fun to_s: String
671 do
672 var l = length
673 var a = calloc_string(l+1)
674 _items.copy_to(a, l, 0, 0)
675
676 # Ensure the afterlast byte is '\0' to nul-terminated char *
677 a[length] = '\0'
678
679 return a.to_s_with_length(length)
680 end
681
682 redef fun <(s)
683 do
684 var i = 0
685 var l1 = length
686 var l2 = s.length
687 while i < l1 and i < l2 do
688 var c1 = self[i].ascii
689 var c2 = s[i].ascii
690 if c1 < c2 then
691 return true
692 else if c2 < c1 then
693 return false
694 end
695 i += 1
696 end
697 if l1 < l2 then
698 return true
699 else
700 return false
701 end
702 end
703
704 # Create a new empty string.
705 init
706 do
707 with_capacity(5)
708 end
709
710 init from(s: String)
711 do
712 _capacity = s.length + 1
713 _length = s.length
714 _items = calloc_string(_capacity)
715 s.items.copy_to(_items, _length, s._index_from, 0)
716 end
717
718 # Create a new empty string with a given capacity.
719 init with_capacity(cap: Int)
720 do
721 assert cap >= 0
722 # _items = new NativeString.calloc(cap)
723 _items = calloc_string(cap+1)
724 _capacity = cap
725 _length = 0
726 end
727
728 redef fun ==(o)
729 do
730 if not o isa Buffer or o is null then return false
731 var l = length
732 if o.length != l then return false
733 var i = 0
734 var it = _items
735 var oit = o._items
736 while i < l do
737 if it[i] != oit[i] then return false
738 i += 1
739 end
740 return true
741 end
742
743 readable private var _capacity: Int
744 end
745
746 ###############################################################################
747 # Refinement #
748 ###############################################################################
749
750 redef class Object
751 # User readable representation of `self`.
752 fun to_s: String do return inspect
753
754 # The class name of the object in NativeString format.
755 private fun native_class_name: NativeString is intern
756
757 # The class name of the object.
758 #
759 # assert 5.class_name == "Int"
760 fun class_name: String do return native_class_name.to_s
761
762 # Developer readable representation of `self`.
763 # Usually, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
764 fun inspect: String
765 do
766 return "<{inspect_head}>"
767 end
768
769 # Return "CLASSNAME:#OBJECTID".
770 # This function is mainly used with the redefinition of the inspect method
771 protected fun inspect_head: String
772 do
773 return "{class_name}:#{object_id.to_hex}"
774 end
775
776 protected fun args: Sequence[String]
777 do
778 return sys.args
779 end
780 end
781
782 redef class Bool
783 # assert true.to_s == "true"
784 # assert false.to_s == "false"
785 redef fun to_s
786 do
787 if self then
788 return once "true"
789 else
790 return once "false"
791 end
792 end
793 end
794
795 redef class Int
796 # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
797 # assume < to_c max const of char
798 fun fill_buffer(s: Buffer, base: Int, signed: Bool)
799 do
800 var n: Int
801 # Sign
802 if self < 0 then
803 n = - self
804 s[0] = '-'
805 else if self == 0 then
806 s[0] = '0'
807 return
808 else
809 n = self
810 end
811 # Fill digits
812 var pos = digit_count(base) - 1
813 while pos >= 0 and n > 0 do
814 s[pos] = (n % base).to_c
815 n = n / base # /
816 pos -= 1
817 end
818 end
819
820 # C function to convert an nit Int to a NativeString (char*)
821 private fun native_int_to_s(len: Int): NativeString is extern "native_int_to_s"
822
823 # return displayable int in base 10 and signed
824 #
825 # assert 1.to_s == "1"
826 # assert (-123).to_s == "-123"
827 redef fun to_s do
828 var len = digit_count(10)
829 return native_int_to_s(len).to_s_with_length(len)
830 end
831
832 # return displayable int in hexadecimal (unsigned (not now))
833 fun to_hex: String do return to_base(16,false)
834
835 # return displayable int in base base and signed
836 fun to_base(base: Int, signed: Bool): String
837 do
838 var l = digit_count(base)
839 var s = new Buffer.from(" " * l)
840 fill_buffer(s, base, signed)
841 return s.to_s
842 end
843 end
844
845 redef class Float
846 # Pretty print self, print needoed decimals up to a max of 3.
847 redef fun to_s do
848 var str = to_precision( 3 )
849 var len = str.length
850 for i in [0..len-1] do
851 var j = len-1-i
852 var c = str[j]
853 if c == '0' then
854 continue
855 else if c == '.' then
856 return str.substring( 0, j+2 )
857 else
858 return str.substring( 0, j+1 )
859 end
860 end
861 return str
862 end
863
864 # `self` representation with `nb` digits after the '.'.
865 fun to_precision(nb: Int): String
866 do
867 if nb == 0 then return self.to_i.to_s
868 var f = self
869 for i in [0..nb[ do f = f * 10.0
870 if self > 0.0 then
871 f = f + 0.5
872 else
873 f = f - 0.5
874 end
875 var i = f.to_i
876 if i == 0 then return "0.0"
877 var s = i.to_s
878 var sl = s.length
879 if sl > nb then
880 var p1 = s.substring(0, s.length-nb)
881 var p2 = s.substring(s.length-nb, nb)
882 return p1 + "." + p2
883 else
884 return "0." + ("0"*(nb-sl)) + s
885 end
886 end
887
888 fun to_precision_native(nb: Int): String import NativeString::to_s `{
889 int size;
890 char *str;
891
892 size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
893 str = malloc(size + 1);
894 sprintf(str, "%.*f", (int)nb, recv );
895
896 return NativeString_to_s( str );
897 `}
898 end
899
900 redef class Char
901 # assert 'x'.to_s == "x"
902 redef fun to_s
903 do
904 var s = new Buffer.with_capacity(1)
905 s[0] = self
906 return s.to_s
907 end
908
909 # Returns true if the char is a numerical digit
910 fun is_numeric: Bool
911 do
912 if self >= '0' and self <= '9'
913 then
914 return true
915 end
916 return false
917 end
918
919 # Returns true if the char is an alpha digit
920 fun is_alpha: Bool
921 do
922 if (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z') then return true
923 return false
924 end
925
926 # Returns true if the char is an alpha or a numeric digit
927 fun is_alphanumeric: Bool
928 do
929 if self.is_numeric or self.is_alpha then return true
930 return false
931 end
932 end
933
934 redef class Collection[E]
935 # Concatenate elements.
936 redef fun to_s
937 do
938 var s = new Buffer
939 for e in self do if e != null then s.append(e.to_s)
940 return s.to_s
941 end
942
943 # Concatenate and separate each elements with `sep`.
944 #
945 # assert [1, 2, 3].join(":") == "1:2:3"
946 # assert [1..3].join(":") == "1:2:3"
947 fun join(sep: String): String
948 do
949 if is_empty then return ""
950
951 var s = new Buffer # Result
952
953 # Concat first item
954 var i = iterator
955 var e = i.item
956 if e != null then s.append(e.to_s)
957
958 # Concat other items
959 i.next
960 while i.is_ok do
961 s.append(sep)
962 e = i.item
963 if e != null then s.append(e.to_s)
964 i.next
965 end
966 return s.to_s
967 end
968 end
969
970 redef class Array[E]
971 # Fast implementation
972 redef fun to_s
973 do
974 var s = new Buffer
975 var i = 0
976 var l = length
977 while i < l do
978 var e = self[i]
979 if e != null then s.append(e.to_s)
980 i += 1
981 end
982 return s.to_s
983 end
984 end
985
986 redef class Map[K,V]
987 # Concatenate couple of 'key value'.
988 # key and value are separated by `couple_sep`.
989 # each couple is separated each couple with `sep`.
990 #
991 # var m = new ArrayMap[Int, String]
992 # m[1] = "one"
993 # m[10] = "ten"
994 # assert m.join("; ", "=") == "1=one; 10=ten"
995 fun join(sep: String, couple_sep: String): String
996 do
997 if is_empty then return ""
998
999 var s = new Buffer # Result
1000
1001 # Concat first item
1002 var i = iterator
1003 var k = i.key
1004 var e = i.item
1005 if e != null then s.append("{k}{couple_sep}{e}")
1006
1007 # Concat other items
1008 i.next
1009 while i.is_ok do
1010 s.append(sep)
1011 k = i.key
1012 e = i.item
1013 if e != null then s.append("{k}{couple_sep}{e}")
1014 i.next
1015 end
1016 return s.to_s
1017 end
1018 end
1019
1020 ###############################################################################
1021 # Native classes #
1022 ###############################################################################
1023
1024 # Native strings are simple C char *
1025 class NativeString
1026 super StringCapable
1027
1028 fun [](index: Int): Char is intern
1029 fun []=(index: Int, item: Char) is intern
1030 fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
1031
1032 # Position of the first nul character.
1033 fun cstring_length: Int
1034 do
1035 var l = 0
1036 while self[l] != '\0' do l += 1
1037 return l
1038 end
1039 fun atoi: Int is intern
1040 fun atof: Float is extern "atof"
1041
1042 redef fun to_s
1043 do
1044 return to_s_with_length(cstring_length)
1045 end
1046
1047 fun to_s_with_length(length: Int): String
1048 do
1049 assert length >= 0
1050 return new String.with_infos(self, length, 0, length - 1)
1051 end
1052
1053 fun to_s_with_copy: String
1054 do
1055 var length = cstring_length
1056 var new_self = calloc_string(length + 1)
1057 copy_to(new_self, length, 0, 0)
1058 return new String.with_infos(new_self, length, 0, length - 1)
1059 end
1060
1061 end
1062
1063 # StringCapable objects can create native strings
1064 interface StringCapable
1065 protected fun calloc_string(size: Int): NativeString is intern
1066 end
1067
1068 redef class Sys
1069 var _args_cache: nullable Sequence[String]
1070
1071 redef fun args: Sequence[String]
1072 do
1073 if _args_cache == null then init_args
1074 return _args_cache.as(not null)
1075 end
1076
1077 # The name of the program as given by the OS
1078 fun program_name: String
1079 do
1080 return native_argv(0).to_s
1081 end
1082
1083 # Initialize `args` with the contents of `native_argc` and `native_argv`.
1084 private fun init_args
1085 do
1086 var argc = native_argc
1087 var args = new Array[String].with_capacity(0)
1088 var i = 1
1089 while i < argc do
1090 args[i-1] = native_argv(i).to_s
1091 i += 1
1092 end
1093 _args_cache = args
1094 end
1095
1096 # First argument of the main C function.
1097 private fun native_argc: Int is intern
1098
1099 # Second argument of the main C function.
1100 private fun native_argv(i: Int): NativeString is intern
1101 end
1102