diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /library/cpp/config | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'library/cpp/config')
-rw-r--r-- | library/cpp/config/config.cpp | 425 | ||||
-rw-r--r-- | library/cpp/config/config.h | 146 | ||||
-rw-r--r-- | library/cpp/config/domscheme.cpp | 1 | ||||
-rw-r--r-- | library/cpp/config/domscheme.h | 155 | ||||
-rw-r--r-- | library/cpp/config/fwd.h | 5 | ||||
-rw-r--r-- | library/cpp/config/ini.cpp | 62 | ||||
-rw-r--r-- | library/cpp/config/ini.h | 7 | ||||
-rw-r--r-- | library/cpp/config/markup.cpp | 258 | ||||
-rw-r--r-- | library/cpp/config/markup.h | 7 | ||||
-rw-r--r-- | library/cpp/config/markupfsm.h.rl6 | 81 | ||||
-rw-r--r-- | library/cpp/config/sax.cpp | 67 | ||||
-rw-r--r-- | library/cpp/config/sax.h | 83 | ||||
-rw-r--r-- | library/cpp/config/support/pp.lua | 63 | ||||
-rw-r--r-- | library/cpp/config/value.cpp | 292 | ||||
-rw-r--r-- | library/cpp/config/value.h | 121 |
15 files changed, 1773 insertions, 0 deletions
diff --git a/library/cpp/config/config.cpp b/library/cpp/config/config.cpp new file mode 100644 index 0000000000..e8271af661 --- /dev/null +++ b/library/cpp/config/config.cpp @@ -0,0 +1,425 @@ +#include "config.h" +#include "markup.h" +#include "ini.h" + +#include <library/cpp/archive/yarchive.h> + +#include <library/cpp/json/json_reader.h> +#include <library/cpp/json/json_writer.h> + +#include <library/cpp/lua/eval.h> + +#include <util/string/cast.h> +#include <util/string/strip.h> +#include <util/string/type.h> + +#include <util/stream/output.h> +#include <util/stream/file.h> +#include <util/memory/blob.h> + +#include <util/generic/singleton.h> +#include <util/stream/str.h> + +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<TCode>(); + } + }; + + 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<TDict>()[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>()) { + TArray& arr = top.GetNonConstant<TArray>(); + + arr.emplace_back(); + + return &arr.back(); + } + + if (top.IsA<TDict>()) { + 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<TConfig> 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<TDict>().Find(key); +} + +const TConfig& TConfig::At(const TStringBuf& key) const { + return Get<TDict>().At(key); +} + +const TConfig& TConfig::operator[](size_t index) const { + return Get<TArray>().Index(index); +} + +size_t TConfig::GetArraySize() const { + return Get<TArray>().size(); +} + +const TConfig& TDict::Find(const TStringBuf& key) const { + const_iterator it = find(key); + + if (it == end()) { + return Default<TConfig>(); + } + + 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<TConfig>(); +} + +const TConfig& TArray::At(size_t index) const { + Y_ENSURE_BT(index < size(), "index " << index << " is out of bounds"); + + return (*this)[index]; +} + +THolder<IInputStream> NConfig::CreatePreprocessor(const TGlobals& g, IInputStream& in) { + return MakeHolder<TPreprocessor>(g, &in); +} diff --git a/library/cpp/config/config.h b/library/cpp/config/config.h new file mode 100644 index 0000000000..16d2d7edf9 --- /dev/null +++ b/library/cpp/config/config.h @@ -0,0 +1,146 @@ +#pragma once + +#include "fwd.h" +#include "value.h" + +#include <library/cpp/json/json_value.h> + +#include <util/generic/hash.h> +#include <util/generic/ptr.h> +#include <util/generic/deque.h> +#include <util/system/type_name.h> +#include <util/generic/vector.h> +#include <util/generic/yexception.h> +#include <util/generic/bt_exception.h> +#include <util/ysaveload.h> + +class IInputStream; +class IOutputStream; + +namespace NConfig { + typedef THashMap<TString, NJson::TJsonValue> TGlobals; + + class TConfigError: public TWithBackTrace<yexception> { + }; + + class TConfigParseError: public TConfigError { + }; + + class TTypeMismatch: public TConfigError { + }; + + struct TArray; + struct TDict; + + class TConfig { + public: + inline TConfig() + : V_(Null()) + { + } + + inline TConfig(IValue* v) + : V_(v) + { + } + + TConfig(const TConfig& config) = default; + TConfig& operator=(const TConfig& config) = default; + + template <class T> + inline bool IsA() const { + return V_->IsA(typeid(T)); + } + + inline bool IsNumeric() const { + return IsA<double>() || IsA<i64>() || IsA<ui64>(); + } + + template <class T> + inline const T& Get() const { + return GetNonConstant<T>(); + } + + template <class T> + inline T& GetNonConstant() const { + if (this->IsA<T>()) { + return *(T*)V_->Ptr(); + } + + if constexpr (std::is_same_v<T, ::NConfig::TArray>) { + NCfgPrivate::ReportTypeMismatch(V_->TypeName(), "array"); + } else if constexpr (std::is_same_v<T, ::NConfig::TDict>) { + NCfgPrivate::ReportTypeMismatch(V_->TypeName(), "dict"); + } else if constexpr (std::is_same_v<T, TString>) { + NCfgPrivate::ReportTypeMismatch(V_->TypeName(), "string"); + } else { + NCfgPrivate::ReportTypeMismatch(V_->TypeName(), ::TypeName<T>()); + } + } + + template <class T> + inline T As() const { + return ValueAs<T>(V_.Get()); + } + + template <class T> + inline T As(T def) const { + return IsNull() ? def : As<T>(); + } + + inline bool IsNull() const noexcept { + return V_.Get() == Null(); + } + + const TConfig& Or(const TConfig& r) const { + return IsNull() ? r : *this; + } + + //assume value is dict + bool Has(const TStringBuf& key) const; + const TConfig& operator[](const TStringBuf& key) const; + const TConfig& At(const TStringBuf& key) const; + + //assume value is array + const TConfig& operator[](size_t index) const; + size_t GetArraySize() const; + + static TConfig FromIni(IInputStream& in, const TGlobals& g = TGlobals()); + static TConfig FromJson(IInputStream& in, const TGlobals& g = TGlobals()); + static TConfig FromLua(IInputStream& in, const TGlobals& g = TGlobals()); + //load yconf format. unsafe, but natural mapping + static TConfig FromMarkup(IInputStream& in, const TGlobals& g = TGlobals()); + + static TConfig FromStream(IInputStream& in, const TGlobals& g = TGlobals()); + + inline void ToJson(IOutputStream& out) const { + V_->ToJson(out); + } + + void DumpJson(IOutputStream& out) const; + void DumpLua(IOutputStream& out) const; + + static TConfig ReadJson(TStringBuf in, const TGlobals& g = TGlobals()); + static TConfig ReadLua(TStringBuf in, const TGlobals& g = TGlobals()); + static TConfig ReadMarkup(TStringBuf in, const TGlobals& g = TGlobals()); + static TConfig ReadIni(TStringBuf in, const TGlobals& g = TGlobals()); + + void Load(IInputStream* stream); + void Save(IOutputStream* stream) const; + + private: + TIntrusivePtr<IValue> V_; + }; + + struct TArray: public TDeque<TConfig> { + const TConfig& Index(size_t index) const; + const TConfig& At(size_t index) const; + }; + + struct TDict: public THashMap<TString, TConfig> { + const TConfig& Find(const TStringBuf& key) const; + const TConfig& At(const TStringBuf& key) const; + }; + + THolder<IInputStream> CreatePreprocessor(const TGlobals& g, IInputStream& in); +} diff --git a/library/cpp/config/domscheme.cpp b/library/cpp/config/domscheme.cpp new file mode 100644 index 0000000000..95f3dc8278 --- /dev/null +++ b/library/cpp/config/domscheme.cpp @@ -0,0 +1 @@ +#include "domscheme.h" diff --git a/library/cpp/config/domscheme.h b/library/cpp/config/domscheme.h new file mode 100644 index 0000000000..613850be01 --- /dev/null +++ b/library/cpp/config/domscheme.h @@ -0,0 +1,155 @@ +#pragma once + +#include "config.h" + +#include <util/generic/algorithm.h> +#include <util/generic/typetraits.h> +#include <util/stream/str.h> + +struct TConfigTraits { + using TValue = NConfig::TConfig; + using TValueRef = const TValue*; + using TConstValueRef = TValueRef; + using TStringType = TString; + + // anyvalue defaults + template <class T> + static inline TValue Value(const T& t) { + return TValue(NConfig::ConstructValue(t)); + } + + template <class T> + static inline TValue Value(std::initializer_list<T> list) { + NConfig::TArray result; + for (const auto& t : list) { + result.push_back(TValue(NConfig::ConstructValue(t))); + } + return TValue(NConfig::ConstructValue(std::move(result))); + } + + static inline TConstValueRef Ref(const TValue& v) { + return &v; + } + + // common ops + static inline bool IsNull(TConstValueRef v) { + return v->IsNull(); + } + + static inline TString ToJson(TConstValueRef v) { + TStringStream str; + v->ToJson(str); + return str.Str(); + } + + // struct ops + static inline TConstValueRef GetField(TConstValueRef v, const TStringBuf& name) { + return &(*v)[name]; + } + + // array ops + static bool IsArray(TConstValueRef v) { + return v->IsA<NConfig::TArray>(); + } + + using TArrayIterator = size_t; + + static inline TConstValueRef ArrayElement(TConstValueRef v, TArrayIterator n) { + return &(*v)[n]; + } + + static inline size_t ArraySize(TConstValueRef v) { + return v->GetArraySize(); + } + + static inline TArrayIterator ArrayBegin(TConstValueRef) { + return 0; + } + + static inline TArrayIterator ArrayEnd(TConstValueRef v) { + return ArraySize(v); + } + + // dict ops + static bool IsDict(TConstValueRef v) { + return v->IsA<NConfig::TDict>(); + } + + static inline TConstValueRef DictElement(TConstValueRef v, TStringBuf key) { + return &(*v)[key]; + } + + static inline size_t DictSize(TConstValueRef v) { + return v->Get<NConfig::TDict>().size(); + } + + using TDictIterator = NConfig::TDict::const_iterator; + + static inline TDictIterator DictBegin(TConstValueRef v) { + return v->Get<NConfig::TDict>().begin(); + } + + static inline TDictIterator DictEnd(TConstValueRef v) { + return v->Get<NConfig::TDict>().end(); + } + + static inline TStringBuf DictIteratorKey(TConstValueRef /*dict*/, const TDictIterator& it) { + return it->first; + } + + static inline TConstValueRef DictIteratorValue(TConstValueRef /*dict*/, const TDictIterator& it) { + return &it->second; + } + + // generic get + template <typename T> + static inline void Get(TConstValueRef v, T def, T& t) { + t = v->As<T>(def); + } + + static inline bool Get(TConstValueRef v, double def, double& t) { + if (v->IsNumeric()) { + t = v->As<double>(def); + return true; + } + t = def; + return false; + } + + template <typename T> + static inline void Get(TConstValueRef v, T& t) { + t = v->As<T>(); + } + + template <typename T> + static inline bool IsValidPrimitive(const T&, TConstValueRef v) { + if (v->IsNull()) { + return true; + } + + try { + v->As<T>(); + + return true; + } catch (const NConfig::TTypeMismatch&) { + } catch (const TBadCastException&) { + } + + return false; + } + + template <class T> + static inline void Set(TValueRef v, T&& t) { + v->GetNonConstant<std::remove_const_t<std::remove_reference_t<T>>>() = t; + } + + // validation ops + static inline TVector<TString> GetKeys(TConstValueRef v) { + TVector<TString> res; + for (const auto& it : v->Get<NConfig::TDict>()) { + res.push_back(it.first); + } + Sort(res.begin(), res.end()); + return res; + } +}; diff --git a/library/cpp/config/fwd.h b/library/cpp/config/fwd.h new file mode 100644 index 0000000000..07caafe661 --- /dev/null +++ b/library/cpp/config/fwd.h @@ -0,0 +1,5 @@ +#pragma once + +namespace NConfig { + class TConfig; +} diff --git a/library/cpp/config/ini.cpp b/library/cpp/config/ini.cpp new file mode 100644 index 0000000000..6c1897dd27 --- /dev/null +++ b/library/cpp/config/ini.cpp @@ -0,0 +1,62 @@ +#include "ini.h" + +#include <util/string/strip.h> +#include <util/stream/input.h> + +using namespace NConfig; + +namespace { + inline TStringBuf StripComment(TStringBuf line) { + return line.Before('#').Before(';'); + } +} + +TConfig NConfig::ParseIni(IInputStream& in) { + TConfig ret = ConstructValue(TDict()); + + { + TConfig* cur = &ret; + TString line; + + while (in.ReadLine(line)) { + TStringBuf tmp = StripComment(line); + TStringBuf stmp = StripString(tmp); + + if (stmp.empty()) { + continue; + } + + if (stmp[0] == '[') { + //start section + if (*(stmp.end() - 1) != ']') { + ythrow TConfigParseError() << "malformed section " << stmp; + } + + stmp = TStringBuf(stmp.data() + 1, stmp.end() - 1); + cur = &ret; + + while (!!stmp) { + TStringBuf section; + + stmp.Split('.', section, stmp); + + cur = &cur->GetNonConstant<TDict>()[section]; + if (!cur->IsA<TDict>()) { + *cur = ConstructValue(TDict()); + } + } + } else { + //value + TStringBuf key, value; + + tmp.Split('=', key, value); + + auto& dict = cur->GetNonConstant<TDict>(); + auto strippedValue = TString(StripString(value)); + dict[StripString(key)] = ConstructValue(strippedValue); + } + } + } + + return ret; +} diff --git a/library/cpp/config/ini.h b/library/cpp/config/ini.h new file mode 100644 index 0000000000..e5c9567224 --- /dev/null +++ b/library/cpp/config/ini.h @@ -0,0 +1,7 @@ +#pragma once + +#include "config.h" + +namespace NConfig { + TConfig ParseIni(IInputStream& in); +} diff --git a/library/cpp/config/markup.cpp b/library/cpp/config/markup.cpp new file mode 100644 index 0000000000..df072c6529 --- /dev/null +++ b/library/cpp/config/markup.cpp @@ -0,0 +1,258 @@ +#include "markup.h" + +#include <util/stream/output.h> +#include <util/stream/mem.h> +#include <util/string/strip.h> +#include <util/string/cast.h> + +using namespace NConfig; + +#define DBG(x) + +namespace { +#define MACHINE_DATA +#include "markupfsm.h" +#undef MACHINE_DATA + + class IXmlCB { + public: + inline void DoTagOpen(const TStringBuf& key) { + DBG(Cerr << "topen" << key << Endl); + + S_.push_back(key); + OnTagOpen(key); + } + + inline void DoTagClose(const TStringBuf& key) { + DBG(Cerr << "tclose" << key << Endl); + + if (S_.empty()) { + ythrow yexception() << "unbalanced tag"; + } + + if (S_.back() != key) { + ythrow yexception() << "unbalanced tag"; + } + + S_.pop_back(); + OnTagClose(); + } + + inline void DoText(const TStringBuf& text) { + DBG(Cerr << "ttext" << text << Endl); + + if (!!text) { + OnText(text); + } + } + + inline void DoAttrKey(const TStringBuf& key) { + DBG(Cerr << "tattrkey" << key << Endl); + + A_ = key; + } + + inline void DoAttrValue(const TStringBuf& key) { + DBG(Cerr << "tattrvalue" << key << Endl); + + if (!A_) { + ythrow yexception() << "dangling attribute"; + } + + OnAttr(A_, key); + A_ = TStringBuf(); + } + + virtual void OnTagOpen(const TStringBuf& key) = 0; + virtual void OnTagClose() = 0; + virtual void OnText(const TStringBuf& text) = 0; + virtual void OnAttr(const TStringBuf& key, const TStringBuf& value) = 0; + virtual ~IXmlCB() = default; + + private: + TVector<TStringBuf> S_; + TStringBuf A_; + }; + + inline void Parse(TStringBuf s, IXmlCB* cb) { + const char* p = s.data(); + const char* pe = s.end(); + const char* eof = pe; + const char* l = p; + + int cs; + TString cur; + +#define MACHINE_INIT +#include "markupfsm.h" +#undef MACHINE_INIT + +#define MACHINE_EXEC +#include "markupfsm.h" +#undef MACHINE_EXEC + + if (cs < ParseXml_first_final) { + ythrow TConfigParseError() << "can not parse markup data at offset " << (p - s.data()); + } + } + + inline IValue* SmartValue(const TStringBuf& v) { + try { + return ConstructValue(FromString<ui64>(v)); + } catch (...) { + } + + try { + return ConstructValue(FromString<i64>(v)); + } catch (...) { + } + + try { + return ConstructValue(FromString<double>(v)); + } catch (...) { + } + + try { + return ConstructValue(FromString<bool>(v)); + } catch (...) { + } + + return ConstructValue(ToString(v)); + } + + inline TConfig Parse(TStringBuf s0) { + struct TXmlParser: public IXmlCB { + inline TXmlParser() + : Root(ConstructValue(TDict())) + { + S.push_back(&Root); + } + + void OnTagOpen(const TStringBuf& key) override { + *Push(key) = ConstructValue(TDict()); + } + + void OnTagClose() override { + S.pop_back(); + } + + static inline bool IsWS(char ch) { + switch (ch) { + case ' ': + case '\t': + case '\r': + case '\n': + case ':': + return true; + } + + return false; + } + + void OnText(const TStringBuf& text) override { + TMemoryInput mi(text.data(), text.size()); + TString line; + + while (mi.ReadLine(line)) { + DBG(Cerr << line << Endl); + + TStringBuf s = StripString(TStringBuf(line)); + + DBG(Cerr << s << Endl); + + if (!s) { + continue; + } + + const char* b = s.data(); + const char* c = b; + const char* e = s.end(); + + while (c < e && !IsWS(*c)) { + ++c; + } + + const TStringBuf key(b, c); + + while (c < e && IsWS(*c)) { + ++c; + } + + const TStringBuf value(c, e); + + if (!key) { + continue; + } + + DBG(Cerr << key << " " << value << Endl); + + SetAttr(key, value); + } + } + + void OnAttr(const TStringBuf& key, const TStringBuf& value) override { + SetAttr(key, value); + } + + inline void SetAttr(const TStringBuf& key, const TStringBuf& value) { + Dict()[ToString(key)] = SmartValue(value); + } + + inline TConfig* Top() { + return S.back(); + } + + inline TConfig* Push(const TStringBuf& key) { + TDict& d = Dict(); + const TString k = ToString(key); + + if (d.find(k) == d.end()) { + S.push_back(&d[k]); + } else { + TConfig tmp = d[k]; + + if (tmp.IsA<TArray>()) { + TArray& arr = d[k].GetNonConstant<TArray>(); + + arr.push_back(TConfig()); + + S.push_back(&arr.back()); + } else { + d[k] = ConstructValue(TArray()); + + TArray& arr = d[k].GetNonConstant<TArray>(); + + arr.push_back(tmp); + arr.push_back(TConfig()); + + S.push_back(&arr.back()); + } + } + + return Top(); + } + + inline TDict& Dict() { + try { + return Top()->GetNonConstant<TDict>(); + } catch (...) { + } + + return Top()->Get<TArray>().back().GetNonConstant<TDict>(); + } + + TConfig Root; + TVector<TConfig*> S; + }; + + TXmlParser cb; + + Parse(s0, &cb); + + return cb.Root; + } +} + +TConfig NConfig::ParseRawMarkup(IInputStream& in) { + return Parse(in.ReadAll()); +} diff --git a/library/cpp/config/markup.h b/library/cpp/config/markup.h new file mode 100644 index 0000000000..88e1207b30 --- /dev/null +++ b/library/cpp/config/markup.h @@ -0,0 +1,7 @@ +#pragma once + +#include "config.h" + +namespace NConfig { + TConfig ParseRawMarkup(IInputStream& in); +} diff --git a/library/cpp/config/markupfsm.h.rl6 b/library/cpp/config/markupfsm.h.rl6 new file mode 100644 index 0000000000..b709faaebf --- /dev/null +++ b/library/cpp/config/markupfsm.h.rl6 @@ -0,0 +1,81 @@ +%%{ + machine ParseXml; + + action startText { + l = p; + } + + action endText { + cb->DoText(TStringBuf(l + 1, p)); + } + + action startSTag { + l = p; + } + + action endSTag { + cb->DoTagOpen(TStringBuf(l, p)); + } + + action startETag { + l = p; + } + + action endETag { + cb->DoTagClose(TStringBuf(l, p)); + } + + action startKey { + l = p; + } + + action endKey { + cb->DoAttrKey(TStringBuf(l, p)); + } + + action startValue { + l = p; + } + + action endValue { + cb->DoAttrValue(TStringBuf(l, p)); + } + + spacesym = [ \r\n\t]; + gsym = (spacesym | '<' | '>' | '/'); + sym = any -- gsym; + asym = sym -- ('=' | '"'); + tag = sym+; + xattrkey = asym+; + xattrval = asym*; + xspace = spacesym*; + mspace = spacesym+; + attr = (xattrkey >startKey %endKey) '=' '"' (xattrval >startValue %endValue) '"'; + attrs = (mspace attr)*; + text = ('>' (any -- '<')*) >startText %endText; + stag = '<' xspace (tag >startSTag %endSTag) attrs xspace; + etag = '<' xspace '/' xspace (tag >startETag %endETag) xspace; + + main := spacesym* ((stag | etag) text)*; +}%% + +#if defined(MACHINE_DATA) +#undef MACHINE_DATA +%%{ + write data; +}%% +#endif + +#if defined(MACHINE_INIT) +#undef MACHINE_INIT +%%{ + write init; +}%% +#endif + +#if defined(MACHINE_EXEC) +#undef MACHINE_EXEC +%%{ + write exec; +}%% +#endif diff --git a/library/cpp/config/sax.cpp b/library/cpp/config/sax.cpp new file mode 100644 index 0000000000..7837fe97b5 --- /dev/null +++ b/library/cpp/config/sax.cpp @@ -0,0 +1,67 @@ +#include "sax.h" + +using namespace NConfig; + +namespace { + class TSax: public IConfig, public IConfig::IValue { + public: + inline TSax(const TConfig& cfg) + : C_(cfg) + { + } + + void DoForEach(IFunc* func) override { + if (C_.IsA<TArray>()) { + const TArray& a = C_.Get<TArray>(); + + for (size_t i = 0; i < a.size(); ++i) { + TSax slave(a[i]); + + func->Consume(ToString(i), &slave); + } + } else if (C_.IsA<TDict>()) { + const TDict& d = C_.Get<TDict>(); + + for (const auto& it : d) { + TSax slave(it.second); + + func->Consume(it.first, &slave); + } + } + } + + TString AsString() override { + if (C_.IsA<TArray>()) { + TSax slave(C_.Get<TArray>()[0]); + return slave.AsString(); + } + return C_.As<TString>(); + } + + bool AsBool() override { + return C_.As<bool>(); + } + + IConfig* AsSubConfig() override { + return this; + } + bool IsContainer() const override { + return C_.IsA<TArray>() || C_.IsA<TDict>(); + } + void DumpJson(IOutputStream& stream) const override { + C_.DumpJson(stream); + } + void DumpLua(IOutputStream& stream) const override { + C_.DumpLua(stream); + } + + private: + TConfig C_; + }; +} + +namespace NConfig { + THolder<IConfig> ConfigParser(IInputStream& in, const TGlobals& globals) { + return MakeHolder<TSax>(TConfig::FromStream(in, globals)); + } +} diff --git a/library/cpp/config/sax.h b/library/cpp/config/sax.h new file mode 100644 index 0000000000..8ce252c9a7 --- /dev/null +++ b/library/cpp/config/sax.h @@ -0,0 +1,83 @@ +#pragma once + +#include "config.h" + +#include <util/string/cast.h> +#include <util/generic/maybe.h> +#include <util/generic/string.h> +#include <util/generic/vector.h> +#include <util/generic/yexception.h> + +class IInputStream; + +namespace NConfig { + class IConfig { + public: + class IValue { + public: + virtual TString AsString() = 0; + virtual bool AsBool() = 0; + virtual IConfig* AsSubConfig() = 0; + + virtual bool IsContainer() const = 0; + }; + + class IFunc { + public: + inline void Consume(const TString& key, IValue* value) { + DoConsume(key, value); + } + + virtual ~IFunc() = default; + + private: + virtual void DoConsume(const TString& key, IValue* value) { + (void)key; + (void)value; + } + }; + + virtual ~IConfig() = default; + + inline void ForEach(IFunc* func) { + DoForEach(func); + } + virtual void DumpJson(IOutputStream& stream) const = 0; + virtual void DumpLua(IOutputStream& stream) const = 0; + + private: + virtual void DoForEach(IFunc* func) = 0; + }; + + template <class T> + static inline bool ParseFromString(const TString& s, TMaybe<T>& t) { + t.ConstructInPlace(FromString<T>(s)); + + return true; + } + + template <class T> + static inline bool ParseFromString(const TString& s, THolder<T>& t) { + t = MakeHolder<T>(FromString<T>(s)); + + return true; + } + + template <class T> + static inline bool ParseFromString(const TString& s, T& t) { + t = FromString<T>(s); + + return true; + } + + THolder<IConfig> ConfigParser(IInputStream& in, const TGlobals& globals = TGlobals()); +} + +#define START_PARSE \ + void DoConsume(const TString& key, NConfig::IConfig::IValue* value) override { \ + (void)key; \ + (void)value; +#define END_PARSE \ + ythrow NConfig::TConfigParseError() << "unsupported key(" << key.Quote() << ")"; \ + } +#define ON_KEY(k, v) if (key == k && NConfig::ParseFromString(value->AsString(), v)) diff --git a/library/cpp/config/support/pp.lua b/library/cpp/config/support/pp.lua new file mode 100644 index 0000000000..d6a85653a2 --- /dev/null +++ b/library/cpp/config/support/pp.lua @@ -0,0 +1,63 @@ +local function off(i) + local ss = '' + local j = 0 + + while j < i do + ss = ss .. ' ' + j = j + 1 + end + + return ss +end + +local function fmtkey(key) + if type(key) == 'string' and key:match('^[_%a][_%w]*$') then + return key + end + + return '[' .. string.format('%q', key) .. ']' +end + +local function pp(v, i) + if type(v) == "string" then + return string.format('%q', v) + end + + if type(v) == "number" then + return tostring(v) + end + + if type(v) == "nil" then + return 'nil' + end + + if type(v) == "boolean" then + return tostring(v) + end + + if type(v) == "table" then + local ret = "{\n" + local curoff = 1 + + for x, y in pairs(v) do + if type(x) == 'number' and x == curoff then + ret = ret .. off(i + 1) .. pp(y, i + 1) .. ';\n' + curoff = curoff + 1 + else + ret = ret .. off(i + 1) .. fmtkey(x) .. " = " .. pp(y, i + 1) .. ';\n' + end + end + + return ret .. off(i) .. "}" + end + + if v == nil then + return 'nil' + end + + return "" +end + +local function prettify(v) + return pp(v, 0) +end diff --git a/library/cpp/config/value.cpp b/library/cpp/config/value.cpp new file mode 100644 index 0000000000..776cd2b66c --- /dev/null +++ b/library/cpp/config/value.cpp @@ -0,0 +1,292 @@ +#include "value.h" +#include "config.h" + +#include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h> + +#include <util/generic/algorithm.h> +#include <util/system/type_name.h> +#include <util/generic/singleton.h> +#include <util/string/cast.h> +#include <util/string/strip.h> +#include <util/string/type.h> + +using namespace NConfig; + +namespace { + template <class T> + class TValue: public IValue { + public: + inline TValue(const T& t) + : T_(t) + { + } + + bool IsA(const std::type_info& info) const override { + return info == typeid(T); + } + + TString TypeName() const override { + return ::TypeName<T>(); + } + + void* Ptr() const override { + return (void*)&T_; + } + + void ToJson(IOutputStream& out) const override { + out << AsString(); + } + + bool AsBool() const override { + return (bool)AsDouble(); + } + + protected: + T T_; + }; + + class TNullValue: public TValue<TNull> { + public: + inline TNullValue() + : TValue<TNull>(TNull()) + { + Ref(); + } + + double AsDouble() const override { + return 0; + } + + ui64 AsUInt() const override { + return 0; + } + + i64 AsInt() const override { + return 0; + } + + TString AsString() const override { + return TString(); + } + + void ToJson(IOutputStream& out) const override { + out << "null"; + } + + TString TypeName() const override { + return "null"; + } + }; + + template <class T> + class TNumericValue: public TValue<T> { + public: + inline TNumericValue(const T& t) + : TValue<T>(t) + { + } + + double AsDouble() const override { + return this->T_; + } + + ui64 AsUInt() const override { + return this->T_; + } + + i64 AsInt() const override { + return this->T_; + } + }; + + class TBoolValue: public TNumericValue<bool> { + public: + inline TBoolValue(bool v) + : TNumericValue<bool>(v) + { + } + + TString AsString() const override { + return T_ ? "true" : "false"; + } + }; + + template <class T> + class TArithmeticValue: public TNumericValue<T> { + public: + inline TArithmeticValue(T v) + : TNumericValue<T>(v) + { + } + + TString AsString() const override { + return ToString(this->T_); + } + }; + + class TStringValue: public TValue<TString> { + public: + inline TStringValue(const TString& v) + : TValue<TString>(v) + { + } + + template <class T> + inline T AsT() const { + const TStringBuf s = StripString(TStringBuf(T_)); + + if (IsTrue(s)) { + return true; + } + + if (IsFalse(s)) { + return false; + } + + return FromString<T>(s); + } + + double AsDouble() const override { + return AsT<double>(); + } + + ui64 AsUInt() const override { + return AsT<ui64>(); + } + + i64 AsInt() const override { + return AsT<i64>(); + } + + TString AsString() const override { + return T_; + } + + void ToJson(IOutputStream& out) const override { + NEscJ::EscapeJ<true, true>(T_, out); + } + + TString TypeName() const override { + return "string"; + } + }; + + template <class T> + class TContainer: public TValue<T> { + public: + inline TContainer(const T& t) + : TValue<T>(t) + { + } + + double AsDouble() const override { + NCfgPrivate::ReportTypeMismatch(this->TypeName(), "double"); + } + + ui64 AsUInt() const override { + NCfgPrivate::ReportTypeMismatch(this->TypeName(), "uint"); + } + + i64 AsInt() const override { + NCfgPrivate::ReportTypeMismatch(this->TypeName(), "int"); + } + + bool AsBool() const override { + NCfgPrivate::ReportTypeMismatch(this->TypeName(), "bool"); + } + + TString AsString() const override { + NCfgPrivate::ReportTypeMismatch(this->TypeName(), "string"); + } + }; + + class TArrayValue: public TContainer<TArray> { + public: + inline TArrayValue(const TArray& v) + : TContainer<TArray>(v) + { + } + + void ToJson(IOutputStream& s) const override { + s << "["; + + for (TArray::const_iterator it = T_.begin(); it != T_.end(); ++it) { + if (it != T_.begin()) { + s << ","; + } + + it->ToJson(s); + } + + s << "]"; + } + + TString TypeName() const override { + return "array"; + } + }; + + class TDictValue: public TContainer<TDict> { + public: + inline TDictValue(const TDict& v) + : TContainer<TDict>(v) + { + } + + void ToJson(IOutputStream& s) const override { + s << "{"; + + TVector<TStringBuf> buf; + buf.reserve(T_.size()); + for (const auto& t : T_) { + buf.push_back(t.first); + } + Sort(buf.begin(), buf.end()); + for (TVector<TStringBuf>::const_iterator kit = buf.begin(); kit != buf.end(); ++kit) { + TStringBuf key = *kit; + TDict::const_iterator it = T_.find(key); + + if (kit != buf.begin()) { + s << ","; + } + + NEscJ::EscapeJ<true, true>(key, s); + + s << ":"; + + it->second.ToJson(s); + } + + s << "}"; + } + + TString TypeName() const override { + return "dict"; + } + }; +} + +#define DECLARE(type1, type2) \ + IValue* ConstructValueImpl(const type2& t) { \ + return new type1(t); \ + } + +namespace NConfig { + namespace NCfgPrivate { + DECLARE(TBoolValue, bool) + DECLARE(TArithmeticValue<double>, double) + DECLARE(TArithmeticValue<i64>, i64) + DECLARE(TArithmeticValue<ui64>, ui64) + DECLARE(TStringValue, TString) + DECLARE(TArrayValue, TArray) + DECLARE(TDictValue, TDict) + } + + IValue* Null() { + return Singleton<TNullValue>(); + } + + [[noreturn]] void NCfgPrivate::ReportTypeMismatch(TStringBuf realType, TStringBuf expectedType) { + ythrow TTypeMismatch() << "type mismatch (real: " << realType << ", expected: " << expectedType << ')'; + } +} diff --git a/library/cpp/config/value.h b/library/cpp/config/value.h new file mode 100644 index 0000000000..bfd1e2f8c3 --- /dev/null +++ b/library/cpp/config/value.h @@ -0,0 +1,121 @@ +#pragma once + +#include <typeinfo> + +#include <util/generic/ptr.h> +#include <util/generic/cast.h> +#include <util/generic/string.h> +#include <util/generic/typetraits.h> + +class IOutputStream; + +namespace NConfig { + class IValue: public TAtomicRefCount<IValue> { + public: + virtual ~IValue() = default; + + virtual bool IsA(const std::type_info& info) const = 0; + virtual TString TypeName() const = 0; + virtual void* Ptr() const = 0; + + virtual ui64 AsUInt() const = 0; + virtual i64 AsInt() const = 0; + virtual double AsDouble() const = 0; + virtual bool AsBool() const = 0; + virtual TString AsString() const = 0; + + virtual void ToJson(IOutputStream& out) const = 0; + }; + + namespace NCfgPrivate { + struct TDummy { + }; + + template <class T> + inline IValue* ConstructValueImpl(const T& t, ...) { + extern IValue* ConstructValueImpl(const T& t); + + return ConstructValueImpl(t); + } + + template <class T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr> + inline IValue* ConstructValueImpl(const T& t, TDummy) { + extern IValue* ConstructValueImpl(const double& t); + + return ConstructValueImpl(t); + } + + template <class T, std::enable_if_t<std::is_integral<T>::value>* = nullptr> + inline IValue* ConstructValueImpl(const T& t, TDummy) { + typedef std::conditional_t<std::is_signed<T>::value, i64, ui64> Type; + extern IValue* ConstructValueImpl(const Type& t); + + return ConstructValueImpl(t); + } + + template <class T, std::enable_if_t<std::is_convertible<T, TString>::value>* = nullptr> + inline IValue* ConstructValueImpl(const T& t, TDummy) { + extern IValue* ConstructValueImpl(const TString& t); + + return ConstructValueImpl(t); + } + + inline IValue* ConstructValueImpl(const bool& t, TDummy) { + extern IValue* ConstructValueImpl(const bool& t); + + return ConstructValueImpl(t); + } + }; + + template <class T> + inline IValue* ConstructValue(const T& t) { + return NCfgPrivate::ConstructValueImpl(t, NCfgPrivate::TDummy()); + } + + IValue* Null(); + + namespace NCfgPrivate { + template <bool Unsigned> + struct TSelector { + static inline ui64 Cvt(const IValue* v) { + return v->AsUInt(); + } + }; + + template <> + struct TSelector<false> { + static inline i64 Cvt(const IValue* v) { + return v->AsInt(); + } + }; + + [[noreturn]] void ReportTypeMismatch(TStringBuf realType, TStringBuf expectedType); + } + + template <class T> + inline T ValueAs(const IValue* val) { + typedef NCfgPrivate::TSelector<std::is_unsigned<T>::value> TCvt; + + return SafeIntegerCast<T>(TCvt::Cvt(val)); + } + + template <> + inline double ValueAs(const IValue* val) { + return val->AsDouble(); + } + + template <> + inline float ValueAs(const IValue* val) { + return (float)val->AsDouble(); + } + + template <> + inline bool ValueAs(const IValue* val) { + return val->AsBool(); + } + + template <> + inline TString ValueAs(const IValue* val) { + return val->AsString(); + } +} |