-// Distributed under the MIT license. Copyright (c) 2010, Ivan Vashchaev
-
-#include <string.h>
-#include "json.h"
-
-// true if character represent a digit
-#define IS_DIGIT(c) (c >= '0' && c <= '9')
-
-// convert string to integer
-static char *atoi(char *first, char *last, int *out)
-{
- int sign = 1;
- if (first != last)
- {
- if (*first == '-')
- {
- sign = -1;
- ++first;
- }
- else if (*first == '+')
- {
- ++first;
- }
- }
-
- int result = 0;
- for (; first != last && IS_DIGIT(*first); ++first)
- {
- result = 10 * result + (*first - '0');
- }
- *out = result * sign;
-
- return first;
-}
-
-// convert hexadecimal string to unsigned integer
-static char *hatoui(char *first, char *last, unsigned int *out)
-{
- unsigned int result = 0;
- for (; first != last; ++first)
- {
- int digit;
- if (IS_DIGIT(*first))
- {
- digit = *first - '0';
- }
- else if (*first >= 'a' && *first <= 'f')
- {
- digit = *first - 'a' + 10;
- }
- else if (*first >= 'A' && *first <= 'F')
- {
- digit = *first - 'A' + 10;
- }
- else
- {
- break;
- }
- result = 16 * result + (unsigned int)digit;
- }
- *out = result;
-
- return first;
-}
-
-// convert string to floating point
-static char *atof(char *first, char *last, float *out)
-{
- // sign
- float sign = 1;
- if (first != last)
- {
- if (*first == '-')
- {
- sign = -1;
- ++first;
- }
- else if (*first == '+')
- {
- ++first;
- }
- }
-
- // integer part
- float result = 0;
- for (; first != last && IS_DIGIT(*first); ++first)
- {
- result = 10 * result + (float)(*first - '0');
- }
-
- // fraction part
- if (first != last && *first == '.')
- {
- ++first;
-
- float inv_base = 0.1f;
- for (; first != last && IS_DIGIT(*first); ++first)
- {
- result += (float)(*first - '0') * inv_base;
- inv_base *= 0.1f;
- }
- }
-
- // result w\o exponent
- result *= sign;
-
- // exponent
- bool exponent_negative = false;
- int exponent = 0;
- if (first != last && (*first == 'e' || *first == 'E'))
- {
- ++first;
-
- if (*first == '-')
- {
- exponent_negative = true;
- ++first;
- }
- else if (*first == '+')
- {
- ++first;
- }
-
- for (; first != last && IS_DIGIT(*first); ++first)
- {
- exponent = 10 * exponent + (*first - '0');
- }
- }
-
- if (exponent)
- {
- float power_of_ten = 10;
- for (; exponent > 1; exponent--)
- {
- power_of_ten *= 10;
- }
-
- if (exponent_negative)
- {
- result /= power_of_ten;
- }
- else
- {
- result *= power_of_ten;
- }
- }
-
- *out = result;
-
- return first;
-}
-
-static inline json_value *json_alloc(block_allocator *allocator)
-{
- json_value *value = (json_value *)allocator->malloc(sizeof(json_value));
- memset(value, 0, sizeof(json_value));
- return value;
-}
-
-static inline void json_append(json_value *lhs, json_value *rhs)
-{
- rhs->parent = lhs;
- if (lhs->last_child)
- {
- lhs->last_child = lhs->last_child->next_sibling = rhs;
- }
- else
- {
- lhs->first_child = lhs->last_child = rhs;
- }
-}
-
-#define ERROR(it, desc)\
- *error_pos = it;\
- *error_desc = desc;\
- *error_line = 1 - escaped_newlines;\
- for (char *c = it; c != source; --c)\
- if (*c == '\n') ++*error_line;\
- return 0
-
-#define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");}
-
-json_value *json_parse(char *source, char **error_pos, const char **error_desc, int *error_line, block_allocator *allocator)
-{
- json_value *root = 0;
- json_value *top = 0;
-
- char *name = 0;
- char *it = source;
-
- int escaped_newlines = 0;
-
- while (*it)
- {
- // skip white space
- while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA')
- {
- ++it;
- }
-
- switch (*it)
- {
- case '\0':
- break;
- case '{':
- case '[':
- {
- // create new value
- json_value *object = json_alloc(allocator);
-
- // name
- object->name = name;
- name = 0;
-
- // type
- object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY;
-
- // skip open character
- ++it;
-
- // set top and root
- if (top)
- {
- json_append(top, object);
- }
- else if (!root)
- {
- root = object;
- }
- else
- {
- ERROR(it, "Second root. Only one root allowed");
- }
- top = object;
- }
- break;
-
- case '}':
- case ']':
- {
- if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY))
- {
- ERROR(it, "Mismatch closing brace/bracket");
- }
-
- // skip close character
- ++it;
-
- // set top
- top = top->parent;
- }
- break;
-
- case ':':
- if (!top || top->type != JSON_OBJECT)
- {
- ERROR(it, "Unexpected character");
- }
- ++it;
- break;
-
- case ',':
- CHECK_TOP();
- ++it;
- break;
-
- case '"':
- {
- CHECK_TOP();
-
- // skip '"' character
- ++it;
-
- char *first = it;
- char *last = it;
- while (*it)
- {
- if ((unsigned char)*it < '\x20')
- {
- ERROR(first, "Control characters not allowed in strings");
- }
- else if (*it == '\\')
- {
- switch (it[1])
- {
- case '"':
- *last = '"';
- break;
- case '\\':
- *last = '\\';
- break;
- case '/':
- *last = '/';
- break;
- case 'b':
- *last = '\b';
- break;
- case 'f':
- *last = '\f';
- break;
- case 'n':
- *last = '\n';
- ++escaped_newlines;
- break;
- case 'r':
- *last = '\r';
- break;
- case 't':
- *last = '\t';
- break;
- case 'u':
- {
- unsigned int codepoint;
- if (hatoui(it + 2, it + 6, &codepoint) != it + 6)
- {
- ERROR(it, "Bad unicode codepoint");
- }
-
- if (codepoint <= 0x7F)
- {
- *last = (char)codepoint;
- }
- else if (codepoint <= 0x7FF)
- {
- *last++ = (char)(0xC0 | (codepoint >> 6));
- *last = (char)(0x80 | (codepoint & 0x3F));
- }
- else if (codepoint <= 0xFFFF)
- {
- *last++ = (char)(0xE0 | (codepoint >> 12));
- *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));
- *last = (char)(0x80 | (codepoint & 0x3F));
- }
- }
- it += 4;
- break;
- default:
- ERROR(first, "Unrecognized escape sequence");
- }
-
- ++last;
- it += 2;
- }
- else if (*it == '"')
- {
- *last = 0;
- ++it;
- break;
- }
- else
- {
- *last++ = *it++;
- }
- }
-
- if (!name && top->type == JSON_OBJECT)
- {
- // field name in object
- name = first;
- }
- else
- {
- // new string value
- json_value *object = json_alloc(allocator);
-
- object->name = name;
- name = 0;
-
- object->type = JSON_STRING;
- object->string_value = first;
-
- json_append(top, object);
- }
- }
- break;
-
- case 'n':
- case 't':
- case 'f':
- {
- CHECK_TOP();
-
- // new null/bool value
- json_value *object = json_alloc(allocator);
-
- object->name = name;
- name = 0;
-
- // null
- if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l')
- {
- object->type = JSON_NULL;
- it += 4;
- }
- // true
- else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e')
- {
- object->type = JSON_BOOL;
- object->int_value = 1;
- it += 4;
- }
- // false
- else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e')
- {
- object->type = JSON_BOOL;
- object->int_value = 0;
- it += 5;
- }
- else
- {
- ERROR(it, "Unknown identifier");
- }
-
- json_append(top, object);
- }
- break;
-
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- CHECK_TOP();
-
- // new number value
- json_value *object = json_alloc(allocator);
-
- object->name = name;
- name = 0;
-
- object->type = JSON_INT;
-
- char *first = it;
- while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}')
- {
- if (*it == '.' || *it == 'e' || *it == 'E')
- {
- object->type = JSON_FLOAT;
- }
- ++it;
- }
-
- if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it)
- {
- ERROR(first, "Bad integer number");
- }
-
- if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it)
- {
- ERROR(first, "Bad float number");
- }
-
- json_append(top, object);
- }
- break;
-
- default:
- ERROR(it, "Unexpected character");
- }
- }
-
- if (top)
- {
- ERROR(it, "Not all objects/arrays have been properly closed");
- }
-
- return root;
-}
+// Distributed under the MIT license. Copyright (c) 2010, Ivan Vashchaev\r
+\r
+#include <string.h>\r
+#include "json.h"\r
+\r
+// true if character represent a digit\r
+#define IS_DIGIT(c) (c >= '0' && c <= '9')\r
+\r
+// convert string to integer\r
+static char *atoi(char *first, char *last, int *out)\r
+{\r
+ int sign = 1;\r
+ if (first != last)\r
+ {\r
+ if (*first == '-')\r
+ {\r
+ sign = -1;\r
+ ++first;\r
+ }\r
+ else if (*first == '+')\r
+ {\r
+ ++first;\r
+ }\r
+ }\r
+\r
+ int result = 0;\r
+ for (; first != last && IS_DIGIT(*first); ++first)\r
+ {\r
+ result = 10 * result + (*first - '0');\r
+ }\r
+ *out = result * sign;\r
+\r
+ return first;\r
+}\r
+\r
+// convert hexadecimal string to unsigned integer\r
+static char *hatoui(char *first, char *last, unsigned int *out)\r
+{\r
+ unsigned int result = 0;\r
+ for (; first != last; ++first)\r
+ {\r
+ int digit;\r
+ if (IS_DIGIT(*first))\r
+ {\r
+ digit = *first - '0';\r
+ }\r
+ else if (*first >= 'a' && *first <= 'f')\r
+ {\r
+ digit = *first - 'a' + 10;\r
+ }\r
+ else if (*first >= 'A' && *first <= 'F')\r
+ {\r
+ digit = *first - 'A' + 10;\r
+ }\r
+ else\r
+ {\r
+ break;\r
+ }\r
+ result = 16 * result + (unsigned int)digit;\r
+ }\r
+ *out = result;\r
+\r
+ return first;\r
+}\r
+\r
+// convert string to floating point\r
+static char *atof(char *first, char *last, float *out)\r
+{\r
+ // sign\r
+ float sign = 1;\r
+ if (first != last)\r
+ {\r
+ if (*first == '-')\r
+ {\r
+ sign = -1;\r
+ ++first;\r
+ }\r
+ else if (*first == '+')\r
+ {\r
+ ++first;\r
+ }\r
+ }\r
+\r
+ // integer part\r
+ float result = 0;\r
+ for (; first != last && IS_DIGIT(*first); ++first)\r
+ {\r
+ result = 10 * result + (float)(*first - '0');\r
+ }\r
+\r
+ // fraction part\r
+ if (first != last && *first == '.')\r
+ {\r
+ ++first;\r
+\r
+ float inv_base = 0.1f;\r
+ for (; first != last && IS_DIGIT(*first); ++first)\r
+ {\r
+ result += (float)(*first - '0') * inv_base;\r
+ inv_base *= 0.1f;\r
+ }\r
+ }\r
+\r
+ // result w\o exponent\r
+ result *= sign;\r
+\r
+ // exponent\r
+ bool exponent_negative = false;\r
+ int exponent = 0;\r
+ if (first != last && (*first == 'e' || *first == 'E'))\r
+ {\r
+ ++first;\r
+\r
+ if (*first == '-')\r
+ {\r
+ exponent_negative = true;\r
+ ++first;\r
+ }\r
+ else if (*first == '+')\r
+ {\r
+ ++first;\r
+ }\r
+\r
+ for (; first != last && IS_DIGIT(*first); ++first)\r
+ {\r
+ exponent = 10 * exponent + (*first - '0');\r
+ }\r
+ }\r
+\r
+ if (exponent)\r
+ {\r
+ float power_of_ten = 10;\r
+ for (; exponent > 1; exponent--)\r
+ {\r
+ power_of_ten *= 10;\r
+ }\r
+\r
+ if (exponent_negative)\r
+ {\r
+ result /= power_of_ten;\r
+ }\r
+ else\r
+ {\r
+ result *= power_of_ten;\r
+ }\r
+ }\r
+\r
+ *out = result;\r
+\r
+ return first;\r
+}\r
+\r
+static inline json_value *json_alloc(block_allocator *allocator)\r
+{\r
+ json_value *value = (json_value *)allocator->malloc(sizeof(json_value));\r
+ memset(value, 0, sizeof(json_value));\r
+ return value;\r
+}\r
+\r
+static inline void json_append(json_value *lhs, json_value *rhs)\r
+{\r
+ rhs->parent = lhs;\r
+ if (lhs->last_child)\r
+ {\r
+ lhs->last_child = lhs->last_child->next_sibling = rhs;\r
+ }\r
+ else\r
+ {\r
+ lhs->first_child = lhs->last_child = rhs;\r
+ }\r
+}\r
+\r
+#define ERROR(it, desc)\\r
+ *error_pos = it;\\r
+ *error_desc = desc;\\r
+ *error_line = 1 - escaped_newlines;\\r
+ for (char *c = it; c != source; --c)\\r
+ if (*c == '\n') ++*error_line;\\r
+ return 0\r
+\r
+#define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");}\r
+\r
+json_value *json_parse(char *source, char **error_pos, const char **error_desc, int *error_line, block_allocator *allocator)\r
+{\r
+ json_value *root = 0;\r
+ json_value *top = 0;\r
+\r
+ char *name = 0;\r
+ char *it = source;\r
+\r
+ int escaped_newlines = 0;\r
+\r
+ while (*it)\r
+ {\r
+ // skip white space\r
+ while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA')\r
+ {\r
+ ++it;\r
+ }\r
+\r
+ switch (*it)\r
+ {\r
+ case '\0':\r
+ break;\r
+ case '{':\r
+ case '[':\r
+ {\r
+ // create new value\r
+ json_value *object = json_alloc(allocator);\r
+\r
+ // name\r
+ object->name = name;\r
+ name = 0;\r
+\r
+ // type\r
+ object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY;\r
+\r
+ // skip open character\r
+ ++it;\r
+\r
+ // set top and root\r
+ if (top)\r
+ {\r
+ json_append(top, object);\r
+ }\r
+ else if (!root)\r
+ {\r
+ root = object;\r
+ }\r
+ else\r
+ {\r
+ ERROR(it, "Second root. Only one root allowed");\r
+ }\r
+ top = object;\r
+ }\r
+ break;\r
+\r
+ case '}':\r
+ case ']':\r
+ {\r
+ if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY))\r
+ {\r
+ ERROR(it, "Mismatch closing brace/bracket");\r
+ }\r
+\r
+ // skip close character\r
+ ++it;\r
+\r
+ // set top\r
+ top = top->parent;\r
+ }\r
+ break;\r
+\r
+ case ':':\r
+ if (!top || top->type != JSON_OBJECT)\r
+ {\r
+ ERROR(it, "Unexpected character");\r
+ }\r
+ ++it;\r
+ break;\r
+\r
+ case ',':\r
+ CHECK_TOP();\r
+ ++it;\r
+ break;\r
+\r
+ case '"':\r
+ {\r
+ CHECK_TOP();\r
+\r
+ // skip '"' character\r
+ ++it;\r
+\r
+ char *first = it;\r
+ char *last = it;\r
+ while (*it)\r
+ {\r
+ if ((unsigned char)*it < '\x20')\r
+ {\r
+ ERROR(first, "Control characters not allowed in strings");\r
+ }\r
+ else if (*it == '\\')\r
+ {\r
+ switch (it[1])\r
+ {\r
+ case '"':\r
+ *last = '"';\r
+ break;\r
+ case '\\':\r
+ *last = '\\';\r
+ break;\r
+ case '/':\r
+ *last = '/';\r
+ break;\r
+ case 'b':\r
+ *last = '\b';\r
+ break;\r
+ case 'f':\r
+ *last = '\f';\r
+ break;\r
+ case 'n':\r
+ *last = '\n';\r
+ ++escaped_newlines;\r
+ break;\r
+ case 'r':\r
+ *last = '\r';\r
+ break;\r
+ case 't':\r
+ *last = '\t';\r
+ break;\r
+ case 'u':\r
+ {\r
+ unsigned int codepoint;\r
+ if (hatoui(it + 2, it + 6, &codepoint) != it + 6)\r
+ {\r
+ ERROR(it, "Bad unicode codepoint");\r
+ }\r
+\r
+ if (codepoint <= 0x7F)\r
+ {\r
+ *last = (char)codepoint;\r
+ }\r
+ else if (codepoint <= 0x7FF)\r
+ {\r
+ *last++ = (char)(0xC0 | (codepoint >> 6));\r
+ *last = (char)(0x80 | (codepoint & 0x3F));\r
+ }\r
+ else if (codepoint <= 0xFFFF)\r
+ {\r
+ *last++ = (char)(0xE0 | (codepoint >> 12));\r
+ *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));\r
+ *last = (char)(0x80 | (codepoint & 0x3F));\r
+ }\r
+ }\r
+ it += 4;\r
+ break;\r
+ default:\r
+ ERROR(first, "Unrecognized escape sequence");\r
+ }\r
+\r
+ ++last;\r
+ it += 2;\r
+ }\r
+ else if (*it == '"')\r
+ {\r
+ *last = 0;\r
+ ++it;\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ *last++ = *it++;\r
+ }\r
+ }\r
+\r
+ if (!name && top->type == JSON_OBJECT)\r
+ {\r
+ // field name in object\r
+ name = first;\r
+ }\r
+ else\r
+ {\r
+ // new string value\r
+ json_value *object = json_alloc(allocator);\r
+\r
+ object->name = name;\r
+ name = 0;\r
+\r
+ object->type = JSON_STRING;\r
+ object->string_value = first;\r
+\r
+ json_append(top, object);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case 'n':\r
+ case 't':\r
+ case 'f':\r
+ {\r
+ CHECK_TOP();\r
+\r
+ // new null/bool value\r
+ json_value *object = json_alloc(allocator);\r
+\r
+ object->name = name;\r
+ name = 0;\r
+\r
+ // null\r
+ if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l')\r
+ {\r
+ object->type = JSON_NULL;\r
+ it += 4;\r
+ }\r
+ // true\r
+ else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e')\r
+ {\r
+ object->type = JSON_BOOL;\r
+ object->int_value = 1;\r
+ it += 4;\r
+ }\r
+ // false\r
+ else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e')\r
+ {\r
+ object->type = JSON_BOOL;\r
+ object->int_value = 0;\r
+ it += 5;\r
+ }\r
+ else\r
+ {\r
+ ERROR(it, "Unknown identifier");\r
+ }\r
+\r
+ json_append(top, object);\r
+ }\r
+ break;\r
+\r
+ case '-':\r
+ case '0':\r
+ case '1':\r
+ case '2':\r
+ case '3':\r
+ case '4':\r
+ case '5':\r
+ case '6':\r
+ case '7':\r
+ case '8':\r
+ case '9':\r
+ {\r
+ CHECK_TOP();\r
+\r
+ // new number value\r
+ json_value *object = json_alloc(allocator);\r
+\r
+ object->name = name;\r
+ name = 0;\r
+\r
+ object->type = JSON_INT;\r
+\r
+ char *first = it;\r
+ while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}')\r
+ {\r
+ if (*it == '.' || *it == 'e' || *it == 'E')\r
+ {\r
+ object->type = JSON_FLOAT;\r
+ }\r
+ ++it;\r
+ }\r
+\r
+ if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it)\r
+ {\r
+ ERROR(first, "Bad integer number");\r
+ }\r
+\r
+ if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it)\r
+ {\r
+ ERROR(first, "Bad float number");\r
+ }\r
+\r
+ json_append(top, object);\r
+ }\r
+ break;\r
+\r
+ default:\r
+ ERROR(it, "Unexpected character");\r
+ }\r
+ }\r
+\r
+ if (top)\r
+ {\r
+ ERROR(it, "Not all objects/arrays have been properly closed");\r
+ }\r
+\r
+ return root;\r
+}\r