/* * Copyright (c) 2012-2014 Daniele Bartolini and individual contributors. * License: https://github.com/taylor001/crown/blob/master/LICENSE */ #include "json.h" #include "container_types.h" #include "string_utils.h" #include "dynamic_string.h" #include "map.h" namespace crown { namespace json { static const char* next(const char* str, const char c = 0) { CE_ASSERT_NOT_NULL(str); if (c && c != (*str)) { CE_ASSERT(false, "Expected '%c' got '%c'", c, (*str)); } return str + 1; } static const char* skip_whites(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; while ((*ch) && (*ch) <= ' ') ch = next(ch); return ch; } static const char* skip_comments(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; while ((*ch) == '/') { ch = next(ch, '/'); ch = next(ch, '/'); while ((*ch) && (*ch) != '\n') ch = next(ch); ch = skip_whites(ch); } return ch; } static const char* skip_string(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; bool escaped = false; if ((*ch) == '"') { while ((*(ch = next(ch))) != 0) { if ((*ch) == '"' && !escaped) { ch = next(ch); return ch; } else if ((*ch) == '\\') escaped = true; else escaped = false; } } return ch; } static const char* skip_number(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; while ((*ch) && (((*ch) >= '0' && (*ch) <= '9') || (*ch) == '-' || (*ch) == '.' || (*ch) == '+' || (*ch) == 'e' || (*ch) == 'E')) { ch = next(ch); } return ch; } static const char* skip_object(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; uint32_t brackets = 1; if ((*ch) == '{') { brackets++; ch = next(ch, '{'); while ((*ch) && brackets != 1) { if ((*ch) == '}') brackets--; else if ((*ch) == '{') brackets++; ch = next(ch); } } return ch; } static const char* skip_array(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; uint32_t brackets = 1; if ((*ch) == '[') { brackets++; ch = next(ch, '['); while ((*ch) && brackets != 1) { if ((*ch) == ']') brackets--; else if ((*ch) == '[') brackets++; ch = next(ch); } } return ch; } static const char* skip_bool(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; switch ((*ch)) { case 't': { ch = next(ch, 't'); ch = next(ch, 'r'); ch = next(ch, 'u'); ch = next(ch, 'e'); break; } case 'f': { ch = next(ch, 'f'); ch = next(ch, 'a'); ch = next(ch, 'l'); ch = next(ch, 's'); ch = next(ch, 'e'); break; } default: { break; } } return ch; } static const char* skip_null(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; if ((*ch) == 'n') { ch = next(ch, 'n'); ch = next(ch, 'u'); ch = next(ch, 'l'); ch = next(ch, 'l'); } return ch; } static const char* skip_value(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; ch = skip_array(ch); ch = skip_object(ch); ch = skip_number(ch); ch = skip_string(ch); ch = skip_bool(ch); ch = skip_null(ch); return ch; } static bool is_escapee(char c) { return c == '"' || c == '\\' || c == '/' || c == '\b' || c == '\f' || c == '\n' || c == '\r' || c == '\t'; } JSONType::Enum type(const char* s) { CE_ASSERT_NOT_NULL(s); const char c = s[0]; switch (c) { case '{': return JSONType::OBJECT; case '[': return JSONType::ARRAY; case '"': return JSONType::STRING; case '-': return JSONType::NUMBER; default: return (c >= '0' && c <= '9') ? JSONType::NUMBER : (c == 'n' ? JSONType::NIL : JSONType::BOOL); } } void parse_string(const char* s, DynamicString& str) { CE_ASSERT_NOT_NULL(s); const char* ch = s; if ((*ch) == '"') { while ((*(ch = next(ch)))) { // Empty string if ((*ch) == '"') { ch = next(ch); return; } else if ((*ch) == '\\') { ch = next(ch); if ((*ch) == 'u') { CE_FATAL("Not supported at the moment"); } else if (is_escapee(*ch)) { str += (*ch); } else { // Go to invalid string break; } } else { str += (*ch); } } } CE_FATAL("Bad string"); } double parse_number(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; Array str(default_allocator()); if ((*ch) == '-') { array::push_back(str, '-'); ch = next(ch, '-'); } while ((*ch) >= '0' && (*ch) <= '9') { array::push_back(str, (*ch)); ch = next(ch); } if ((*ch) == '.') { array::push_back(str, '.'); while ((*(ch = next(ch))) && (*ch) >= '0' && (*ch) <= '9') { array::push_back(str, *ch); } } if ((*ch) == 'e' || (*ch) == 'E') { array::push_back(str, *ch); ch = next(ch); if ((*ch) == '-' || (*ch) == '+') { array::push_back(str, *ch); ch = next(ch); } while ((*ch) >= '0' && (*ch) <= '9') { array::push_back(str, *ch); ch = next(ch); } } // Ensure null terminated array::push_back(str, '\0'); return parse_double(array::begin(str)); } bool parse_bool(const char* s) { CE_ASSERT_NOT_NULL(s); const char* ch = s; switch(*ch) { case 't': { ch = next(ch, 't'); ch = next(ch, 'r'); ch = next(ch, 'u'); ch = next(ch, 'e'); return true; } case 'f': { ch = next(ch, 'f'); ch = next(ch, 'a'); ch = next(ch, 'l'); ch = next(ch, 's'); ch = next(ch, 'e'); return false; } default: { CE_FATAL("Bad boolean"); return false; } } } int32_t parse_int(const char* s) { CE_ASSERT_NOT_NULL(s); return (int32_t) parse_number(s); } float parse_float(const char* s) { CE_ASSERT_NOT_NULL(s); return (float) parse_number(s); } void parse_array(const char* s, Array& array) { CE_ASSERT_NOT_NULL(s); const char* ch = s; if ((*ch) == '[') { ch = next(ch, '['); // Skip whitespaces while ((*ch) && (*ch) <= ' ') { ch = next(ch); } if ((*ch) == ']') { ch = next(ch, ']'); return; } while (*ch) { array::push_back(array, ch); ch = skip_value(ch); ch = skip_whites(ch); // Closing bracket (top-most array) if ((*ch) == ']') { ch = next(ch, ']'); return; } // Skip until next ',' ch = next(ch, ','); // Skip whites, eventually ch = skip_whites(ch); } } CE_FATAL("Bad array"); } void parse_object(const char* s, Map& object) { CE_ASSERT_NOT_NULL(s); const char* ch = s; if ((*ch) == '{') { ch = next(ch, '{'); ch = skip_whites(ch); ch = skip_comments(ch); if ((*ch) == '}') { next(ch, '}'); return; } while (*ch) { DynamicString key; parse_string(ch, key); ch = skip_string(ch); ch = skip_whites(ch); ch = next(ch, ':'); ch = skip_whites(ch); map::set(object, key, ch); ch = skip_value(ch); ch = skip_whites(ch); ch = skip_comments(ch); if ((*ch) == '}') { next(ch, '}'); return; } ch = next(ch, ','); ch = skip_whites(ch); ch = skip_comments(ch); } } CE_FATAL("Bad object"); } } // namespace json } // namespace crown