46 explicit Parser(
const std::string&
text) : s_(
text) {}
48 bool parse(
Node& out, std::string*
error) {
50 if (!parseValue(out)) {
51 if (
error) *
error =
"invalid JSON near offset " + std::to_string(pos_);
55 if (pos_ != s_.size()) {
56 if (
error) *
error =
"trailing data at offset " + std::to_string(pos_);
63 static constexpr size_t kMaxDepth = 256;
64 const std::string& s_;
68 while (pos_ < s_.size() && (s_[pos_] ==
' ' || s_[pos_] ==
'\t' || s_[pos_] ==
'\n' || s_[pos_] ==
'\r'))
72 bool peek(
char c)
const {
return pos_ < s_.size() && s_[pos_] ==
c; }
74 bool parseValue(
Node& out,
size_t depth = 0) {
75 if (
depth > kMaxDepth)
return false;
76 if (pos_ >= s_.size())
return false;
78 case '{':
return parseObject(out,
depth);
79 case '[':
return parseArray(out,
depth);
81 if (!parseString(out.
stringVal))
return false;
84 case 't':
return parseLiteral(
"true", out,
true);
85 case 'f':
return parseLiteral(
"false", out,
false);
86 case 'n':
return parseNull(out);
87 default:
return parseNumber(out);
91 bool parseLiteral(
const char* lit,
Node& out,
bool value) {
92 const size_t n = std::char_traits<char>::length(lit);
93 if (s_.compare(pos_,
n, lit) != 0)
return false;
100 bool parseNull(
Node& out) {
101 if (s_.compare(pos_, 4,
"null") != 0)
return false;
107 bool parseNumber(
Node& out) {
108 const size_t start = pos_;
109 if (pos_ < s_.size() && s_[pos_] ==
'-') ++pos_;
110 if (pos_ >= s_.size())
return false;
111 if (s_[pos_] ==
'0') {
114 if (pos_ < s_.size() && s_[pos_] >=
'0' && s_[pos_] <=
'9')
return false;
115 }
else if (s_[pos_] >=
'1' && s_[pos_] <=
'9') {
116 while (pos_ < s_.size() && s_[pos_] >=
'0' && s_[pos_] <=
'9') ++pos_;
120 bool integral =
true;
121 if (pos_ < s_.size() && s_[pos_] ==
'.') {
124 const size_t fractionStart = pos_;
125 while (pos_ < s_.size() && s_[pos_] >=
'0' && s_[pos_] <=
'9') ++pos_;
126 if (pos_ == fractionStart)
return false;
128 if (pos_ < s_.size() && (s_[pos_] ==
'e' || s_[pos_] ==
'E')) {
131 if (pos_ < s_.size() && (s_[pos_] ==
'-' || s_[pos_] ==
'+')) ++pos_;
132 const size_t exponentStart = pos_;
133 while (pos_ < s_.size() && s_[pos_] >=
'0' && s_[pos_] <=
'9') ++pos_;
134 if (pos_ == exponentStart)
return false;
136 const std::string num = s_.substr(
start, pos_ -
start);
140 if (used != num.size() || !std::isfinite(out.
numberVal))
return false;
144 out.
intVal = std::stoll(num);
156 if (!integral)
return false;
167 bool parseString(std::string& out) {
168 if (!peek(
'"'))
return false;
171 while (pos_ < s_.size()) {
172 const char c = s_[pos_++];
173 if (
c ==
'"')
return true;
175 if (
static_cast<unsigned char>(
c) < 0x20)
return false;
179 if (pos_ >= s_.size())
return false;
180 const char esc = s_[pos_++];
182 case '"': out +=
'"';
break;
183 case '\\': out +=
'\\';
break;
184 case '/': out +=
'/';
break;
185 case 'b': out +=
'\b';
break;
186 case 'f': out +=
'\f';
break;
187 case 'n': out +=
'\n';
break;
188 case 'r': out +=
'\r';
break;
189 case 't': out +=
'\t';
break;
191 if (pos_ + 4 > s_.size())
return false;
192 const char hex[5] = {s_[pos_], s_[pos_ + 1], s_[pos_ + 2], s_[pos_ + 3],
'\0'};
195 const unsigned cp =
static_cast<unsigned>(std::strtoul(hex, &
end, 16));
196 if (!
end || *
end !=
'\0')
return false;
198 if (cp >= 0xD800 && cp <= 0xDBFF && pos_ + 6 <= s_.size() && s_[pos_] ==
'\\' &&
199 s_[pos_ + 1] ==
'u') {
200 const char lohex[5] = {s_[pos_ + 2], s_[pos_ + 3], s_[pos_ + 4], s_[pos_ + 5],
'\0'};
202 char* loEnd =
nullptr;
203 const unsigned lo =
static_cast<unsigned>(std::strtoul(lohex, &loEnd, 16));
204 if (loEnd && *loEnd ==
'\0' && lo >= 0xDC00 && lo <= 0xDFFF) {
205 appendUtf8(out, 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00));
209 }
else if (cp >= 0xD800 && cp <= 0xDFFF) {
216 default:
return false;
222 static void appendUtf8(std::string& out,
unsigned cp) {
224 out +=
static_cast<char>(cp);
225 }
else if (cp < 0x800) {
226 out +=
static_cast<char>(0xC0 | (cp >> 6));
227 out +=
static_cast<char>(0x80 | (cp & 0x3F));
228 }
else if (cp < 0x10000) {
229 out +=
static_cast<char>(0xE0 | (cp >> 12));
230 out +=
static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
231 out +=
static_cast<char>(0x80 | (cp & 0x3F));
233 out +=
static_cast<char>(0xF0 | (cp >> 18));
234 out +=
static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
235 out +=
static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
236 out +=
static_cast<char>(0x80 | (cp & 0x3F));
240 bool parseObject(
Node& out,
size_t depth) {
251 if (!parseString(
key))
return false;
252 for (
const std::string& existing : out.
memberNames)
253 if (existing ==
key)
return false;
255 if (!peek(
':'))
return false;
259 if (!parseValue(val,
depth + 1))
return false;
268 if (!peek(
','))
return false;
273 bool parseArray(
Node& out,
size_t depth) {
284 if (!parseValue(val,
depth + 1))
return false;
285 out.
elements.push_back(std::move(val));
292 if (!peek(
','))
return false;
299std::string numberToString(
const Node&
n) {
300 if (
n.hasInt64)
return std::to_string(
n.intVal);
301 std::ostringstream os;
306bool stringToDouble(
const std::string&
s,
double& out) {
309 const double v = std::stod(
s, &used);
310 while (used <
s.size() && (
s[used] ==
' ' ||
s[used] ==
'\t')) ++used;
311 if (used !=
s.size())
return false;
338 for (
size_t i = 0; i < node_->
memberNames.size(); ++i)
344 std::vector<std::string> out;
352 if (!node_)
return 0;
365 if (!node_)
return fallback;
366 switch (node_->
kind) {
370 if (node_->
stringVal ==
"true")
return true;
371 if (node_->
stringVal ==
"false")
return false;
373 default:
return fallback;
378 if (!node_)
return fallback;
379 switch (node_->
kind) {
384 return stringToDouble(node_->
stringVal,
v) ?
v : fallback;
386 default:
return fallback;
393 if (!node_)
return fallback;
395 if (node_->
intVal < std::numeric_limits<int>::min() ||
396 node_->
intVal > std::numeric_limits<int>::max())
398 return static_cast<int>(node_->
intVal);
400 const double d =
asDouble(
static_cast<double>(fallback));
401 if (!std::isfinite(
d) ||
d <
static_cast<double>(std::numeric_limits<int>::min()) ||
402 d >
static_cast<double>(std::numeric_limits<int>::max()))
404 return static_cast<int>(
d);
408 return static_cast<float>(
asDouble(
static_cast<double>(fallback)));
412 if (!node_)
return fallback;
413 switch (node_->
kind) {
417 default:
return fallback;
432 std::vector<std::string> out;
434 out.reserve(node_->
elements.size());
444 std::vector<int> out;
448 for (
size_t i = 0; i <
n; ++i) out.push_back(arr.
at(i).
asInt(0));
453 std::vector<float> out;
457 for (
size_t i = 0; i <
n; ++i) out.push_back(arr.
at(i).
asFloat(0.f));
462 std::unordered_map<std::string, std::string> out;
470 std::unordered_map<std::string, int> out;
479void appendEscapedString(std::string_view
value, std::string&
output) {
480 constexpr char hex[] =
"0123456789abcdef";
482 for (
const unsigned char byte :
value) {
484 case '"':
output +=
"\\\"";
break;
485 case '\\':
output +=
"\\\\";
break;
486 case '\b':
output +=
"\\b";
break;
487 case '\f':
output +=
"\\f";
break;
488 case '\n':
output +=
"\\n";
break;
489 case '\r':
output +=
"\\r";
break;
490 case '\t':
output +=
"\\t";
break;
494 output.push_back(hex[
byte >> 4]);
495 output.push_back(hex[
byte & 0x0f]);
497 output.push_back(
static_cast<char>(
byte));
508 using T = std::decay_t<
decltype(
current)>;
509 if constexpr (std::is_same_v<T, std::monostate>) {
512 }
else if constexpr (std::is_same_v<T, bool>) {
515 }
else if constexpr (std::is_same_v<T, std::int64_t>) {
518 if (
error != std::errc())
return false;
521 }
else if constexpr (std::is_same_v<T, double>) {
522 if (!std::isfinite(
current))
return false;
523 std::ostringstream stream;
524 stream.imbue(std::locale::classic());
525 stream.precision(std::numeric_limits<double>::max_digits10);
527 const std::string
number = stream.str();
529 if (
number.find_first_of(
".eE") == std::string_view::npos)
output +=
".0";
531 }
else if constexpr (std::is_same_v<T, std::string>) {
534 }
else if constexpr (std::is_same_v<T, eve::Value::Array>) {
544 }
else if constexpr (std::is_same_v<T, eve::Value::Object>) {
579Document& Document::operator=(Document&&) noexcept = default;
583 auto node = std::make_unique<Node>();
585 if (!parser.parse(*
node,
error))
return doc;
586 doc.root_ = std::move(
node);
std::unique_ptr< gpgpu::GpuBuffer > buffer
Owning, renderer-independent dynamic values.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
The canonical owning dynamic value used by data-facing protocols.
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
double asDouble() const
Return the Double payload; the caller must have checked the kind.
std::int64_t asInt() const
Return the Int64 payload; the caller must have checked the kind.
bool asBool() const
Return the boolean payload; the caller must have checked the kind.
EVENGINE_API_FOUNDATION public API.
EVENGINE_API_FOUNDATION public API.
float asFloat(float fallback=0.f) const
As float.
bool getBool(const char *key, bool fallback=false) const
Returns the bool.
bool isObject() const
True when object.
bool has(const char *key) const
True when active.
double asDouble(double fallback=0.0) const
As double.
bool isIntegerLiteral() const
True when the JSON number had no fraction or exponent.
Value get(const char *key) const
Returns the value.
size_t size() const
Returns the size of .
bool isArray() const
True when array.
Value at(size_t index) const
At.
bool isString() const
True when string.
float getFloat(const char *key, float fallback=0.f) const
Returns the float.
int getInt(const char *key, int fallback=0) const
Returns the int.
bool isNull() const
True when null.
bool isNumber() const
True when number.
std::string getString(const char *key, const std::string &fallback={}) const
Returns the string.
int asInt(int fallback=0) const
As int.
std::vector< std::string > getStringArray(const char *key) const
Returns the string array.
bool asBool(bool fallback=false) const
As bool.
double getDouble(const char *key, double fallback=0.0) const
Returns the double.
bool isBool() const
True when bool.
std::vector< std::string > keys() const
Keys.
std::string asString(const std::string &fallback={}) const
As string.
bool isInt64() const
True when the JSON number was an integer literal that fits Int64.
std::unordered_map< std::string, int > getIntMap(const char *key) const
std::int64_t asInt64(std::int64_t fallback=0) const
Read an exact Int64 literal, or return the caller-supplied default for other numbers.
std::unordered_map< std::string, std::string > getStringMap(const char *key) const
std::vector< int > getIntArray(const char *key) const
Returns the int array.
std::vector< std::string > toStringArray() const
To string array.
std::vector< float > getFloatArray(const char *key) const
Returns the float array.
Result< std::string > stringify(const eve::Value &value)
Serialize the canonical owning Value as deterministic compact JSON.
std::vector< Node > elements
std::vector< std::string > memberNames
std::vector< Node > memberValues