Skip to content

Commit 5b16a20

Browse files
author
Suhel Chakraborty
authored
Fixed empty object/array parsing issue (#8)
* Fixed empty object/array parsing issue * Enhanced example
1 parent 38c4b32 commit 5b16a20

File tree

11 files changed

+439
-1880
lines changed

11 files changed

+439
-1880
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1-
.vscode
1+
.vscode/**/*
2+
!.vscode/settings.json
3+
!.vscode/tasks.json
4+
!.vscode/launch.json
5+
!.vscode/extensions.json
26
bin

.vscode/launch.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Launch",
9+
"type": "cppdbg",
10+
"request": "launch",
11+
"program": "${workspaceFolder}/bin/a.out",
12+
"cwd": "${workspaceFolder}/bin",
13+
"preLaunchTask": "Build",
14+
"linux": {
15+
"MIMode": "gdb",
16+
"miDebuggerPath": "/usr/bin/gdb"
17+
},
18+
"osx": {
19+
"MIMode": "lldb"
20+
},
21+
"windows": {
22+
"MIMode": "gdb",
23+
"miDebuggerPath": "C:\\MinGw\\bin\\gdb.exe"
24+
}
25+
}
26+
]
27+
}

.vscode/settings.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"files.associations": {
3+
"*.x": "linkerscript",
4+
"typeinfo": "c",
5+
"__bit_reference": "c",
6+
"__functional_base": "c",
7+
"__node_handle": "c",
8+
"algorithm": "c",
9+
"atomic": "c",
10+
"bitset": "c",
11+
"chrono": "c",
12+
"cstddef": "c",
13+
"__memory": "c",
14+
"functional": "c",
15+
"iterator": "c",
16+
"limits": "c",
17+
"locale": "c",
18+
"memory": "c",
19+
"optional": "c",
20+
"ratio": "c",
21+
"system_error": "c",
22+
"tuple": "c",
23+
"type_traits": "c",
24+
"vector": "c",
25+
"__config": "c",
26+
"__hash_table": "c",
27+
"__split_buffer": "c",
28+
"array": "c",
29+
"initializer_list": "c",
30+
"string": "c",
31+
"string_view": "c",
32+
"unordered_map": "c",
33+
"utility": "c"
34+
}
35+
}

.vscode/tasks.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"tasks": [
3+
{
4+
"type": "shell",
5+
"label": "Build",
6+
"command": "/usr/bin/clang",
7+
"args": [
8+
"-fdiagnostics-color=always",
9+
"-g",
10+
"-rdynamic",
11+
"${workspaceFolder}/*.c",
12+
"-o",
13+
"${workspaceFolder}/bin/a.out"
14+
],
15+
"options": {
16+
"cwd": "${workspaceFolder}"
17+
},
18+
"problemMatcher": ["$gcc"],
19+
"group": "build",
20+
"detail": "C build task"
21+
}
22+
],
23+
"version": "2.0.0"
24+
}

Makefile

Lines changed: 0 additions & 2 deletions
This file was deleted.

asteroids.json

Lines changed: 202 additions & 0 deletions
Large diffs are not rendered by default.

example.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,46 @@
1-
#include "example.h"
2-
31
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
44

55
#include "json.h"
66

7+
const char *read_file(const char *path) {
8+
FILE *file = fopen(path, "r");
9+
if (file == NULL) {
10+
fprintf(stderr, "Expected file \"%s\" not found", path);
11+
return NULL;
12+
}
13+
fseek(file, 0, SEEK_END);
14+
long len = ftell(file);
15+
fseek(file, 0, SEEK_SET);
16+
char *buffer = malloc(len);
17+
18+
if (buffer == NULL) {
19+
fprintf(stderr, "Unable to allocate memory for file");
20+
fclose(file);
21+
return NULL;
22+
}
23+
24+
fread(buffer, 1, len, file);
25+
26+
return (const char *)buffer;
27+
}
28+
729
int main() {
30+
const char *json = read_file("../asteroids.json");
31+
if (json == NULL) {
32+
return -1;
33+
}
34+
835
json_object_t *object = json_parse(json);
36+
free((void *)json);
37+
38+
if (object == NULL) {
39+
return -1;
40+
}
41+
942
json_print(object, 2);
1043
json_free(object);
44+
1145
return 0;
1246
}

example.h

Lines changed: 0 additions & 1780 deletions
This file was deleted.

