#pragma once #include #include #include #include #include #include #include #include #include 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() {} YYCC_DEL_CLS_COPY_MOVE(CmdSplitter); std::deque Convert(const std::string& u8cmd); protected: char mCmdChar; std::string* mBuffer; std::deque* mResult; StateType mState, mPreState; void ProcSpace(void); void ProcSingle(void); void ProcDouble(void); void ProcEscape(void); void ProcNormal(void); }; class HelpDocument { public: HelpDocument(); ~HelpDocument(); YYCC_DEL_CLS_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) {} YYCC_DEF_CLS_COPY_MOVE(StackItem); std::string m_Name; std::string m_Desc; }; std::deque m_Stack; struct ResultItem { ResultItem() : m_CmdDesc(), m_ArgDesc() {} ResultItem(const std::string& desc) : m_CmdDesc(desc), m_ArgDesc() {} YYCC_DEF_CLS_COPY_MOVE(ResultItem); std::string m_CmdDesc; std::vector m_ArgDesc; }; std::vector m_Results; }; enum class NodeType { Literal, Choice, Argument }; class ArgumentsMap; using ExecutionFct = std::function; class AbstractNode { public: AbstractNode(); virtual ~AbstractNode(); YYCC_DEL_CLS_COPY_MOVE(AbstractNode); AbstractNode* Then(AbstractNode*); AbstractNode* Executes(ExecutionFct, const char* = nullptr); AbstractNode* Comment(const char*); public: void Help(HelpDocument*); bool Consume(std::deque&, 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 m_Literals; std::vector m_Choices; std::vector m_Args; ExecutionFct m_Execution; std::string m_ExecutionDesc; std::string m_Comment; }; class CommandRoot : public AbstractNode { public: CommandRoot(); virtual ~CommandRoot(); YYCC_DEL_CLS_COPY_MOVE(CommandRoot); // Root use special consume and help functions. bool RootConsume(std::deque&); 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(); YYCC_DEL_CLS_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& vocabulary); virtual ~Choice(); YYCC_DEL_CLS_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 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(); YYCC_DEL_CLS_COPY_MOVE(AbstractArgument); template T GetData() { return reinterpret_cast(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; class IntArgument : public AbstractArgument { public: using vType = int32_t; IntArgument(const char* argname, IntLimit limit = nullptr) : AbstractArgument(argname), m_IntLimit(limit) {} virtual ~IntArgument() {} YYCC_DEL_CLS_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() {} YYCC_DEL_CLS_COPY_MOVE(StringArgument); protected: virtual bool BeginParse(const std::string&) override; virtual void EndParse() override; }; class EncodingArgument : public AbstractArgument { public: using vType = std::vector; EncodingArgument(const char* argname) : AbstractArgument(argname) {} virtual ~EncodingArgument() {} YYCC_DEL_CLS_COPY_MOVE(EncodingArgument); protected: virtual bool BeginParse(const std::string&) override; virtual void EndParse() override; }; class ArgumentsMap { public: ArgumentsMap() : m_Data() {} ~ArgumentsMap() {} YYCC_DEL_CLS_COPY_MOVE(ArgumentsMap); void Add(const std::string& k, AbstractNode* v); void Remove(const std::string& k); template _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(node)->GetData<_Ty*>()); case NodeType::Choice: return reinterpret_cast<_Ty*>(dynamic_cast(node)->GetIndex()); case NodeType::Literal: default: throw std::runtime_error("No such argument type."); } } protected: std::unordered_map m_Data; }; }