feat: expand Json

This commit is contained in:
dualfroz committed 2026-09-07 23:07:01 +02:00
1 parent 1656cdc870
commit 2c183a8071
1 file changed
+170
+170
View File
@@ -298,3 +298,173 @@ private:
return true;
}
bool parseEscape(std::string& out) {
if (atEnd()) {
fail("unexpected end of input after '\\'");
return false;
}
int escapeLine = line;
int escapeColumn = column;
char c = advance();
switch (c) {
case '"': out.push_back('"'); return true;
case '\\': out.push_back('\\'); return true;
case '/': out.push_back('/'); return true;
case 'b': out.push_back('\b'); return true;
case 'f': out.push_back('\f'); return true;
case 'n': out.push_back('\n'); return true;
case 'r': out.push_back('\r'); return true;
case 't': out.push_back('\t'); return true;
case 'u': break;
default:
failAt(escapeLine, escapeColumn,
std::string("unknown escape sequence '\\") + c + "'");
return false;
}
uint32_t unit = 0u;
if (!parseHexQuad(unit)) return false;
if (unit >= 0xDC00u && unit <= 0xDFFFu) {
failAt(escapeLine, escapeColumn, "low surrogate without a preceding high surrogate");
return false;
}
if (unit >= 0xD800u && unit <= 0xDBFFu) {
if (cursor + 1u >= src.size() || src[cursor] != '\\' || src[cursor + 1u] != 'u') {
failAt(escapeLine, escapeColumn, "high surrogate without a following low surrogate");
return false;
}
advance();
advance();
uint32_t low = 0u;
if (!parseHexQuad(low)) return false;
if (low < 0xDC00u || low > 0xDFFFu) {
failAt(escapeLine, escapeColumn, "high surrogate followed by a non low surrogate");
return false;
}
uint32_t combined = 0x10000u + ((unit - 0xD800u) << 10) + (low - 0xDC00u);
appendUtf8(combined, out);
return true;
}
appendUtf8(unit, out);
return true;
}
bool parseString(std::string& out) {
advance();
out.clear();
for (;;) {
if (atEnd()) {
fail("unexpected end of input inside a string");
return false;
}
unsigned char c = static_cast<unsigned char>(peek());
if (c == '"') {
advance();
return true;
}
if (c == '\\') {
advance();
if (!parseEscape(out)) return false;
continue;
}
if (c < 0x20u) {
fail("raw control character in a string, escape it as \\u00" +
std::string(1, "0123456789abcdef"[(c >> 4) & 0xFu]) +
std::string(1, "0123456789abcdef"[c & 0xFu]));
return false;
}
if (c < 0x80u) {
out.push_back(static_cast<char>(c));
advance();
continue;
}
uint32_t codePoint = 0u;
std::size_t length = decodeUtf8(src, cursor, codePoint);
if (length == 0u) {
fail("malformed UTF-8 byte sequence in a string");
return false;
}
for (std::size_t i = 0u; i < length; ++i) out.push_back(advance());
}
}
bool parseNumber(JsonValue& out) {
std::size_t start = cursor;
int startLine = line;
int startColumn = column;
bool negative = false;
if (peek() == '-') {
negative = true;
advance();
}
if (atEnd()) {
fail("unexpected end of input inside a number");
return false;
}
if (peek() == '0') {
advance();
if (!atEnd() && peek() >= '0' && peek() <= '9') {
fail("leading zero in a number");
return false;
}
} else if (peek() >= '1' && peek() <= '9') {
while (!atEnd() && peek() >= '0' && peek() <= '9') advance();
} else {
fail("expected a digit after the sign");
return false;
}
bool fractional = false;
if (!atEnd() && peek() == '.') {
fractional = true;
advance();
if (atEnd() || peek() < '0' || peek() > '9') {
fail("expected a digit after the decimal point");
return false;
}
while (!atEnd() && peek() >= '0' && peek() <= '9') advance();
}
if (!atEnd() && (peek() == 'e' || peek() == 'E')) {
fractional = true;
advance();
if (!atEnd() && (peek() == '+' || peek() == '-')) advance();
if (atEnd() || peek() < '0' || peek() > '9') {
fail("expected a digit in the exponent");
return false;
}
while (!atEnd() && peek() >= '0' && peek() <= '9') advance();
}
std::string token = src.substr(start, cursor - start);
if (!fractional) {
const unsigned long long limit =
negative ? 9223372036854775808ull : 9223372036854775807ull;
unsigned long long magnitude = 0ull;
bool overflow = false;
for (char c : token) {
if (c == '-') continue;
magnitude = magnitude * 10ull + static_cast<unsigned long long>(c - '0');
if (magnitude > limit) {
overflow = true;
break;
}
}
if (!overflow) {
if (magnitude == 9223372036854775808ull) {
out = JsonValue::makeInt(-9223372036854775807ll - 1ll);
} else {
long long whole = static_cast<long long>(magnitude);
out = JsonValue::makeInt(negative ? -whole : whole);
}
return true;
}
}
double value = std::strtod(token.c_str(), nullptr);
if (!std::isfinite(value)) {
failAt(startLine, startColumn, "number out of the representable range");
return false;
}
out = JsonValue::makeNumber(value);
return true;
}
};