#include #include #include #include #include #include "common.h" // Compare string with template class TemplateComparator { TemplateComparator() = delete; TemplateComparator(const TemplateComparator&) = delete; TemplateComparator(TemplateComparator&&) = delete; struct Block { using pBlock=std::unique_ptr; enum Type {NOTDEF,TEXT,OPTIONAL,VARIANTS,DELIM}; size_t b,e; Type type; const struct Block* parent; pBlock next,child; Block() = delete; Block(const struct Block&) = delete; Block(struct Block&&) = delete; Block(Type t, const struct Block* p=nullptr):b(0),e(0),type(t),parent(p),next(nullptr),child(nullptr) {} Block(size_t bb, size_t ee, const struct Block* p=nullptr):b(bb),e(ee),type(TEXT),parent(p),next(nullptr),child(nullptr) {} }; struct Cursor { const struct Block* block; size_t offset; bool operator <(const struct Cursor& c) const {return blockb+offset];} Cursor(const struct Block* b, size_t o):block(b),offset(o) {} }; using Cursors=std::set; using pCursor=std::set::const_iterator; Block::pBlock Parse(); void InitCursors(const struct Block* blk); bool CmpSmb(const char c); Block::pBlock root; Cursors cursors; const std::string s; public: TemplateComparator(const char* str):s(str) {root=Parse();} TemplateComparator(const std::string& str):s(str) {root=Parse();} TemplateComparator(std::string&& str):s(std::move(str)) {root=Parse();} bool Compare(const std::string& str) { Reset(); for(size_t pos=0; pos class Parameter { std::string name; bool initialised; protected: Parameter() = default; Parameter(const Parameter&) = delete; Parameter(std::string&& str): name(std::move(str)), initialised(false) {} Parameter(const std::string& str): name(str), initialised(false) {} void SetState(bool newini) {initialised=newini;} public: bool Initialised() const {return initialised;} const std::string& Name() {return name;} constexpr bool isOptional() const {return Optional;} static constexpr bool optional=Optional; }; // Class for parameter which must be in named pair template class NamedParameter: public Converter, public Parameter { NamedParameter() = delete; NamedParameter(const NamedParameter&) = delete; using Parameter::Initialised; protected: TemplateComparator compar; public: typedef Converter ConverterType; // Constructor specifies template for comparison template NamedParameter(N&& n, T&& t):compar(std::forward(t)) {Parameter(std::forward(n));} // Return error in case of repeated initialisation bool Init(const ObjectBase* p, const std::string& str, std::string& err) { if(!compar.Compare(str)) return true; if(Initialised()) return !Converter::Convert(p,err); else SetState(Converter::Convert(p,err)); return true; } }; template using ONPar=NamedParameter; template using RNPar=NamedParameter; // Class for parameter which can be in named pair or not template class NamedFreeParameter: public NamedParameter { NamedFreeParameter() = delete; NamedFreeParameter(const NamedFreeParameter&) = delete; using NamedParameter::compar; using NamedParameter::Initialised; public: typedef Converter ConverterType; // Constructor specifies template for comparison template NamedFreeParameter(N&& n, T&& t):NamedParameter(std::forward(n),std::forward(t)) {} // Return error in case of repeated initialisation bool Init(const ObjectBase* p, const std::string& str) { if(!compar.Compare(str)) return true; if(Initialised()) return !Converter::Convert(p); else SetState(Converter::Convert(p)); return true; } bool Init(const ObjectBase* p) { if(Initialised()) return !Converter::Convert(p); else SetState(Converter::Convert(p)); return true; } }; template using ONFPar=NamedFreeParameter; template using RNFPar=NamedFreeParameter; // Class for parameter which can be in some positions in list template class PositionalParameter: public Converter, public Parameter { using IType=ObjectList::IndexType; PositionalParameter() = delete; PositionalParameter(const PositionalParameter&) = delete; std::set positions; IType initpos; using Parameter::Initialised; using Parameter::SetState; // Constructor specifies list of possible positions PositionalParameter(IType i):initpos(0) {positions.insert(i);} template PositionalParameter(IType i, Args... args): PositionalParameter(args...) {positions.insert(i);} public: typedef Converter ConverterType; template PositionalParameter(S&& str, IType i):initpos(0) {Parameter(std::forward(str)); positions.insert(i);} template PositionalParameter(S&& str, IType i, Args... args): PositionalParameter(args...) {Parameter(std::forward(str)); positions.insert(i);} // Return error in case of repeated initialisation bool Init(IType i, const ObjectBase* p, std::string& err) { std::string cerr; if(positions.cend()==positions.find(i)) return true; if(Initialised()) return !Converter::Convert(p,cerr); else { bool res=Converter::Convert(p,cerr); if(res) initpos=i; else { if(1==positions.size() && !O) // Only one position for required parameter. Error message must describe reason of failed conversion { err=std::move(cerr); return false; } } SetState(res); } return true; } IType InitialisedFromPosition() const {return initpos;} }; template using OPosPar=PositionalParameter; template using RPosPar=PositionalParameter; // Parsing positional parameters // Generic template template class ParsePositionalParameters; // Recursive template template class ParsePositionalParameters: ParsePositionalParameters { typedef PositionalParameter PType; static_assert(std::is_same::value,"ParsePositionalParameters template argument(s) must be PositionalParameter"); PType& par; ParsePositionalParameters() = delete; ParsePositionalParameters(const ParsePositionalParameters&) = delete; ParsePositionalParameters(ParsePositionalParameters&&) = delete; protected: template void SetError(Str&& str) {ParsePositionalParameters::SetError(std::forward(str));} bool TryInit(ObjectList::IndexType i, const ObjectBase* ob) { std::string err; bool res=par.Init(i,ob,err); if(!res) SetError(err.empty()?("Ambiguous definition of parameter "+par.Name()):("Parameter "+par.Name()+" in position "+ToString(i)+": "+err)); return res && ParsePositionalParameters::TryInit(i,ob); } bool Check() { if(!(par.Initialised() || par.isOptional())) { SetError("Parameter "+par.Name()+" required, but not set"); return false; } return ParsePositionalParameters::Check(); } // Return value: number of parameters initialised from position i (count only first two) size_t isUsed(ObjectList::IndexType i, bool used=false) const { if(!par.Initialised() || (par.InitialisedFromPosition()!=i)) return ParsePositionalParameters::isUsed(i,used); if(used) // Error, two parameters initialised from one position { SetError(par.Name()); return 1; } auto res=ParsePositionalParameters::isUsed(i,true); if(res==1) // Two parameters initialised from one position, name of second parameter in error string { SetError("Parameters "+Error()+" and "+par.Name()+" are initialised from object in position "+ToString(i)); return 2; } else return 1; // All ok } public: ParsePositionalParameters(PType& p, Args... args):par(p) {ParsePositionalParameters(args...);} std::string Error() const {return ParsePositionalParameters::Error();} bool Parse(const ObjectList* list) { std::string err; for(ObjectList::IndexType i=0; iSize(); i++) { if(!TryInit(i,list->At(i),err)) { SetError("Error parsing argument "+ToString(i)+": "+err); return false; } } if(!Check()) return false; for(ObjectList::IndexType i=0; iSize(); i++) { auto n=isUsed(i); if(n>1) return false; // Error message already set if(0==n) { SetError("Unused object in position "+ToString(i)); return false; } } return true; } }; // Bottom of recursion template<> class ParsePositionalParameters<> { std::string err; ParsePositionalParameters(const ParsePositionalParameters&) = delete; ParsePositionalParameters(ParsePositionalParameters&&) = delete; protected: ParsePositionalParameters() = default; bool TryInit(ObjectList::IndexType i, const ObjectBase* ob) const {return true;} size_t isUsed(ObjectList::IndexType i, bool used=false) const {return 0;} bool Check() const {return true;} template void SetError(Str&& str) {err=std::forward(str);} std::string Error() const {return err;} };