#include "config.h" #include "markup.h" #include "ini.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace NConfig; using namespace NJson; namespace { const unsigned char CODE[] = { #include "code.inc" }; struct TCode: public TArchiveReader { inline TCode() : TArchiveReader(TBlob::NoCopy(CODE, sizeof(CODE))) { } static inline TCode& Instance() { return *Singleton(); } }; class TPreprocessor: public IInputStream { public: inline TPreprocessor(const TGlobals& g, IInputStream* in) : I_(nullptr, 0) , S_(in) { E_.SetVars(g); } size_t DoRead(void* ptr, size_t len) override { while (true) { const size_t read = I_.Read(ptr, len); if (read) { return read; } do { if (!S_.ReadLine(C_)) { return 0; } } while (IsComment(C_)); C_ = E_.Preprocess(C_); C_.append('\n'); I_.Reset(C_.data(), C_.size()); } } static inline bool IsComment(TStringBuf s) { s = StripString(s); return !s.empty() && s[0] == '#'; } private: TMemoryInput I_; TBufferedInput S_; TLuaEval E_; TString C_; }; } void TConfig::DumpJson(IOutputStream& out) const { TString tmp; { TStringOutput out2(tmp); ToJson(out2); } { TJsonValue v; TStringInput in(tmp); ReadJsonTree(&in, &v); WriteJson(&out, &v, true, true); } } TConfig TConfig::FromJson(IInputStream& in, const TGlobals& g) { class TJSONReader: public TJsonCallbacks { public: inline TJSONReader() : Cur(nullptr) { } inline bool OnBoolean(bool b) override { *Next() = ConstructValue(b); return true; } inline bool OnInteger(long long i) override { *Next() = ConstructValue((i64)i); return true; } inline bool OnUInteger(unsigned long long i) override { *Next() = ConstructValue((ui64)i); return true; } inline bool OnDouble(double d) override { *Next() = ConstructValue(d); return true; } inline bool OnString(const TStringBuf& s) override { *Next() = ConstructValue(ToString(s)); return true; } inline bool OnOpenMap() override { Push(ConstructValue(TDict())); return true; } inline bool OnCloseMap() override { Pop(); return true; } inline bool OnOpenArray() override { Push(ConstructValue(TArray())); return true; } inline bool OnCloseArray() override { Pop(); return true; } inline bool OnMapKey(const TStringBuf& k) override { if (S.empty()) { ythrow yexception() << "shit happen"; } Cur = &S.back().GetNonConstant()[ToString(k)]; return true; } inline void Push(const TConfig& el) { *Next() = el; S.push_back(el); } inline void Pop() { if (S.empty()) { ythrow yexception() << "shit happen"; } S.pop_back(); } inline TConfig* Next() { if (S.empty()) { return &Root; } TConfig& top = S.back(); if (top.IsA()) { TArray& arr = top.GetNonConstant(); arr.emplace_back(); return &arr.back(); } if (top.IsA()) { if (Cur) { TConfig* ret = Cur; Cur = nullptr; return ret; } } ythrow yexception() << "shit happen"; } inline void OnError(size_t off, TStringBuf reason) override { Y_UNUSED(off); if (!FirstErrorReason) { FirstErrorReason = reason; } } TConfig Root; TConfig* Cur; TVector S; TString FirstErrorReason; }; TJSONReader r; TString data = in.ReadAll(); TMemoryInput mi(data.data(), data.size()); TPreprocessor p(g, &mi); if (!NJson::ReadJson(&p, false, true, &r)) { if (!!r.FirstErrorReason) { ythrow TConfigParseError() << "Error parsing json: " << r.FirstErrorReason; } else { ythrow TConfigParseError() << "Could not parse json " << data.Quote(); } } return r.Root; } namespace { struct TData { const char* Prologue; const char* Epilogue; }; const TData DATA[] = { {"", "\nassert(not (instance == nil))\nreturn instance\n"}, {"", "\nassert(not (main == nil))\nreturn main\n"}, {"return ", "\n"}, {"", "\n"}, }; } TConfig TConfig::FromLua(IInputStream& in, const TGlobals& g) { const TString& data = in.ReadAll(); TString json; for (size_t i = 0; i < Y_ARRAY_SIZE(DATA); ++i) { TStringStream ss; ss << TStringBuf("local function configgen()") << DATA[i].Prologue << data << DATA[i].Epilogue << TStringBuf("end\n\nreturn require('json').encode(configgen())\n"); try { json = TLuaEval().SetVars(g).EvalRaw(ss.Str()); break; } catch (...) { if (i == (Y_ARRAY_SIZE(DATA) - 1)) { throw; } } } TMemoryInput mi(json.data(), json.size()); return FromJson(mi); } TConfig TConfig::FromMarkup(IInputStream& in, const TGlobals& g) { TPreprocessor pin(g, &in); return ParseRawMarkup(pin); } TConfig TConfig::FromIni(IInputStream& in, const TGlobals& g) { TPreprocessor pin(g, &in); return ParseIni(pin); } void TConfig::DumpLua(IOutputStream& out) const { TLuaEval e; TStringStream ss; ToJson(ss); e.SetVariable("jsonval", ss.Str()); out << "return " << e.EvalRaw(TCode::Instance().ObjectByKey("/pp.lua")->ReadAll() + "\nreturn prettify(require('json').decode(jsonval))\n"); } TConfig TConfig::FromStream(IInputStream& in, const TGlobals& g) { const TString& tmp = in.ReadAll(); TString luaParsingError = ""; try { TMemoryInput mi(tmp.data(), tmp.size()); return FromLua(mi, g); } catch (const yexception& e) { luaParsingError = e.AsStrBuf(); } catch (...) { luaParsingError = "unknown error"; } TMemoryInput mi(tmp.data(), tmp.size()); try { return FromJson(mi, g); } catch (const yexception& e) { const TStringBuf& jsonParsingError = e.AsStrBuf(); ythrow yexception() << "Could not parse config:\nParsing as lua: " << luaParsingError << "\nParsing as json: " << jsonParsingError; } } TConfig TConfig::ReadJson(TStringBuf in, const TGlobals& g) { TMemoryInput mi(in.data(), in.size()); return FromJson(mi, g); } TConfig TConfig::ReadLua(TStringBuf in, const TGlobals& g) { TMemoryInput mi(in.data(), in.size()); return FromLua(mi, g); } TConfig TConfig::ReadMarkup(TStringBuf in, const TGlobals& g) { TMemoryInput mi(in.data(), in.size()); return FromMarkup(mi, g); } TConfig TConfig::ReadIni(TStringBuf in, const TGlobals& g) { TMemoryInput mi(in.data(), in.size()); return FromIni(mi, g); } void TConfig::Load(IInputStream* input) { TString string; ::Load(input, string); TStringInput stream(string); *this = FromJson(stream); } void TConfig::Save(IOutputStream* output) const { TString result; TStringOutput stream(result); DumpJson(stream); ::Save(output, result); } bool TConfig::Has(const TStringBuf& key) const { return !operator[](key).IsNull(); } const TConfig& TConfig::operator[](const TStringBuf& key) const { return Get().Find(key); } const TConfig& TConfig::At(const TStringBuf& key) const { return Get().At(key); } const TConfig& TConfig::operator[](size_t index) const { return Get().Index(index); } size_t TConfig::GetArraySize() const { return Get().size(); } const TConfig& TDict::Find(const TStringBuf& key) const { const_iterator it = find(key); if (it == end()) { return Default(); } return it->second; } const TConfig& TDict::At(const TStringBuf& key) const { const_iterator it = find(key); Y_ENSURE_BT(it != end(), "missing key '" << key << "'"); return it->second; } const TConfig& TArray::Index(size_t index) const { if (index < size()) { return (*this)[index]; } return Default(); } const TConfig& TArray::At(size_t index) const { Y_ENSURE_BT(index < size(), "index " << index << " is out of bounds"); return (*this)[index]; } THolder NConfig::CreatePreprocessor(const TGlobals& g, IInputStream& in) { return MakeHolder(g, &in); }