lib/core: handling of plus (+) sign when using to_i with Text objects
[nit.git] / lib / core / text / fixed_ints_text.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Text services to complement `fixed_ints`
16 module fixed_ints_text
17
18 import abstract_text
19 import string_search
20
21 in "C" `{
22 #include <inttypes.h>
23 `}
24
25 redef class Int8
26 # C function to calculate the length of the `CString` to receive `self`
27 private fun to_s_len: Int `{
28 return snprintf(NULL, 0, "%"PRIi8, self);
29 `}
30
31 # C function to convert a nit Int to a CString (char*)
32 private fun native_to_s(nstr: CString, strlen: Int) `{
33 snprintf(nstr, strlen, "%"PRIi8, self);
34 `}
35
36 # Displayable Int8
37 #
38 # assert 1i8.to_s == "1"
39 # assert (-123i8).to_s == "-123"
40 redef fun to_s do
41 var nslen = to_s_len
42 var ns = new CString(nslen + 1)
43 ns[nslen] = 0
44 native_to_s(ns, nslen + 1)
45 return ns.to_s_unsafe(nslen, copy=false)
46 end
47 end
48
49 redef class Int16
50 private fun to_s_len: Int `{
51 return snprintf(NULL, 0, "%"PRIi16, self);
52 `}
53
54 # C function to convert a nit Int to a CString (char*)
55 private fun native_to_s(nstr: CString, strlen: Int) `{
56 snprintf(nstr, strlen, "%"PRIi16, self);
57 `}
58
59 # Displayable Int16
60 #
61 # assert 1i16.to_s == "1"
62 # assert (-123i16).to_s == "-123"
63 redef fun to_s do
64 var nslen = to_s_len
65 var ns = new CString(nslen + 1)
66 ns[nslen] = 0
67 native_to_s(ns, nslen + 1)
68 return ns.to_s_unsafe(nslen, copy=false)
69 end
70 end
71
72 redef class UInt16
73 # C function to calculate the length of the `CString` to receive `self`
74 private fun to_s_len: Int `{
75 return snprintf(NULL, 0, "%"PRIu16, self);
76 `}
77
78 # C function to convert a nit Int to a CString (char*)
79 private fun native_to_s(nstr: CString, strlen: Int) `{
80 snprintf(nstr, strlen, "%"PRIu16, self);
81 `}
82
83 # Displayable UInt16
84 #
85 # assert 1u16.to_s == "1"
86 # assert (-123u16).to_s == "65413"
87 redef fun to_s do
88 var nslen = to_s_len
89 var ns = new CString(nslen + 1)
90 ns[nslen] = 0
91 native_to_s(ns, nslen + 1)
92 return ns.to_s_unsafe(nslen, copy=false)
93 end
94 end
95
96 redef class Int32
97 # C function to calculate the length of the `CString` to receive `self`
98 private fun to_s_len: Int `{
99 return snprintf(NULL, 0, "%"PRIi32, self);
100 `}
101
102 # C function to convert a nit Int to a CString (char*)
103 private fun native_to_s(nstr: CString, strlen: Int) `{
104 snprintf(nstr, strlen, "%"PRIi32, self);
105 `}
106
107 # Displayable Int32
108 #
109 # assert 1i32.to_s == "1"
110 # assert (-123i32).to_s == "-123"
111 redef fun to_s do
112 var nslen = to_s_len
113 var ns = new CString(nslen + 1)
114 ns[nslen] = 0
115 native_to_s(ns, nslen + 1)
116 return ns.to_s_unsafe(nslen, copy=false)
117 end
118 end
119
120 redef class UInt32
121 # C function to calculate the length of the `CString` to receive `self`
122 private fun to_s_len: Int `{
123 return snprintf(NULL, 0, "%"PRIu32, self);
124 `}
125
126 # C function to convert a nit Int to a CString (char*)
127 private fun native_to_s(nstr: CString, strlen: Int) `{
128 snprintf(nstr, strlen, "%"PRIu32, self);
129 `}
130
131 # Displayable UInt32
132 #
133 # assert 1u32.to_s == "1"
134 # assert (-123u32).to_s == "4294967173"
135 redef fun to_s do
136 var nslen = to_s_len
137 var ns = new CString(nslen + 1)
138 ns[nslen] = 0
139 native_to_s(ns, nslen + 1)
140 return ns.to_s_unsafe(nslen, copy=false)
141 end
142 end
143
144 redef class Text
145
146 # Removes the numeric head of `self` if present
147 #
148 # intrude import core::fixed_ints_text
149 # assert "0xFFEF".strip_numhead == "FFEF"
150 # assert "0o7364".strip_numhead == "7364"
151 # assert "0b01001".strip_numhead == "01001"
152 # assert "98".strip_numhead == "98"
153 private fun strip_numhead: Text do
154 if get_numhead != "" then return substring_from(2)
155 return self
156 end
157
158 # Gets the numeric head of `self` if present
159 # Returns "" otherwise
160 #
161 # intrude import core::fixed_ints_text
162 # assert "0xFEFF".get_numhead == "0x"
163 # assert "0b01001".get_numhead == "0b"
164 # assert "0o872".get_numhead == "0o"
165 # assert "98".get_numhead == ""
166 private fun get_numhead: Text do
167 if self.length < 2 then return ""
168 var c = self[0]
169 if c != '0' then return ""
170 c = self[1]
171 if c == 'x' or c == 'b' or c == 'o' or
172 c == 'X' or c == 'B' or c == 'O' then return substring(0, 2)
173 return ""
174 end
175
176 # Removes the numeric extension if present
177 #
178 # intrude import core::fixed_ints_text
179 # assert "0xFEFFu8".strip_numext == "0xFEFF"
180 # assert "0b01001u8".strip_numext == "0b01001"
181 # assert "0o872u8".strip_numext == "0o872"
182 # assert "98".strip_numext == "98"
183 private fun strip_numext: Text do
184 var ext = get_numext
185 if ext != "" then return substring(0, length - ext.length)
186 return self
187 end
188
189 # Gets the numeric extension (i/u 8/16/32) in `self` is present
190 # Returns "" otherwise
191 #
192 # intrude import core::fixed_ints_text
193 # assert "0xFEFFu8".get_numext == "u8"
194 # assert "0b01001u8".get_numext == "u8"
195 # assert "0o872u8".get_numext == "u8"
196 # assert "98".get_numext == ""
197 private fun get_numext: Text do
198 var len = self.length
199 var max = if self.length < 3 then self.length else 3
200 for i in [1 .. max] do
201 var c = self[len - i]
202 if c == 'i' or c == 'u' then return substring_from(len - i)
203 end
204 return ""
205 end
206
207 # Is `self` a well-formed Integer (i.e. parsable via `to_i`)
208 #
209 # assert "123".is_int
210 # assert "0b1011".is_int
211 # assert "-34".is_int
212 # assert "+45".is_int
213 # assert not "0x_".is_int
214 # assert not "0xGE".is_int
215 # assert not "".is_int
216 # assert not "Not an Int".is_int
217 # assert not "-".is_int
218 fun is_int: Bool do
219 if byte_length == 0 then return false
220 var s = remove_all('_')
221 var pos = 0
222 var len = s.length
223 while pos < len and (s[pos] == '-' or s[pos] == '+') do
224 pos += 1
225 end
226 s = s.substring_from(pos)
227 var rets = s.strip_numhead
228 if rets == "" then return false
229 var hd = get_numhead
230 if hd == "0x" or hd == "0X" then return rets.is_hex
231 if hd == "0b" or hd == "0B" then return rets.is_bin
232 if hd == "0o" or hd == "0O" then return rets.is_oct
233 return rets.is_dec
234 end
235
236 redef fun to_i
237 do
238 assert self.is_int
239 var s = remove_all('_')
240 var val = 0
241 var neg = false
242 var pos = 0
243 loop
244 if s[pos] == '-' then
245 neg = not neg
246 pos += 1
247 else if s[pos] == '+' then
248 pos += 1
249 else
250 break
251 end
252 end
253 s = s.substring_from(pos)
254 if s.length >= 2 then
255 var s1 = s[1]
256 if s1 == 'x' or s1 == 'X' then
257 val = s.substring_from(2).to_hex
258 else if s1 == 'o' or s1 == 'O' then
259 val = s.substring_from(2).to_oct
260 else if s1 == 'b' or s1 == 'B' then
261 val = s.substring_from(2).to_bin
262 else if s1.is_numeric then
263 val = s.to_dec
264 end
265 else
266 val = s.to_dec
267 end
268 return if neg then -val else val
269 end
270
271 # Is `self` a valid integer ?
272 #
273 # assert "0xFE46u8".is_num
274 # assert "0b0100".is_num
275 # assert "0o645".is_num
276 # assert "897u8".is_num
277 fun is_num: Bool do
278 var prefix = get_numhead
279 var s = strip_numhead.strip_numext.remove_all('_')
280 if prefix != "" then
281 var c = prefix[1]
282 if c == 'x' or c == 'X' then return s.is_hex
283 if c == 'o' or c == 'O' then return s.is_oct
284 if c == 'b' or c == 'B' then return s.is_bin
285 end
286 return s.is_dec
287 end
288
289 # If `self` is a properly formatted integer, returns the corresponding value
290 # Returns `null` otherwise
291 #
292 # assert "0xFEu8".to_num == 254u8
293 # assert "0b10_10".to_num != 10u8
294 fun to_num: nullable Numeric do
295 if not is_num then return null
296 var s = remove_all('_')
297 var ext = s.get_numext
298 var trunk = s.strip_numext
299 if trunk.strip_numhead == "" then return null
300 var trval = trunk.to_i
301 if ext == "u8" then
302 return trval.to_b
303 else if ext == "i8" then
304 return trval.to_i8
305 else if ext == "i16" then
306 return trval.to_i16
307 else if ext == "u16" then
308 return trval.to_u16
309 else if ext == "i32" then
310 return trval.to_i32
311 else if ext == "u32" then
312 return trval.to_u32
313 else if ext == "" then
314 return trval
315 else
316 return null
317 end
318 end
319 end