#include "fiko/mcp/json.hpp" #include #include #include namespace fiko::mcp { namespace { const std::string g_bosMetin; const JsonDizi g_bosDizi; const JsonNesne g_bosNesne; const Json g_yok; } // namespace Json Json::bos() { Json j; j.tip_ = JsonTip::Bos; return j; } Json Json::mantik(bool v) { Json j; j.tip_ = JsonTip::Mantik; j.m_ = v; return j; } Json Json::sayi(double v) { Json j; j.tip_ = JsonTip::Sayi; j.s_ = v; return j; } Json Json::tam(long long v) { Json j; j.tip_ = JsonTip::Sayi; j.s_ = (double)v; j.tamMi_ = true; return j; } Json Json::metin(std::string v) { Json j; j.tip_ = JsonTip::Metin; j.t_ = std::move(v); return j; } Json Json::dizi(JsonDizi v) { Json j; j.tip_ = JsonTip::Dizi; j.d_ = std::move(v); return j; } Json Json::nesne(JsonNesne v) { Json j; j.tip_ = JsonTip::Nesne; j.n_ = std::move(v); return j; } bool Json::mantikAl(bool vars) const { if (tip_ == JsonTip::Mantik) return m_; if (tip_ == JsonTip::Sayi) return s_ != 0.0; return vars; } double Json::sayiAl(double vars) const { return tip_ == JsonTip::Sayi ? s_ : vars; } long long Json::tamAl(long long vars) const { if (tip_ == JsonTip::Sayi) return (long long)llround(s_); if (tip_ == JsonTip::Metin) return atoll(t_.c_str()); // "123" gelen istemciler için return vars; } const std::string& Json::metinAl() const { return tip_ == JsonTip::Metin ? t_ : g_bosMetin; } const JsonDizi& Json::diziAl() const { return tip_ == JsonTip::Dizi ? d_ : g_bosDizi; } const JsonNesne& Json::nesneAl() const { return tip_ == JsonTip::Nesne ? n_ : g_bosNesne; } const Json& Json::operator[](const std::string& anahtar) const { if (tip_ != JsonTip::Nesne) return g_yok; const auto it = n_.find(anahtar); return it == n_.end() ? g_yok : it->second; } void Json::ekle(const std::string& anahtar, Json v) { if (tip_ != JsonTip::Nesne) { tip_ = JsonTip::Nesne; n_.clear(); } n_[anahtar] = std::move(v); } void Json::it(Json v) { if (tip_ != JsonTip::Dizi) { tip_ = JsonTip::Dizi; d_.clear(); } d_.push_back(std::move(v)); } std::string jsonKacis(const std::string& s) { std::string o; o.reserve(s.size() + 8); for (unsigned char c : s) { switch (c) { case '"': o += "\\\""; break; case '\\': o += "\\\\"; break; case '\n': o += "\\n"; break; case '\r': o += "\\r"; break; case '\t': o += "\\t"; break; case '\b': o += "\\b"; break; case '\f': o += "\\f"; break; default: if (c < 0x20) { // diğer kontrol karakterleri \u00XX char tmp[8]; std::snprintf(tmp, sizeof(tmp), "\\u%04x", (unsigned)c); o += tmp; } else { o.push_back((char)c); // UTF-8 baytları olduğu gibi geçer } } } return o; } std::string Json::yaz() const { switch (tip_) { case JsonTip::Yok: case JsonTip::Bos: return "null"; case JsonTip::Mantik: return m_ ? "true" : "false"; case JsonTip::Sayi: { char tmp[40]; if (tamMi_ || (std::isfinite(s_) && s_ == std::floor(s_) && std::fabs(s_) < 1e15)) std::snprintf(tmp, sizeof(tmp), "%lld", (long long)llround(s_)); else if (!std::isfinite(s_)) return "null"; // NaN/Inf JSON'da yok → null (istemci çökmesin) else std::snprintf(tmp, sizeof(tmp), "%.17g", s_); return tmp; } case JsonTip::Metin: return "\"" + jsonKacis(t_) + "\""; case JsonTip::Dizi: { std::string o = "["; for (size_t i = 0; i < d_.size(); ++i) { if (i) o += ','; o += d_[i].yaz(); } return o + "]"; } case JsonTip::Nesne: { std::string o = "{"; bool ilk = true; for (const auto& [k, v] : n_) { if (!ilk) o += ','; ilk = false; o += "\"" + jsonKacis(k) + "\":" + v.yaz(); } return o + "}"; } } return "null"; } // ── Ayrıştırıcı ───────────────────────────────────────────────────────────── namespace { struct Cozucu { const char* p; const char* son; std::string hata; void bosluk() { while (p < son && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) ++p; } bool bitti() const { return p >= son; } // UTF-16 kod noktasını UTF-8'e yaz (yüzey çifti dahil). static void utf8Yaz(std::string& o, unsigned kod) { if (kod < 0x80) o.push_back((char)kod); else if (kod < 0x800) { o.push_back((char)(0xC0 | (kod >> 6))); o.push_back((char)(0x80 | (kod & 0x3F))); } else if (kod < 0x10000) { o.push_back((char)(0xE0 | (kod >> 12))); o.push_back((char)(0x80 | ((kod >> 6) & 0x3F))); o.push_back((char)(0x80 | (kod & 0x3F))); } else { o.push_back((char)(0xF0 | (kod >> 18))); o.push_back((char)(0x80 | ((kod >> 12) & 0x3F))); o.push_back((char)(0x80 | ((kod >> 6) & 0x3F))); o.push_back((char)(0x80 | (kod & 0x3F))); } } bool onaltilik(unsigned& out) { if (son - p < 4) return false; out = 0; for (int i = 0; i < 4; ++i) { const char c = p[i]; unsigned d; if (c >= '0' && c <= '9') d = (unsigned)(c - '0'); else if (c >= 'a' && c <= 'f') d = (unsigned)(c - 'a' + 10); else if (c >= 'A' && c <= 'F') d = (unsigned)(c - 'A' + 10); else return false; out = (out << 4) | d; } p += 4; return true; } bool metinCoz(std::string& out) { if (bitti() || *p != '"') { hata = "metin bekleniyordu"; return false; } ++p; while (p < son && *p != '"') { if (*p == '\\') { ++p; if (bitti()) { hata = "kacis yarim"; return false; } switch (*p) { case '"': out.push_back('"'); ++p; break; case '\\': out.push_back('\\'); ++p; break; case '/': out.push_back('/'); ++p; break; case 'n': out.push_back('\n'); ++p; break; case 'r': out.push_back('\r'); ++p; break; case 't': out.push_back('\t'); ++p; break; case 'b': out.push_back('\b'); ++p; break; case 'f': out.push_back('\f'); ++p; break; case 'u': { ++p; unsigned kod = 0; if (!onaltilik(kod)) { hata = "gecersiz \\u"; return false; } if (kod >= 0xD800 && kod <= 0xDBFF && son - p >= 6 && p[0] == '\\' && p[1] == 'u') { const char* yedek = p; p += 2; unsigned alt = 0; if (onaltilik(alt) && alt >= 0xDC00 && alt <= 0xDFFF) kod = 0x10000 + ((kod - 0xD800) << 10) + (alt - 0xDC00); else p = yedek; // yüzey çifti değil → tek kod noktası olarak yaz } utf8Yaz(out, kod); break; } default: hata = "bilinmeyen kacis"; return false; } } else { out.push_back(*p++); } } if (bitti()) { hata = "metin kapanmadi"; return false; } ++p; // kapanış tırnağı return true; } bool coz(Json& out, int derinlik) { if (derinlik > 64) { hata = "cok derin"; return false; } // özyineleme koruması bosluk(); if (bitti()) { hata = "beklenmedik son"; return false; } switch (*p) { case 'n': if (son - p >= 4 && std::string(p, p + 4) == "null") { p += 4; out = Json::bos(); return true; } hata = "gecersiz deger"; return false; case 't': if (son - p >= 4 && std::string(p, p + 4) == "true") { p += 4; out = Json::mantik(true); return true; } hata = "gecersiz deger"; return false; case 'f': if (son - p >= 5 && std::string(p, p + 5) == "false") { p += 5; out = Json::mantik(false); return true; } hata = "gecersiz deger"; return false; case '"': { std::string s; if (!metinCoz(s)) return false; out = Json::metin(std::move(s)); return true; } case '[': { ++p; out = Json::dizi(); bosluk(); if (!bitti() && *p == ']') { ++p; return true; } while (true) { Json e; if (!coz(e, derinlik + 1)) return false; out.it(std::move(e)); bosluk(); if (bitti()) { hata = "dizi kapanmadi"; return false; } if (*p == ',') { ++p; continue; } if (*p == ']') { ++p; return true; } hata = "dizide ',' veya ']' bekleniyordu"; return false; } } case '{': { ++p; out = Json::nesne(); bosluk(); if (!bitti() && *p == '}') { ++p; return true; } while (true) { bosluk(); std::string k; if (!metinCoz(k)) return false; bosluk(); if (bitti() || *p != ':') { hata = "':' bekleniyordu"; return false; } ++p; Json v; if (!coz(v, derinlik + 1)) return false; out.ekle(k, std::move(v)); bosluk(); if (bitti()) { hata = "nesne kapanmadi"; return false; } if (*p == ',') { ++p; continue; } if (*p == '}') { ++p; return true; } hata = "nesnede ',' veya '}' bekleniyordu"; return false; } } default: { char* bit = nullptr; const double v = std::strtod(p, &bit); if (bit == p) { hata = "sayi bekleniyordu"; return false; } p = bit; out = Json::sayi(v); return true; } } } }; } // namespace Json Json::coz(const std::string& metin, std::string* hata) { Cozucu c{metin.data(), metin.data() + metin.size(), {}}; Json out; if (!c.coz(out, 0)) { if (hata) *hata = c.hata; return Json(); // tip Yok } return out; } } // namespace fiko::mcp