diff --git a/core/src/Json.cpp b/core/src/Json.cpp index fe35290..11c938f 100644 --- a/core/src/Json.cpp +++ b/core/src/Json.cpp @@ -155,3 +155,146 @@ private: return true; } + bool parseValue(int depth, JsonValue& out) { + if (atEnd()) { + fail("unexpected end of input, expected a value"); + return false; + } + char c = peek(); + if (c == '{') return parseObject(depth, out); + if (c == '[') return parseArray(depth, out); + if (c == '"') { + std::string text; + if (!parseString(text)) return false; + out = JsonValue::makeString(std::move(text)); + return true; + } + if (c == '-' || (c >= '0' && c <= '9')) return parseNumber(out); + if (c == 't') return parseLiteral("true", JsonValue::makeBool(true), out); + if (c == 'f') return parseLiteral("false", JsonValue::makeBool(false), out); + if (c == 'n') return parseLiteral("null", JsonValue::makeNull(), out); + fail(std::string("unexpected character '") + c + "', expected a value"); + return false; + } + + bool parseLiteral(const char* word, JsonValue value, JsonValue& out) { + std::size_t length = std::strlen(word); + if (src.compare(cursor, length, word) != 0) { + fail(std::string("unknown literal, expected ") + word); + return false; + } + for (std::size_t i = 0u; i < length; ++i) advance(); + out = std::move(value); + return true; + } + + bool parseObject(int depth, JsonValue& out) { + if (depth > depthLimit) { + fail("nesting deeper than the " + std::to_string(depthLimit) + " level limit"); + return false; + } + advance(); + out = JsonValue::makeObject(); + skipWhitespace(); + if (!atEnd() && peek() == '}') { + advance(); + return true; + } + for (;;) { + skipWhitespace(); + if (atEnd() || peek() != '"') { + fail("expected a quoted member name"); + return false; + } + std::string key; + if (!parseString(key)) return false; + skipWhitespace(); + if (!expect(':', "expected ':' after the member name")) return false; + skipWhitespace(); + JsonValue member; + if (!parseValue(depth + 1, member)) return false; + out.set(key, std::move(member)); + skipWhitespace(); + if (atEnd()) { + fail("unexpected end of input, expected ',' or '}'"); + return false; + } + char c = peek(); + if (c == ',') { + advance(); + skipWhitespace(); + if (!atEnd() && peek() == '}') { + fail("trailing comma before '}'"); + return false; + } + continue; + } + if (c == '}') { + advance(); + return true; + } + fail(std::string("unexpected character '") + c + "', expected ',' or '}'"); + return false; + } + } + + bool parseArray(int depth, JsonValue& out) { + if (depth > depthLimit) { + fail("nesting deeper than the " + std::to_string(depthLimit) + " level limit"); + return false; + } + advance(); + out = JsonValue::makeArray(); + skipWhitespace(); + if (!atEnd() && peek() == ']') { + advance(); + return true; + } + for (;;) { + skipWhitespace(); + JsonValue element; + if (!parseValue(depth + 1, element)) return false; + out.push(std::move(element)); + skipWhitespace(); + if (atEnd()) { + fail("unexpected end of input, expected ',' or ']'"); + return false; + } + char c = peek(); + if (c == ',') { + advance(); + skipWhitespace(); + if (!atEnd() && peek() == ']') { + fail("trailing comma before ']'"); + return false; + } + continue; + } + if (c == ']') { + advance(); + return true; + } + fail(std::string("unexpected character '") + c + "', expected ',' or ']'"); + return false; + } + } + + bool parseHexQuad(uint32_t& out) { + uint32_t value = 0u; + for (int i = 0; i < 4; ++i) { + if (atEnd()) { + fail("unexpected end of input inside a \\u escape"); + return false; + } + int digit = hexDigitValue(peek()); + if (digit < 0) { + fail(std::string("'") + peek() + "' is not a hexadecimal digit"); + return false; + } + value = (value << 4) | static_cast(digit); + advance(); + } + out = value; + return true; + } +