-// 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
+// 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;
+}