lib: add some examples/unit tests
[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, ) == "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 == -1.2e-3
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 end
243
244 # Immutable strings of characters.
245 class String
246 super Comparable
247 super AbstractString
248 super StringCapable
249
250 redef type OTHER: String
251
252 # Index in _items of the start of the string
253 readable var _index_from: Int
254
255 # Indes in _items of the last item of the string
256 readable var _index_to: Int
257
258 ################################################
259 # AbstractString specific methods #
260 ################################################
261
262 redef fun [](index) do
263 assert index >= 0
264 # Check that the index (+ index_from) is not larger than indexTo
265 # In other terms, if the index is valid
266 assert (index + _index_from) <= _index_to
267 return _items[index + _index_from]
268 end
269
270 redef fun substring(from: Int, count: Int): String
271 do
272 assert count >= 0
273
274 if from < 0 then
275 count += from
276 if count < 0 then count = 0
277 from = 0
278 end
279
280 var realFrom = _index_from + from
281
282 if (realFrom + count) > _index_to then return new String.from_substring(realFrom, _index_to, _items)
283
284 if count == 0 then return ""
285
286 return new String.from_substring(realFrom, realFrom + count - 1, _items)
287 end
288
289 redef fun substring_from(from: Int): String
290 do
291 if from > _length then return ""
292 if from < 0 then from = 0
293 return substring(from, _length)
294 end
295
296 redef fun has_substring(str: String, pos: Int): Bool
297 do
298 var itsindex = str._length - 1
299
300 var myindex = pos + itsindex
301 var myitems = _items
302
303 var itsitems = str._items
304
305 if myindex > _length or itsindex > myindex then return false
306
307 var itsindexfrom = str.index_from
308 itsindex += itsindexfrom
309 myindex += index_from
310
311 while itsindex >= itsindexfrom do
312 if myitems[myindex] != itsitems[itsindex] then return false
313 myindex -= 1
314 itsindex -= 1
315 end
316
317 return true
318 end
319
320 redef fun to_upper: String
321 do
322 var outstr = calloc_string(self._length + 1)
323 var out_index = 0
324
325 var myitems = self._items
326 var index_from = self._index_from
327 var max = self._index_to
328
329 while index_from <= max do
330 outstr[out_index] = myitems[index_from].to_upper
331 out_index += 1
332 index_from += 1
333 end
334
335 outstr[self.length] = '\0'
336
337 return new String.with_native(outstr, self._length)
338 end
339
340 redef fun to_lower : String
341 do
342 var outstr = calloc_string(self._length + 1)
343 var out_index = 0
344
345 var myitems = self._items
346 var index_from = self._index_from
347 var max = self._index_to
348
349 while index_from <= max do
350 outstr[out_index] = myitems[index_from].to_lower
351 out_index += 1
352 index_from += 1
353 end
354
355 outstr[self.length] = '\0'
356
357 return new String.with_native(outstr, self._length)
358 end
359
360 redef fun trim: String
361 do
362 if self._length == 0 then return self
363 # find position of the first non white space char (ascii < 32) from the start of the string
364 var start_pos = self._index_from
365 while _items[start_pos].ascii <= 32 do
366 start_pos += 1
367 if start_pos == _index_to + 1 then return ""
368 end
369 # find position of the first non white space char from the end of the string
370 var end_pos = _index_to
371 while _items[end_pos].ascii <= 32 do
372 end_pos -= 1
373 if end_pos == start_pos then return _items[start_pos].to_s
374 end
375 start_pos -= index_from
376 end_pos -= index_from
377 return self.substring(start_pos, end_pos - start_pos + 1)
378 end
379
380 redef fun output
381 do
382 var i = self._index_from
383 var imax = self._index_to
384 while i <= imax do
385 _items[i].output
386 i += 1
387 end
388 end
389
390 ##################################################
391 # String Specific Methods #
392 ##################################################
393
394 # Creates a String object as a substring of another String
395 #
396 # From : index to start at
397 #
398 # To : Index to stop at (from + count -1)
399 #
400 private init from_substring(from: Int, to: Int, internalString: NativeString)
401 do
402 _items = internalString
403 _index_from = from
404 _index_to = to
405 _length = to - from + 1
406 end
407
408 # Create a new string from a given char *.
409 init with_native(nat: NativeString, size: Int)
410 do
411 assert size >= 0
412 _items = nat
413 _length = size
414 _index_from = 0
415 _index_to = _length - 1
416 end
417
418 # Create a new string from a null terminated char *.
419 init from_cstring(str: NativeString)
420 do
421 with_native(str,str.cstring_length)
422 end
423
424 # Creates a new Nit String from an existing CString
425 # Pretty much equals to from_cstring but copies instead
426 # of passing a reference
427 # Avoids manual/automatic dealloc problems when dealing with native C code
428 init copy_from_native(str: NativeString)
429 do
430 var temp_length = str.cstring_length
431 var new_str = calloc_string(temp_length + 1)
432 str.copy_to(new_str, temp_length, 0, 0)
433 new_str[temp_length] = '\0'
434 with_native(new_str, temp_length)
435 end
436
437 # Return a null terminated char *
438 fun to_cstring: NativeString
439 do
440 #return items
441 if _index_from > 0 or _index_to != items.cstring_length - 1 then
442 var newItems = calloc_string(_length + 1)
443 self.items.copy_to(newItems, _length, _index_from, 0)
444 newItems[length] = '\0'
445 return newItems
446 end
447 return _items
448 end
449
450 redef fun ==(other)
451 do
452 if not other isa String or other is null then return false
453
454 if self.object_id == other.object_id then return true
455
456 var my_length = _length
457
458 if other._length != my_length then return false
459
460 var my_index = _index_from
461 var its_index = other._index_from
462
463 var last_iteration = my_index + my_length
464
465 var itsitems = other._items
466 var myitems = self._items
467
468 while my_index < last_iteration do
469 if myitems[my_index] != itsitems[its_index] then return false
470 my_index += 1
471 its_index += 1
472 end
473
474 return true
475 end
476
477 # The comparison between two strings is done on a lexicographical basis
478 #
479 # assert "aa" < "b" == true
480 redef fun <(other)
481 do
482 if self.object_id == other.object_id then return false
483
484 var my_curr_char : Char
485 var its_curr_char : Char
486
487 var curr_id_self = self._index_from
488 var curr_id_other = other._index_from
489
490 var my_items = self._items
491 var its_items = other._items
492
493 var my_length = self._length
494 var its_length = other._length
495
496 var max_iterations = curr_id_self + my_length
497
498 while curr_id_self < max_iterations do
499 my_curr_char = my_items[curr_id_self]
500 its_curr_char = its_items[curr_id_other]
501
502 if my_curr_char != its_curr_char then
503 if my_curr_char < its_curr_char then return true
504 return false
505 end
506
507 curr_id_self += 1
508 curr_id_other += 1
509 end
510
511 return my_length < its_length
512 end
513
514 # The concatenation of `self` with `s`
515 #
516 # assert "hello " + "world!" == "hello world!"
517 fun +(s: String): String
518 do
519 var my_length = self._length
520 var its_length = s._length
521
522 var target_string = calloc_string(my_length + its_length + 1)
523
524 self._items.copy_to(target_string, my_length, _index_from, 0)
525 s._items.copy_to(target_string, its_length, s._index_from, my_length)
526
527 target_string[my_length + its_length] = '\0'
528
529 return new String.with_native(target_string, my_length + its_length)
530 end
531
532 # `i` repetitions of `self`
533 #
534 # assert "abc"*3 == "abcabcabc"
535 # assert "abc"*1 == "abc"
536 # assert "abc"*0 == ""
537 fun *(i: Int): String
538 do
539 assert i >= 0
540
541 var my_length = self._length
542
543 var final_length = my_length * i
544
545 var my_items = self._items
546
547 var target_string = calloc_string((final_length) + 1)
548
549 target_string[final_length] = '\0'
550
551 var current_last = 0
552
553 for iteration in [1 .. i] do
554 my_items.copy_to(target_string, my_length, 0, current_last)
555 current_last += my_length
556 end
557
558 return new String.with_native(target_string, final_length)
559 end
560
561 redef fun to_s do return self
562
563 redef fun hash
564 do
565 # djb2 hash algorythm
566 var h = 5381
567 var i = _length - 1
568
569 var myitems = _items
570 var strStart = _index_from
571
572 i += strStart
573
574 while i >= strStart do
575 h = (h * 32) + h + self._items[i].ascii
576 i -= 1
577 end
578
579 return h
580 end
581 end
582
583 # Mutable strings of characters.
584 class Buffer
585 super AbstractString
586 super Comparable
587 super StringCapable
588 super AbstractArray[Char]
589
590 redef type OTHER: String
591
592 redef fun []=(index, item)
593 do
594 if index == length then
595 add(item)
596 return
597 end
598 assert index >= 0 and index < length
599 _items[index] = item
600 end
601
602 redef fun add(c)
603 do
604 if _capacity <= length then enlarge(length + 5)
605 _items[length] = c
606 _length += 1
607 end
608
609 redef fun enlarge(cap)
610 do
611 var c = _capacity
612 if cap <= c then return
613 while c <= cap do c = c * 2 + 2
614 var a = calloc_string(c+1)
615 _items.copy_to(a, length, 0, 0)
616 _items = a
617 _capacity = c
618 end
619
620 redef fun append(s)
621 do
622 if s isa String then
623 var sl = s.length
624 if _capacity < _length + sl then enlarge(_length + sl)
625 s.items.copy_to(_items, sl, s._index_from, _length)
626 _length += sl
627 else
628 super
629 end
630 end
631
632 redef fun to_s: String
633 do
634 var l = length
635 var a = calloc_string(l+1)
636 _items.copy_to(a, l, 0, 0)
637
638 # Ensure the afterlast byte is '\0' to nul-terminated char *
639 a[length] = '\0'
640
641 return new String.with_native(a, length)
642 end
643
644 redef fun <(s)
645 do
646 var i = 0
647 var l1 = length
648 var l2 = s.length
649 while i < l1 and i < l2 do
650 var c1 = self[i].ascii
651 var c2 = s[i].ascii
652 if c1 < c2 then
653 return true
654 else if c2 < c1 then
655 return false
656 end
657 i += 1
658 end
659 if l1 < l2 then
660 return true
661 else
662 return false
663 end
664 end
665
666 # Create a new empty string.
667 init
668 do
669 with_capacity(5)
670 end
671
672 init from(s: String)
673 do
674 _capacity = s.length + 1
675 _length = s.length
676 _items = calloc_string(_capacity)
677 s.items.copy_to(_items, _length, s._index_from, 0)
678 end
679
680 # Create a new empty string with a given capacity.
681 init with_capacity(cap: Int)
682 do
683 assert cap >= 0
684 # _items = new NativeString.calloc(cap)
685 _items = calloc_string(cap+1)
686 _capacity = cap
687 _length = 0
688 end
689
690 redef fun ==(o)
691 do
692 if not o isa Buffer or o is null then return false
693 var l = length
694 if o.length != l then return false
695 var i = 0
696 var it = _items
697 var oit = o._items
698 while i < l do
699 if it[i] != oit[i] then return false
700 i += 1
701 end
702 return true
703 end
704
705 readable private var _capacity: Int
706 end
707
708 ###############################################################################
709 # Refinement #
710 ###############################################################################
711
712 redef class Object
713 # User readable representation of `self`.
714 fun to_s: String do return inspect
715
716 # The class name of the object in NativeString format.
717 private fun native_class_name: NativeString is intern
718
719 # The class name of the object.
720 #
721 # assert 5.class_name == "Int"
722 fun class_name: String do return new String.from_cstring(native_class_name)
723
724 # Developer readable representation of `self`.
725 # Usually, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
726 fun inspect: String
727 do
728 return "<{inspect_head}>"
729 end
730
731 # Return "CLASSNAME:#OBJECTID".
732 # This function is mainly used with the redefinition of the inspect method
733 protected fun inspect_head: String
734 do
735 return "{class_name}:#{object_id.to_hex}"
736 end
737
738 protected fun args: Sequence[String]
739 do
740 return sys.args
741 end
742 end
743
744 redef class Bool
745 # assert true.to_s == "true"
746 # assert false.to_s == "false"
747 redef fun to_s
748 do
749 if self then
750 return once "true"
751 else
752 return once "false"
753 end
754 end
755 end
756
757 redef class Int
758 # Fill `s` with the digits in base `base` of `self` (and with the '-' sign if 'signed' and negative).
759 # assume < to_c max const of char
760 fun fill_buffer(s: Buffer, base: Int, signed: Bool)
761 do
762 var n: Int
763 # Sign
764 if self < 0 then
765 n = - self
766 s[0] = '-'
767 else if self == 0 then
768 s[0] = '0'
769 return
770 else
771 n = self
772 end
773 # Fill digits
774 var pos = digit_count(base) - 1
775 while pos >= 0 and n > 0 do
776 s[pos] = (n % base).to_c
777 n = n / base # /
778 pos -= 1
779 end
780 end
781
782 # C function to convert an nit Int to a NativeString (char*)
783 private fun native_int_to_s(len: Int): NativeString is extern "native_int_to_s"
784
785 # return displayable int in base 10 and signed
786 #
787 # assert 1.to_s == "1"
788 # assert (-123).to_s == "-123"
789 redef fun to_s do
790 var len = digit_count(10)
791 return new String.from_cstring(native_int_to_s(len))
792 end
793
794 # return displayable int in hexadecimal (unsigned (not now))
795 fun to_hex: String do return to_base(16,false)
796
797 # return displayable int in base base and signed
798 fun to_base(base: Int, signed: Bool): String
799 do
800 var l = digit_count(base)
801 var s = new Buffer.from(" " * l)
802 fill_buffer(s, base, signed)
803 return s.to_s
804 end
805 end
806
807 redef class Float
808 # Pretty print self, print needoed decimals up to a max of 3.
809 redef fun to_s do
810 var str = to_precision( 3 )
811 var len = str.length
812 for i in [0..len-1] do
813 var j = len-1-i
814 var c = str[j]
815 if c == '0' then
816 continue
817 else if c == '.' then
818 return str.substring( 0, j+2 )
819 else
820 return str.substring( 0, j+1 )
821 end
822 end
823 return str
824 end
825
826 # `self` representation with `nb` digits after the '.'.
827 fun to_precision(nb: Int): String
828 do
829 if nb == 0 then return self.to_i.to_s
830 var f = self
831 for i in [0..nb[ do f = f * 10.0
832 if self > 0.0 then
833 f = f + 0.5
834 else
835 f = f - 0.5
836 end
837 var i = f.to_i
838 if i == 0 then return "0.0"
839 var s = i.to_s
840 var sl = s.length
841 if sl > nb then
842 var p1 = s.substring(0, s.length-nb)
843 var p2 = s.substring(s.length-nb, nb)
844 return p1 + "." + p2
845 else
846 return "0." + ("0"*(nb-sl)) + s
847 end
848 end
849
850 fun to_precision_native(nb: Int): String import String::from_cstring `{
851 int size;
852 char *str;
853
854 size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
855 str = malloc(size + 1);
856 sprintf(str, "%.*f", (int)nb, recv );
857
858 return new_String_from_cstring( str );
859 `}
860 end
861
862 redef class Char
863 # assert 'x'.to_s == "x"
864 redef fun to_s
865 do
866 var s = new Buffer.with_capacity(1)
867 s[0] = self
868 return s.to_s
869 end
870
871 # Returns true if the char is a numerical digit
872 fun is_numeric: Bool
873 do
874 if self >= '0' and self <= '9'
875 then
876 return true
877 end
878 return false
879 end
880
881 # Returns true if the char is an alpha digit
882 fun is_alpha: Bool
883 do
884 if (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z') then return true
885 return false
886 end
887
888 # Returns true if the char is an alpha or a numeric digit
889 fun is_alphanumeric: Bool
890 do
891 if self.is_numeric or self.is_alpha then return true
892 return false
893 end
894 end
895
896 redef class Collection[E]
897 # Concatenate elements.
898 redef fun to_s
899 do
900 var s = new Buffer
901 for e in self do if e != null then s.append(e.to_s)
902 return s.to_s
903 end
904
905 # Concatenate and separate each elements with `sep`.
906 #
907 # assert [1, 2, 3].join(":") == "1:2:3"
908 # assert [1..3].join(":") == "1:2:3"
909 fun join(sep: String): String
910 do
911 if is_empty then return ""
912
913 var s = new Buffer # Result
914
915 # Concat first item
916 var i = iterator
917 var e = i.item
918 if e != null then s.append(e.to_s)
919
920 # Concat other items
921 i.next
922 while i.is_ok do
923 s.append(sep)
924 e = i.item
925 if e != null then s.append(e.to_s)
926 i.next
927 end
928 return s.to_s
929 end
930 end
931
932 redef class Array[E]
933 # Fast implementation
934 redef fun to_s
935 do
936 var s = new Buffer
937 var i = 0
938 var l = length
939 while i < l do
940 var e = self[i]
941 if e != null then s.append(e.to_s)
942 i += 1
943 end
944 return s.to_s
945 end
946 end
947
948 redef class Map[K,V]
949 # Concatenate couple of 'key value'.
950 # key and value are separated by `couple_sep`.
951 # each couple is separated each couple with `sep`.
952 #
953 # var m = new ArrayMap[Int, String]
954 # m[1] = "one"
955 # m[10] = "ten"
956 # assert m.join("; ", "=") == "1=one; 10=ten"
957 fun join(sep: String, couple_sep: String): String
958 do
959 if is_empty then return ""
960
961 var s = new Buffer # Result
962
963 # Concat first item
964 var i = iterator
965 var k = i.key
966 var e = i.item
967 if e != null then s.append("{k}{couple_sep}{e}")
968
969 # Concat other items
970 i.next
971 while i.is_ok do
972 s.append(sep)
973 k = i.key
974 e = i.item
975 if e != null then s.append("{k}{couple_sep}{e}")
976 i.next
977 end
978 return s.to_s
979 end
980 end
981
982 ###############################################################################
983 # Native classes #
984 ###############################################################################
985
986 # Native strings are simple C char *
987 class NativeString
988 fun [](index: Int): Char is intern
989 fun []=(index: Int, item: Char) is intern
990 fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
991
992 # Position of the first nul character.
993 fun cstring_length: Int
994 do
995 var l = 0
996 while self[l] != '\0' do l += 1
997 return l
998 end
999 fun atoi: Int is intern
1000 fun atof: Float is extern "atof"
1001 end
1002
1003 # StringCapable objects can create native strings
1004 interface StringCapable
1005 protected fun calloc_string(size: Int): NativeString is intern
1006 end
1007
1008 redef class Sys
1009 var _args_cache: nullable Sequence[String]
1010
1011 redef fun args: Sequence[String]
1012 do
1013 if _args_cache == null then init_args
1014 return _args_cache.as(not null)
1015 end
1016
1017 # The name of the program as given by the OS
1018 fun program_name: String
1019 do
1020 return new String.from_cstring(native_argv(0))
1021 end
1022
1023 # Initialize `args` with the contents of `native_argc` and `native_argv`.
1024 private fun init_args
1025 do
1026 var argc = native_argc
1027 var args = new Array[String].with_capacity(0)
1028 var i = 1
1029 while i < argc do
1030 args[i-1] = new String.from_cstring(native_argv(i))
1031 i += 1
1032 end
1033 _args_cache = args
1034 end
1035
1036 # First argument of the main C function.
1037 private fun native_argc: Int is intern
1038
1039 # Second argument of the main C function.
1040 private fun native_argv(i: Int): NativeString is intern
1041 end
1042