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