Directory: | ./ |
---|---|
File: | src/json.c |
Date: | 2021-09-04 00:13:15 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 261 | 274 | 95.3% |
Branches: | 136 | 156 | 87.2% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /***************************************************************************/ /** | ||
2 | |||
3 | @file json.c | ||
4 | |||
5 | @author Stephen Brennan | ||
6 | |||
7 | @date Created Sunday, 22 November 2015 | ||
8 | |||
9 | @brief JSON parsing! | ||
10 | |||
11 | @copyright Copyright (c) 2015, Stephen Brennan. Released under the | ||
12 | Revised BSD License. See LICENSE.txt for details. | ||
13 | |||
14 | *******************************************************************************/ | ||
15 | |||
16 | #include <assert.h> | ||
17 | #include <stdbool.h> | ||
18 | #include <stddef.h> | ||
19 | #include <stdio.h> | ||
20 | #include <string.h> | ||
21 | |||
22 | #include "json_private.h" | ||
23 | #include "nosj.h" | ||
24 | |||
25 | // forward declaration of the main parser | ||
26 | static struct json_parser json_parse_rec(char *text, struct json_token *arr, | ||
27 | size_t maxtoken, struct json_parser p); | ||
28 | |||
29 | /** | ||
30 | @brief Return true if c is a whitespace character according to the JSON spec. | ||
31 | */ | ||
32 | 2417 | static bool json_isspace(char c) | |
33 | { | ||
34 |
8/8✓ Branch 0 taken 1183 times.
✓ Branch 1 taken 1234 times.
✓ Branch 2 taken 1182 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1181 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 213 times.
✓ Branch 7 taken 968 times.
|
2417 | return (c == ' ' || c == '\t' || c == '\r' || c == '\n'); |
35 | } | ||
36 | |||
37 | /** | ||
38 | @brief Return true if c could be the beginning of a JSON number. | ||
39 | */ | ||
40 | 99 | static bool json_isnumber(char c) | |
41 | { | ||
42 |
5/6✓ Branch 0 taken 93 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 92 times.
✓ Branch 5 taken 1 times.
|
99 | return (c == '-' || ('0' <= c && c <= '9')); |
43 | } | ||
44 | |||
45 | /** | ||
46 | @brief Place a token in the next open slot of arr. | ||
47 | |||
48 | If arr is null, we do nothing (so that the parser can be called for an | ||
49 | initial memory estimate). Otherwise, if we've run past the end of the token | ||
50 | buffer, return an error. | ||
51 | @param arr The token buffer. May be null. | ||
52 | @param tok The token to add. | ||
53 | @param p The parser state. | ||
54 | */ | ||
55 | 497 | void json_settoken(struct json_token *arr, struct json_token tok, | |
56 | struct json_parser p, size_t maxtoken) | ||
57 | { | ||
58 |
3/4✓ Branch 0 taken 253 times.
✓ Branch 1 taken 244 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 253 times.
|
497 | if (arr == NULL || p.tokenidx >= maxtoken) { |
59 | 244 | return; | |
60 | } | ||
61 | 253 | arr[p.tokenidx] = tok; | |
62 | } | ||
63 | |||
64 | /** | ||
65 | @brief Set the "next" pointer in a token to be a new value. | ||
66 | |||
67 | If arr is null, this does nothing. If we've run past the end of the buffer, | ||
68 | do nothing. | ||
69 | @param arr The token buffer. May be null. | ||
70 | @param tokidx The index of the token to update. | ||
71 | @param next New value for next. | ||
72 | */ | ||
73 | 177 | static void json_setnext(struct json_token *arr, size_t tokidx, size_t next, | |
74 | size_t maxtoken) | ||
75 | { | ||
76 |
3/4✓ Branch 0 taken 92 times.
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 92 times.
|
177 | if (arr == NULL || tokidx >= maxtoken) { |
77 | 85 | return; | |
78 | } | ||
79 | 92 | struct json_token tok = arr[tokidx]; | |
80 | 92 | tok.next = next; | |
81 | 92 | arr[tokidx] = tok; | |
82 | } | ||
83 | |||
84 | /** | ||
85 | @brief Set the "child" pointer in a token to be a new value. | ||
86 | |||
87 | If arr is null, this does nothing. If we've run past the end of the buffer, | ||
88 | do nothing. | ||
89 | @param arr The token buffer. May be null. | ||
90 | @param tokidx The index of the token to update. | ||
91 | @param child New value for child. | ||
92 | */ | ||
93 | 208 | static void json_setchild(struct json_token *arr, size_t tokidx, size_t child, | |
94 | size_t maxtoken) | ||
95 | { | ||
96 |
3/4✓ Branch 0 taken 108 times.
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 108 times.
|
208 | if (arr == NULL || tokidx >= maxtoken) { |
97 | 100 | return; | |
98 | } | ||
99 | 108 | struct json_token tok = arr[tokidx]; | |
100 | 108 | tok.child = child; | |
101 | 108 | arr[tokidx] = tok; | |
102 | } | ||
103 | |||
104 | /** | ||
105 | @brief Set the "end" index in a token to be a new value. | ||
106 | |||
107 | If arr is null, this does nothing. If we've run past the end of the buffer, | ||
108 | do nothing. | ||
109 | @param arr The token buffer. May be null. | ||
110 | @param tokidx The index of the token to update. | ||
111 | @param end New value for end. | ||
112 | */ | ||
113 | 52 | static void json_setend(struct json_token *arr, size_t tokidx, size_t end, | |
114 | size_t maxtoken) | ||
115 | { | ||
116 |
3/4✓ Branch 0 taken 32 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
|
52 | if (arr == NULL || tokidx >= maxtoken) { |
117 | 20 | return; | |
118 | } | ||
119 | 32 | struct json_token tok = arr[tokidx]; | |
120 | 32 | tok.end = end; | |
121 | 32 | arr[tokidx] = tok; | |
122 | } | ||
123 | |||
124 | /** | ||
125 | @brief Set the "length" index in a token to be a new value. | ||
126 | |||
127 | If arr is null, this does nothing. If we've run past the end of the buffer, | ||
128 | do nothing. | ||
129 | @param arr The token buffer. May be null. | ||
130 | @param tokidx The index of the token to update. | ||
131 | @param length New value for end. | ||
132 | */ | ||
133 | 52 | static void json_setlength(struct json_token *arr, size_t tokidx, size_t length, | |
134 | size_t maxtoken) | ||
135 | { | ||
136 |
3/4✓ Branch 0 taken 32 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
|
52 | if (arr == NULL || tokidx >= maxtoken) { |
137 | 20 | return; | |
138 | } | ||
139 | 32 | struct json_token tok = arr[tokidx]; | |
140 | 32 | tok.length = length; | |
141 | 32 | arr[tokidx] = tok; | |
142 | } | ||
143 | |||
144 | /** | ||
145 | @brief Return the parser state with textidx pointed at the next non-ws char. | ||
146 | @param text The text we're parsing. | ||
147 | @param p The current parser state | ||
148 | @returns The new parser state | ||
149 | */ | ||
150 | 968 | static struct json_parser json_skip_whitespace(char *text, struct json_parser p) | |
151 | { | ||
152 |
3/4✓ Branch 1 taken 1449 times.
✓ Branch 2 taken 968 times.
✓ Branch 3 taken 1449 times.
✗ Branch 4 not taken.
|
2417 | while (json_isspace(text[p.textidx]) && text[p.textidx] != '\0') { |
153 | 1449 | p.textidx++; | |
154 | } | ||
155 | 968 | return p; | |
156 | } | ||
157 | |||
158 | /** | ||
159 | @brief Parse the "true" literal. | ||
160 | @param text The text we're parsing. | ||
161 | @param arr The token buffer. | ||
162 | @param maxtoken The length of the token buffer. | ||
163 | @param p The parser state. | ||
164 | @returns Parser state after parsing true. | ||
165 | */ | ||
166 | 17 | static struct json_parser json_parse_true(char *text, struct json_token *arr, | |
167 | size_t maxtoken, struct json_parser p) | ||
168 | { | ||
169 | struct json_token tok; | ||
170 | 17 | tok.type = JSON_TRUE; | |
171 | 17 | tok.start = p.textidx; | |
172 | 17 | tok.end = p.textidx + 3; | |
173 | 17 | tok.length = 0; | |
174 | 17 | tok.child = 0; | |
175 | 17 | tok.next = 0; | |
176 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
|
17 | if (strncmp("true", text + p.textidx, 4) == 0) { |
177 | 16 | json_settoken(arr, tok, p, maxtoken); | |
178 | 16 | p.textidx += 4; | |
179 | 16 | p.tokenidx += 1; | |
180 | 16 | return p; | |
181 | } else { | ||
182 | 1 | p.error = JSONERR_UNEXPECTED_TOKEN; | |
183 | 1 | return p; | |
184 | } | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | @brief Parse the "false" literal. | ||
189 | @param text The text we're parsing. | ||
190 | @param arr The token buffer. | ||
191 | @param maxtoken The length of the token buffer. | ||
192 | @param p The parser state. | ||
193 | @returns Parser state after parsing false. | ||
194 | */ | ||
195 | 20 | static struct json_parser json_parse_false(char *text, struct json_token *arr, | |
196 | size_t maxtoken, | ||
197 | struct json_parser p) | ||
198 | { | ||
199 | (void)maxtoken; // unused | ||
200 | struct json_token tok; | ||
201 | 20 | tok.type = JSON_FALSE; | |
202 | 20 | tok.start = p.textidx; | |
203 | 20 | tok.end = p.textidx + 4; | |
204 | 20 | tok.length = 0; | |
205 | 20 | tok.child = 0; | |
206 | 20 | tok.next = 0; | |
207 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 times.
|
20 | if (strncmp("false", text + p.textidx, 5) == 0) { |
208 | 19 | json_settoken(arr, tok, p, maxtoken); | |
209 | 19 | p.textidx += 5; | |
210 | 19 | p.tokenidx += 1; | |
211 | 19 | return p; | |
212 | } else { | ||
213 | 1 | p.error = JSONERR_UNEXPECTED_TOKEN; | |
214 | 1 | return p; | |
215 | } | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | @brief Parse the "null" literal. | ||
220 | @param text The text we're parsing. | ||
221 | @param arr The token buffer. | ||
222 | @param maxtoken The length of the token buffer. | ||
223 | @param p The parser state. | ||
224 | @returns Parser state after parsing null. | ||
225 | */ | ||
226 | 23 | static struct json_parser json_parse_null(char *text, struct json_token *arr, | |
227 | size_t maxtoken, struct json_parser p) | ||
228 | { | ||
229 | struct json_token tok; | ||
230 | 23 | tok.type = JSON_NULL; | |
231 | 23 | tok.start = p.textidx; | |
232 | 23 | tok.end = p.textidx + 3; | |
233 | 23 | tok.length = 0; | |
234 | 23 | tok.child = 0; | |
235 | 23 | tok.next = 0; | |
236 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1 times.
|
23 | if (strncmp("null", text + p.textidx, 4) == 0) { |
237 | 22 | json_settoken(arr, tok, p, maxtoken); | |
238 | 22 | p.textidx += 4; | |
239 | 22 | p.tokenidx += 1; | |
240 | 22 | return p; | |
241 | } else { | ||
242 | 1 | p.error = JSONERR_UNEXPECTED_TOKEN; | |
243 | 1 | return p; | |
244 | } | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | @brief Parse an array. | ||
249 | @param text The text we're parsing. | ||
250 | @param arr The token buffer. | ||
251 | @param maxtoken The length of the token buffer. | ||
252 | @param p The parser state. | ||
253 | @returns Parser state after parsing the array. | ||
254 | */ | ||
255 | 31 | static struct json_parser json_parse_array(char *text, struct json_token *arr, | |
256 | size_t maxtoken, | ||
257 | struct json_parser p) | ||
258 | { | ||
259 | 31 | size_t array_tokenidx = p.tokenidx, prev_tokenidx, curr_tokenidx, | |
260 | 31 | length = 0; | |
261 | 31 | struct json_token tok = { | |
262 | .type = JSON_ARRAY, | ||
263 | 31 | .start = p.textidx, | |
264 | .length = 0, | ||
265 | .end = 0, | ||
266 | .child = 0, | ||
267 | .next = 0, | ||
268 | }; | ||
269 | 31 | json_settoken(arr, tok, p, maxtoken); | |
270 | |||
271 | // current char is [, so we need to go past it. | ||
272 | 31 | p.textidx++; | |
273 | 31 | p.tokenidx++; | |
274 | |||
275 | // Skip through whitespace. | ||
276 | 31 | p = json_skip_whitespace(text, p); | |
277 |
2/2✓ Branch 0 taken 67 times.
✓ Branch 1 taken 28 times.
|
95 | while (text[p.textidx] != ']') { |
278 | |||
279 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 66 times.
|
67 | if (text[p.textidx] == '\0') { |
280 | 1 | p.error = JSONERR_PREMATURE_EOF; | |
281 | 1 | return p; | |
282 | } | ||
283 | // Parse a value. | ||
284 | 66 | prev_tokenidx = curr_tokenidx; | |
285 | 66 | curr_tokenidx = p.tokenidx; | |
286 | 66 | p = json_parse_rec(text, arr, maxtoken, p); | |
287 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 65 times.
|
66 | if (p.error != JSONERR_NO_ERROR) { |
288 | 1 | return p; | |
289 | } | ||
290 | |||
291 | // Now set some bookkeeping of previous values. | ||
292 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 41 times.
|
65 | if (tok.child == 0) { |
293 | // If this is the first element of the list, set the | ||
294 | // list's child to point to it. | ||
295 | 24 | tok.child = curr_tokenidx; | |
296 | 24 | json_setchild(arr, array_tokenidx, curr_tokenidx, | |
297 | maxtoken); | ||
298 | } else { | ||
299 | // Otherwise set the previous element's next pointer to | ||
300 | // point to it. | ||
301 | 41 | json_setnext(arr, prev_tokenidx, curr_tokenidx, | |
302 | maxtoken); | ||
303 | } | ||
304 | |||
305 | 65 | length++; | |
306 | |||
307 | // Skip whitespace. | ||
308 | 65 | p = json_skip_whitespace(text, p); | |
309 |
2/2✓ Branch 0 taken 43 times.
✓ Branch 1 taken 22 times.
|
65 | if (text[p.textidx] == ',') { |
310 | 43 | p.textidx++; | |
311 | 43 | p = json_skip_whitespace(text, p); | |
312 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
|
22 | } else if (text[p.textidx] != ']') { |
313 | // If there was no comma, this better be the end of the | ||
314 | // object. | ||
315 | 1 | p.error = JSONERR_EXPECTED_TOKEN; | |
316 | 1 | p.errorarg = ','; | |
317 | 1 | return p; | |
318 | } | ||
319 | } | ||
320 | |||
321 | // Set the end of the array token to point to the closing bracket, then | ||
322 | // move it up. | ||
323 | 28 | json_setend(arr, array_tokenidx, p.textidx, maxtoken); | |
324 | 28 | json_setlength(arr, array_tokenidx, length, maxtoken); | |
325 | 28 | p.textidx++; | |
326 | 28 | return p; | |
327 | } | ||
328 | |||
329 | /** | ||
330 | @brief Parse an object. | ||
331 | @param text The text we're parsing. | ||
332 | @param arr The token buffer. | ||
333 | @param maxtoken The length of the token buffer. | ||
334 | @param p The parser state. | ||
335 | @returns Parser state after parsing the object. | ||
336 | */ | ||
337 | 35 | static struct json_parser json_parse_object(char *text, struct json_token *arr, | |
338 | size_t maxtoken, | ||
339 | struct json_parser p) | ||
340 | { | ||
341 | 35 | size_t object_tokenidx = p.tokenidx, prev_keyidx, curr_keyidx, | |
342 | 35 | length = 0; | |
343 | 35 | struct json_token tok = { | |
344 | .type = JSON_OBJECT, | ||
345 | 35 | .start = p.textidx, | |
346 | .length = 0, | ||
347 | .end = 0, | ||
348 | .child = 0, | ||
349 | .next = 0, | ||
350 | }; | ||
351 | 35 | json_settoken(arr, tok, p, maxtoken); | |
352 | |||
353 | // current char is {, so we need to go past it. | ||
354 | 35 | p.textidx++; | |
355 | 35 | p.tokenidx++; | |
356 | |||
357 | // Skip through whitespace. | ||
358 | 35 | p = json_skip_whitespace(text, p); | |
359 |
2/2✓ Branch 0 taken 170 times.
✓ Branch 1 taken 24 times.
|
194 | while (text[p.textidx] != '}') { |
360 | // Make sure the string didn't end. | ||
361 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 169 times.
|
170 | if (text[p.textidx] == '\0') { |
362 | 1 | p.error = JSONERR_PREMATURE_EOF; | |
363 | 1 | return p; | |
364 | } | ||
365 | |||
366 | // Parse a string (key) and value. | ||
367 | 169 | prev_keyidx = curr_keyidx; | |
368 | 169 | curr_keyidx = p.tokenidx; | |
369 | 169 | p = json_parse_string(text, arr, maxtoken, p); | |
370 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 162 times.
|
169 | if (p.error != JSONERR_NO_ERROR) { |
371 | 7 | return p; | |
372 | } | ||
373 | 162 | p = json_skip_whitespace(text, p); | |
374 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 161 times.
|
162 | if (text[p.textidx] != ':') { |
375 | 1 | p.error = JSONERR_EXPECTED_TOKEN; | |
376 | 1 | p.errorarg = ':'; | |
377 | 1 | return p; | |
378 | } | ||
379 | 161 | p.textidx++; | |
380 | 161 | p = json_parse_rec(text, arr, maxtoken, p); | |
381 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 160 times.
|
161 | if (p.error != JSONERR_NO_ERROR) { |
382 | 1 | return p; | |
383 | } | ||
384 | |||
385 | // Now set some bookkeeping of previous values. | ||
386 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 136 times.
|
160 | if (tok.child == 0) { |
387 | // If this is the first element of the list, set the | ||
388 | // list's child to point to it. | ||
389 | 24 | tok.child = curr_keyidx; | |
390 | 24 | json_setchild(arr, object_tokenidx, curr_keyidx, | |
391 | maxtoken); | ||
392 | } else { | ||
393 | // Otherwise set the previous element's next pointer to | ||
394 | // point to it. | ||
395 | 136 | json_setnext(arr, prev_keyidx, curr_keyidx, maxtoken); | |
396 | } | ||
397 | // Set the key's child pointer to point at its value. Just | ||
398 | // cause we can. | ||
399 | 160 | json_setchild(arr, curr_keyidx, curr_keyidx + 1, maxtoken); | |
400 | |||
401 | 160 | length++; | |
402 | |||
403 | // Skip whitespace. | ||
404 | 160 | p = json_skip_whitespace(text, p); | |
405 |
2/2✓ Branch 0 taken 138 times.
✓ Branch 1 taken 22 times.
|
160 | if (text[p.textidx] == ',') { |
406 | 138 | p.textidx++; | |
407 | 138 | p = json_skip_whitespace(text, p); | |
408 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
|
22 | } else if (text[p.textidx] != '}') { |
409 | // If there was no comma, this better be the end of the | ||
410 | // object. | ||
411 | 1 | p.error = JSONERR_EXPECTED_TOKEN; | |
412 | 1 | p.errorarg = ','; | |
413 | 1 | return p; | |
414 | } | ||
415 | } | ||
416 | |||
417 | // Set the end of the array token to point to the closing bracket, then | ||
418 | // move it up. | ||
419 | 24 | json_setend(arr, object_tokenidx, p.textidx, maxtoken); | |
420 | 24 | json_setlength(arr, object_tokenidx, length, maxtoken); | |
421 | 24 | p.textidx++; | |
422 | 24 | return p; | |
423 | } | ||
424 | |||
425 | char *parse_number_state[] = { | ||
426 | "START", "MINUS", "ZERO", | ||
427 | "DIGIT", "DECIMAL", "DECIMAL_ACCEPT", | ||
428 | "EXPONENT", "EXPONENT_DIGIT", "EXPONENT_DIGIT_ACCEPT", | ||
429 | "END" | ||
430 | }; | ||
431 | |||
432 | /** | ||
433 | @brief Parse a string number. | ||
434 | @param text The text we're parsing. | ||
435 | @param arr The token buffer. | ||
436 | @param maxtoken The length of the token buffer. | ||
437 | @param p The parser state. | ||
438 | @returns Parser state after parsing the number. | ||
439 | */ | ||
440 | 98 | static struct json_parser json_parse_number(char *text, struct json_token *arr, | |
441 | size_t maxtoken, | ||
442 | struct json_parser p) | ||
443 | { | ||
444 | 98 | struct json_token tok = { .type = JSON_NUMBER, | |
445 | 98 | .start = p.textidx, | |
446 | .length = 0, // not used | ||
447 | .end = 0, | ||
448 | .child = 0, | ||
449 | .next = 0 }; | ||
450 | enum state { | ||
451 | START, | ||
452 | MINUS, | ||
453 | ZERO, | ||
454 | DIGIT, | ||
455 | DECIMAL, | ||
456 | DECIMAL_ACCEPT, | ||
457 | EXPONENT, | ||
458 | EXPONENT_DIGIT, | ||
459 | EXPONENT_DIGIT_ACCEPT, | ||
460 | END | ||
461 | 98 | } state = START; | |
462 | |||
463 | /* | ||
464 | This function is completely described by this FSM. States marked by | ||
465 | asterisk are accepting. Unexpected input at accepting states ends the | ||
466 | number, and unexpected input at rejecting states causes an error. This | ||
467 | state machine is designed to accept any input given by the diagram in | ||
468 | the ECMA JSON spec. | ||
469 | |||
470 | -----START----- | ||
471 | / | (-) \ | ||
472 | / v \ | ||
473 | (0) | +----MINUS----+ | (1-9) | ||
474 | v v (0) (1-9) v v | ||
475 | *ZERO* *DIGIT*-------- | ||
476 | | \ (.) (.) / |-\ (0-9) \ | ||
477 | | --->DECIMAL<--- \ | ||
478 | | | \ | ||
479 | | v (0-9) /----\ (0-9) | | ||
480 | | *DECIMAL_ACCEPT* ----/ | | ||
481 | | | / | ||
482 | |(e,E) v (e,E) (e,E) / | ||
483 | +-----> EXPONENT <------------- | ||
484 | / \ | ||
485 | (+,-)v v (0-9) | ||
486 | EXPONENT_DIGIT *EXPONENT_DIGIT_ACCEPT* | ||
487 | \-----------/ \ /(0-9) | ||
488 | (0-9) \--/ | ||
489 | */ | ||
490 | |||
491 | // printf("input: %s\n", text + p.textidx); | ||
492 |
2/2✓ Branch 0 taken 346 times.
✓ Branch 1 taken 98 times.
|
444 | while (state != END) { |
493 | 346 | char c = text[p.textidx]; | |
494 | // printf("state: %s\n", parse_number_state[state]); | ||
495 |
9/11✓ Branch 0 taken 98 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 199 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
346 | switch (state) { |
496 | 98 | case START: | |
497 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 88 times.
|
98 | if (c == '0') { |
498 | 10 | state = ZERO; | |
499 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 82 times.
|
88 | } else if (c == '-') { |
500 | 6 | state = MINUS; | |
501 |
2/4✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
|
82 | } else if ('1' <= c && c <= '9') { |
502 | 82 | state = DIGIT; | |
503 | } else { | ||
504 | ✗ | p.error = JSONERR_INVALID_NUMBER; | |
505 | ✗ | state = END; // ERROR | |
506 | } | ||
507 | 98 | break; | |
508 | 6 | case MINUS: | |
509 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
|
6 | if (c == '0') { |
510 | 1 | state = ZERO; | |
511 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
5 | } else if ('1' <= c && c <= '9') { |
512 | 4 | state = DIGIT; | |
513 | } else { | ||
514 | 1 | p.error = JSONERR_INVALID_NUMBER; | |
515 | 1 | state = END; // ERROR | |
516 | } | ||
517 | 6 | break; | |
518 | 11 | case ZERO: | |
519 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
|
11 | if (c == '.') { |
520 | 1 | state = DECIMAL; | |
521 |
3/4✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
10 | } else if (c == 'e' || c == 'E') { |
522 | 1 | state = EXPONENT; | |
523 | } else { | ||
524 | 9 | state = END; | |
525 | } | ||
526 | 11 | break; | |
527 | 199 | case DIGIT: | |
528 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 195 times.
|
199 | if (c == '.') { |
529 | 4 | state = DECIMAL; | |
530 |
4/4✓ Branch 0 taken 188 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 187 times.
|
195 | } else if (c == 'e' || c == 'E') { |
531 | 8 | state = EXPONENT; | |
532 |
4/4✓ Branch 0 taken 119 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 113 times.
✓ Branch 3 taken 6 times.
|
187 | } else if ('0' <= c && c <= '9') { |
533 | 113 | state = DIGIT; | |
534 | } else { | ||
535 | 74 | state = END; | |
536 | } | ||
537 | 199 | break; | |
538 | 5 | case DECIMAL: | |
539 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
5 | if ('0' <= c && c <= '9') { |
540 | 4 | state = DECIMAL_ACCEPT; | |
541 | } else { | ||
542 | 1 | p.error = JSONERR_INVALID_NUMBER; | |
543 | 1 | state = END; // ERROR | |
544 | } | ||
545 | 5 | break; | |
546 | 5 | case DECIMAL_ACCEPT: | |
547 |
4/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
5 | if ('0' <= c && c <= '9') { |
548 | 1 | state = DECIMAL_ACCEPT; | |
549 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
4 | } else if (c == 'e' || c == 'E') { |
550 | 1 | state = EXPONENT; | |
551 | } else { | ||
552 | 3 | state = END; | |
553 | } | ||
554 | 5 | break; | |
555 | 10 | case EXPONENT: | |
556 |
4/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 6 times.
|
10 | if (c == '+' || c == '-') { |
557 | 4 | state = EXPONENT_DIGIT; | |
558 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
6 | } else if ('0' <= c && c <= '9') { |
559 | 4 | state = EXPONENT_DIGIT_ACCEPT; | |
560 | } else { | ||
561 | 2 | p.error = JSONERR_INVALID_NUMBER; | |
562 | 2 | state = END; // ERROR | |
563 | } | ||
564 | 10 | break; | |
565 | 4 | case EXPONENT_DIGIT: | |
566 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
4 | if ('0' <= c && c <= '9') { |
567 | 3 | state = EXPONENT_DIGIT_ACCEPT; | |
568 | } else { | ||
569 | 1 | p.error = JSONERR_INVALID_NUMBER; | |
570 | 1 | state = END; // ERROR | |
571 | } | ||
572 | 4 | break; | |
573 | 8 | case EXPONENT_DIGIT_ACCEPT: | |
574 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
8 | if ('0' <= c && c <= '9') { |
575 | 1 | state = EXPONENT_DIGIT_ACCEPT; | |
576 | } else { | ||
577 | 7 | state = END; | |
578 | } | ||
579 | 8 | break; | |
580 | ✗ | case END: | |
581 | // never happens | ||
582 | ✗ | assert(false); | |
583 | } | ||
584 | 346 | p.textidx++; | |
585 | } | ||
586 | |||
587 | 98 | p.textidx--; // the character we failed on | |
588 | 98 | tok.end = p.textidx - 1; // the previous character | |
589 | 98 | json_settoken(arr, tok, p, maxtoken); | |
590 | 98 | p.tokenidx++; | |
591 | 98 | return p; | |
592 | } | ||
593 | |||
594 | /** | ||
595 | @brief Parse any JSON value. | ||
596 | @param text The text we're parsing. | ||
597 | @param arr The token buffer. | ||
598 | @param maxtoken The length of the token buffer. | ||
599 | @param p The parser state. | ||
600 | @returns Parser state after parsing the value. | ||
601 | */ | ||
602 | 334 | static struct json_parser json_parse_rec(char *text, struct json_token *arr, | |
603 | size_t maxtoken, struct json_parser p) | ||
604 | { | ||
605 | 334 | p = json_skip_whitespace(text, p); | |
606 | |||
607 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 332 times.
|
334 | if (text[p.textidx] == '\0') { |
608 | 2 | p.error = JSONERR_PREMATURE_EOF; | |
609 | 2 | return p; | |
610 | } | ||
611 | |||
612 |
7/7✓ Branch 0 taken 35 times.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 107 times.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 20 times.
✓ Branch 5 taken 23 times.
✓ Branch 6 taken 99 times.
|
332 | switch (text[p.textidx]) { |
613 | 35 | case '{': | |
614 | 35 | return json_parse_object(text, arr, maxtoken, p); | |
615 | 31 | case '[': | |
616 | 31 | return json_parse_array(text, arr, maxtoken, p); | |
617 | 107 | case '"': | |
618 | 107 | return json_parse_string(text, arr, maxtoken, p); | |
619 | 17 | case 't': | |
620 | 17 | return json_parse_true(text, arr, maxtoken, p); | |
621 | 20 | case 'f': | |
622 | 20 | return json_parse_false(text, arr, maxtoken, p); | |
623 | 23 | case 'n': | |
624 | 23 | return json_parse_null(text, arr, maxtoken, p); | |
625 | 99 | default: | |
626 |
2/2✓ Branch 1 taken 98 times.
✓ Branch 2 taken 1 times.
|
99 | if (json_isnumber(text[p.textidx])) { |
627 | 98 | return json_parse_number(text, arr, maxtoken, p); | |
628 | } else { | ||
629 | 1 | p.error = JSONERR_UNEXPECTED_TOKEN; | |
630 | 1 | return p; | |
631 | } | ||
632 | } | ||
633 | } | ||
634 | |||
635 | char *json_type_str[] = { "object", "array", "number", "string", | ||
636 | "true", "false", "null" }; | ||
637 | |||
638 | char *json_error_str[] = { | ||
639 | "no error", | ||
640 | "encountered an invalid numeric literal", | ||
641 | "string ended prematurely", | ||
642 | "unexpected token", | ||
643 | "invalid surrogate pair", | ||
644 | "expected token '%c'", | ||
645 | }; | ||
646 | |||
647 | 107 | struct json_parser json_parse(char *text, struct json_token *arr, | |
648 | size_t maxtoken) | ||
649 | { | ||
650 | 107 | struct json_parser parser = { .textidx = 0, | |
651 | .tokenidx = 0, | ||
652 | .error = JSONERR_NO_ERROR, | ||
653 | .errorarg = 0 }; | ||
654 | 107 | return json_parse_rec(text, arr, maxtoken, parser); | |
655 | } | ||
656 | |||
657 | ✗ | void json_print(struct json_token *arr, size_t n) | |
658 | { | ||
659 | size_t i; | ||
660 | ✗ | for (i = 0; i < n; i++) { | |
661 | ✗ | printf("%03lu: " | |
662 | "%6s\t%04lu-%04lu,\tlength=%lu,\tchild=%lu,\tnext=%lu\n", | ||
663 | ✗ | i, json_type_str[arr[i].type], arr[i].start, arr[i].end, | |
664 | ✗ | arr[i].length, arr[i].child, arr[i].next); | |
665 | } | ||
666 | } | ||
667 | |||
668 | ✗ | void json_print_error(FILE *f, struct json_parser p) | |
669 | { | ||
670 | ✗ | fprintf(f, "at character %lu: ", p.textidx); | |
671 | ✗ | fprintf(f, json_error_str[p.error], p.errorarg); | |
672 | ✗ | fprintf(f, "\n"); | |
673 | } | ||
674 |