277 lines
7.5 KiB
C++
277 lines
7.5 KiB
C++
#pragma once
|
|
|
|
#include <VTAll.hpp>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <functional>
|
|
#include <deque>
|
|
#include <unordered_map>
|
|
#include <stdexcept>
|
|
#include <cinttypes>
|
|
#include <initializer_list>
|
|
|
|
namespace Unvirt::CmdHelper {
|
|
|
|
class CmdSplitter {
|
|
public:
|
|
enum class StateType : int {
|
|
SPACE,
|
|
SINGLE,
|
|
DOUBLE,
|
|
ESCAPE,
|
|
NORMAL
|
|
};
|
|
public:
|
|
CmdSplitter() :
|
|
mCmdChar(0), mBuffer(nullptr), mResult(nullptr),
|
|
mState(StateType::NORMAL), mPreState(StateType::NORMAL) {}
|
|
~CmdSplitter() {}
|
|
LIBCMO_DISABLE_COPY_MOVE(CmdSplitter);
|
|
|
|
std::deque<std::string> Convert(const std::string& u8cmd);
|
|
protected:
|
|
char mCmdChar;
|
|
std::string* mBuffer;
|
|
std::deque<std::string>* mResult;
|
|
|
|
StateType mState, mPreState;
|
|
|
|
void ProcSpace(void);
|
|
void ProcSingle(void);
|
|
void ProcDouble(void);
|
|
void ProcEscape(void);
|
|
void ProcNormal(void);
|
|
};
|
|
|
|
class HelpDocument {
|
|
public:
|
|
HelpDocument();
|
|
~HelpDocument();
|
|
LIBCMO_DISABLE_COPY_MOVE(HelpDocument);
|
|
|
|
void Push(const std::string& arg_name, const std::string& arg_desc);
|
|
void Pop();
|
|
void Terminate(std::string& command_desc);
|
|
void Print();
|
|
|
|
protected:
|
|
struct StackItem {
|
|
StackItem() : m_Name(), m_Desc() {}
|
|
StackItem(const std::string& name, const std::string& desc) : m_Name(name), m_Desc(desc) {}
|
|
LIBCMO_DEFAULT_COPY_MOVE(StackItem);
|
|
std::string m_Name;
|
|
std::string m_Desc;
|
|
};
|
|
std::deque<StackItem> m_Stack;
|
|
struct ResultItem {
|
|
ResultItem() : m_CmdDesc(), m_ArgDesc() {}
|
|
ResultItem(const std::string& desc) : m_CmdDesc(desc), m_ArgDesc() {}
|
|
LIBCMO_DEFAULT_COPY_MOVE(ResultItem);
|
|
std::string m_CmdDesc;
|
|
std::vector<StackItem> m_ArgDesc;
|
|
};
|
|
std::vector<ResultItem> m_Results;
|
|
};
|
|
|
|
enum class NodeType {
|
|
Literal, Choice, Argument
|
|
};
|
|
class ArgumentsMap;
|
|
using ExecutionFct = std::function<void(const ArgumentsMap*)>;
|
|
class AbstractNode {
|
|
public:
|
|
AbstractNode();
|
|
virtual ~AbstractNode();
|
|
LIBCMO_DISABLE_COPY_MOVE(AbstractNode);
|
|
|
|
AbstractNode* Then(AbstractNode*);
|
|
AbstractNode* Executes(ExecutionFct, const char* = nullptr);
|
|
AbstractNode* Comment(const char*);
|
|
|
|
public:
|
|
void Help(HelpDocument*);
|
|
bool Consume(std::deque<std::string>&, ArgumentsMap*);
|
|
virtual NodeType GetNodeType() = 0;
|
|
virtual bool IsConflictWith(AbstractNode*) = 0;
|
|
protected:
|
|
virtual std::string GetHelpSymbol() = 0;
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) = 0;
|
|
virtual void EndAccept(ArgumentsMap*) = 0;
|
|
|
|
std::vector<AbstractNode*> m_Literals;
|
|
std::vector<AbstractNode*> m_Choices;
|
|
std::vector<AbstractNode*> m_Args;
|
|
ExecutionFct m_Execution;
|
|
std::string m_ExecutionDesc;
|
|
std::string m_Comment;
|
|
};
|
|
|
|
class CommandRoot : public AbstractNode {
|
|
public:
|
|
CommandRoot();
|
|
virtual ~CommandRoot();
|
|
LIBCMO_DISABLE_COPY_MOVE(CommandRoot);
|
|
|
|
// Root use special consume and help functions.
|
|
bool RootConsume(std::deque<std::string>&);
|
|
HelpDocument* RootHelp();
|
|
|
|
public:
|
|
virtual NodeType GetNodeType() override { throw std::logic_error("Root can not be called."); }
|
|
virtual bool IsConflictWith(AbstractNode*) override { throw std::logic_error("Root can not be called."); }
|
|
protected:
|
|
virtual std::string GetHelpSymbol() override { throw std::logic_error("Root can not be called."); }
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override { throw std::logic_error("Root can not be called."); }
|
|
virtual void EndAccept(ArgumentsMap*) override { throw std::logic_error("Root can not be called."); }
|
|
};
|
|
|
|
class Literal : public AbstractNode {
|
|
friend class Choice;
|
|
friend class AbstractArgument;
|
|
public:
|
|
Literal(const char* words);
|
|
virtual ~Literal();
|
|
LIBCMO_DISABLE_COPY_MOVE(Literal);
|
|
|
|
public:
|
|
virtual NodeType GetNodeType() override;
|
|
virtual bool IsConflictWith(AbstractNode* node) override;
|
|
protected:
|
|
virtual std::string GetHelpSymbol() override;
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override;
|
|
virtual void EndAccept(ArgumentsMap*) override;
|
|
|
|
std::string m_Literal;
|
|
};
|
|
|
|
class Choice : public AbstractNode {
|
|
friend class Literal;
|
|
friend class AbstractArgument;
|
|
public:
|
|
using vType = size_t;
|
|
Choice(const char* argname, const std::initializer_list<std::string>& vocabulary);
|
|
virtual ~Choice();
|
|
LIBCMO_DISABLE_COPY_MOVE(Choice);
|
|
|
|
vType* GetIndex();
|
|
|
|
public:
|
|
virtual NodeType GetNodeType() override;
|
|
virtual bool IsConflictWith(AbstractNode* node) override;
|
|
protected:
|
|
virtual std::string GetHelpSymbol() override;
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override;
|
|
virtual void EndAccept(ArgumentsMap*) override;
|
|
|
|
std::string m_ChoiceName;
|
|
std::vector<std::string> m_Vocabulary;
|
|
bool m_Accepted;
|
|
size_t m_GottenIndex;
|
|
};
|
|
|
|
class AbstractArgument : public AbstractNode {
|
|
friend class Literal;
|
|
friend class Choice;
|
|
public:
|
|
AbstractArgument(const char* argname);
|
|
virtual ~AbstractArgument();
|
|
LIBCMO_DISABLE_COPY_MOVE(AbstractArgument);
|
|
|
|
template<class T>
|
|
T GetData() {
|
|
return reinterpret_cast<T>(m_ParsedData);
|
|
}
|
|
|
|
public:
|
|
virtual NodeType GetNodeType() override;
|
|
virtual bool IsConflictWith(AbstractNode* node) override;
|
|
protected:
|
|
virtual std::string GetHelpSymbol() override;
|
|
virtual bool BeginAccept(const std::string&, ArgumentsMap*) override;
|
|
virtual void EndAccept(ArgumentsMap*) override;
|
|
|
|
virtual bool BeginParse(const std::string&) = 0;
|
|
virtual void EndParse() = 0;
|
|
|
|
std::string m_ArgName;
|
|
bool m_Accepted;
|
|
void* m_ParsedData;
|
|
};
|
|
|
|
/**
|
|
* @brief Return true mean this value can accept.
|
|
*/
|
|
using IntLimit = std::function<bool(int32_t)>;
|
|
class IntArgument : public AbstractArgument {
|
|
public:
|
|
using vType = int32_t;
|
|
IntArgument(const char* argname, IntLimit limit = nullptr) :
|
|
AbstractArgument(argname), m_IntLimit(limit) {}
|
|
virtual ~IntArgument() {}
|
|
LIBCMO_DISABLE_COPY_MOVE(IntArgument);
|
|
|
|
protected:
|
|
virtual bool BeginParse(const std::string&) override;
|
|
virtual void EndParse() override;
|
|
|
|
IntLimit m_IntLimit;
|
|
};
|
|
|
|
class StringArgument : public AbstractArgument {
|
|
public:
|
|
using vType = std::string;
|
|
StringArgument(const char* argname) : AbstractArgument(argname) {}
|
|
virtual ~StringArgument() {}
|
|
LIBCMO_DISABLE_COPY_MOVE(StringArgument);
|
|
|
|
protected:
|
|
virtual bool BeginParse(const std::string&) override;
|
|
virtual void EndParse() override;
|
|
};
|
|
|
|
class EncodingArgument : public AbstractArgument {
|
|
public:
|
|
using vType = std::vector<std::string>;
|
|
EncodingArgument(const char* argname) : AbstractArgument(argname) {}
|
|
virtual ~EncodingArgument() {}
|
|
LIBCMO_DISABLE_COPY_MOVE(EncodingArgument);
|
|
|
|
protected:
|
|
virtual bool BeginParse(const std::string&) override;
|
|
virtual void EndParse() override;
|
|
};
|
|
|
|
class ArgumentsMap {
|
|
public:
|
|
ArgumentsMap() : m_Data() {}
|
|
~ArgumentsMap() {}
|
|
LIBCMO_DISABLE_COPY_MOVE(ArgumentsMap);
|
|
|
|
void Add(const std::string& k, AbstractNode* v);
|
|
void Remove(const std::string& k);
|
|
|
|
template<class _Ty>
|
|
_Ty* Get(const char* k) const {
|
|
if (k == nullptr) throw std::invalid_argument("Null argument name.");
|
|
std::string conv(k);
|
|
|
|
auto finder = m_Data.find(conv);
|
|
if (finder == m_Data.end()) throw std::invalid_argument("No such argument name.");
|
|
AbstractNode* node = finder->second;
|
|
switch (node->GetNodeType()) {
|
|
case NodeType::Argument:
|
|
return reinterpret_cast<_Ty*>(dynamic_cast<AbstractArgument*>(node)->GetData<_Ty*>());
|
|
case NodeType::Choice:
|
|
return reinterpret_cast<_Ty*>(dynamic_cast<Choice*>(node)->GetIndex());
|
|
case NodeType::Literal:
|
|
default:
|
|
throw std::runtime_error("No such argument type.");
|
|
}
|
|
}
|
|
|
|
protected:
|
|
std::unordered_map<std::string, AbstractNode*> m_Data;
|
|
};
|
|
|
|
}
|