#include "fiko/zeka/kural.hpp" #include "simdjson.h" #include namespace fiko::zeka { namespace { namespace sj = simdjson; using EylemParam = std::unordered_map; // ---- Eylem registry (pipeline stage registry kalıbı) ------------------------ struct EylemDefteri { std::unordered_map fnler; std::mutex kilit; }; EylemDefteri& eylemDefterHam() { static EylemDefteri d; return d; } void cekirdekEylemler(EylemDefteri& d) { // ozYaz: eşleşen düğüme anahtar=deger yazar (varsa ÜZERİNE — idempotent koşum) d.fnler["ozYaz"] = [](GrafDeposu& g, u64 dugum, const EylemParam& p) { auto a = p.find("anahtar"); auto v = p.find("deger"); if (a == p.end() || !std::holds_alternative(a->second) || v == p.end()) return false; g.ozAyarla(dugum, std::get(a->second), v->second); return true; }; d.fnler["etiketle"] = [](GrafDeposu& g, u64 dugum, const EylemParam& p) { auto it = p.find("etiket"); if (it == p.end()) it = p.find("deger"); // {"etiketle":"x"} kısayolu if (it == p.end() || !std::holds_alternative(it->second)) return false; g.ozAyarla(dugum, "kullanici_etiket", it->second); return true; }; // dugumEkle: eşleşen düğüme "bagli" yeni düğüm; tip dışı parametreler yeni // düğümün özellikleri olur (örn. mesaj) d.fnler["dugumEkle"] = [](GrafDeposu& g, u64 dugum, const EylemParam& p) { auto t = p.find("tip"); if (t == p.end() || !std::holds_alternative(t->second)) return false; u64 yeni = g.dugumEkle(std::get(t->second)); for (const auto& [k, v] : p) if (k != "tip") g.ozAyarla(yeni, k, v); g.iliskiEkle("bagli", yeni, dugum); return true; }; } EylemDefteri& eylemDefter() { static bool ilk = [] { cekirdekEylemler(eylemDefterHam()); return true; }(); (void)ilk; return eylemDefterHam(); } EylemFn eylemBul(const std::string& ad) { auto& d = eylemDefter(); std::lock_guard kilit(d.kilit); auto it = d.fnler.find(ad); return it == d.fnler.end() ? EylemFn{} : it->second; } // Çekirdek eylemlerin zorunlu parametreleri kayıt anında denetlenir (plugin // eylemler kendi fn'inde denetler — false döner). std::string eylemParamDenetle(const KuralEylem& e) { auto metinVar = [&](const char* ad) { auto it = e.param.find(ad); return it != e.param.end() && std::holds_alternative(it->second); }; if (e.ad == "ozYaz") { if (!metinVar("anahtar") || !e.param.count("deger")) return "ozYaz: 'anahtar' (metin) ve 'deger' gerekli"; } else if (e.ad == "etiketle") { if (!metinVar("etiket") && !metinVar("deger")) return "etiketle: 'etiket' (metin) gerekli"; } else if (e.ad == "dugumEkle") { if (!metinVar("tip")) return "dugumEkle: 'tip' (metin) gerekli"; } return {}; } // ---- JSON okuma (pipeline.cpp'deki biçimin ikizi — orası anonim ns) --------- Deger degerOku(sj::ondemand::value v) { switch (v.type().value()) { case sj::ondemand::json_type::number: { i64 t; if (!v.get_int64().get(t)) return Deger(t); return Deger(v.get_double().value()); } case sj::ondemand::json_type::string: return Deger(std::string(v.get_string().value())); case sj::ondemand::json_type::boolean: return Deger(v.get_bool().value()); default: return Deger{}; } } Deger degerOku(sj::simdjson_result v) { sj::ondemand::value g; if (v.get(g)) return Deger{}; return degerOku(g); } } // namespace void eylemKaydet(std::string_view ad, EylemFn fn) { auto& d = eylemDefter(); std::lock_guard kilit(d.kilit); d.fnler[std::string(ad)] = std::move(fn); } bool eylemVar(std::string_view ad) { auto& d = eylemDefter(); std::lock_guard kilit(d.kilit); return d.fnler.count(std::string(ad)) > 0; } // ---- KuralMotoru ------------------------------------------------------------ bool KuralMotoru::kuralEkle(std::string ad, Pipeline kosul, std::vector eylemler, std::string* hata) { auto hataYaz = [&](std::string m) { if (hata) *hata = std::move(m); return false; }; if (ad.empty()) return hataYaz("Kural adı boş olamaz"); for (const auto& k : kurallar_) if (k.ad == ad) return hataYaz("Kural zaten var: '" + ad + "'"); if (!kosul.derle()) return hataYaz("Kural '" + ad + "': koşul derlenemedi: " + kosul.hata()); for (const auto& e : eylemler) { if (!eylemVar(e.ad)) return hataYaz("Kural '" + ad + "': bilinmeyen eylem '" + e.ad + "'"); std::string ph = eylemParamDenetle(e); if (!ph.empty()) return hataYaz("Kural '" + ad + "': " + ph); } kurallar_.push_back(Kural{std::move(ad), std::move(kosul), std::move(eylemler)}); return true; } bool KuralMotoru::jsonYukle(const std::string& json, std::string* hata) { auto hataYaz = [&](std::string m) { if (hata) *hata = std::move(m); return false; }; struct HamKural { std::string ad, kosulJson; std::vector eylemler; }; std::vector hamlar; std::string metin = json; metin.reserve(metin.size() + sj::SIMDJSON_PADDING); try { sj::ondemand::parser ayristirici; sj::padded_string_view pv(metin.data(), metin.size(), metin.capacity()); sj::ondemand::document belge; auto err = ayristirici.iterate(pv).get(belge); if (err) return hataYaz(std::string("Kural JSON hatası: ") + sj::error_message(err)); bool kurallarVar = false; for (auto alan : belge.get_object()) { std::string_view ad = alan.unescaped_key().value(); if (ad != "kurallar") continue; // bilinmeyen kök alanlar ileri uyumluluk kurallarVar = true; for (auto eleman : alan.value().get_array()) { HamKural h; for (auto f : eleman.get_object()) { std::string_view fk = f.unescaped_key().value(); if (fk == "ad") { h.ad = std::string(f.value().get_string().value()); } else if (fk == "kosul") { sj::ondemand::object ko; if (f.value().get_object().get(ko)) return hataYaz("Kural JSON: 'kosul' nesne olmalı ({\"pipeline\":[...]})"); // raw_json alt metni Pipeline::jsonDanKur'a aynen verilir — tek şema h.kosulJson = std::string(ko.raw_json().value()); } else if (fk == "eylemler") { for (auto e : f.value().get_array()) { for (auto ef : e.get_object()) { KuralEylem ke; ke.ad = std::string(ef.unescaped_key().value()); auto v = ef.value(); if (v.type().value() == sj::ondemand::json_type::object) { for (auto pf : v.get_object()) { std::string pk(pf.unescaped_key().value()); Deger dv = degerOku(pf.value()); if (std::holds_alternative(dv)) return hataYaz("Kural JSON: '" + ke.ad + "' eyleminde '" + pk + "' skaler olmalı"); ke.param.emplace(std::move(pk), std::move(dv)); } } else { Deger dv = degerOku(v); if (std::holds_alternative(dv)) return hataYaz("Kural JSON: '" + ke.ad + "' eylem değeri okunamadı"); ke.param.emplace("deger", std::move(dv)); } h.eylemler.push_back(std::move(ke)); } } } } if (h.ad.empty()) return hataYaz("Kural JSON: 'ad' gerekli"); if (h.kosulJson.empty()) return hataYaz("Kural '" + h.ad + "': 'kosul' gerekli"); hamlar.push_back(std::move(h)); } } if (!kurallarVar) return hataYaz("Kural JSON: 'kurallar' dizisi yok"); } catch (const sj::simdjson_error& e) { return hataYaz(std::string("Kural JSON hatası: ") + e.what()); } const size_t eskiBoy = kurallar_.size(); for (auto& h : hamlar) { std::string kh; if (!kuralEkle(std::move(h.ad), Pipeline::jsonDanKur(h.kosulJson), std::move(h.eylemler), &kh)) { kurallar_.resize(eskiBoy); // atomik yükleme: hatada hiçbiri kalmaz return hataYaz(std::move(kh)); } } return true; } bool KuralMotoru::kaldir(std::string_view ad) { for (auto it = kurallar_.begin(); it != kurallar_.end(); ++it) { if (it->ad == ad) { kurallar_.erase(it); return true; } } return false; } std::vector KuralMotoru::listele() const { std::vector adlar; adlar.reserve(kurallar_.size()); for (const auto& k : kurallar_) adlar.push_back(k.ad); return adlar; } std::vector KuralMotoru::calistir(GrafDeposu& graf) { std::vector sonuclar; sonuclar.reserve(kurallar_.size()); for (auto& k : kurallar_) { KuralSonuc s; s.kural = k.ad; PipelineSonuc ps = k.kosul.calistir(graf); if (!ps.basarili()) { s.hata = "Kural '" + k.ad + "': " + ps.hata; sonuclar.push_back(std::move(s)); continue; } s.eslesen = (u32)ps.kimlikler.size(); std::vector fnler; fnler.reserve(k.eylemler.size()); for (const auto& e : k.eylemler) fnler.push_back(eylemBul(e.ad)); for (u64 id : ps.kimlikler) { if (!graf.dugum(id)) continue; // önceki kural/eylem silmiş olabilir for (size_t i = 0; i < fnler.size(); ++i) if (fnler[i] && fnler[i](graf, id, k.eylemler[i].param)) ++s.uygulanan; } sonuclar.push_back(std::move(s)); } return sonuclar; } } // namespace fiko::zeka