From 168d76939b0bc69fffa27a57de03164745eb4ff2 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 27 Aug 2023 14:21:44 +0800 Subject: [PATCH] fix command helper --- Unvirt/CmdHelper.cpp | 478 ++++++++++++++++++++++++++++++++------- Unvirt/CmdHelper.hpp | 274 +++++++++------------- Unvirt/UnvirtContext.cpp | 102 ++++----- Unvirt/UnvirtContext.hpp | 20 +- 4 files changed, 554 insertions(+), 320 deletions(-) diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index a473f1e..b79ffae 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -150,46 +150,145 @@ namespace Unvirt::CmdHelper { } #pragma endregion +#pragma region Help Document + + HelpDocument::HelpDocument() : m_Stack(), m_Results() {} + + HelpDocument::~HelpDocument() {} + + void HelpDocument::Push(const std::string& arg_name, const std::string& arg_desc) { + m_Stack.emplace_back(StackItem { arg_name, arg_desc }); + } + + void HelpDocument::Pop() { + m_Stack.pop_back(); + } + + void HelpDocument::Terminate(std::string& command_desc) { + // create new result and copy stack + ResultItem result(command_desc); + result.m_ArgDesc.insert(result.m_ArgDesc.end(), m_Stack.begin(), m_Stack.end()); + // add into result + m_Results.emplace_back(std::move(result)); + } + + void HelpDocument::Print() { + for (auto& item : m_Results) { + for (auto& cmd : item.m_ArgDesc) { + fputs(cmd.m_Name.c_str(), stdout); + fputc(' ', stdout); + } + fputc('\n', stdout); + + if (!item.m_CmdDesc.empty()) { + fputs(item.m_CmdDesc.c_str(), stdout); + fputc('\n', stdout); + } + + for (auto& cmd : item.m_ArgDesc) { + if (!cmd.m_Desc.empty()) { + fprintf(stdout, "\t%s: %s\n", cmd.m_Name.c_str(), cmd.m_Desc.c_str()); + } + } + + fputc('\n', stdout); + } + } + +#pragma endregion + #pragma region Abstract Node + AbstractNode::AbstractNode() : + m_Execution(nullptr), m_Comment(), + m_Literals(), m_Choices(), m_Args() {} + + AbstractNode::~AbstractNode() { + for (auto& ptr : m_Literals) { + delete ptr; + } + for (auto& ptr : m_Choices) { + delete ptr; + } + for (auto& ptr : m_Args) { + delete ptr; + } + } + AbstractNode* AbstractNode::Then(AbstractNode* node) { - // check literal duplication - for (auto& pnode : m_Nodes) { + // check conflict + for (auto& pnode : m_Literals) { if (pnode->IsConflictWith(node)) throw std::invalid_argument("conflict node."); } + for (auto& pnode : m_Choices) { + if (pnode->IsConflictWith(node)) + throw std::invalid_argument("conflict node."); + } + for (auto& pnode : m_Args) { + if (pnode->IsConflictWith(node)) + throw std::invalid_argument("conflict node."); + } + // add into list - m_Nodes.emplace_back(node); + switch (node->GetNodeType()) { + case NodeType::Argument: + m_Literals.emplace_back(node); + break; + case NodeType::Choice: + m_Choices.emplace_back(node); + break; + case NodeType::Literal: + m_Args.emplace_back(node); + break; + default: + throw std::runtime_error("No such node type."); + } + return this; } AbstractNode* AbstractNode::Executes(ExecutionFct fct, const char* cmt) { - if (m_Execution != nullptr) - throw std::invalid_argument("duplicated executions."); + if (m_Execution != nullptr) throw std::invalid_argument("duplicated executions."); + if (fct == nullptr) throw std::invalid_argument("no function."); m_Execution = fct; + m_ExecutionDesc = cmt == nullptr ? "" : cmt; + return this; + } + + AbstractNode* AbstractNode::Comment(const char* cmt) { + if (cmt == nullptr) + throw std::invalid_argument("no comment."); m_Comment = cmt; return this; } - void AbstractNode::Help(HelpDocument& doc) { + void AbstractNode::Help(HelpDocument* doc) { // add self - BeginHelp(doc); + std::string symbol(GetHelpSymbol()); + doc->Push(symbol, m_Comment); // check terminal if (m_Execution != nullptr) { - doc.Terminate(m_Comment); + doc->Terminate(m_ExecutionDesc); } // iterate children - for (auto& pnode : m_Nodes) { + for (auto& pnode : m_Literals) { + pnode->Help(doc); + } + for (auto& pnode : m_Choices) { + pnode->Help(doc); + } + for (auto& pnode : m_Args) { pnode->Help(doc); } // pop self - EndHelp(doc); + doc->Pop(); } - bool AbstractNode::Consume(std::deque& arglist, ArgumentsMap& argmap) { + bool AbstractNode::Consume(std::deque& arglist, ArgumentsMap* argmap) { // if no data can consume, return if (arglist.empty()) return false; @@ -222,7 +321,19 @@ namespace Unvirt::CmdHelper { } else { // have following command, try match them // iterate literal and argument to check terminal - for (auto& pnode : m_Nodes) { + for (auto& pnode : m_Literals) { + if (pnode->Consume(arglist, argmap)) { + CONSUME_DEFER; + return true; + } + } + for (auto& pnode : m_Choices) { + if (pnode->Consume(arglist, argmap)) { + CONSUME_DEFER; + return true; + } + } + for (auto& pnode : m_Args) { if (pnode->Consume(arglist, argmap)) { CONSUME_DEFER; return true; @@ -240,7 +351,240 @@ namespace Unvirt::CmdHelper { #pragma endregion -#pragma region Argument Impl +#pragma region Command Root + + CommandRoot::CommandRoot() : AbstractNode() {} + + CommandRoot::~CommandRoot() {} + + bool CommandRoot::RootConsume(std::deque& arglist) { + // if no data can consume, return + if (arglist.empty()) return false; + + // 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; + } + + 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); + } + + return doc; + } + +#pragma endregion + +#pragma region Literal + + Literal::Literal(const char* words) : + AbstractNode(), + m_Literal(words == nullptr ? "" : words) { + if (words == nullptr || m_Literal.empty()) + throw std::invalid_argument("Invalid literal."); + } + + Literal::~Literal() {} + + NodeType Literal::GetNodeType() { + return NodeType::Literal; + } + + bool Literal::IsConflictWith(AbstractNode* node) { + switch (node->GetNodeType()) { + case NodeType::Literal: + return dynamic_cast(node)->m_Literal == m_Literal; + case NodeType::Choice: + for (const auto& item : dynamic_cast(node)->m_Vocabulary) { + if (item == m_Literal) return true; + } + return false; + case NodeType::Argument: + return false; + default: + throw std::runtime_error("No such node type."); + } + } + + std::string Literal::GetHelpSymbol() { + return m_Literal; + } + + bool Literal::BeginAccept(const std::string& strl, ArgumentsMap*) { + return strl == m_Literal; + } + + void Literal::EndAccept(ArgumentsMap*) {} + +#pragma endregion + +#pragma region Choice + + Choice::Choice(const char* argname, const std::initializer_list& vocabulary) : + AbstractNode(), + m_GottenIndex(0u), m_Accepted(false), + m_ChoiceName(argname == nullptr ? "" : argname), m_Vocabulary(vocabulary) { + if (argname == nullptr || m_ChoiceName.empty()) + throw std::invalid_argument("Invalid choice name."); + if (m_Vocabulary.size() < 2) + throw std::invalid_argument("Too less vocabulary. At least 2 items."); + } + + Choice::~Choice() {} + + size_t* Choice::GetIndex() { + return &m_GottenIndex; + } + + NodeType Choice::GetNodeType() { + return NodeType::Choice; + } + + bool Choice::IsConflictWith(AbstractNode* node) { + switch (node->GetNodeType()) { + case NodeType::Literal: + { + Literal* pliteral = dynamic_cast(node); + for (const auto& word : m_Vocabulary) { + if (word == pliteral->m_Literal) + return true; + } + return false; + } + case NodeType::Choice: + { + Choice* pchoice = dynamic_cast(node); + if (pchoice->m_ChoiceName == m_ChoiceName) + return true; + + for (const auto& thisword : m_Vocabulary) { + for (const auto& thatword : pchoice->m_Vocabulary) { + if (thisword == thatword) + return true; + } + } + return false; + } + case NodeType::Argument: + return m_ChoiceName == dynamic_cast(node)->m_ArgName; + default: + throw std::runtime_error("No such node type."); + } + } + + std::string Choice::GetHelpSymbol() { + std::string switches; + for (const auto& item : m_Vocabulary) { + if (!switches.empty()) switches += " | "; + switches += item; + } + return "[" + switches + "]"; + } + + bool Choice::BeginAccept(const std::string& strl, ArgumentsMap* amap) { + for (size_t i = 0; i < m_Vocabulary.size(); ++i) { + if (strl == m_Vocabulary[i]) { + m_Accepted = true; + m_GottenIndex = i; + amap->Add(m_ChoiceName, this); + return true; + } + } + + return false; + } + + void Choice::EndAccept(ArgumentsMap* amap) { + if (m_Accepted) { + m_Accepted = false; + amap->Remove(m_ChoiceName); + } + } + +#pragma endregion + +#pragma region Abstract Argument + + AbstractArgument::AbstractArgument(const char* 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."); + } + + 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; + } + } + +#pragma endregion + +#pragma region Argument Detail Impl bool IntArgument::BeginParse(const std::string& val) { char* pend = nullptr; @@ -255,97 +599,69 @@ namespace Unvirt::CmdHelper { return false; } - m_ParsedData = new int32_t(value); + m_ParsedData = new IntArgument::vType(value); return true; } void IntArgument::EndParse() { - delete reinterpret_cast(m_ParsedData); + delete reinterpret_cast(m_ParsedData); m_ParsedData = nullptr; } bool StringArgument::BeginParse(const std::string& strl) { // string always accept every text - m_ParsedData = new std::string(strl); + m_ParsedData = new StringArgument::vType(strl); return true; } void StringArgument::EndParse() { - delete reinterpret_cast(m_ParsedData); + 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 Argument Map - bool CommandRoot::RootConsume(std::deque& arglist) { - // if no data can consume, return - if (arglist.empty()) return false; - - // create a argument map - ArgumentsMap amap; - - // and we only just need iterate all children - for (auto& pnode : m_Nodes) { - if (pnode->Consume(arglist, amap)) { - return true; - } - } - - // no matched - return false; + void ArgumentsMap::Add(const std::string& k, AbstractNode* v) { + m_Data.emplace(std::make_pair(k, v)); } - HelpDocument* CommandRoot::RootHelp() { - HelpDocument* doc = new HelpDocument(); - - // we only just need iterate all children - for (auto& pnode : m_Nodes) { - pnode->Help(*doc); - } - - return doc; + void ArgumentsMap::Remove(const std::string& k) { + m_Data.erase(k); } #pragma endregion -#pragma region Help Document - - void HelpDocument::Terminate(std::string& command_desc) { - // create new result and copy stack - ResultItem result(command_desc); - result.m_ArgDesc.insert(result.m_ArgDesc.end(), m_Stack.begin(), m_Stack.end()); - // add into result - m_Results.emplace_back(std::move(result)); - } - - void HelpDocument::Print() { - fputs("Command Help:\n", stdout); - - for (auto& item : m_Results) { - for (auto& cmd : item.m_ArgDesc) { - fputs(cmd.m_Name.c_str(), stdout); - fputc(' ', stdout); - } - fputc('\n', stdout); - - if (!item.m_CmdDesc.empty()) { - fputs(item.m_CmdDesc.c_str(), stdout); - fputc('\n', stdout); - } - - for (auto& cmd : item.m_ArgDesc) { - if (!cmd.m_Desc.empty()) { - fprintf(stdout, "\t%s: %s\n", cmd.m_Name.c_str(), cmd.m_Desc.c_str()); - } - } - - fputc('\n', stdout); - } - } - -#pragma endregion - - } diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index 77c2144..a7a0709 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -45,19 +45,12 @@ namespace Unvirt::CmdHelper { class HelpDocument { public: - HelpDocument() : m_Stack(), m_Results() {} - ~HelpDocument() {} + HelpDocument(); + ~HelpDocument(); LIBCMO_DISABLE_COPY_MOVE(HelpDocument); - void PushLiteral(const std::string& literal_name) { - m_Stack.emplace_back(StackItem(literal_name, "")); - } - void PushArgument(const std::string& arg_name, const std::string& arg_desc) { - m_Stack.emplace_back(StackItem(arg_name, arg_desc)); - } - void Pop() { - m_Stack.pop_back(); - } + void Push(const std::string& arg_name, const std::string& arg_desc); + void Pop(); void Terminate(std::string& command_desc); void Print(); @@ -80,210 +73,127 @@ namespace Unvirt::CmdHelper { std::vector m_Results; }; - class AbstractArgument; - class ArgumentsMap { - public: - ArgumentsMap() : m_Data() {} - ~ArgumentsMap() {} - LIBCMO_DISABLE_COPY_MOVE(ArgumentsMap); - - void Add(const std::string& k, AbstractArgument* v) { - m_Data.emplace(std::make_pair(k, v)); - } - void Remove(const std::string& k) { - m_Data.erase(k); - } - AbstractArgument* Get(const char* k) const { - auto finder = m_Data.find(k); - if (finder == m_Data.end()) throw std::invalid_argument("No such argument name."); - return finder->second; - } - - protected: - std::unordered_map m_Data; + enum class NodeType { + Literal, Choice, Argument }; - - class CommandRoot; - using ExecutionFct = std::function; + class ArgumentsMap; + using ExecutionFct = std::function; class AbstractNode { - friend class CommandRoot; public: - AbstractNode() : m_Execution(nullptr), m_Nodes(), m_Comment() {} - virtual ~AbstractNode() { - for (auto& ptr : m_Nodes) { - delete ptr; - } - } + AbstractNode(); + virtual ~AbstractNode(); LIBCMO_DISABLE_COPY_MOVE(AbstractNode); AbstractNode* Then(AbstractNode*); - AbstractNode* Executes(ExecutionFct, const char*); + AbstractNode* Executes(ExecutionFct, const char* = nullptr); + AbstractNode* Comment(const char*); - protected: - void Help(HelpDocument&); - bool Consume(std::deque&, ArgumentsMap&); - virtual bool IsLiteral() = 0; + public: + void Help(HelpDocument*); + bool Consume(std::deque&, ArgumentsMap*); + virtual NodeType GetNodeType() = 0; virtual bool IsConflictWith(AbstractNode*) = 0; - virtual void BeginHelp(HelpDocument&) = 0; - virtual void EndHelp(HelpDocument&) = 0; - virtual bool BeginAccept(const std::string&, ArgumentsMap&) = 0; - virtual void EndAccept(ArgumentsMap&) = 0; + protected: + virtual std::string GetHelpSymbol() = 0; + virtual bool BeginAccept(const std::string&, ArgumentsMap*) = 0; + virtual void EndAccept(ArgumentsMap*) = 0; - std::vector m_Nodes; + 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() : AbstractNode() {} - virtual ~CommandRoot() {} + CommandRoot(); + virtual ~CommandRoot(); LIBCMO_DISABLE_COPY_MOVE(CommandRoot); // Root use special consume and help functions. bool RootConsume(std::deque&); HelpDocument* RootHelp(); - protected: - virtual bool IsLiteral() override { throw std::logic_error("Root can not be called."); } + 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."); } - virtual void BeginHelp(HelpDocument&) override { throw std::logic_error("Root can not be called."); } - virtual void EndHelp(HelpDocument&) 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."); } + 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) : AbstractNode(), m_Literal(words) { - if (m_Literal.empty() || words == nullptr) throw std::invalid_argument("Invalid literal."); - } - virtual ~Literal() {} + Literal(const char* words); + virtual ~Literal(); LIBCMO_DISABLE_COPY_MOVE(Literal); + public: + virtual NodeType GetNodeType() override; + virtual bool IsConflictWith(AbstractNode* node) override; protected: - virtual bool IsLiteral() override { return true; } - virtual bool IsConflictWith(AbstractNode* node) override { - Literal* pliteral = dynamic_cast(node); - if (pliteral == nullptr) return false; - return pliteral->m_Literal == this->m_Literal; - } - virtual void BeginHelp(HelpDocument& doc) override { - doc.PushLiteral(m_Literal); - } - virtual void EndHelp(HelpDocument& doc) override { - doc.Pop(); - } - virtual bool BeginAccept(const std::string& strl, ArgumentsMap&) override { return strl == m_Literal; } - virtual void EndAccept(ArgumentsMap&) override {} + 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: - Choice(const char* argname, const std::initializer_list& vocabulary) : - AbstractNode(), m_ChoiceName(argname == nullptr ? "" : argname), m_Vocabulary(vocabulary) { - if (m_ChoiceName.empty() || argname == nullptr) throw std::invalid_argument("Invalid choice name."); - if (m_Vocabulary.size() < 2) throw std::invalid_argument("Too less vocabulary. At least 2 items."); - } - virtual ~Choice() {} - LIBCMO_DISABLE_COPY_MOVE(Choice) + using vType = int32_t; + Choice(const char* argname, const std::initializer_list& vocabulary); + virtual ~Choice(); + LIBCMO_DISABLE_COPY_MOVE(Choice); + size_t* GetIndex(); + + public: + virtual NodeType GetNodeType() override; + virtual bool IsConflictWith(AbstractNode* node) override; protected: - virtual bool IsLiteral() override { return true; } - virtual bool IsConflictWith(AbstractNode* node) override { - Literal* pliteral = dynamic_cast(node); - if (pliteral != nullptr) { - for (const auto& word : m_Vocabulary) { - if (word == pliteral->m_Literal) - return true; - } - return false; - } - - Choice* pchoice = dynamic_cast(node); - if (pchoice != nullptr) { - for (const auto& thisword : m_Vocabulary) { - for (const auto& thatword : pchoice->m_Vocabulary) { - if (thisword == thatword) - return true; - } - } - return false; - } - - return false; - } - virtual void BeginHelp(HelpDocument& doc) override { - std::string switches; - for (const auto& item : m_Vocabulary) { - if (!switches.empty()) switches += " | "; - switches += item; - } - doc.PushLiteral("[" + switches + "]"); - } - virtual void EndHelp(HelpDocument& doc) override { - doc.Pop(); - } - virtual bool BeginAccept(const std::string& strl, ArgumentsMap& amap) override { - for (const auto& item : m_Vocabulary) { - if (strl == item) { - - } - } - } - virtual void EndAccept(ArgumentsMap&) override {} + 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, const char* argdesc) : AbstractNode(), - m_ArgName(argname == nullptr ? "" : argname), m_ArgDesc(argdesc), m_Accepted(false), m_ParsedData(nullptr) { - if (m_ArgName.empty() || argname == nullptr) throw std::invalid_argument("Invalid argument name."); - } - virtual ~AbstractArgument() {} + AbstractArgument(const char* argname); + virtual ~AbstractArgument(); LIBCMO_DISABLE_COPY_MOVE(AbstractArgument); template - T* GetData() { return reinterpret_cast(m_ParsedData); } + 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; - virtual bool IsLiteral() override { return false; } - virtual bool IsConflictWith(AbstractNode* node) override { - return false; - } - virtual void BeginHelp(HelpDocument& doc) override { - std::string newargname = "<"; - newargname.append(m_ArgName); - newargname.append(">"); - doc.PushArgument(newargname, m_ArgDesc); - } - virtual void EndHelp(HelpDocument& doc) override { - doc.Pop(); - } - virtual bool BeginAccept(const std::string& strl, ArgumentsMap& amap) override { - m_Accepted = BeginParse(strl); - if (m_Accepted) amap.Add(m_ArgName, this); - return m_Accepted; - } - virtual void EndAccept(ArgumentsMap& amap) override { - if (m_Accepted) { - amap.Remove(m_ArgName); - EndParse(); - m_Accepted = false; - } - } - std::string m_ArgName; - std::string m_ArgDesc; bool m_Accepted; void* m_ParsedData; }; @@ -294,8 +204,9 @@ namespace Unvirt::CmdHelper { using IntLimit = std::function; class IntArgument : public AbstractArgument { public: - IntArgument(const char* argname, const char* argdesc = nullptr, IntLimit limit = nullptr) : - AbstractArgument(argname, argdesc), m_IntLimit(limit) {} + using vType = int32_t; + IntArgument(const char* argname, IntLimit limit = nullptr) : + AbstractArgument(argname), m_IntLimit(limit) {} virtual ~IntArgument() {} LIBCMO_DISABLE_COPY_MOVE(IntArgument); @@ -308,7 +219,8 @@ namespace Unvirt::CmdHelper { class StringArgument : public AbstractArgument { public: - StringArgument(const char* argname, const char* argdesc = nullptr) : AbstractArgument(argname, argdesc) {} + using vType = std::string; + StringArgument(const char* argname) : AbstractArgument(argname) {} virtual ~StringArgument() {} LIBCMO_DISABLE_COPY_MOVE(StringArgument); @@ -319,7 +231,8 @@ namespace Unvirt::CmdHelper { class EncodingArgument : public AbstractArgument { public: - EncodingArgument(const char* argname, const char* argdesc = nullptr) : AbstractArgument(argname, argdesc) {} + using vType = std::vector; + EncodingArgument(const char* argname) : AbstractArgument(argname) {} virtual ~EncodingArgument() {} LIBCMO_DISABLE_COPY_MOVE(EncodingArgument); @@ -328,5 +241,36 @@ namespace Unvirt::CmdHelper { 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 + _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 dynamic_cast(node)->GetData<_Ty*>(); + case NodeType::Choice: + return dynamic_cast(node)->GetIndex(); + case NodeType::Literal: + default: + throw std::runtime_error("No such argument type."); + } + } + + protected: + std::unordered_map m_Data; + }; } diff --git a/Unvirt/UnvirtContext.cpp b/Unvirt/UnvirtContext.cpp index 724545c..0ba2c91 100644 --- a/Unvirt/UnvirtContext.cpp +++ b/Unvirt/UnvirtContext.cpp @@ -6,50 +6,23 @@ namespace Unvirt::Context { #pragma region Encoding Arg - // 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 std::vector(SplitString(strl, ",")); - return true; - } - - void EncodingArgument::EndParse() { - delete reinterpret_cast*>(m_ParsedData); - m_ParsedData = nullptr; - } - #pragma endregion #pragma region UnvirtContext Misc UnvirtContext::UnvirtContext() : m_Root(), m_Splitter(), m_Help(nullptr), - m_PageLen(10u), m_Ctx(nullptr), m_FileReader(nullptr) { + m_PageLen(10u), m_OrderExit(false), + m_Ctx(nullptr), m_FileReader(nullptr) { // create command root CmdHelper::CommandRoot* root = &m_Root; + root->Then( - (new CmdHelper::Literal("deepload"))->Then( - (new CmdHelper::StringArgument("filepath", "The path to loading file."))->Executes( + (new CmdHelper::Literal("deepload")) + ->Then((new CmdHelper::StringArgument("filepath")) + ->Comment("The path to loading file.") + ->Executes( std::bind(&UnvirtContext::ProcLoad, this, true, std::placeholders::_1), "Load a Virtools composition deeply. Load file to CKObject stage." ) @@ -78,28 +51,28 @@ namespace Unvirt::Context { root->Then( - (new CmdHelper::Literal("foo"))->Then( - (new CmdHelper::Literal("bar"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void { - fprintf(stdout, "foobar!\n"); - }, "simple foobar") - )->Then( - (new CmdHelper::IntArgument("bar", "the calling target 1"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void { - fprintf(stdout, "foo%" PRIi32 "!\n", *(amap.Get("bar")->GetData())); - }, "specialized foo bar") - ) + (new CmdHelper::Literal("foo"))->Then( + (new CmdHelper::Literal("bar"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void { + fprintf(stdout, "foobar!\n"); + }, "simple foobar") )->Then( + (new CmdHelper::IntArgument("bar", "the calling target 1"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void { + fprintf(stdout, "foo%" PRIi32 "!\n", *(amap.Get("bar")->GetData())); + }, "specialized foo bar") + ) + )->Then( (new CmdHelper::Literal("call"))->Then( - (new CmdHelper::IntArgument("bar", "the calling taget 2"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void { - fprintf(stdout, "call%" PRIi32 "!\n", *(amap.Get("bar")->GetData())); - }, "calling someone") - ) - ); - // create help - m_Help = root->RootHelp(); + (new CmdHelper::IntArgument("bar", "the calling taget 2"))->Executes([](const CmdHelper::ArgumentsMap& amap) -> void { + fprintf(stdout, "call%" PRIi32 "!\n", *(amap.Get("bar")->GetData())); + }, "calling someone") + ) + ); + // create help + m_Help = root->RootHelp(); - // create context - m_Ctx = new LibCmo::CK2::CKContext(); - m_Ctx->SetOutputCallback(std::bind(&UnvirtContext::PrintContextMsg, this, std::placeholders::_1)); + // create context + m_Ctx = new LibCmo::CK2::CKContext(); + m_Ctx->SetOutputCallback(std::bind(&UnvirtContext::PrintContextMsg, this, std::placeholders::_1)); } UnvirtContext::~UnvirtContext() { @@ -165,14 +138,15 @@ namespace Unvirt::Context { #pragma region Proc Detail - void UnvirtContext::ProcLoad(bool is_deep, const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcLoad(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (HasOpenedFile()) { PrintCommonError("Already have a opened file. Close it before calling \"load\"."); return; } - std::string filepath = *amap.Get("filepath")->GetData(); + std::string filepath = *amap->Get("filepath"); + bool is_deep = *amap->Get("stage") == 0; // proc m_FileReader = new LibCmo::CK2::CKFileReader(m_Ctx); @@ -193,7 +167,7 @@ namespace Unvirt::Context { } } - void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (!HasOpenedFile()) { this->PrintCommonError("No loaded file."); @@ -204,7 +178,7 @@ namespace Unvirt::Context { ClearDocument(); } - void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (!HasOpenedFile()) { PrintCommonError("No loaded file."); @@ -215,7 +189,7 @@ namespace Unvirt::Context { Unvirt::StructFormatter::PrintCKFileInfo(m_FileReader->GetFileInfo()); } - void UnvirtContext::ProcLs(ViewPart parts, const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcLs(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (!HasOpenedFile()) { this->PrintCommonError("No loaded file."); @@ -257,15 +231,15 @@ namespace Unvirt::Context { } } - void UnvirtContext::ProcData(ViewPart parts, const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcData( const CmdHelper::ArgumentsMap* amap) { } - void UnvirtContext::ProcChunk(ViewPart parts, const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcChunk(const CmdHelper::ArgumentsMap* amap) { } - void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap* amap) { // check requirement int32_t count = *amap.Get("count")->GetData(); if (count <= 0) { @@ -277,11 +251,11 @@ namespace Unvirt::Context { m_PageLen = static_cast(count); } - void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap* amap) { } - void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap* amap) { // check requirement std::string temppath = *amap.Get("temppath")->GetData(); @@ -289,7 +263,7 @@ namespace Unvirt::Context { m_Ctx->SetTempPath(temppath.c_str()); } - void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap& amap) { + void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap* amap) { m_OrderExit = true; } diff --git a/Unvirt/UnvirtContext.hpp b/Unvirt/UnvirtContext.hpp index 5889683..d560b08 100644 --- a/Unvirt/UnvirtContext.hpp +++ b/Unvirt/UnvirtContext.hpp @@ -27,16 +27,16 @@ namespace Unvirt::Context { }; void PrintCommonError(const char* u8_fmt, ...); - void ProcLoad(bool is_deep, const CmdHelper::ArgumentsMap& amap); - void ProcUnLoad(const CmdHelper::ArgumentsMap& amap); - void ProcInfo(const CmdHelper::ArgumentsMap& amap); - void ProcLs(ViewPart parts, const CmdHelper::ArgumentsMap& amap); - void ProcData(ViewPart parts, const CmdHelper::ArgumentsMap& amap); - void ProcChunk(ViewPart parts, const CmdHelper::ArgumentsMap& amap); - void ProcItems(const CmdHelper::ArgumentsMap& amap); - void ProcEncoding(const CmdHelper::ArgumentsMap& amap); - void ProcTemp(const CmdHelper::ArgumentsMap& amap); - void ProcExit(const CmdHelper::ArgumentsMap& amap); + void ProcLoad(const CmdHelper::ArgumentsMap* amap); + void ProcUnLoad(const CmdHelper::ArgumentsMap* amap); + void ProcInfo(const CmdHelper::ArgumentsMap* amap); + void ProcLs(const CmdHelper::ArgumentsMap* amap); + void ProcData(const CmdHelper::ArgumentsMap* amap); + void ProcChunk(const CmdHelper::ArgumentsMap* amap); + void ProcItems(const CmdHelper::ArgumentsMap* amap); + void ProcEncoding(const CmdHelper::ArgumentsMap* amap); + void ProcTemp(const CmdHelper::ArgumentsMap* amap); + void ProcExit(const CmdHelper::ArgumentsMap* amap); protected: bool HasOpenedFile();