stdlib : AbstractString/String, Added trim function and unit test
[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 if my_length != its_length then
504 if my_length < its_length then return true
505 return false
506 end
507
508 return false
509 end
510
511 # The concatenation of `self' with `r'
512 fun +(s: String): String
513 do
514 var my_length = self._length
515 var its_length = s._length
516
517 var target_string = calloc_string(my_length + its_length + 1)
518
519 self._items.copy_to(target_string, my_length, _index_from, 0)
520 s._items.copy_to(target_string, its_length, s._index_from, my_length)
521
522 target_string[my_length + its_length] = '\0'
523
524 return new String.with_native(target_string, my_length + its_length)
525 end
526
527 # i repetitions of self
528 fun *(i: Int): String
529 do
530 assert i >= 0
531
532 var my_length = self._length
533
534 var final_length = my_length * i
535
536 var my_items = self._items
537
538 var target_string = calloc_string((final_length) + 1)
539
540 target_string[final_length] = '\0'
541
542 var current_last = 0
543
544 for iteration in [1 .. i] do
545 my_items.copy_to(target_string, my_length, 0, current_last)
546 current_last += my_length
547 end
548
549 return new String.with_native(target_string, final_length)
550 end
551
552 redef fun to_s do return self
553
554 redef fun hash
555 do
556 # djb2 hash algorythm
557 var h = 5381
558 var i = _length - 1
559
560 var myitems = _items
561 var strStart = _index_from
562
563 i += strStart
564
565 while i >= strStart do
566 h = (h * 32) + h + self._items[i].ascii
567 i -= 1
568 end
569
570 return h
571 end
572 end
573
574 # Mutable strings of characters.
575 class Buffer
576 super AbstractString
577 super Comparable
578 super StringCapable
579 super AbstractArray[Char]
580
581 redef type OTHER: String
582
583 redef fun []=(index, item)
584 do
585 if index == length then
586 add(item)
587 return
588 end
589 assert index >= 0 and index < length
590 _items[index] = item
591 end
592
593 redef fun add(c)
594 do
595 if _capacity <= length then enlarge(length + 5)
596 _items[length] = c
597 _length += 1
598 end
599
600 redef fun enlarge(cap)
601 do
602 var c = _capacity
603 if cap <= c then return
604 while c <= cap do c = c * 2 + 2
605 var a = calloc_string(c+1)
606 _items.copy_to(a, length, 0, 0)
607 _items = a
608 _capacity = c
609 end
610
611 redef fun append(s)
612 do
613 if s isa String then
614 var sl = s.length
615 if _capacity < _length + sl then enlarge(_length + sl)
616 s.items.copy_to(_items, sl, s._index_from, _length)
617 _length += sl
618 else
619 super
620 end
621 end
622
623 redef fun to_s: String
624 do
625 var l = length
626 var a = calloc_string(l+1)
627 _items.copy_to(a, l, 0, 0)
628
629 # Ensure the afterlast byte is '\0' to nul-terminated char *
630 a[length] = '\0'
631
632 return new String.with_native(a, length)
633 end
634
635 redef fun <(s)
636 do
637 var i = 0
638 var l1 = length
639 var l2 = s.length
640 while i < l1 and i < l2 do
641 var c1 = self[i].ascii
642 var c2 = s[i].ascii
643 if c1 < c2 then
644 return true
645 else if c2 < c1 then
646 return false
647 end
648 i += 1
649 end
650 if l1 < l2 then
651 return true
652 else
653 return false
654 end
655 end
656
657 # Create a new empty string.
658 init
659 do
660 with_capacity(5)
661 end
662
663 init from(s: String)
664 do
665 _capacity = s.length + 1
666 _length = s.length
667 _items = calloc_string(_capacity)
668 s.items.copy_to(_items, _length, s._index_from, 0)
669 end
670
671 # Create a new empty string with a given capacity.
672 init with_capacity(cap: Int)
673 do
674 assert cap >= 0
675 # _items = new NativeString.calloc(cap)
676 _items = calloc_string(cap+1)
677 _capacity = cap
678 _length = 0
679 end
680
681 redef fun ==(o)
682 do
683 if not o isa Buffer or o is null then return false
684 var l = length
685 if o.length != l then return false
686 var i = 0
687 var it = _items
688 var oit = o._items
689 while i < l do
690 if it[i] != oit[i] then return false
691 i += 1
692 end
693 return true
694 end
695
696 readable private var _capacity: Int
697 end
698
699 ###############################################################################
700 # Refinement #
701 ###############################################################################
702
703 redef class Object
704 # User readable representation of `self'.
705 fun to_s: String do return inspect
706
707 # The class name of the object in NativeString format.
708 private fun native_class_name: NativeString is intern
709
710 # The class name of the object.
711 # FIXME: real type information is not available at runtime.
712 # Therefore, for instance, an instance of List[Bool] has just
713 # "List" for class_name
714 fun class_name: String do return new String.from_cstring(native_class_name)
715
716 # Developer readable representation of `self'.
717 # Usually, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
718 fun inspect: String
719 do
720 return "<{inspect_head}>"
721 end
722
723 # Return "CLASSNAME:#OBJECTID".
724 # This function is mainly used with the redefinition of the inspect method
725 protected fun inspect_head: String
726 do
727 return "{class_name}:#{object_id.to_hex}"
728 end
729
730 protected fun args: Sequence[String]
731 do
732 return sys.args
733 end
734 end
735
736 redef class Bool
737 redef fun to_s
738 do
739 if self then
740 return once "true"
741 else
742 return once "false"
743 end
744 end
745 end
746
747 redef class Int
748 fun fill_buffer(s: Buffer, base: Int, signed: Bool)
749 # Fill `s' with the digits in base 'base' of `self' (and with the '-' sign if 'signed' and negative).
750 # assume < to_c max const of char
751 do
752 var n: Int
753 # Sign
754 if self < 0 then
755 n = - self
756 s[0] = '-'
757 else if self == 0 then
758 s[0] = '0'
759 return
760 else
761 n = self
762 end
763 # Fill digits
764 var pos = digit_count(base) - 1
765 while pos >= 0 and n > 0 do
766 s[pos] = (n % base).to_c
767 n = n / base # /
768 pos -= 1
769 end
770 end
771
772 # return displayable int in base 10 and signed
773 redef fun to_s do return to_base(10,true)
774
775 # return displayable int in hexadecimal (unsigned (not now))
776 fun to_hex: String do return to_base(16,false)
777
778 # return displayable int in base base and signed
779 fun to_base(base: Int, signed: Bool): String
780 do
781 var l = digit_count(base)
782 var s = new Buffer.from(" " * l)
783 fill_buffer(s, base, signed)
784 return s.to_s
785 end
786 end
787
788 redef class Float
789 # Pretty print self, print needed decimals up to a max of 6.
790 redef fun to_s do
791 var str = to_precision( 6 )
792 var len = str.length
793 for i in [0..len-1] do
794 var j = len-1-i
795 var c = str[j]
796 if c == '0' then
797 continue
798 else if c == '.' then
799 return str.substring( 0, j+2 )
800 else
801 return str.substring( 0, j+1 )
802 end
803 end
804 return str
805 end
806
807 # `self' representation with `nb' digits after the '.'.
808 fun to_precision(nb: Int): String import String::from_cstring `{
809 int size;
810 char *str;
811
812 size = snprintf(NULL, 0, "%.*f", (int)nb, recv);
813 str = malloc(size + 1);
814 sprintf(str, "%.*f", (int)nb, recv );
815
816 return new_String_from_cstring( str );
817 `}
818 end
819
820 redef class Char
821 redef fun to_s
822 do
823 var s = new Buffer.with_capacity(1)
824 s[0] = self
825 return s.to_s
826 end
827
828 # Returns true if the char is a numerical digit
829 fun is_numeric: Bool
830 do
831 if self >= '0' and self <= '9'
832 then
833 return true
834 end
835 return false
836 end
837
838 # Returns true if the char is an alpha digit
839 fun is_alpha: Bool
840 do
841 if (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z') then return true
842 return false
843 end
844
845 # Returns true if the char is an alpha or a numeric digit
846 fun is_alphanumeric: Bool
847 do
848 if self.is_numeric or self.is_alpha then return true
849 return false
850 end
851 end
852
853 redef class Collection[E]
854 # Concatenate elements.
855 redef fun to_s
856 do
857 var s = new Buffer
858 for e in self do if e != null then s.append(e.to_s)
859 return s.to_s
860 end
861
862 # Concatenate and separate each elements with `sep'.
863 fun join(sep: String): String
864 do
865 if is_empty then return ""
866
867 var s = new Buffer # Result
868
869 # Concat first item
870 var i = iterator
871 var e = i.item
872 if e != null then s.append(e.to_s)
873
874 # Concat other items
875 i.next
876 while i.is_ok do
877 s.append(sep)
878 e = i.item
879 if e != null then s.append(e.to_s)
880 i.next
881 end
882 return s.to_s
883 end
884 end
885
886 redef class Array[E]
887 # Fast implementation
888 redef fun to_s
889 do
890 var s = new Buffer
891 var i = 0
892 var l = length
893 while i < l do
894 var e = self[i]
895 if e != null then s.append(e.to_s)
896 i += 1
897 end
898 return s.to_s
899 end
900 end
901
902 redef class Map[K,V]
903 # Concatenate couple of 'key value'.
904 # key and value are separated by 'couple_sep'.
905 # each couple is separated each couple with `sep'.
906 fun join(sep: String, couple_sep: String): String
907 do
908 if is_empty then return ""
909
910 var s = new Buffer # Result
911
912 # Concat first item
913 var i = iterator
914 var k = i.key
915 var e = i.item
916 if e != null then s.append("{k}{couple_sep}{e}")
917
918 # Concat other items
919 i.next
920 while i.is_ok do
921 s.append(sep)
922 k = i.key
923 e = i.item
924 if e != null then s.append("{k}{couple_sep}{e}")
925 i.next
926 end
927 return s.to_s
928 end
929 end
930
931 ###############################################################################
932 # Native classes #
933 ###############################################################################
934
935 # Native strings are simple C char *
936 class NativeString
937 fun [](index: Int): Char is intern
938 fun []=(index: Int, item: Char) is intern
939 fun copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
940
941 # Position of the first nul character.
942 fun cstring_length: Int
943 do
944 var l = 0
945 while self[l] != '\0' do l += 1
946 return l
947 end
948 fun atoi: Int is intern
949 fun atof: Float is extern "atof"
950 end
951
952 # StringCapable objects can create native strings
953 interface StringCapable
954 protected fun calloc_string(size: Int): NativeString is intern
955 end
956
957 redef class Sys
958 var _args_cache: nullable Sequence[String]
959
960 redef fun args: Sequence[String]
961 do
962 if _args_cache == null then init_args
963 return _args_cache.as(not null)
964 end
965
966 # The name of the program as given by the OS
967 fun program_name: String
968 do
969 return new String.from_cstring(native_argv(0))
970 end
971
972 # Initialize `args' with the contents of `native_argc' and `native_argv'.
973 private fun init_args
974 do
975 var argc = native_argc
976 var args = new Array[String].with_capacity(0)
977 var i = 1
978 while i < argc do
979 args[i-1] = new String.from_cstring(native_argv(i))
980 i += 1
981 end
982 _args_cache = args
983 end
984
985 private fun native_argc: Int is extern "kernel_Sys_Sys_native_argc_0" # First argument of the main C function.
986
987 private fun native_argv(i: Int): NativeString is extern "kernel_Sys_Sys_native_argv_1" # Second argument of the main C function.
988 end
989