diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index b10c475..dfb03f0 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -1,4 +1,5 @@ #include "CmdHelper.hpp" +#include namespace Unvirt::CmdHelper { @@ -231,6 +232,43 @@ namespace Unvirt::CmdHelper { } } +#pragma endregion + +#pragma region Conflict Set + + ConflictSet::ConflictSet() : m_ConflictSet() {} + + ConflictSet::~ConflictSet() {} + + void ConflictSet::AddLiteral(const std::u8string_view& value) { + if (value.empty()) + throw std::invalid_argument("try to insert empty item to conflict set."); + auto result = m_ConflictSet.emplace(u8"literal:" + std::u8string(value)); + if (!result.second) + throw std::runtime_error("try to insert duplicated item in conflict set."); + } + + void ConflictSet::AddArgument(const std::u8string_view& value) { + if (value.empty()) + throw std::invalid_argument("try to insert empty item to conflict set."); + auto result = m_ConflictSet.emplace(u8"argument:" + std::u8string(value)); + if (!result.second) + throw std::runtime_error("try to insert duplicated item in conflict set."); + } + + bool ConflictSet::IsConflictWith(const ConflictSet& rhs) const { + // create a cache to store computed intersection + std::vector intersection; + // compute intersection + std::set_intersection( + this->m_ConflictSet.begin(), this->m_ConflictSet.end(), + rhs.m_ConflictSet.begin(), rhs.m_ConflictSet.begin(), + std::back_inserter(intersection) + ); + // check whether it is empty intersection + return !intersection.empty(); + } + #pragma endregion namespace Nodes { @@ -243,6 +281,8 @@ namespace Unvirt::CmdHelper { AbstractNode::~AbstractNode() {} AbstractNode& AbstractNode::Executes(FctExecution_t fct, const std::u8string_view& exec_desc) { + if (this->IsRootNode()) + throw std::logic_error("root node should not have execution."); if (m_Execution != nullptr) throw std::invalid_argument("you should not assign execution multiuple times."); if (fct == nullptr) @@ -253,52 +293,76 @@ namespace Unvirt::CmdHelper { } AbstractNode& AbstractNode::Comment(const std::u8string_view& comment) { + if (this->IsRootNode()) + throw std::logic_error("root node should not have comment."); m_Comment = comment; return *this; } void AbstractNode::Help(HelpDocument& doc) { - // Push self symbol to help document stack. - doc.Push(GetHelpSymbol(), m_Comment); + // If this node is not root node + if (!this->IsRootNode()) { + // Push self symbol to help document stack. + if (!this->IsRootNode()) { + doc.Push(GetHelpSymbol(), m_Comment); + } - // Check whether this node is terminal. - // If it is, terminate it once. - if (m_Execution != nullptr) { - doc.Terminate(m_ExecutionDesc); + // Check whether this node is terminal. + // If it is, terminate it once. + if (m_Execution != nullptr) { + doc.Terminate(m_ExecutionDesc); + } } - // Then process its children nodes. + // Then process its children nodes (both root node and common node). for (auto& node : m_Nodes) { node->Help(doc); } // Pop self from help document stack - doc.Pop(); + // if this node is not root node + if (!this->IsRootNode()) { + doc.Pop(); + } } bool AbstractNode::Consume(CmdSplitter::Result_t& al, ArgumentsMap& am) { - // if no data can consume, return + // If no command to be consumed, return directly. if (al.empty()) return false; - // backup current value - std::u8string cur_cmd = al.front(); - // consume self - if (!BeginConsume(cur_cmd, am)) { - // fail to consume self. not matched. return - return false; + // Process for self if we are not root node + std::u8string cur_cmd; + if (!this->IsRootNode()) { + // Backup the top item in command stack for personal consume. + cur_cmd = al.front(); + // Try to consume it for self. + if (!BeginConsume(cur_cmd, am)) { + // Fail to consume self. + // It means that this command is not matched with self. + // Return directly. + return false; + } + // Yes, command matched, we try consume it for child nodes. + // Pop the top item of command stack for child nodes processing. + al.pop_front(); } - // pop front for processing child nodes. - al.pop_front(); - + // Define free function if we are not the root node. + // Because we just pop the top item of command stack. #define CONSUME_DEFER \ +if (!this->IsRootNode()) { \ al.emplace_front(cur_cmd); \ - EndConsume(am); + EndConsume(am); \ +} if (al.empty()) { - // if no more data for parsing. - // this is must be a terminal. - // check whether we have execution. + // Root node do not have execution, return false directly + if (this->IsRootNode()) { + CONSUME_DEFER; + return false; + } + // If no more data for parsing, this is must be a terminal. + // Check whether we have execution if we are not root node. if (m_Execution == nullptr) { CONSUME_DEFER; return false; @@ -308,9 +372,9 @@ namespace Unvirt::CmdHelper { return true; } } else { - // still have data to be parsed. try to match them. - // iterate node list to find the real terminal - // however, we need iterate literal and choice first, the iterate argument. + // Command stack still item to be consumed. + // To consume them, we need iterate node list and find the real terminal. + // However, we need iterate literal and choice first, the iterate argument. for (auto& node : m_Nodes) { if (node->IsArgument()) continue; if (node->Consume(al, am)) { @@ -326,7 +390,9 @@ namespace Unvirt::CmdHelper { } } - // if still nothing to match, return false + // If all node can not consume it, + // it means that this command can not match this node tree. + // Return false directly. CONSUME_DEFER; return false; } @@ -337,18 +403,35 @@ namespace Unvirt::CmdHelper { #pragma endregion +#pragma region Root Node + + RootNode::RootNode() : AbstractNode() {} + RootNode::~RootNode() {} + + bool RootNode::IsRootNode() { return true; } + bool RootNode::IsArgument() { throw std::logic_error("this function is not allowed on root function."); } + const ConflictSet& RootNode::GetConflictSet() { throw std::logic_error("this function is not allowed on root function."); } + std::u8string RootNode::GetHelpSymbol() { throw std::logic_error("this function is not allowed on root function."); } + bool RootNode::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { throw std::logic_error("this function is not allowed on root function."); } + void RootNode::EndConsume(ArgumentsMap& am) { throw std::logic_error("this function is not allowed on root function."); } + +#pragma endregion + #pragma region Literal Literal::Literal(const std::u8string_view& words) : - AbstractNode(), m_Literal(words), m_ConflictSet { m_Literal } { + AbstractNode(), m_Literal(words), m_ConflictSet() { + // check argument if (words.empty()) throw std::invalid_argument("The word of literal node should not be empty."); + // set conflict set + m_ConflictSet.AddLiteral(m_Literal); } - Literal::~Literal() {} + bool Literal::IsRootNode() { return false; } bool Literal::IsArgument() { return false; } - const std::set& Literal::GetConflictSet() { return m_ConflictSet; } + const ConflictSet& Literal::GetConflictSet() { return m_ConflictSet; } std::u8string Literal::GetHelpSymbol() { return m_Literal; } bool Literal::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { return cur_cmd == m_Literal; } void Literal::EndConsume(ArgumentsMap& am) {} @@ -366,15 +449,20 @@ namespace Unvirt::CmdHelper { throw std::invalid_argument("Choice argument name should not be empty."); if (m_Vocabulary.size() < 2u) throw std::invalid_argument("Too less vocabulary for choice. At least 2 items."); + std::set vocabulary_set(m_Vocabulary.begin(), m_Vocabulary.end()); + if (vocabulary_set.size() != m_Vocabulary.size()) + throw std::invalid_argument("Vocabulary of choice should not have duplicated items."); // init conflict set - m_ConflictSet.insert(m_ChoiceName); - m_ConflictSet.insert(m_Vocabulary.begin(), m_Vocabulary.end()); + m_ConflictSet.AddArgument(m_ChoiceName); + for (const auto& word : m_Vocabulary) { + m_ConflictSet.AddLiteral(word); + } } - Choice::~Choice() {} + bool Choice::IsRootNode() { return false; } bool Choice::IsArgument() { return false; } - const std::set& Choice::GetConflictSet() { return m_ConflictSet; } + const ConflictSet& Choice::GetConflictSet() { return m_ConflictSet; } std::u8string Choice::GetHelpSymbol() { return YYCC::StringHelper::Printf(u8"[%s]", YYCC::StringHelper::Join(m_Vocabulary, u8" | ").c_str() @@ -383,7 +471,7 @@ namespace Unvirt::CmdHelper { bool Choice::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { for (size_t i = 0; i < m_Vocabulary.size(); ++i) { if (cur_cmd == m_Vocabulary[i]) { - am.Add(m_ChoiceName, cur_cmd); + am.Add(m_ChoiceName, i); return true; } } @@ -397,175 +485,89 @@ namespace Unvirt::CmdHelper { AbstractArgument::AbstractArgument(const std::u8string_view& argname) : AbstractNode(), - m_ArgName(argname == nullptr ? "" : argname), - m_Accepted(false), m_ParsedData(nullptr) { - if (argname == nullptr || m_ArgName.empty()) - throw std::invalid_argument("Invalid argument name."); + m_ArgumentName(argname), m_ConflictSet() { + // check argument + if (argname.empty()) + throw std::invalid_argument("Argument name should not be empty."); + // setup conflict set + m_ConflictSet.AddArgument(m_ArgumentName); } - AbstractArgument::~AbstractArgument() {} - NodeType AbstractArgument::GetNodeType() { - return NodeType::Argument; - } - - bool AbstractArgument::IsConflictWith(AbstractNode* node) { - switch (node->GetNodeType()) { - case NodeType::Literal: - return false; - case NodeType::Choice: - return m_ArgName == dynamic_cast(node)->m_ChoiceName; - case NodeType::Argument: - return m_ArgName == dynamic_cast(node)->m_ArgName; - default: - throw std::runtime_error("No such node type."); - } - } - - std::string AbstractArgument::GetHelpSymbol() { - std::string newargname = "<"; - newargname.append(m_ArgName); - newargname.append(">"); - return newargname; - } - - bool AbstractArgument::BeginAccept(const std::string& strl, ArgumentsMap* amap) { - m_Accepted = BeginParse(strl); - if (m_Accepted) amap->Add(m_ArgName, this); - return m_Accepted; - } - - void AbstractArgument::EndAccept(ArgumentsMap* amap) { - if (m_Accepted) { - amap->Remove(m_ArgName); - EndParse(); - m_Accepted = false; - } + bool AbstractArgument::IsRootNode() { return false; } + bool AbstractArgument::IsArgument() { return true; } + const ConflictSet& AbstractArgument::GetConflictSet() { return m_ConflictSet; } + std::u8string AbstractArgument::GetHelpSymbol() { + return YYCC::StringHelper::Printf(u8"<%s>", m_ArgumentName.c_str()); } + //bool AbstractArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { return false; } + void AbstractArgument::EndConsume(ArgumentsMap& am) { am.Remove(m_ArgumentName); } #pragma endregion -#pragma region Argument Detail Impl +#pragma region Arithmetic Argument - bool IntArgument::BeginParse(const std::string& val) { - char* pend = nullptr; - errno = 0; - int64_t v = std::strtoll(val.c_str(), &pend, 10); + // It's a template class + // We are forced to implement it in header file. - if (pend == val.c_str() || errno == ERANGE) return false; +#pragma endregion - // check limit - int32_t value = static_cast(v); - if (m_IntLimit != nullptr && !m_IntLimit(value)) { +#pragma region String Argument + + bool StringArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { + // check constraint + if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(cur_cmd)) return false; - } - - m_ParsedData = new IntArgument::vType(value); + // accept + am.Add(m_ArgumentName, cur_cmd); return true; } - void IntArgument::EndParse() { - delete reinterpret_cast(m_ParsedData); - m_ParsedData = nullptr; - } +#pragma endregion - bool StringArgument::BeginParse(const std::string& strl) { - // string always accept every text - m_ParsedData = new StringArgument::vType(strl); +#pragma region Encoding List Argument + + bool EncodingListArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { + // split given argument + std::vector encs = YYCC::StringHelper::Split(cur_cmd, u8","); + // add into map + am.Add(m_ArgumentName, encs); return true; } - void StringArgument::EndParse() { - delete reinterpret_cast(m_ParsedData); - m_ParsedData = nullptr; - } - - // Copy from Gamepiaynmo/BallanceModLoader - std::vector SplitString(const std::string& str, const std::string& de) { - size_t lpos, pos = 0; - std::vector res; - - lpos = str.find_first_not_of(de, pos); - while (lpos != std::string::npos) { - pos = str.find_first_of(de, lpos); - res.push_back(str.substr(lpos, pos - lpos)); - if (pos == std::string::npos) break; - - lpos = str.find_first_not_of(de, pos); - } - - if (pos != std::string::npos) - res.push_back(""); - - return res; - } - - bool EncodingArgument::BeginParse(const std::string& strl) { - // encoding always accept every text - m_ParsedData = new EncodingArgument::vType(SplitString(strl, ",")); - return true; - } - - void EncodingArgument::EndParse() { - delete reinterpret_cast(m_ParsedData); - m_ParsedData = nullptr; - } - #pragma endregion } -#pragma region Command Root +#pragma region Command Parser - CommandRoot::CommandRoot() : AbstractNode() {} + CommandParser::CommandParser() {} - CommandRoot::~CommandRoot() {} + CommandParser::~CommandParser() {} - bool CommandRoot::RootConsume(std::deque& arglist) { - // if no data can consume, return - if (arglist.empty()) return false; + bool CommandParser::Parse(const CmdSplitter::Result_t& cmds) { + // Create a copy of given command + CmdSplitter::Result_t al(cmds); + // Create argument map + ArgumentsMap am; - // create a argument map - ArgumentsMap amap; - - // and we only just need iterate all children - for (auto& pnode : m_Literals) { - if (pnode->Consume(arglist, &amap)) { - return true; - } - } - for (auto& pnode : m_Choices) { - if (pnode->Consume(arglist, &amap)) { - return true; - } - } - for (auto& pnode : m_Args) { - if (pnode->Consume(arglist, &amap)) { - return true; - } - } - - // no matched - return false; + // Call root node Consume function and return its result. + return m_RootNode.Consume(al, am); } - HelpDocument* CommandRoot::RootHelp() { - HelpDocument* doc = new HelpDocument(); - - // we only just need iterate all children - for (auto& pnode : m_Literals) { - pnode->Help(doc); - } - for (auto& pnode : m_Choices) { - pnode->Help(doc); - } - for (auto& pnode : m_Args) { - pnode->Help(doc); - } - + HelpDocument CommandParser::Help() { + // Create help docuemnt + HelpDocument doc; + // use node tree to fill help document. + m_RootNode.Help(doc); + // return result. return doc; } + Nodes::RootNode& CommandParser::GetRoot() { + return m_RootNode; + } + #pragma endregion } diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index 1d84a97..0da8d01 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -129,7 +128,7 @@ namespace Unvirt::CmdHelper { throw std::runtime_error("try to add an existing key."); } template, int> = 0> - const _Ty& Get() const { + const _Ty& Get(const std::u8string_view& key) const { // check argument if (key.empty()) throw std::invalid_argument("argument key should not be empty"); @@ -185,10 +184,55 @@ namespace Unvirt::CmdHelper { std::vector m_Results; }; + class ConflictSet { + public: + ConflictSet(); + ~ConflictSet(); + YYCC_DEF_CLS_COPY_MOVE(ConflictSet); + + public: + /** + * @brief Add literal item into conflict set. + * @param[in] value Literal item. + * @remarks + * \li Literal item is the string input in command line. + * \li The word in Literal, and the vocabulary in Choice should be put by this function. + * \li Added item will add \c literal: prefix to make it in literal scope, + * so that it will not be compared with argument name items. + * Because we allow 2 literal item and argument name item have same name. + */ + void AddLiteral(const std::u8string_view& value); + /** + * @brief Add argument name item into conflict + * @param[in] value Argument name item. + * @remarks + * \li Argument name item is the key name put in ArgumentsMap. + * \li The argument name in Choice and Argument should be put by this function. + * \li Added item will add \c argument: prefix to make it in argument name scope, + * so that it will not be compared with literal items. + * Because we allow 2 literal item and argument name item have same name. + */ + void AddArgument(const std::u8string_view& value); + /** + * @brief Check whether this set is conflict with another. + * @param[in] rhs The set to be compared. + * @return True if they have conflict. + * @remarks + * This function simply compute the intersection of two set. + * If the result is not empty, it means that there is a conflict. + */ + bool IsConflictWith(const ConflictSet& rhs) const; + + protected: + std::set m_ConflictSet; + }; + + // Forward declaration of CommandParser for Nodes::RootNode + class CommandParser; + namespace Nodes { class AbstractNode { - friend class CommandRoot; public: using FctExecution_t = void(*)(const ArgumentsMap&); @@ -220,9 +264,17 @@ namespace Unvirt::CmdHelper { bool Consume(CmdSplitter::Result_t& al, ArgumentsMap& am); protected: + /** + * @brief Check whether current node is root node. + * @return True if it is, otherwise false. + * @remarks + * This function usually return false. + * It only return true when using special node called root node with CommandParser. + */ + virtual bool IsRootNode() = 0; /** * @brief Check whether current node is argument. - * @return True if it is. + * @return True if it is, otherwise false. * @remakrs * \li Sub-class must implement this function. * \li This function is used internally because when consuming nodes, @@ -248,7 +300,7 @@ namespace Unvirt::CmdHelper { * \li Choice: Put its vocabulary and associated argument name in set. * \li Argument: Put its argument name in set. */ - virtual const std::set& GetConflictSet() = 0; + virtual const ConflictSet& GetConflictSet() = 0; /** * @brief Get the string presented in syntax part in help messages. * @return The string presented in syntax part in help messages. @@ -292,24 +344,18 @@ namespace Unvirt::CmdHelper { * @param[in] node The node instance to be added. * @return Return self for chain calling. */ - template, int> = 0> + template && !std::is_same_v, int> = 0> AbstractNode& Then(_Ty&& node) { // create node first - auto new_node = std::make_shared<_Ty>(std::forward<_Types>(node)); + auto new_node = std::make_shared<_Ty>(std::forward<_Ty>(node)); + // check root node. + if (new_node->IsRootNode()) + throw std::invalid_argument("root node should not be inserted as child node."); // check conflict - std::vector intersection; const auto& new_node_set = new_node->GetConflictSet(); - for (auto& node : mNodes) { + for (auto& node : m_Nodes) { const auto& node_set = node->GetConflictSet(); - // compute intersection - intersection.clear(); - std::set_intersection( - new_node_set.begin(), new_node_set.end(), - node_set.begin(), node_set.begin(), - std::back_inserter(intersection) - ); - // check whether it is empty intersection - if (!intersection.empty()) + if (new_node_set.IsConflictWith(node_set)) throw std::invalid_argument("try to add a conflict node. please check your code."); } // add into list @@ -333,6 +379,22 @@ namespace Unvirt::CmdHelper { }; + class RootNode : public AbstractNode { + friend class CommandParser; + public: + RootNode(); + virtual ~RootNode(); + YYCC_DEF_CLS_COPY_MOVE(RootNode); + + protected: + virtual bool IsRootNode() override; + virtual bool IsArgument() override; + virtual const ConflictSet& GetConflictSet() override; + virtual std::u8string GetHelpSymbol() override; + virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; + virtual void EndConsume(ArgumentsMap& am) override; + }; + class Literal : public AbstractNode { public: Literal(const std::u8string_view& words); @@ -340,34 +402,36 @@ namespace Unvirt::CmdHelper { YYCC_DEF_CLS_COPY_MOVE(Literal); protected: + virtual bool IsRootNode() override; virtual bool IsArgument() override; - virtual const std::set& GetConflictSet() override; + virtual const ConflictSet& GetConflictSet() override; virtual std::u8string GetHelpSymbol() override; virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; virtual void EndConsume(ArgumentsMap& am) override; std::u8string m_Literal; - std::set m_ConflictSet; + ConflictSet m_ConflictSet; }; class Choice : public AbstractNode { public: - using ArgValue_t = size_t; + using ArgValue_t = AMItems::ArithmeticItem; public: Choice(const std::u8string_view& argname, const std::initializer_list& vocabulary); virtual ~Choice(); YYCC_DEF_CLS_COPY_MOVE(Choice); - + protected: + virtual bool IsRootNode() override; virtual bool IsArgument() override; - virtual const std::set& GetConflictSet() override; + virtual const ConflictSet& GetConflictSet() override; virtual std::u8string GetHelpSymbol() override; virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; virtual void EndConsume(ArgumentsMap& am) override; std::u8string m_ChoiceName; std::vector m_Vocabulary; - std::set m_ConflictSet; + ConflictSet m_ConflictSet; }; class AbstractArgument : public AbstractNode { @@ -375,65 +439,77 @@ namespace Unvirt::CmdHelper { AbstractArgument(const std::u8string_view& argname); virtual ~AbstractArgument(); YYCC_DEF_CLS_COPY_MOVE(AbstractArgument); - + // AbstractArgument do not implement BeginConsume(). // Because it involve the detail of data parsing. // However, other parts are shared by all argument type. protected: + virtual bool IsRootNode() override; virtual bool IsArgument() override; - virtual const std::set& GetConflictSet() override; + virtual const ConflictSet& GetConflictSet() override; virtual std::u8string GetHelpSymbol() override; - // virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; + //virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; virtual void EndConsume(ArgumentsMap& am) override; - std::u8string m_ArgName; - std::set m_ConflictSet; + std::u8string m_ArgumentName; + ConflictSet m_ConflictSet; }; - /** - * @brief Return true mean this value can accept. - */ - using IntLimit = std::function; - class IntArgument : public AbstractArgument { + template, int> = 0> + class ArithmeticArgument : 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); + using ArgValue_t = AMItems::ArithmeticItem<_Ty>; + using Constraint_t = YYCC::Constraints::Constraint<_Ty>; + public: + ArithmeticArgument(const std::u8string_view& argname, Constraint_t constraint = Constraint_t {}) : + AbstractArgument(argname), m_Constraint(constraint) {} + virtual ~ArithmeticArgument() {} + YYCC_DEF_CLS_COPY_MOVE(ArithmeticArgument); protected: - virtual bool BeginParse(const std::string&) override; - virtual void EndParse() override; - - IntLimit m_IntLimit; + virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override { + // try parse + _Ty result; + if (!YYCC::ParserHelper::TryParse<_Ty>(cur_cmd, result)) + return false; + // check constraint + if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(result)) + return false; + // okey + am.Add(m_ArgumentName, result); + return true; + } + protected: + Constraint_t m_Constraint; }; class StringArgument : public AbstractArgument { public: - using vType = std::string; - StringArgument(const char* argname) : - AbstractArgument(argname) {} + using ArgValue_t = AMItems::StringItem; + using Constraint_t = YYCC::Constraints::Constraint; + public: + StringArgument(const std::u8string_view& argname, Constraint_t constraint = Constraint_t {}) : + AbstractArgument(argname), m_Constraint(constraint) {} virtual ~StringArgument() {} - YYCC_DEL_CLS_COPY_MOVE(StringArgument); + YYCC_DEF_CLS_COPY_MOVE(StringArgument); protected: - virtual bool BeginParse(const std::string&) override; - virtual void EndParse() override; + virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; + Constraint_t m_Constraint; }; - class EncodingArgument : public AbstractArgument { + class EncodingListArgument : public AbstractArgument { public: - using vType = std::vector; - EncodingArgument(const char* argname) : + using ArgValue_t = AMItems::StringArrayItem; + public: + EncodingListArgument(const std::u8string_view& argname) : AbstractArgument(argname) {} - virtual ~EncodingArgument() {} - YYCC_DEL_CLS_COPY_MOVE(EncodingArgument); + virtual ~EncodingListArgument() {} + YYCC_DEF_CLS_COPY_MOVE(EncodingListArgument); protected: - virtual bool BeginParse(const std::string&) override; - virtual void EndParse() override; + virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; }; } @@ -444,24 +520,12 @@ namespace Unvirt::CmdHelper { ~CommandParser(); public: - bool Parse(std::deque& cmds); + bool Parse(const CmdSplitter::Result_t& cmds); HelpDocument Help(); + Nodes::RootNode& GetRoot(); private: - class RootNode : Nodes::AbstractNode { - public: - RootNode(); - virtual ~RootNode(); - YYCC_DEF_CLS_COPY_MOVE(RootNode); - - protected: - virtual bool IsArgument() override; - virtual const std::set& GetConflictSet() override; - virtual std::u8string GetHelpSymbol() override; - virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; - virtual void EndConsume(ArgumentsMap& am) override; - }; - RootNode m_RootNode; + Nodes::RootNode m_RootNode; }; //class CommandRoot : public AbstractNode {