--- /dev/null
+// Distributed under the MIT license. Copyright (c) 2010, Ivan Vashchaev
+
+
+
+
+#include <string.h>
+#include "json.h"
+
+namespace mc_json{
+
+// 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;
+}
+
+} // end namespace mc_json