json_value *json_parse(char *source, char **error_pos, 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) { switch (*it) { 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++; } }
enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed, const char *raw) { tokenVal.clear(); consumed = 0; const char *rawStart = raw; while ((*raw) && (isspace(*raw))) // skip whitespace raw++; switch (*raw) { case 0: return JTOK_NONE; case '{': raw++; consumed = (raw - rawStart); return JTOK_OBJ_OPEN; case '}': raw++; consumed = (raw - rawStart); return JTOK_OBJ_CLOSE; case '[': raw++; consumed = (raw - rawStart); return JTOK_ARR_OPEN; case ']': raw++; consumed = (raw - rawStart); return JTOK_ARR_CLOSE; case ':': raw++; consumed = (raw - rawStart); return JTOK_COLON; case ',': raw++; consumed = (raw - rawStart); return JTOK_COMMA; case 'n': case 't': case 'f': if (!strncmp(raw, "null", 4)) { raw += 4; consumed = (raw - rawStart); return JTOK_KW_NULL; } else if (!strncmp(raw, "true", 4)) { raw += 4; consumed = (raw - rawStart); return JTOK_KW_TRUE; } else if (!strncmp(raw, "false", 5)) { raw += 5; consumed = (raw - rawStart); return JTOK_KW_FALSE; } else return JTOK_ERR; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { // part 1: int std::string numStr; const char *first = raw; const char *firstDigit = first; if (!isdigit(*firstDigit)) firstDigit++; if ((*firstDigit == '0') && isdigit(firstDigit[1])) return JTOK_ERR; numStr += *raw; // copy first char raw++; if ((*first == '-') && (!isdigit(*raw))) return JTOK_ERR; while ((*raw) && isdigit(*raw)) { // copy digits numStr += *raw; raw++; } // part 2: frac if (*raw == '.') { numStr += *raw; // copy . raw++; if (!isdigit(*raw)) return JTOK_ERR; while ((*raw) && isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } // part 3: exp if (*raw == 'e' || *raw == 'E') { numStr += *raw; // copy E raw++; if (*raw == '-' || *raw == '+') { // copy +/- numStr += *raw; raw++; } if (!isdigit(*raw)) return JTOK_ERR; while ((*raw) && isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } tokenVal = numStr; consumed = (raw - rawStart); return JTOK_NUMBER; } case '"': { raw++; // skip " std::string valStr; while (*raw) { if (*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash switch (*raw) { case '"': valStr += "\""; break; case '\\': valStr += "\\"; break; case '/': valStr += "/"; break; case 'b': valStr += "\b"; break; case 'f': valStr += "\f"; break; case 'n': valStr += "\n"; break; case 'r': valStr += "\r"; break; case 't': valStr += "\t"; break; case 'u': { char buf[4] = {0,0,0,0}; char *last = &buf[0]; unsigned int codepoint; if (hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; 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)); } valStr += buf; raw += 4; break; } default: return JTOK_ERR; } raw++; // skip esc'd char }