GCC Code Coverage Report


Directory: src/
File: src/frontend/parse-literal-int.cpp
Date: 2025-10-15 09:43:47
Exec Total Coverage
Lines: 119 192 62.0%
Functions: 1 1 100.0%
Branches: 70 99 70.7%

Line Branch Exec Source
1 #include <gyoji-frontend.hpp>
2 #include <gyoji-misc/jstring.hpp>
3
4 using namespace Gyoji::frontend::integers;
5 using namespace Gyoji::mir;
6
7 static std::string u8_type("u8");
8 static std::string u16_type("u16");
9 static std::string u32_type("u32");
10 static std::string u64_type("u64");
11
12 static std::string i8_type("i8");
13 static std::string i16_type("i16");
14 static std::string i32_type("i32");
15 static std::string i64_type("i64");
16
17 222 bool Gyoji::frontend::integers::parse_literal_int(
18 const Gyoji::context::CompilerContext & compiler_context,
19 const Gyoji::mir::Types & types,
20 const Gyoji::frontend::tree::Terminal & literal_int_token,
21 ParseLiteralIntResult & result
22 )
23 {
24 222 size_t radix = 10;
25 // Extremely ineficcient, but we need to resolve
26 // the specific type of integer here
27 // so that the MIR layer knows what we mean.
28 // Actually, the whole of this logic
29 // should probably move down one notch to the
30 // construction OP_LITERAL in the FunctionResolver
31 222 result.parsed_type = nullptr;
32
33 222 std::string integer_part;
34 222 Type* type_part = nullptr;
35
36 222 bool sign_positive = true;
37
38 222 const std::string & token_value = literal_int_token.get_value();
39 222 size_t len = token_value.size();
40
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 212 times.
222 if (Gyoji::misc::endswith(token_value, u8_type)) {
41 10 integer_part = token_value.substr(0, len - u8_type.size());
42 20 type_part = types.get_type("u8");
43 }
44
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 198 times.
212 else if (Gyoji::misc::endswith(token_value, u16_type)) {
45 14 integer_part = token_value.substr(0, len - u16_type.size());
46 28 type_part = types.get_type("u16");
47 }
48
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 166 times.
198 else if (Gyoji::misc::endswith(token_value, u32_type)) {
49 32 integer_part = token_value.substr(0, len - u32_type.size());
50 64 type_part = types.get_type("u32");
51 }
52
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 148 times.
166 else if (Gyoji::misc::endswith(token_value, u64_type)) {
53 18 integer_part = token_value.substr(0, len - u64_type.size());
54 36 type_part = types.get_type("u64");
55 }
56
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 138 times.
148 else if (Gyoji::misc::endswith(token_value, i8_type)) {
57 10 integer_part = token_value.substr(0, len - i8_type.size());
58 20 type_part = types.get_type("i8");
59 }
60
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 132 times.
138 else if (Gyoji::misc::endswith(token_value, i16_type)) {
61 6 integer_part = token_value.substr(0, len - i16_type.size());
62 12 type_part = types.get_type("i16");
63 }
64
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 126 times.
132 else if (Gyoji::misc::endswith(token_value, i32_type)) {
65 6 integer_part = token_value.substr(0, len - i32_type.size());
66 12 type_part = types.get_type("i32");
67 }
68
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 116 times.
126 else if (Gyoji::misc::endswith(token_value, i64_type)) {
69 10 integer_part = token_value.substr(0, len - i64_type.size());
70 20 type_part = types.get_type("i64");
71 }
72 else {
73 // If not specified, we assume u32 or i32 depending
74 // on whether there's a '-' at the start.
75 116 integer_part = token_value;
76
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 114 times.
116 if (integer_part[0] == '-') {
77 2 type_part = types.get_type("i32");
78 2 sign_positive = false;
79 }
80 else {
81 114 type_part = types.get_type("u32");
82 114 sign_positive = true;
83 }
84 }
85
86 // If the remaining part of the number
87 // starts with a '-' then we flip the sign bit
88 // to make it negative and remove the '-' so
89 // we can parse the radix part.
90
2/2
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 210 times.
444 if (Gyoji::misc::startswith(integer_part, "-")) {
91 12 integer_part = integer_part.substr(1);
92 12 sign_positive = false;
93 }
94 else {
95 210 sign_positive = true;
96 }
97
98 // Next, we need to check that
99 // if it's a negative number, it is
100 // consistent with the signedness of the type.
101
4/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 210 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 222 times.
222 if (!sign_positive && type_part->is_unsigned()) {
102 compiler_context
103 .get_errors()
104 .add_simple_error(
105 literal_int_token.get_source_ref(),
106 "Integer literal type mismatch",
107 std::string("Type of integer literal was unsigned, but a negative value was given")
108 );
109 return false;
110 }
111
112
113 // Remove the superfluous '_' which are only
114 // supported for readability purposes and play
115 // no semantic role.
116 // Hex is supported
117 444 integer_part = Gyoji::misc::string_remove(integer_part, "_");
118
2/2
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 194 times.
444 if (Gyoji::misc::startswith(integer_part, "0x")) {
119 28 integer_part = Gyoji::misc::string_remove(integer_part, "0x");
120 28 radix = 16;
121 }
122 // Binary is supported
123
2/2
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 176 times.
388 else if (Gyoji::misc::startswith(integer_part, "0b")) {
124 18 integer_part = Gyoji::misc::string_remove(integer_part, "0b");
125 18 radix = 2;
126 }
127 // Octal is supported.
128
2/2
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 172 times.
352 else if (Gyoji::misc::startswith(integer_part, "0o")) {
129 4 integer_part = Gyoji::misc::string_remove(integer_part, "0o");
130 4 radix = 8;
131 }
132 // If radix is not specified, assume decimal.
133
134 222 const char *source_cstring = integer_part.c_str();
135 222 size_t length = integer_part.size();
136
137 222 char *endptr = nullptr;
138 222 errno = 0;
139
140
141
8/9
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 146 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
222 switch (type_part->get_type()) {
142 10 case Type::TYPE_PRIMITIVE_u8:
143 {
144 10 unsigned long number = strtoul(source_cstring, &endptr, radix);
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (endptr != (source_cstring + length)) {
146 compiler_context
147 .get_errors()
148 .add_simple_error(
149 literal_int_token.get_source_ref(),
150 "Invalid integer literal",
151 std::string("Literal value contained extraneous characters: ") + integer_part
152 );
153 return false;
154 }
155
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (errno == ERANGE || number >= 256) {
156 compiler_context
157 .get_errors()
158 .add_simple_error(
159 literal_int_token.get_source_ref(),
160 "Invalid integer literal",
161 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in a u8")
162 );
163 return false;
164 }
165 10 result.parsed_type = type_part;
166 10 result.u8_value = (unsigned char)number;
167 10 return true;
168 }
169 14 case Type::TYPE_PRIMITIVE_u16:
170 {
171 14 unsigned long number = strtoul(source_cstring, &endptr, radix);
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (endptr != (source_cstring + length)) {
173 compiler_context
174 .get_errors()
175 .add_simple_error(
176 literal_int_token.get_source_ref(),
177 "Invalid integer literal",
178 std::string("Literal value contained extraneous characters: ") + integer_part
179 );
180 return false;
181 }
182
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if (errno == ERANGE || number > 0xffff) {
183 compiler_context
184 .get_errors()
185 .add_simple_error(
186 literal_int_token.get_source_ref(),
187 "Invalid integer literal",
188 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in a u16")
189 );
190 return false;
191 }
192 14 result.parsed_type = type_part;
193 14 result.u16_value = (unsigned short)number;
194 14 return true;
195 }
196 break;
197 146 case Type::TYPE_PRIMITIVE_u32:
198 {
199 146 unsigned long number = strtoull(source_cstring, &endptr, radix);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 146 times.
146 if (endptr != (source_cstring + length)) {
201 compiler_context
202 .get_errors()
203 .add_simple_error(
204 literal_int_token.get_source_ref(),
205 "Invalid integer literal",
206 std::string("Literal value contained extraneous characters: ") + integer_part
207 );
208 return false;
209 }
210
2/4
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 146 times.
146 if (errno == ERANGE || number > 0xffffffff) {
211 compiler_context
212 .get_errors()
213 .add_simple_error(
214 literal_int_token.get_source_ref(),
215 "Invalid integer literal",
216 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in a u32")
217 );
218 return false;
219 }
220 146 result.parsed_type = type_part;
221 146 result.u32_value = (unsigned int)number;
222 146 return true;
223 }
224 break;
225 18 case Type::TYPE_PRIMITIVE_u64:
226 {
227 18 unsigned long number = strtoull(source_cstring, &endptr, radix);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (endptr != (source_cstring + length)) {
229 compiler_context
230 .get_errors()
231 .add_simple_error(
232 literal_int_token.get_source_ref(),
233 "Invalid integer literal",
234 std::string("Literal value contained extraneous characters: ") + integer_part
235 );
236 return false;
237 }
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (errno == ERANGE) {
239 compiler_context
240 .get_errors()
241 .add_simple_error(
242 literal_int_token.get_source_ref(),
243 "Invalid integer literal",
244 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in a u64")
245 );
246 return false;
247 }
248 18 result.parsed_type = type_part;
249 18 result.u64_value = (unsigned long)number;
250 18 return true;
251 }
252 break;
253
254 // Signed
255 10 case Type::TYPE_PRIMITIVE_i8:
256 {
257 10 long number = strtol(source_cstring, &endptr, radix);
258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (endptr != (source_cstring + length)) {
259 compiler_context
260 .get_errors()
261 .add_simple_error(
262 literal_int_token.get_source_ref(),
263 "Invalid integer literal",
264 std::string("Literal value contained extraneous characters: ") + integer_part
265 );
266 return false;
267 }
268
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 number = sign_positive ? number : -number;
269
3/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
10 if (errno == ERANGE || number < -128 || number > 127) {
270 compiler_context
271 .get_errors()
272 .add_simple_error(
273 literal_int_token.get_source_ref(),
274 "Invalid integer literal",
275 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in a i8")
276 );
277 return false;
278 }
279 10 result.parsed_type = type_part;
280 10 result.i8_value = (char)number;
281 10 return true;
282 }
283 6 case Type::TYPE_PRIMITIVE_i16:
284 {
285 6 long number = strtol(source_cstring, &endptr, radix);
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (endptr != (source_cstring + length)) {
287 compiler_context
288 .get_errors()
289 .add_simple_error(
290 literal_int_token.get_source_ref(),
291 "Invalid integer literal",
292 std::string("Literal value contained extraneous characters: ") + integer_part
293 );
294 return false;
295 }
296
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 number = sign_positive ? number : -number;
297
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (errno == ERANGE || number < -32768 || number > 32767) {
298 compiler_context
299 .get_errors()
300 .add_simple_error(
301 literal_int_token.get_source_ref(),
302 "Invalid integer literal",
303 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in an i16")
304 );
305 return false;
306 }
307 6 result.parsed_type = type_part;
308 6 result.i16_value = (short)number;
309 6 return true;
310 }
311 break;
312 8 case Type::TYPE_PRIMITIVE_i32:
313 {
314 8 long number = strtoll(source_cstring, &endptr, radix);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (endptr != (source_cstring + length)) {
316 compiler_context
317 .get_errors()
318 .add_simple_error(
319 literal_int_token.get_source_ref(),
320 "Invalid integer literal",
321 std::string("Literal value contained extraneous characters: ") + integer_part
322 );
323 return false;
324 }
325
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 number = sign_positive ? number : -number;
326
3/6
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
8 if (errno == ERANGE || number < -2147483648 || number > 2147483647) {
327 compiler_context
328 .get_errors()
329 .add_simple_error(
330 literal_int_token.get_source_ref(),
331 "Invalid integer literal",
332 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in an i32")
333 );
334 return false;
335 }
336 8 result.parsed_type = type_part;
337 8 result.i32_value = (int)number;
338 8 return true;
339 }
340 break;
341 10 case Type::TYPE_PRIMITIVE_i64:
342 {
343 10 long number = strtoll(source_cstring, &endptr, radix);
344
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (endptr != (source_cstring + length)) {
345 compiler_context
346 .get_errors()
347 .add_simple_error(
348 literal_int_token.get_source_ref(),
349 "Invalid integer literal",
350 std::string("Literal value contained extraneous characters: ") + integer_part
351 );
352 return false;
353 }
354
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 number = sign_positive ? number : -number;
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (errno == ERANGE) {
356 compiler_context
357 .get_errors()
358 .add_simple_error(
359 literal_int_token.get_source_ref(),
360 "Invalid integer literal",
361 std::string("Literal value ") + integer_part + std::string(" is outside the range of values allowed in a u64")
362 );
363 return false;
364 }
365 10 result.parsed_type = type_part;
366 10 result.i64_value = (long)number;
367 10 return true;
368 }
369 break;
370
371 default:
372 compiler_context
373 .get_errors()
374 .add_simple_error(
375 literal_int_token.get_source_ref(),
376 "Compiler Bug! Invalid integer literal",
377 std::string("Unsupported primitive literal type ") + type_part->get_name()
378 );
379 return false;
380 }
381 /* Unreachable */
382 222 }
383
384