First NIT release and new clean mercurial repository
[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 # This module is about character strings.
15 package string
16
17 intrude import array
18
19 ###############################################################################
20 # String #
21 ###############################################################################
22
23 # Strings are arrays of characters.
24 class String
25 special AbstractArray[Char]
26 special Comparable
27 special StringCapable
28
29 redef type OTHER: String
30
31 redef meth [](index) do return _items[index]
32
33 redef meth []=(index, item)
34 do
35 if index == length then
36 add(item)
37 return
38 end
39 assert index >= 0 and index < length
40 _items[index] = item
41 end
42
43 redef meth add(c)
44 do
45 if _capacity <= length then enlarge(length + 5)
46 _items[length] = c
47 _length += 1
48 end
49
50 redef meth enlarge(cap)
51 do
52 var c = _capacity
53 if cap <= c then return
54 while c <= cap do c = c * 2 + 2
55 var a = calloc_string(c)
56 _items.copy_to(a, length, 0, 0)
57 _items = a
58 _capacity = c
59 end
60
61 redef meth append(s)
62 do
63 if s isa String then
64 var sl = s.length
65 if _capacity < length + sl then enlarge(length + sl)
66 s.items.copy_to(_items, sl, 0, length)
67 _length += sl
68 else
69 super
70 end
71 end
72
73 # The concatenation of `self' with `r'
74 meth +(s: String): String
75 do
76 var r = new String.with_capacity(length + s.length)
77 r.append(self)
78 r.append(s)
79 return r
80 end
81
82 # i repetitions of self
83 meth *(i: Int): String
84 do
85 assert i >= 0
86 var r = new String.with_capacity(length * i)
87 while i > 0 do
88 r.append(self)
89 i -= 1
90 end
91 return r
92 end
93
94 # Clone.
95 redef meth to_s: String do return new String.from(self)
96
97 # If `self' contains only digits, return the corresponding integer
98 meth to_i: Int
99 do
100 # Shortcut
101 return to_cstring.atoi
102 end
103
104 # If `self' contains only digits and alpha <= 'f', return the corresponding integer.
105 meth to_hex: Int do return a_to(16)
106
107 # If `self' contains only digits and letters, return the corresponding integer in a given base
108 meth a_to(base: Int) : Int
109 do
110 var i = 0
111 var neg = false
112
113 for c in self
114 do
115 var v = c.to_i
116 if v > base then
117 if neg then
118 return -i
119 else
120 return i
121 end
122 else if v < 0 then
123 neg = true
124 else
125 i = i * base + v
126 end
127 end
128 if neg then
129 return -i
130 else
131 return i
132 end
133 end
134
135 # Return a null terminated char *
136 meth to_cstring: NativeString
137 do
138 self[length] = '\0'
139 _length -= 1
140 return _items
141 end
142
143 # Create a substring.
144 #
145 # "abcd".substring(1, 2) # --> "bc"
146 # "abcd".substring(-1, 2) # --> "a"
147 # "abcd".substring(1, 0) # --> ""
148 # "abcd".substring(2, 5) # --> "cd"
149 meth substring(from: Int, count: Int): String
150 do
151 assert count >= 0
152 count += from
153 if from < 0 then from = 0
154 if count > length then count = length
155 if from < count then
156 var r = new String.with_capacity(count - from)
157 while from < count do
158 r.push(_items[from])
159 from += 1
160 end
161 return r
162 else
163 return ""
164 end
165 end
166
167 # Create a substring with the string beginning at the 'from' position
168 #
169 # "abcd".substring(1) # --> "bcd"
170 # "abcd".substring(-1) # --> "abcd"
171 # "abcd".substring(2) # --> "cd"
172 meth substring_from(from: Int): String
173 do
174 assert from < length
175 return substring(from, length - from)
176 end
177
178 # is this string a substring of the 'of' string from pos 'pos'
179 #
180 # "bc".is_substring("abcd",1) # --> true
181 # "bc".is_substring("abcd",2) # --> false
182 meth has_substring(str: String, pos: Int): Bool
183 do
184 var itsindex = str.length - 1
185 var myindex = pos + itsindex
186 var myitems = _items
187 var itsitems = str._items
188 if myindex > length or itsindex > myindex then return false
189 while itsindex > 0 do
190 if myitems[myindex] != itsitems[itsindex] then return false
191 myindex -= myindex
192 itsindex -= itsindex
193 end
194 return true
195 end
196
197 # Is this string prefixed by 'prefix'
198 #
199 # "abc".is_prefix("abcd") # --> true
200 # "bc".is_prefix("abcd") # --> false
201 meth has_prefix(prefix: String): Bool do return has_substring(prefix,0)
202
203 # Is this string suffixed by 'suffix'
204 #
205 # "abcd".has_suffix("abc") # --> false
206 # "abcd".has_suffix("bcd") # --> true
207 meth has_suffix(suffix: String): Bool do return has_substring(suffix, length - suffix.length)
208
209 redef meth <(s)
210 do
211 var i = 0
212 var l1 = length
213 var l2 = s.length
214 while i < l1 and i < l2 do
215 var c1 = self[i].ascii
216 var c2 = s[i].ascii
217 if c1 < c2 then
218 return true
219 else if c2 < c1 then
220 return false
221 end
222 i += 1
223 end
224 if l1 < l2 then
225 return true
226 else
227 return false
228 end
229 end
230
231 # Create a new empty string.
232 init
233 do
234 with_capacity(5)
235 end
236
237 init from(s: String)
238 do
239 _capacity = s.length + 1
240 _length = s.length
241 _items = calloc_string(_capacity)
242 s.items.copy_to(_items, _length, 0, 0)
243 end
244
245 # Create a new empty string with a given capacity.
246 init with_capacity(cap: Int)
247 do
248 assert cap >= 0
249 # _items = new NativeString.calloc(cap)
250 _items = calloc_string(cap)
251 _capacity = cap
252 _length = 0
253 end
254
255 # Create a new string from a given char *.
256 init with_native(nat: NativeString, size: Int)
257 do
258 assert size >= 0
259 _items = nat
260 _capacity = size
261 _length = size
262 end
263
264 # Create a new string from a null terminated char *.
265 init from_cstring(str: NativeString)
266 do
267 var size = str.cstring_length
268 _items = str
269 _capacity = size + 1 # Since there is a trailing \n
270 _length = size
271 end
272
273 # Create a string of `count' chararter `c'
274 init filled_with(c: Char, count: Int)
275 do
276 with_capacity(count)
277 var i = 0
278 while i < count do
279 _items[i] = c
280 i += 1
281 end
282 _length = count
283 end
284
285 redef meth output
286 do
287 var i = 0
288 while i < length do
289 _items[i].output
290 i += 1
291 end
292 end
293
294 redef meth ==(o)
295 do
296 if not o isa String or o is null then return false
297 assert o isa String
298 var l = length
299 if o.length != l then return false
300 var i = 0
301 var it = _items
302 var oit = o._items
303 while i < l do
304 if it[i] != oit[i] then return false
305 i += 1
306 end
307 return true
308 end
309
310 # String to upper case
311 meth to_upper: String
312 do
313 var s = new String.with_capacity(length)
314 for i in self do s.add(i.to_upper)
315 return s
316 end
317
318 # String to lower case
319 meth to_lower : String
320 do
321 var s = new String.with_capacity(length)
322 for i in self do s.add(i.to_lower)
323 return s
324 end
325
326 readable private attr _items: NativeString
327 readable private attr _capacity: Int
328 end
329
330 ###############################################################################
331 # Refinement #
332 ###############################################################################
333
334 redef class Object
335 # meth class_name: String is extern intern # The name of the class
336
337 # User redeable representation of `self'.
338 meth to_s: String do return inspect
339
340 # Developper readable representation of `self'.
341 # Usualy, it uses the form "<CLASSNAME:#OBJECTID bla bla bla>"
342 meth inspect: String
343 do
344 var r = inspect_head
345 r.add('>')
346 return r
347 end
348
349 # Return "<CLASSNAME:#OBJECTID".
350 # This fuction is mainly used with the redefinition of the inspect(0) method
351 protected meth inspect_head: String
352 do
353 return "<{object_id.to_hex}"
354 end
355
356 protected meth args: IndexedCollection[String]
357 do
358 return sys.args
359 end
360 end
361
362 redef class Bool
363 redef meth to_s
364 do
365 if self then
366 return once "true"
367 else
368 return once "false"
369 end
370 end
371 end
372
373 redef class Int
374 meth fill_string(s: String, base: Int, signed: Bool)
375 # Fill `s' with the digits in base 'base' of `self' (and with the '-' sign if 'signed' and negative).
376 # assume < to_c max const of char
377 do
378 var n: Int
379 # Sign
380 if self < 0 then
381 n = - self
382 s[0] = '-'
383 else if self == 0 then
384 s[0] = '0'
385 return
386 else
387 n = self
388 end
389 # Fill digits
390 var pos = digit_count(base) - 1
391 while pos >= 0 and n > 0 do
392 s[pos] = (n % base).to_c
393 n = n / base # /
394 pos -= 1
395 end
396 end
397
398 # return displayable int in base 10 and signed
399 redef meth to_s do return to_base(10,true)
400
401 # return displayable int in hexadecimal (unsigned (not now))
402 meth to_hex: String do return to_base(16,false)
403
404 # return displayable int in base base and signed
405 meth to_base(base: Int, signed: Bool): String
406 do
407 var l = digit_count(base)
408 var s = new String.filled_with(' ', l)
409 fill_string(s, base, signed)
410 return s
411 end
412 end
413
414 redef class Float
415 redef meth to_s do return to_precision(6)
416
417 # `self' representation with `nb' digits after the '.'.
418 meth to_precision(nb: Int): String
419 do
420 if nb == 0 then return to_i.to_s
421
422 var i = to_i
423 var dec = 1.0
424 while nb > 0 do
425 dec = dec * 10.0
426 nb -= 1
427 end
428 var d = ((self-i.to_f)*dec).to_i
429 return "{i}.{d}"
430 end
431 end
432
433 redef class Char
434 redef meth to_s
435 do
436 var s = new String.with_capacity(1)
437 s[0] = self
438 return s
439 end
440 end
441
442 redef class Collection[E]
443 # Concatenate elements.
444 redef meth to_s
445 do
446 var s = new String
447 for e in self do if e != null then s.append(e.to_s)
448 return s
449 end
450
451 # Concatenate and separate each elements with `sep'.
452 meth join(sep: String): String
453 do
454 if is_empty then return ""
455
456 var s = new String # Result
457
458 # Concat first item
459 var i = iterator
460 var e = i.item
461 if e != null then s.append(e.to_s)
462
463 # Concat other items
464 i.next
465 while i.is_ok do
466 s.append(sep)
467 e = i.item
468 if e != null then s.append(e.to_s)
469 i.next
470 end
471 return s
472 end
473 end
474
475 redef class Map[K,V]
476 # Concatenate couple of 'key value' separate by 'couple_sep' and separate each couple with `sep'.
477 meth map_join(sep: String, couple_sep: String): String
478 do
479 if is_empty then return ""
480
481 var s = new String # Result
482
483 # Concat first item
484 var i = iterator
485 var k = i.key
486 var e = i.item
487 if e != null then s.append("{k}{couple_sep}{e}")
488
489 # Concat other items
490 i.next
491 while i.is_ok do
492 s.append(sep)
493 k = i.key
494 e = i.item
495 if e != null then s.append("{k}{couple_sep}{e}")
496 i.next
497 end
498 return s
499 end
500 end
501
502 ###############################################################################
503 # Native classe #
504 ###############################################################################
505
506 # Native strings are simple C char *
507 class NativeString
508 meth [](index: Int): Char is intern
509 meth []=(index: Int, item: Char) is intern
510 meth copy_to(dest: NativeString, length: Int, from: Int, to: Int) is intern
511
512 # Position of the first nul character.
513 meth cstring_length: Int
514 do
515 var l = 0
516 while self[l] != '\0' do l += 1
517 return l
518 end
519 meth atoi: Int is intern
520 end
521
522 # StringCapable objects can create native strings
523 class StringCapable
524 protected meth calloc_string(size: Int): NativeString is intern
525 end
526
527 redef class Sys
528 attr _args_cache: IndexedCollection[String]
529
530 redef meth args: IndexedCollection[String]
531 do
532 if _args_cache == null then init_args
533 return _args_cache
534 end
535
536 # The name of the program as given by the OS
537 meth program_name: String
538 do
539 return new String.from_cstring(native_argv(0))
540 end
541
542 # Initialize `args' with the contents of `native_argc' and `native_argv'.
543 private meth init_args
544 do
545 var argc = native_argc
546 var args = new Array[String].with_capacity(0)
547 var i = 1
548 while i < argc do
549 args[i-1] = new String.from_cstring(native_argv(i))
550 i += 1
551 end
552 _args_cache = args
553 end
554
555 private meth native_argc: Int is extern "kernel_Sys_Sys_native_argc_0" # First argument of the main C function.
556
557 private meth native_argv(i: Int): NativeString is extern "kernel_Sys_Sys_native_argv_1" # Second argument of the main C function.
558 end
559