diff --git a/parson.c b/parson.c index b273ba1..6277056 100644 --- a/parson.c +++ b/parson.c @@ -1,6 +1,6 @@ /* Parson ( http://kgabis.github.com/parson/ ) - Copyright (c) 2012 Krzysztof Gabis + Copyright (c) 2013 Krzysztof Gabis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -72,6 +72,8 @@ struct json_array_t { }; /* Various */ +static char * read_file(const char *filename); +static void remove_comments(char *string, const char *start_token, const char *end_token); static int try_realloc(void **ptr, size_t new_size); static char * parson_strndup(const char *string, size_t n); static int is_utf(const unsigned char *string); @@ -136,6 +138,51 @@ static int is_decimal(const char *string, size_t length) { return 1; } +static char * read_file(const char * filename) { + FILE *fp = fopen(filename, "r"); + size_t file_size; + char *file_contents; + if (!fp) { return NULL; } + fseek(fp, 0L, SEEK_END); + file_size = ftell(fp); + rewind(fp); + file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); + if (!file_contents) { fclose(fp); return NULL; } + if (fread(file_contents, file_size, 1, fp) < 1) { + if (ferror(fp)) { fclose(fp); return NULL; } + } + fclose(fp); + file_contents[file_size] = '\0'; + return file_contents; +} + +static void remove_comments(char *string, const char *start_token, const char *end_token) { + int in_string = 0, escaped = 0, i; + char *ptr = NULL, current_char; + size_t start_token_len = strlen(start_token); + size_t end_token_len = strlen(end_token); + if (start_token_len == 0 || end_token_len == 0) + return; + while ((current_char = *string) != '\0') { + if (current_char == '\\' && !escaped) { + escaped = 1; + string++; + continue; + } else if (current_char == '\"' && !escaped) { + in_string = !in_string; + } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) { + for(i = 0; i < start_token_len; i++) string[i] = ' '; + string = string + start_token_len; + ptr = strstr(string, end_token); + if (!ptr) return; + for (i = 0; i < (ptr - string) + end_token_len; i++) string[i] = ' '; + string = ptr + end_token_len - 1; + } + escaped = 0; + string++; + } +} + /* JSON Object */ static JSON_Object * json_object_init(void) { JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); @@ -312,7 +359,8 @@ static const char * get_processed_string(const char **string) { unprocessed_ptr++; if (!is_utf((const unsigned char*)unprocessed_ptr) || sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) { - parson_free(output); return NULL; + parson_free(output); + return NULL; } if (utf_val < 0x80) { current_char = utf_val; @@ -490,32 +538,56 @@ static JSON_Value * parse_null_value(const char **string) { /* Parser API */ JSON_Value * json_parse_file(const char *filename) { - FILE *fp = fopen(filename, "r"); - size_t file_size; - char *file_contents; - JSON_Value *output_value; - if (!fp) { return NULL; } - fseek(fp, 0L, SEEK_END); - file_size = ftell(fp); - rewind(fp); - file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); - if (!file_contents) { fclose(fp); return NULL; } - if (fread(file_contents, file_size, 1, fp) < 1) { - if (ferror(fp)) { fclose(fp); return NULL; } + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (!file_contents) { + return NULL; } - fclose(fp); - file_contents[file_size] = '\0'; output_value = json_parse_string(file_contents); parson_free(file_contents); return output_value; } +JSON_Value * json_parse_file_with_comments(const char *filename) { + char *file_contents = read_file(filename); + JSON_Value *output_value = NULL; + if (!file_contents) { + return NULL; + } + output_value = json_parse_string_with_comments(file_contents); + parson_free(file_contents); + return output_value; +} + JSON_Value * json_parse_string(const char *string) { if (!string || (*string != '{' && *string != '[')) { return NULL; } return parse_value((const char**)&string, 0); } +JSON_Value * json_parse_string_with_comments(const char *string) { + JSON_Value *result = NULL; + char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL; + string_mutable_copy = parson_strndup(string, strlen(string)); + if (!string_mutable_copy) { + return NULL; + } + remove_comments(string_mutable_copy, "/*", "*/"); + remove_comments(string_mutable_copy, "//", "\n"); + puts(string_mutable_copy); + string_mutable_copy_ptr = string_mutable_copy; + skip_whitespaces(&string_mutable_copy_ptr); + if (*string_mutable_copy_ptr != '{' && *string_mutable_copy_ptr != '[') { + parson_free(string_mutable_copy); + return NULL; + } + result = parse_value((const char**)&string_mutable_copy_ptr, 0); + parson_free(string_mutable_copy); + return result; +} + + /* JSON Object API */ + JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) { return json_object_nget_value(object, name, strlen(name)); } diff --git a/parson.h b/parson.h index 00728b1..ef21890 100644 --- a/parson.h +++ b/parson.h @@ -1,6 +1,6 @@ /* Parson ( http://kgabis.github.com/parson/ ) - Copyright (c) 2012 Krzysztof Gabis + Copyright (c) 2013 Krzysztof Gabis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -30,6 +30,8 @@ extern "C" #endif #include /* size_t */ + +#define PARSON_VERSION 20131130 /* Types and enums */ typedef struct json_object_t JSON_Object; @@ -46,12 +48,21 @@ typedef enum json_value_type { JSONBoolean = 6 } JSON_Value_Type; + /* Parses first JSON value in a file, returns NULL in case of error */ JSON_Value * json_parse_file(const char *filename); +/* Parses first JSON value in a file and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_file_with_comments(const char *filename); + /* Parses first JSON value in a string, returns NULL in case of error */ JSON_Value * json_parse_string(const char *string); +/* Parses first JSON value in a string and ignores comments (/ * * / and //), + returns NULL in case of error */ +JSON_Value * json_parse_string_with_comments(const char *string); + /* JSON Object */ JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); const char * json_object_get_string (const JSON_Object *object, const char *name); diff --git a/tests.c b/tests.c index 2046ec2..b357e6d 100644 --- a/tests.c +++ b/tests.c @@ -1,6 +1,6 @@ /* Parson ( http://kgabis.github.com/parson/ ) - Copyright (c) 2012 Krzysztof Gabis + Copyright (c) 2013 Krzysztof Gabis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -32,10 +32,14 @@ else{puts(" FAIL");tests_failed++;} #define STREQ(A, B) (A && B ? strcmp(A, B) == 0 : 0) + void test_suite_1(void); -void test_suite_2(void); +void test_suite_2(JSON_Value *value); +void test_suite_2_no_comments(void); +void test_suite_2_with_commnets(void); void test_suite_3(void); +char *read_file(const char *filename); void print_commits_info(const char *username, const char *repo); static int tests_passed; @@ -45,7 +49,8 @@ int main() { /* Example function from readme file: */ /* print_commits_info("torvalds", "linux"); */ test_suite_1(); - test_suite_2(); + test_suite_2_no_comments(); + test_suite_2_with_commnets(); test_suite_3(); printf("Tests failed: %d\n", tests_failed); printf("Tests passed: %d\n", tests_passed); @@ -61,31 +66,35 @@ void test_suite_1(void) { if (val) { json_value_free(val); } TEST((val = json_parse_file("tests/test_1_3.txt")) != NULL); if (val) { json_value_free(val); } + + TEST((val = json_parse_file_with_comments("tests/test_1_1.txt")) != NULL); + if (val) { json_value_free(val); } + TEST((val = json_parse_file_with_comments("tests/test_1_2.txt")) != NULL); + if (val) { json_value_free(val); } + TEST((val = json_parse_file_with_comments("tests/test_1_3.txt")) != NULL); + if (val) { json_value_free(val); } + } /* Testing correctness of parsed values */ -void test_suite_2(void) { - JSON_Value *root_value; - JSON_Object *object; +void test_suite_2(JSON_Value *root_value) { + JSON_Object *root_object; JSON_Array *array; size_t i; - const char *filename = "tests/test_2.txt"; - printf("Testing %s:\n", filename); - root_value = json_parse_file(filename); TEST(root_value); TEST(json_value_get_type(root_value) == JSONObject); - object = json_value_get_object(root_value); - TEST(STREQ(json_object_get_string(object, "string"), "lorem ipsum")); - TEST(STREQ(json_object_get_string(object, "utf string"), "lorem ipsum")); - TEST(STREQ(json_object_get_string(object, "utf-8 string"), "あいうえお")); - TEST(json_object_get_number(object, "positive one") == 1.0); - TEST(json_object_get_number(object, "negative one") == -1.0); - TEST(json_object_get_number(object, "hard to parse number") == -0.000314); - TEST(json_object_get_boolean(object, "boolean true") == 1); - TEST(json_object_get_boolean(object, "boolean false") == 0); - TEST(json_value_get_type(json_object_get_value(object, "null")) == JSONNull); + root_object = json_value_get_object(root_value); + TEST(STREQ(json_object_get_string(root_object, "string"), "lorem ipsum")); + TEST(STREQ(json_object_get_string(root_object, "utf string"), "lorem ipsum")); + TEST(STREQ(json_object_get_string(root_object, "utf-8 string"), "あいうえお")); + TEST(json_object_get_number(root_object, "positive one") == 1.0); + TEST(json_object_get_number(root_object, "negative one") == -1.0); + TEST(json_object_get_number(root_object, "hard to parse number") == -0.000314); + TEST(json_object_get_boolean(root_object, "boolean true") == 1); + TEST(json_object_get_boolean(root_object, "boolean false") == 0); + TEST(json_value_get_type(json_object_get_value(root_object, "null")) == JSONNull); - array = json_object_get_array(object, "string array"); + array = json_object_get_array(root_object, "string array"); if (array != NULL && json_array_get_count(array) > 1) { TEST(STREQ(json_array_get_string(array, 0), "lorem")); TEST(STREQ(json_array_get_string(array, 1), "ipsum")); @@ -93,7 +102,7 @@ void test_suite_2(void) { tests_failed++; } - array = json_object_get_array(object, "x^2 array"); + array = json_object_get_array(root_object, "x^2 array"); if (array != NULL) { for (i = 0; i < json_array_get_count(array); i++) { TEST(json_array_get_number(array, i) == (i * i)); @@ -102,26 +111,46 @@ void test_suite_2(void) { tests_failed++; } - TEST(json_object_get_array(object, "non existent array") == NULL); - TEST(STREQ(json_object_dotget_string(object, "object.nested string"), "str")); - TEST(json_object_dotget_boolean(object, "object.nested true") == 1); - TEST(json_object_dotget_boolean(object, "object.nested false") == 0); - TEST(json_object_dotget_value(object, "object.nested null") != NULL); - TEST(json_object_dotget_number(object, "object.nested number") == 123); + TEST(json_object_get_array(root_object, "non existent array") == NULL); + TEST(STREQ(json_object_dotget_string(root_object, "object.nested string"), "str")); + TEST(json_object_dotget_boolean(root_object, "object.nested true") == 1); + TEST(json_object_dotget_boolean(root_object, "object.nested false") == 0); + TEST(json_object_dotget_value(root_object, "object.nested null") != NULL); + TEST(json_object_dotget_number(root_object, "object.nested number") == 123); - TEST(json_object_dotget_value(object, "should.be.null") == NULL); - TEST(json_object_dotget_value(object, "should.be.null.") == NULL); - TEST(json_object_dotget_value(object, ".") == NULL); - TEST(json_object_dotget_value(object, "") == NULL); + TEST(json_object_dotget_value(root_object, "should.be.null") == NULL); + TEST(json_object_dotget_value(root_object, "should.be.null.") == NULL); + TEST(json_object_dotget_value(root_object, ".") == NULL); + TEST(json_object_dotget_value(root_object, "") == NULL); - array = json_object_dotget_array(object, "object.nested array"); + array = json_object_dotget_array(root_object, "object.nested array"); if (array != NULL && json_array_get_count(array) > 1) { TEST(STREQ(json_array_get_string(array, 0), "lorem")); TEST(STREQ(json_array_get_string(array, 1), "ipsum")); } else { tests_failed++; } - TEST(json_object_dotget_boolean(object, "nested true")); + TEST(json_object_dotget_boolean(root_object, "nested true")); + + TEST(STREQ(json_object_get_string(root_object, "/**/"), "comment")); + TEST(STREQ(json_object_get_string(root_object, "//"), "comment")); +} + +void test_suite_2_no_comments(void) { + const char *filename = "tests/test_2.txt"; + JSON_Value *root_value = NULL; + printf("Testing %s:\n", filename); + root_value = json_parse_file(filename); + test_suite_2(root_value); + json_value_free(root_value); +} + +void test_suite_2_with_commnets(void) { + const char *filename = "tests/test_2_comments.txt"; + JSON_Value *root_value = NULL; + printf("Testing %s:\n", filename); + root_value = json_parse_file_with_comments(filename); + test_suite_2(root_value); json_value_free(root_value); } @@ -132,6 +161,8 @@ void test_suite_3(void) { TEST(json_parse_string(NULL) == NULL); TEST(json_parse_string("") == NULL); /* empty string */ TEST(json_parse_string("[\"lorem\",]") == NULL); + TEST(json_parse_string("{\"lorem\":\"ipsum\",}") == NULL); + TEST(json_parse_string("{lorem:ipsum}") == NULL); TEST(json_parse_string("[,]") == NULL); TEST(json_parse_string("[,") == NULL); TEST(json_parse_string("[") == NULL); diff --git a/tests/test_2.txt b/tests/test_2.txt index 279d477..aa32863 100644 --- a/tests/test_2.txt +++ b/tests/test_2.txt @@ -1,7 +1,7 @@ { "string" : "lorem ipsum", "utf string" : "\u006corem\u0020ipsum", - "utf-8 string": "あいうえお", + "utf-8 string": "あいうえお", "positive one" : 1, "negative one" : -1, "pi" : 3.14, @@ -12,10 +12,15 @@ "string array" : ["lorem", "ipsum"], "x^2 array" : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100], "object_empty" : { }, + "/*" : null, "object" : { "nested string" : "str", - "nested true" : true, + "nested true" : true, "nested false" : false, "nested null" : null, "nested number" : 123, - "nested array" : ["lorem", "ipsum"] } + "nested array" : ["lorem", "ipsum"] }, + "*/" : null, + "/**/" : "comment", + "//" : "comment", + "#" : "comment" } diff --git a/tests/test_2_comments.txt b/tests/test_2_comments.txt new file mode 100644 index 0000000..ca85784 --- /dev/null +++ b/tests/test_2_comments.txt @@ -0,0 +1,34 @@ +/* + *Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor + *ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + *dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +*/ +// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor +{ /* lorem ipsum */ + "string" : "lorem ipsum", // lorem ipsum + "utf string" : "\u006corem\u0020ipsum", // lorem ipsum // + "utf-8 string": "あいうえお", // /* lorem ipsum */ + "positive one" : 1, + "negative one" : -1, + "pi" : 3.14, + "hard to parse number" : -3.14e-4, + "boolean true" : true, + "boolean false" : false, + "null" : null, + "string array" : ["lorem",/*in array*/"ipsum"], + "x^2 array" : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100], +/* "x^2 array" : [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100], +*/ "object_empty" : { }, + "/*" : null, + "object" : { "nested string" : "str", + "nested true" : /* lorem ipsum */ true, + "nested false" : false, + "nested null" : null, // lorem ipsum + "nested number" : 123, + "nested array" : ["lorem", "ipsum"] }, + "*/" : null, + "/**/" : "comment", + "//" : "comment" +} +/**/ +// \ No newline at end of file