Files
stupidsimcpp/util/tdgame/customjson.hpp
2026-02-18 12:28:18 -05:00

124 lines
4.5 KiB
C++

#ifndef TDGAME_CJ_HPP
#define TDGAME_CJ_HPP
#include <map>
#include <sstream>
#include <variant>
#include <string>
#include <vector>
struct customJson {
struct Node {
std::variant<std::nullptr_t, bool, double, std::string, std::vector<Node>, std::map<std::string, Node>> value;
Node() : value(nullptr) {}
Node(bool b) : value(b) {}
Node(double d) : value(d) {}
Node(const std::string& s) : value(s) {}
Node(const char* s) : value(std::string(s)) {}
Node(std::vector<Node> a) : value(a) {}
Node(std::map<std::string, Node> o) : value(o) {}
// Accessors with type checking
const std::map<std::string, Node>& as_object() const { return std::get<std::map<std::string, Node>>(value); }
const std::vector<Node>& as_array() const { return std::get<std::vector<Node>>(value); }
const std::string& as_string() const { return std::get<std::string>(value); }
double as_double() const { return std::get<double>(value); }
bool as_bool() const { return std::get<bool>(value); }
bool is_null() const { return std::holds_alternative<std::nullptr_t>(value); }
// Convenience accessor
const Node& at(const std::string& key) const { return as_object().at(key); }
bool contains(const std::string& key) const { return as_object().count(key); }
};
static void skip_whitespace(std::string::const_iterator& it, const std::string::const_iterator& end) {
while (it != end && isspace(*it)) ++it;
}
static std::string parse_string(std::string::const_iterator& it, const std::string::const_iterator& end) {
std::string result;
if (*it == '"') ++it;
while (it != end && *it != '"') {
if (*it == '\\') { // Handle basic escapes
++it;
if (it != end) result += *it;
} else {
result += *it;
}
++it;
}
if (it != end && *it == '"') ++it;
return result;
}
static Node parse_number_or_literal(std::string::const_iterator& it, const std::string::const_iterator& end) {
std::string literal;
while (it != end && (isalnum(*it) || *it == '.' || *it == '-')) {
literal += *it;
++it;
}
if (literal == "true") return Node(true);
if (literal == "false") return Node(false);
if (literal == "null") return Node(nullptr);
try {
return Node(std::stod(literal));
} catch (...) {
throw std::runtime_error("Invalid number or literal: " + literal);
}
}
static std::vector<Node> parse_array(std::string::const_iterator& it, const std::string::const_iterator& end) {
std::vector<Node> arr;
if (*it == '[') ++it;
skip_whitespace(it, end);
while (it != end && *it != ']') {
arr.push_back(parse_node(it, end));
skip_whitespace(it, end);
if (it != end && *it == ',') {
++it;
skip_whitespace(it, end);
}
}
if (it != end && *it == ']') ++it;
return arr;
}
static std::map<std::string, Node> parse_object(std::string::const_iterator& it, const std::string::const_iterator& end) {
std::map<std::string, Node> obj;
if (*it == '{') ++it;
skip_whitespace(it, end);
while (it != end && *it != '}') {
std::string key = parse_string(it, end);
skip_whitespace(it, end);
if (it != end && *it == ':') ++it;
skip_whitespace(it, end);
obj[key] = parse_node(it, end);
skip_whitespace(it, end);
if (it != end && *it == ',') {
++it;
skip_whitespace(it, end);
}
}
if (it != end && *it == '}') ++it;
return obj;
}
static Node parse_node(std::string::const_iterator& it, const std::string::const_iterator& end) {
skip_whitespace(it, end);
if (it == end) throw std::runtime_error("Unexpected end of input");
switch (*it) {
case '{': return Node(parse_object(it, end));
case '[': return Node(parse_array(it, end));
case '"': return Node(parse_string(it, end));
default: return parse_number_or_literal(it, end);
}
}
static Node parse(const std::string& json_str) {
auto it = json_str.cbegin();
return parse_node(it, json_str.cend());
}
};
#endif