aboutsummaryrefslogblamecommitdiffstats
path: root/library/cpp/scheme/scimpl_select.rl6
blob: 11aa549b78b5cfef7e0b1e262b9771628eb46346 (plain) (tree)
1
2
3
4
5
                                      

                               
                                                                     

















































                                                
                       










                                                    
                             

















































































































































                                                                                 
                                                     

                                                                            
                                        
                
                           



























                                                                                               
                                                      








                                                                                           
                                    


         
#include <library/cpp/scheme/scimpl.h>

#include <util/string/cast.h>
#include <util/string/escape.h>
#include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h>
#include <util/generic/is_in.h>
#include <util/string/util.h>

namespace NSc {

    template <typename TValue, typename TGetter>
    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 <typename T>
        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 <typename TSelector>
    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<ui32>(TStringBuf(ts, te)));
        }

        bool OnIntQ() {
            return OnInt(FromString<ui32>(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 <typename TSelector>
    bool TSelectorCtx<TSelector>::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 <bool CheckHas>
    struct TGetNext {
        template <typename TValue, typename TIdx>
        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<TSelector<const TValue, TGetNext<true> > > ctx(*this, path);

        if (ctx.SelectPath()) {
            return *ctx.Selector.Current;
        }

        return DefaultValue();
    }

    TValue* TValue::TrySelectOrAdd(TStringBuf path) {
        TSelectorCtx<TSelector<TValue, TGetNext<false> > > ctx(*this, path);

        if (ctx.SelectPath()) {
            return ctx.Selector.Current;
        } else {
            return nullptr;
        }
    }

    TValue TValue::TrySelectAndDelete(TStringBuf path) {
        TSelectorCtx<TSelector<TValue, TGetNext<true> > > 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<TSelector<const TValue, TGetNext<true>>>(*this, path).SelectPath();
    }

    bool TValue::PathValid(TStringBuf path) {
        TSelectorCtx<TSelector<const TValue, TGetNext<false>>> 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<true>(rawKey);
        }

        TSelectorCtx<TSelector<const TValue, TGetNext<false>>> ctx(DefaultValue(), rawKey);
        ctx.SelectPath();
        if (ctx.Selector.HasError || ctx.Selector.Depth > 1 || ctx.Selector.HasArray) {
            return NEscJ::EscapeJ<true>(rawKey);
        } else {
            return ToString(rawKey);
        }
    }

}