#include #include #include #include #include #include namespace NSc { template struct TSelector { TValue& Root; TValue* Current = nullptr; TValue* Parent = nullptr; TStringBuf CurrentDictKey; size_t CurrentArrayKey = 0; size_t Depth = 0; bool HasArray = false; bool HasError = false; TSelector(TValue& root) : Root(root) , Current(&Root) {} template bool Next(T k) { Depth += 1; Parent = Current; Current = TGetter::Next(Current, k); return Current != nullptr; } bool NextDict(TStringBuf k) { return Next(CurrentDictKey = k); } bool NextArray(size_t k) { HasArray = true; return Next(CurrentArrayKey = k); } bool Error() { Parent = nullptr; Current = nullptr; CurrentArrayKey = 0; CurrentDictKey = TStringBuf(); HasError = true; return false; } }; template struct TSelectorCtx { TSelector Selector; TString Buffer; const char* p0 = nullptr; const char* p = nullptr; const char* pe = nullptr; const char* eof = nullptr; const char* ts = nullptr; const char* te = nullptr; int cs = 0; int act = 0; TSelectorCtx(TSelector sel, TStringBuf data) : Selector(sel) , p0(data.data()) , p(data.data()) , pe(data.end()) , eof(data.end()) {} bool OnString(TStringBuf s) { return Selector.NextDict(s); } bool OnInt(size_t k) { return Selector.NextArray(k); } bool OnStrU() { return OnString(TStringBuf(ts, te)); } bool OnStrQ() { return OnString(TStringBuf(ts + 1, te - 1)); } bool OnStrE() { Buffer.clear(); Buffer.reserve(te - ts); UnescapeC(ts + 1, te - ts - 2, Buffer); return OnString(Buffer); } bool OnIntU() { return OnInt(FromString(TStringBuf(ts, te))); } bool OnIntQ() { return OnInt(FromString(TStringBuf(ts + 1, te - 1))); } bool OnError() { Selector.Error(); return false; } bool SelectPath(); }; #if 0 %%{ machine schemeselect; alphtype char; action OnIntU { if (Y_UNLIKELY(!OnIntU())) goto TOKEN_ERROR; } action OnIntQ { if (Y_UNLIKELY(!OnIntQ())) goto TOKEN_ERROR; } action OnStrU { if (Y_UNLIKELY(!OnStrU())) goto TOKEN_ERROR; } action OnStrQ { if (Y_UNLIKELY(!OnStrQ())) goto TOKEN_ERROR; } action OnStrE { if (Y_UNLIKELY(!OnStrE())) goto TOKEN_ERROR; } action OnError { goto TOKEN_ERROR; } intu = [0-9]+; intq = '[' intu ']'; uchar0 = [a-zA-Z_@$] | (0x80 .. 0xFF); uchar = uchar0 | digit | [.\-]; qchar = [^'\\]; #'; dchar = [^"\\]; #"; bchar = [^\]\\]; echar = "\\" any; qechar = qchar | echar; dechar = dchar | echar; bechar = bchar | echar; strq = "'" qchar* "'"; strd = '"' dchar* '"'; strb = '[' bchar* ']'; strqe = "'" qechar* "'"; strde = '"' dechar* '"'; strbe = '[' bechar* ']'; strU = uchar0 uchar*; strQ = strq | strd | strb; strE = strqe | strde | strbe; main := |* intu => OnIntU; intq => OnIntQ; strU => OnStrU; strQ => OnStrQ; strE => OnStrE; '/'; (intu) (any - ('/' | '[' )) => OnError; any => OnError; *|; }%% #endif template bool TSelectorCtx::SelectPath() { try { %%{ write data noerror nofinal; write init; write exec; }%% ; Y_UNUSED(schemeselect_en_main); } catch (const TFromStringException&) { return OnError(); } return Selector.Current; TOKEN_ERROR: return OnError(); } template struct TGetNext { template static TValue* Next(TValue* val, TIdx idx) { if (val) { if (CheckHas && !val->Has(idx)) { return nullptr; } else { return &(*val)[idx]; } } else { return nullptr; } } }; const TValue& TValue::TrySelect(TStringBuf path) const { TSelectorCtx > > ctx(*this, path); if (ctx.SelectPath()) { return *ctx.Selector.Current; } return DefaultValue(); } TValue* TValue::TrySelectOrAdd(TStringBuf path) { TSelectorCtx > > ctx(*this, path); if (ctx.SelectPath()) { return ctx.Selector.Current; } else { return nullptr; } } TValue TValue::TrySelectAndDelete(TStringBuf path) { TSelectorCtx > > ctx(*this, path); if (ctx.SelectPath() && ctx.Selector.Parent) { if (ctx.Selector.Parent->IsArray()) { return ctx.Selector.Parent->Delete(ctx.Selector.CurrentArrayKey); } else if (ctx.Selector.Parent->IsDict()) { return ctx.Selector.Parent->Delete(ctx.Selector.CurrentDictKey); } else { Y_ASSERT(false); return DefaultValue(); } } else { return DefaultValue(); } } bool TValue::PathExists(TStringBuf path) const { return TSelectorCtx>>(*this, path).SelectPath(); } bool TValue::PathValid(TStringBuf path) { TSelectorCtx>> ctx(DefaultValue(), path); return ctx.SelectPath() || !ctx.Selector.HasError; } TString TValue::EscapeForPath(TStringBuf rawKey) { static const str_spn danger{"/[]"}; if (!rawKey || danger.brk(rawKey.begin(), rawKey.end()) != rawKey.end()) { return NEscJ::EscapeJ(rawKey); } TSelectorCtx>> ctx(DefaultValue(), rawKey); ctx.SelectPath(); if (ctx.Selector.HasError || ctx.Selector.Depth > 1 || ctx.Selector.HasArray) { return NEscJ::EscapeJ(rawKey); } else { return ToString(rawKey); } } }