252 lines
5.6 KiB
C++
252 lines
5.6 KiB
C++
#include "pch.h"
|
||
|
||
#include "JsonLite.h"
|
||
|
||
#include <cstring>
|
||
#include <sstream>
|
||
|
||
namespace jsonlite {
|
||
|
||
namespace {
|
||
|
||
struct Parser {
|
||
const char* p;
|
||
const char* end;
|
||
std::string err;
|
||
|
||
explicit Parser(const std::string& s) : p(s.data()), end(s.data() + s.size()) {}
|
||
|
||
void SkipWs() {
|
||
while (p < end && std::isspace(static_cast<unsigned char>(*p))) {
|
||
++p;
|
||
}
|
||
}
|
||
|
||
bool Consume(char c) {
|
||
SkipWs();
|
||
if (p < end && *p == c) {
|
||
++p;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool Expect(char c) {
|
||
if (Consume(c)) return true;
|
||
std::ostringstream oss;
|
||
oss << "expected '" << c << "'";
|
||
err = oss.str();
|
||
return false;
|
||
}
|
||
|
||
bool ParseString(std::string& out) {
|
||
SkipWs();
|
||
if (p >= end || *p != '"') {
|
||
err = "expected string";
|
||
return false;
|
||
}
|
||
++p;
|
||
std::string s;
|
||
while (p < end) {
|
||
char c = *p++;
|
||
if (c == '"') {
|
||
out = std::move(s);
|
||
return true;
|
||
}
|
||
if (c == '\\') {
|
||
if (p >= end) {
|
||
err = "unterminated escape";
|
||
return false;
|
||
}
|
||
char e = *p++;
|
||
switch (e) {
|
||
case '"': s.push_back('"'); break;
|
||
case '\\': s.push_back('\\'); break;
|
||
case '/': s.push_back('/'); break;
|
||
case 'b': s.push_back('\b'); break;
|
||
case 'f': s.push_back('\f'); break;
|
||
case 'n': s.push_back('\n'); break;
|
||
case 'r': s.push_back('\r'); break;
|
||
case 't': s.push_back('\t'); break;
|
||
case 'u':
|
||
// 仅实现最小兼容:不解析 \uXXXX,原样丢弃并报错
|
||
err = "\\uXXXX escape not supported";
|
||
return false;
|
||
default:
|
||
err = "invalid escape";
|
||
return false;
|
||
}
|
||
continue;
|
||
}
|
||
s.push_back(c);
|
||
}
|
||
err = "unterminated string";
|
||
return false;
|
||
}
|
||
|
||
bool ParseNumber(double& out) {
|
||
SkipWs();
|
||
const char* start = p;
|
||
if (p < end && (*p == '-' || *p == '+')) ++p;
|
||
bool any = false;
|
||
while (p < end && std::isdigit(static_cast<unsigned char>(*p))) {
|
||
any = true;
|
||
++p;
|
||
}
|
||
if (p < end && *p == '.') {
|
||
++p;
|
||
while (p < end && std::isdigit(static_cast<unsigned char>(*p))) {
|
||
any = true;
|
||
++p;
|
||
}
|
||
}
|
||
if (p < end && (*p == 'e' || *p == 'E')) {
|
||
++p;
|
||
if (p < end && (*p == '-' || *p == '+')) ++p;
|
||
bool anyExp = false;
|
||
while (p < end && std::isdigit(static_cast<unsigned char>(*p))) {
|
||
anyExp = true;
|
||
++p;
|
||
}
|
||
if (!anyExp) {
|
||
err = "invalid number exponent";
|
||
return false;
|
||
}
|
||
}
|
||
if (!any) {
|
||
err = "invalid number";
|
||
return false;
|
||
}
|
||
std::string token(start, p);
|
||
char* last = nullptr;
|
||
out = std::strtod(token.c_str(), &last);
|
||
if (!last || *last != '\0') {
|
||
err = "invalid number";
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool ParseLiteral(const char* lit) {
|
||
SkipWs();
|
||
size_t n = std::strlen(lit);
|
||
if (static_cast<size_t>(end - p) < n) return false;
|
||
if (std::strncmp(p, lit, n) != 0) return false;
|
||
p += n;
|
||
return true;
|
||
}
|
||
|
||
bool ParseValue(Value& v) {
|
||
SkipWs();
|
||
if (p >= end) {
|
||
err = "unexpected end";
|
||
return false;
|
||
}
|
||
if (*p == '"') {
|
||
std::string s;
|
||
if (!ParseString(s)) return false;
|
||
v = Value::String(std::move(s));
|
||
return true;
|
||
}
|
||
if (*p == '{') {
|
||
err = "nested objects not supported";
|
||
return false;
|
||
}
|
||
if (*p == '[') {
|
||
err = "arrays not supported";
|
||
return false;
|
||
}
|
||
if (*p == 't') {
|
||
if (!ParseLiteral("true")) {
|
||
err = "invalid literal";
|
||
return false;
|
||
}
|
||
v = Value::Bool(true);
|
||
return true;
|
||
}
|
||
if (*p == 'f') {
|
||
if (!ParseLiteral("false")) {
|
||
err = "invalid literal";
|
||
return false;
|
||
}
|
||
v = Value::Bool(false);
|
||
return true;
|
||
}
|
||
if (*p == 'n') {
|
||
if (!ParseLiteral("null")) {
|
||
err = "invalid literal";
|
||
return false;
|
||
}
|
||
v = Value::Null();
|
||
return true;
|
||
}
|
||
double n = 0.0;
|
||
if (!ParseNumber(n)) return false;
|
||
v = Value::Number(n);
|
||
return true;
|
||
}
|
||
|
||
bool ParseObject(Object& out) {
|
||
out.clear();
|
||
if (!Expect('{')) return false;
|
||
SkipWs();
|
||
if (Consume('}')) return true;
|
||
|
||
while (true) {
|
||
std::string key;
|
||
if (!ParseString(key)) return false;
|
||
if (!Expect(':')) return false;
|
||
Value v;
|
||
if (!ParseValue(v)) return false;
|
||
out[std::move(key)] = std::move(v);
|
||
|
||
SkipWs();
|
||
if (Consume('}')) return true;
|
||
if (!Expect(',')) return false;
|
||
}
|
||
}
|
||
};
|
||
|
||
} // namespace
|
||
|
||
bool ParseObject(const std::string& input, Object& out, std::string& err) {
|
||
Parser parser(input);
|
||
if (!parser.ParseObject(out)) {
|
||
err = parser.err.empty() ? "parse error" : parser.err;
|
||
return false;
|
||
}
|
||
parser.SkipWs();
|
||
if (parser.p != parser.end) {
|
||
err = "trailing characters";
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
std::string EscapeString(const std::string& s) {
|
||
std::string out;
|
||
out.reserve(s.size() + 8);
|
||
for (char c : s) {
|
||
switch (c) {
|
||
case '"': out += "\\\""; break;
|
||
case '\\': out += "\\\\"; break;
|
||
case '\b': out += "\\b"; break;
|
||
case '\f': out += "\\f"; break;
|
||
case '\n': out += "\\n"; break;
|
||
case '\r': out += "\\r"; break;
|
||
case '\t': out += "\\t"; break;
|
||
default:
|
||
if (static_cast<unsigned char>(c) < 0x20) {
|
||
// 控制字符:最小处理,替换为空格
|
||
out += ' ';
|
||
} else {
|
||
out += c;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
return out;
|
||
}
|
||
|
||
} // namespace jsonlite
|