feat: expand Json
This commit is contained in:
1 parent
1656cdc870
commit
2c183a8071
1 file changed
+170
@@ -298,3 +298,173 @@ private:
|
|||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Reference in new issue
Block a user