You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
9.4 KiB

#include <iostream>
#include <memory>
#include <set>
#include <string>
#include <vector>
#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<struct Block>;
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 block<c.block;}
bool compare(const std::string& str, const char c) const {return c==str[block->b+offset];}
Cursor(const struct Block* b, size_t o):block(b),offset(o) {}
};
using Cursors=std::set<struct Cursor>;
using pCursor=std::set<struct Cursor>::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<str.length(); ++pos) if(!CmpSmb(str[pos])) return false;
return true;
}
void Reset()
{
cursors.clear();
InitCursors(root.get());
}
};
// Common ancestor for all parameters
template<bool Optional>
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 Converter, bool O>
class NamedParameter: public Converter, public Parameter<O>
{
NamedParameter() = delete;
NamedParameter(const NamedParameter&) = delete;
using Parameter<O>::Initialised;
protected:
TemplateComparator compar;
public:
typedef Converter ConverterType;
// Constructor specifies template for comparison
template<class N, class T>
NamedParameter(N&& n, T&& t):compar(std::forward(t)) {Parameter<O>(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<class Converter>
using ONPar=NamedParameter<Converter,true>;
template<class Converter>
using RNPar=NamedParameter<Converter,false>;
// Class for parameter which can be in named pair or not
template <class Converter, bool O>
class NamedFreeParameter: public NamedParameter<Converter,O>
{
NamedFreeParameter() = delete;
NamedFreeParameter(const NamedFreeParameter&) = delete;
using NamedParameter<Converter,O>::compar;
using NamedParameter<Converter,O>::Initialised;
public:
typedef Converter ConverterType;
// Constructor specifies template for comparison
template<class N, class T>
NamedFreeParameter(N&& n, T&& t):NamedParameter<Converter,O>(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<class Converter>
using ONFPar=NamedFreeParameter<Converter,true>;
template<class Converter>
using RNFPar=NamedFreeParameter<Converter,false>;
// Class for parameter which can be in some positions in list
template <class Converter, bool O>
class PositionalParameter: public Converter, public Parameter<O>
{
using IType=ObjectList::IndexType;
PositionalParameter() = delete;
PositionalParameter(const PositionalParameter&) = delete;
std::set<IType> positions;
IType initpos;
using Parameter<O>::Initialised;
using Parameter<O>::SetState;
// Constructor specifies list of possible positions
PositionalParameter(IType i):initpos(0) {positions.insert(i);}
template<class... Args>
PositionalParameter(IType i, Args... args): PositionalParameter(args...) {positions.insert(i);}
public:
typedef Converter ConverterType;
template<class S>
PositionalParameter(S&& str, IType i):initpos(0) {Parameter<O>(std::forward(str)); positions.insert(i);}
template<class S, class... Args>
PositionalParameter(S&& str, IType i, Args... args): PositionalParameter(args...) {Parameter<O>(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<class Converter>
using OPosPar=PositionalParameter<Converter,true>;
template<class Converter>
using RPosPar=PositionalParameter<Converter,false>;
// Parsing positional parameters
// Generic template
template<class... Args>
class ParsePositionalParameters;
// Recursive template
template<class Arg, class... Args>
class ParsePositionalParameters<Arg, Args...>: ParsePositionalParameters<Args...>
{
typedef PositionalParameter<typename Arg::ConverterType, Arg::optional> PType;
static_assert(std::is_same<PType,Arg>::value,"ParsePositionalParameters template argument(s) must be PositionalParameter");
PType& par;
ParsePositionalParameters() = delete;
ParsePositionalParameters(const ParsePositionalParameters&) = delete;
ParsePositionalParameters(ParsePositionalParameters&&) = delete;
protected:
template<class Str>
void SetError(Str&& str) {ParsePositionalParameters<Args...>::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<Args...>::TryInit(i,ob);
}
bool Check()
{
if(!(par.Initialised() || par.isOptional()))
{
SetError("Parameter "+par.Name()+" required, but not set");
return false;
}
return ParsePositionalParameters<Args...>::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<Args...>::isUsed(i,used);
if(used) // Error, two parameters initialised from one position
{
SetError(par.Name());
return 1;
}
auto res=ParsePositionalParameters<Args...>::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...>(args...);}
std::string Error() const {return ParsePositionalParameters<Args...>::Error();}
bool Parse(const ObjectList* list)
{
std::string err;
for(ObjectList::IndexType i=0; i<list->Size(); 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; i<list->Size(); 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<class Str>
void SetError(Str&& str) {err=std::forward(str);}
std::string Error() const {return err;}
};