json.c

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ static void json_free_object(typed(json_object) *);
2626
static void json_free_array(typed(json_array) *);
2727
static void json_skip_whitespace(typed(json_string) *);
2828
static void json_skip_null(typed(json_string) *);
29-
static result(size) json_index_of(typed(json_string), char);
3029
static result(json_value) json_parse_value(typed(json_string) *, typed(json_type));
3130
static result(json_value) json_parse_string(typed(json_string) *);
32-
static result(size) json_string_len(typed(json_string));
31+
static typed(size) json_string_len(typed(json_string));
3332
static result(json_string) json_parse_escaped_string(typed(json_string), typed(size));
3433
static result(json_value) json_parse_number(typed(json_string) *);
3534
static result(json_value) json_parse_object(typed(json_string) *);
@@ -42,7 +41,7 @@ typed(json_object) * json_parse(typed(json_string) json_str) {
4241
result(json_value) output_result = json_parse_object(&json_str);
4342

4443
if (result_is_err(json_value)(&output_result)) {
45-
fprintf(stderr, "Error parsing JSON: %s", result_unwrap_err(json_value)(&output_result));
44+
fprintf(stderr, "Error parsing JSON: %s", json_error_to_string(result_unwrap_err(json_value)(&output_result)));
4645
return NULL;
4746
}
4847

@@ -57,6 +56,22 @@ void json_free(typed(json_object) * object) {
5756
json_free_object(object);
5857
}
5958

59+
typed(json_string) json_error_to_string(const typed(json_error) err) {
60+
switch (err) {
61+
case JSON_ERROR_EMPTY:
62+
return "Empty";
63+
case JSON_ERROR_INVALID_KEY:
64+
return "Invalid key";
65+
case JSON_ERROR_INVALID_TYPE:
66+
return "Invalid type";
67+
case JSON_ERROR_INVALID_VALUE:
68+
return "Invalid value";
69+
70+
default:
71+
return "Unknown error";
72+
}
73+
}
74+
6075
static void json_print_value(typed(json_type) type, typed(json_value) * value, int indent, int indent_level) {
6176
switch (type) {
6277
case JSON_TYPE_STRING:
@@ -192,26 +207,6 @@ static void json_skip_null(typed(json_string) * str_ptr) {
192207
(*str_ptr) += 4;
193208
}
194209

195-
static result(size) json_index_of(typed(json_string) str, char ch) {
196-
typed(size) pos = 0;
197-
198-
while (*str != '\0') {
199-
// We are gonna skip past escape sequences for now
200-
if (ch == '\\') {
201-
str += 2;
202-
}
203-
204-
if (ch == *str) {
205-
return result_ok(size)(pos);
206-
}
207-
208-
str++;
209-
pos++;
210-
}
211-
212-
return result_err(size)("Not found");
213-
}
214-
215210
static result(json_value) json_parse_value(typed(json_string) * str_ptr, typed(json_type) type) {
216211
switch (type) {
217212
case JSON_TYPE_STRING:
@@ -226,15 +221,21 @@ static result(json_value) json_parse_value(typed(json_string) * str_ptr, typed(j
226221
return json_parse_boolean(str_ptr);
227222
case JSON_TYPE_NULL:
228223
json_skip_null(str_ptr);
229-
return result_err(json_value)("Can't parse null");
224+
return result_err(json_value)(JSON_ERROR_EMPTY);
230225
}
231226
}
232227

233228
static result(json_value) json_parse_string(typed(json_string) * str_ptr) {
234229
// Skip the first '"' character
235230
(*str_ptr)++;
236231

237-
result_try(json_value, size, len, json_string_len(*str_ptr));
232+
typed(size) len = json_string_len(*str_ptr);
233+
if (len == 0) {
234+
// Skip the end quote
235+
(*str_ptr)++;
236+
return result_err(json_value)(JSON_ERROR_EMPTY);
237+
}
238+
238239
result_try(json_value, json_string, output, json_parse_escaped_string(*str_ptr, len));
239240

240241
// Skip to beyond the string
@@ -243,7 +244,7 @@ static result(json_value) json_parse_string(typed(json_string) * str_ptr) {
243244
return result_ok(json_value)((typed(json_value))output);
244245
}
245246

246-
static result(size) json_string_len(typed(json_string) str) {
247+
static typed(size) json_string_len(typed(json_string) str) {
247248
typed(size) len = 0;
248249

249250
typed(json_string) iter = str;
@@ -259,10 +260,7 @@ static result(size) json_string_len(typed(json_string) str) {
259260
iter++;
260261
}
261262

262-
if (len == 0)
263-
return result_err(size)("Invalid size");
264-
265-
return result_ok(size)(len);
263+
return len;
266264
}
267265

268266
static result(json_string) json_parse_escaped_string(typed(json_string) str, typed(size) len) {
@@ -309,7 +307,7 @@ static result(json_string) json_parse_escaped_string(typed(json_string) str, typ
309307
output[offset] = '\\';
310308
break;
311309
default:
312-
return result_err(json_string)("Invalid escape character");
310+
return result_err(json_string)(JSON_ERROR_INVALID_VALUE);
313311
}
314312
} else {
315313
output[offset] = *iter;
@@ -327,11 +325,8 @@ static result(json_value) json_parse_number(typed(json_string) * str_ptr) {
327325
errno = 0;
328326
typed(json_number) number = strtod(*str_ptr, (char **)str_ptr);
329327

330-
if (errno == EINVAL)
331-
return result_err(json_value)("Invalid number");
332-
333-
if (errno == ERANGE)
334-
return result_err(json_value)("Out of range");
328+
if (errno == EINVAL || errno == ERANGE)
329+
return result_err(json_value)(JSON_ERROR_INVALID_VALUE);
335330

336331
return result_ok(json_value)((typed(json_value))number);
337332
}
@@ -342,8 +337,11 @@ static result(json_value) json_parse_object(typed(json_string) * str_ptr) {
342337

343338
json_scrape_whitespace(str_ptr);
344339

345-
if (**str_ptr == '}')
346-
return result_err(json_value)("Empty");
340+
if (**str_ptr == '}') {
341+
// Skip the end '}'
342+
(*str_ptr)++;
343+
return result_err(json_value)(JSON_ERROR_EMPTY);
344+
}
347345

348346
typed(json_entry) *entries = NULL;
349347
typed(size) count = 0;
@@ -357,7 +355,7 @@ static result(json_value) json_parse_object(typed(json_string) * str_ptr) {
357355
if (result_is_ok(json_entry)(&entry_result)) {
358356
count++;
359357
entries = reallocN(entries, typed(json_entry), count);
360-
const typed(json_entry) entry = result_unwrap(json_entry)(&entry_result);
358+
typed(json_entry) entry = result_unwrap(json_entry)(&entry_result);
361359
memcpy(&entries[count - 1], &entry, sizeof(typed(json_entry)));
362360
}
363361

@@ -372,7 +370,7 @@ static result(json_value) json_parse_object(typed(json_string) * str_ptr) {
372370
}
373371

374372
if (entries == NULL)
375-
return result_err(json_value)("No non-null entries");
373+
return result_err(json_value)(JSON_ERROR_EMPTY);
376374

377375
// Skip the '}' closing brace
378376
(*str_ptr)++;
@@ -390,8 +388,11 @@ static result(json_value) json_parse_array(typed(json_string) * str_ptr) {
390388

391389
json_scrape_whitespace(str_ptr);
392390

393-
if (**str_ptr == ']')
394-
return result_err(json_value)("Empty array");
391+
if (**str_ptr == ']') {
392+
// Skip the end ']'
393+
(*str_ptr)++;
394+
return result_err(json_value)(JSON_ERROR_EMPTY);
395+
}
395396

396397
result_try(json_value, json_type, type, json_guess_value_type(*str_ptr));
397398

@@ -405,7 +406,7 @@ static result(json_value) json_parse_array(typed(json_string) * str_ptr) {
405406
if (result_is_ok(json_value)(&value_result)) {
406407
count++;
407408
values = reallocN(values, typed(json_value), count);
408-
const typed(json_value) value = result_unwrap(json_value)(&value_result);
409+
typed(json_value) value = result_unwrap(json_value)(&value_result);
409410
memcpy(&values[count - 1], &value, sizeof(typed(json_value)));
410411
}
411412

@@ -420,6 +421,10 @@ static result(json_value) json_parse_array(typed(json_string) * str_ptr) {
420421
// Skip the ']' closing array
421422
(*str_ptr)++;
422423

424+
if (count == 0) {
425+
return result_err(json_value)(JSON_ERROR_EMPTY);
426+
}
427+
423428
typed(json_array) *array = alloc(typed(json_array));
424429
array->count = count;
425430
array->type = type;
@@ -460,14 +465,14 @@ static result(json_entry) json_parse_entry(typed(json_string) * str_ptr) {
460465
free((void *)key.as_string);
461466
return result_map_err(json_entry, json_type, &type_result);
462467
}
463-
const typed(json_type) type = result_unwrap(json_type)(&type_result);
468+
typed(json_type) type = result_unwrap(json_type)(&type_result);
464469

465470
result(json_value) value_result = json_parse_value(str_ptr, type);
466471
if (result_is_err(json_value)(&value_result)) {
467472
free((void *)key.as_string);
468473
return result_map_err(json_entry, json_value, &value_result);
469474
}
470-
const typed(json_value) value = result_unwrap(json_value)(&value_result);
475+
typed(json_value) value = result_unwrap(json_value)(&value_result);
471476

472477
typed(json_entry) entry = {
473478
.key = key.as_string,
@@ -495,7 +500,7 @@ static result(json_type) json_guess_value_type(typed(json_string) str) {
495500
else if (first_ch == 'n')
496501
type = JSON_TYPE_NULL;
497502
else
498-
return result_err(json_type)("Invalid type");
503+
return result_err(json_type)(JSON_ERROR_INVALID_TYPE);
499504

500505
return result_ok(json_type)(type);
501506
}

json.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ declare_result_type(size);
1212

1313
typed(json_object) * json_parse(typed(json_string) json_str);
1414
void json_print(typed(json_object) * obj, int indent);
15-
void json_free(typed(json_object) * obj);
15+
void json_free(typed(json_object) * obj);
16+
typed(json_string) json_error_to_string(const typed(json_error) err);

0 commit comments

Comments
 (0)