From 07d180f2cb11af041fb28135dfbdc8e2d61182ab Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Thu, 29 Jan 2026 16:46:26 +0800 Subject: [PATCH] refactor: finish CmdHelper refactor in Unvirt --- Unvirt/CmdHelper.cpp | 911 +++++++++++++++++++------------------------ Unvirt/CmdHelper.hpp | 723 +++++++++++----------------------- Unvirt/Unvirt.cpp | 11 - 3 files changed, 614 insertions(+), 1031 deletions(-) diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index 30bae6e..4ef70fc 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -1,574 +1,453 @@ #include "CmdHelper.hpp" -#include -#include +#include +#include namespace Unvirt::CmdHelper { -#pragma region CmdSplitter +#pragma region Enums Parser - const std::deque& CmdSplitter::GetResult() const { - if (!m_ValidResult) - throw std::runtime_error("try to get result from an invalid CmdSplitter."); - return m_Result; + static std::optional ParseLoadStage(const std::u8string_view &sv) { + if (sv == u8"shallow") return LoadStage::Shallow; + else if (sv == u8"deel") return LoadStage::Deep; + else return std::nullopt; } - bool CmdSplitter::Lex(const std::u8string& u8cmd) { - // Clear variables - m_ValidResult = false; - m_Result.clear(); - m_Buffer.clear(); - m_CurrentChar = u8'\0'; - m_State = m_PrevState = StateType::SPACE; + static std::optional ParseLsPart(const std::u8string_view &sv) { + if (sv == u8"mgr") return LsPart::Manager; + else if (sv == u8"obj") return LsPart::Object; + else if (sv == u8"search") return LsPart::Search; + else return std::nullopt; + } - // split - for (char8_t c : u8cmd) { - m_CurrentChar = c; + static std::optional ParseDataPart(const std::u8string_view &sv) { + if (sv == u8"mgr") return DataPart::Manager; + else if (sv == u8"obj") return DataPart::Object; + else return std::nullopt; + } - // skip all invalid characters (ascii code unit lower than space char) - // thus UTF8 code unit can directly accepted. - if (m_CurrentChar < u8' ') - continue; + static std::optional ParseChunkPart(const std::u8string_view &sv) { + if (sv == u8"mgr") return ChunkPart::Manager; + else if (sv == u8"obj") return ChunkPart::Object; + else return std::nullopt; + } - switch (m_State) { - case StateType::SPACE: - ProcSpace(); - break; - case StateType::SINGLE: - ProcSingle(); - break; - case StateType::DOUBLE: - ProcDouble(); - break; - case StateType::ESCAPE: - ProcEscape(); - break; - case StateType::NORMAL: - ProcNormal(); - break; - } - } + static std::optional ParseSearchPart(const std::u8string_view &sv) { + if (sv == u8"mgr") return SearchPart::Manager; + else if (sv == u8"obj") return SearchPart::Object; + else return std::nullopt; + } - // final proc - bool is_success = false; - switch (m_State) { - case StateType::SPACE: - is_success = true; - break; - case StateType::NORMAL: - // push the last one - m_Result.emplace_back(m_Buffer); - is_success = true; - break; - case StateType::SINGLE: - case StateType::DOUBLE: - case StateType::ESCAPE: - // error - is_success = false; - break; - default: - throw std::runtime_error("unreachable code."); - } + static std::optional ParseSearchMode(const std::u8string_view &sv) { + if (sv == u8"plain") return SearchMode::PlainText; + else if (sv == u8"re") return SearchMode::Regex; + else return std::nullopt; + } - // check success - if (is_success) { - m_ValidResult = true; - return true; + static std::optional ParseStyleLevel(const std::u8string_view &sv) { + if (sv == u8"simple") return StyleLevel::Simple; + else if (sv == u8"full") return StyleLevel::Full; + else return std::nullopt; + } + +#pragma endregion + +#pragma region Parameter Stack + + ParamStack::ParamStack(std::vector &¶ms) : params(std::move(params)), cursor(0) {} + + ParamStack::~ParamStack() {} + + std::optional ParamStack::Next() { + if (this->cursor >= this->params.size()) { + return std::nullopt; } else { - m_Result.clear(); - return false; - } - } - - void CmdSplitter::ProcSpace() { - switch (m_CurrentChar) { - case u8'\'': - m_State = StateType::SINGLE; - break; - case u8'"': - m_State = StateType::DOUBLE; - break; - case u8'\\': - m_State = StateType::ESCAPE; - m_PrevState = StateType::NORMAL; - break; - case u8' ': - break; // skip blank - default: - m_Buffer.push_back(m_CurrentChar); - m_State = StateType::NORMAL; - break; - } - } - void CmdSplitter::ProcSingle() { - switch (m_CurrentChar) { - case u8'\'': - m_State = StateType::NORMAL; - break; - case u8'"': - m_Buffer.push_back('"'); - break; - case u8'\\': - m_State = StateType::ESCAPE; - m_PrevState = StateType::SINGLE; - break; - case u8' ': - m_Buffer.push_back(u8' '); - break; - default: - m_Buffer.push_back(m_CurrentChar); - break; - } - } - void CmdSplitter::ProcDouble() { - switch (m_CurrentChar) { - case u8'\'': - m_Buffer.push_back(u8'\''); - break; - case u8'"': - m_State = StateType::NORMAL; - break; - case u8'\\': - m_State = StateType::ESCAPE; - m_PrevState = StateType::DOUBLE; - break; - case u8' ': - m_Buffer.push_back(u8' '); - break; - default: - m_Buffer.push_back(m_CurrentChar); - break; - } - } - void CmdSplitter::ProcEscape() { - // add itself - m_Buffer.push_back(m_CurrentChar); - // restore state - m_State = m_PrevState; - } - void CmdSplitter::ProcNormal() { - switch (m_CurrentChar) { - case u8'\'': - m_Buffer.push_back(u8'\''); - break; - case u8'"': - m_Buffer.push_back(u8'"'); - break; - case u8'\\': - m_State = StateType::ESCAPE; - m_PrevState = StateType::NORMAL; - break; - case u8' ': - m_Result.emplace_back(m_Buffer); - m_Buffer.clear(); - m_State = StateType::SPACE; - break; - default: - m_Buffer.push_back(m_CurrentChar); - break; - } - } -#pragma endregion - -#pragma region Arguments Map - - ArgumentsMap::ArgumentsMap() : m_Data() {} - - ArgumentsMap::~ArgumentsMap() {} - -#pragma endregion - -#pragma region Help Document - - HelpDocument::HelpDocument() : m_Stack(), m_Results() {} - - HelpDocument::~HelpDocument() {} - - HelpDocument::StackItem::StackItem() : m_Name(), m_Desc() {} - - HelpDocument::StackItem::StackItem(const std::u8string& name, const std::u8string& desc) : m_Name(name), m_Desc(desc) {} - - HelpDocument::ResultItem::ResultItem() : m_CmdDesc(), m_ArgDesc() {} - - HelpDocument::ResultItem::ResultItem(const std::u8string& cmd_desc, const std::deque& arg_desc) : - m_CmdDesc(cmd_desc), m_ArgDesc(arg_desc.begin(), arg_desc.end()) {} - - void HelpDocument::Push(const std::u8string& arg_name, const std::u8string& arg_desc) { - m_Stack.emplace_back(StackItem { arg_name, arg_desc }); - } - - void HelpDocument::Pop() { - if (m_Stack.empty()) - throw std::runtime_error("try pop back on an empty help document."); - m_Stack.pop_back(); - } - - void HelpDocument::Terminate(std::u8string& command_desc) { - // create new result - ResultItem result(command_desc, this->m_Stack); - // add into result - m_Results.emplace_back(std::move(result)); - } - - void HelpDocument::Print() { - for (auto& cmd : m_Results) { - // syntax - // check whether all description of argument are emtpty. - bool empty_arg_desc = true; - YYCC::ConsoleHelper::Write(YYCC_COLOR_LIGHT_YELLOW(u8"Syntax: ")); - for (const auto& arg : cmd.m_ArgDesc) { - if (!arg.m_Desc.empty()) empty_arg_desc = false; - YYCC::ConsoleHelper::Format(u8"%s ", arg.m_Name.c_str()); - } - YYCC::ConsoleHelper::WriteLine(u8""); - // command description - if (!cmd.m_CmdDesc.empty()) { - YYCC::ConsoleHelper::FormatLine(YYCC_COLOR_LIGHT_YELLOW(u8"Description: ") "%s", cmd.m_CmdDesc.c_str()); - } - // argument description - if (!empty_arg_desc) { - YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Arguments:")); - for (auto& arg : cmd.m_ArgDesc) { - if (!arg.m_Desc.empty()) { - YYCC::ConsoleHelper::FormatLine(u8"\t" YYCC_COLOR_LIGHT_GREEN("%s") ": % s", - arg.m_Name.c_str(), arg.m_Desc.c_str() - ); - } - } - } - // space between each commands - YYCC::ConsoleHelper::WriteLine(u8""); + return this->params[this->cursor++]; } } #pragma endregion -#pragma region Conflict Set +#pragma region Commander - ConflictSet::ConflictSet() : m_ConflictSet() {} + Commander::Commander() {} - ConflictSet::~ConflictSet() {} + Commander::~Commander() {} - 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."); + Result Commander::Dispatch(const std::u8string_view &cmd) const { + auto stack = this->BuildStack(cmd); + if (stack.has_value()) { + return this->Branch(stack.value()); + } else { + return std::unexpected(stack.error()); + } } - 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."); + Result Commander::BuildStack(const std::u8string_view &cmd) const { + yycc::carton::lexer61::Lexer61 lexer; + auto rv = lexer.lex(cmd); + if (rv.has_value()) { + return ParamStack(std::move(rv.value())); + } else { + return std::unexpected(Error::Lexer); + } } - 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 region Branch + +#define CHECK_NO_MORE_PARAM(stack) \ + if (stack.Next().has_value()) { \ + return std::unexpected(Error::TooMuchParam); \ + } +#define SAFE_CALL_DELEGATE(delegate, payload) \ + if ((delegate) != nullptr) { \ + (delegate)(payload); \ } -#pragma endregion + Result Commander::Branch(ParamStack &stack) const { + auto param = stack.Next(); + if (param.has_value()) { + const auto &verb = param.value(); - namespace Nodes { - -#pragma region Abstract Node - - AbstractNode::AbstractNode() : - m_Execution(nullptr), m_Comment(), m_Nodes() {} - - 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) - throw std::invalid_argument("the function passed for executing should not be nullptr."); - m_Execution = fct; - m_ExecutionDesc = exec_desc; - return *this; - } - - 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) { - // 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); - } - } - - // 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 - // if this node is not root node - if (!this->IsRootNode()) { - doc.Pop(); - } - } - - bool AbstractNode::Consume(CmdSplitter::Result_t& al, ArgumentsMap& am) { - // If no command to be consumed, return directly. - if (al.empty()) 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(); - } - - // 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); \ -} - - if (al.empty()) { - // 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; - } else { - m_Execution(am); - CONSUME_DEFER; - return true; - } + if (verb == u8"load") { + return this->LoadBranch(stack); + } else if (verb == u8"unload") { + return this->UnloadBranch(stack); + } else if (verb == u8"save") { + return this->SaveBranch(stack); + } else if (verb == u8"info") { + return this->InfoBranch(stack); + } else if (verb == u8"ls") { + return this->LsBranch(stack); + } else if (verb == u8"data") { + return this->DataBranch(stack); + } else if (verb == u8"chunk") { + return this->ChunkBranch(stack); + } else if (verb == u8"search") { + return this->SearchBranch(stack); + } else if (verb == u8"items") { + return this->ItemsBranch(stack); + } else if (verb == u8"style") { + return this->StyleBranch(stack); + } else if (verb == u8"encoding") { + return this->EncodingBranch(stack); + } else if (verb == u8"temp") { + return this->TempBranch(stack); + } else if (verb == u8"rsc") { + return this->RscBranch(stack); + } else if (verb == u8"test") { + return this->TestBranch(stack); + } else if (verb == u8"version") { + return this->VersionBranch(stack); + } else if (verb == u8"help") { + return this->HelpBranch(stack); + } else if (verb == u8"exit") { + return this->ExitBranch(stack); } else { - // 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)) { - CONSUME_DEFER; - return true; - } - } - for (auto& node : m_Nodes) { - if (!node->IsArgument()) continue; - if (node->Consume(al, am)) { - CONSUME_DEFER; - return true; - } - } - - // 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; + return std::unexpected(Error::BadVerb); } - -#undef CONSUME_DEFER - + } else { + return std::unexpected(Error::TooLessParam); } + } + Result Commander::LoadBranch(ParamStack &stack) const { + auto stage_param = stack.Next(); + if (!stage_param.has_value()) return std::unexpected(Error::TooLessParam); + auto stage_arg = ParseLoadStage(stage_param.value()); + if (!stage_arg.has_value()) return std::unexpected(Error::BadArg); -#pragma endregion + auto filepath_arg = stack.Next(); + if (!filepath_arg.has_value()) return std::unexpected(Error::TooLessParam); -#pragma region Root Node + CHECK_NO_MORE_PARAM(stack) - RootNode::RootNode() : AbstractNode() {} - RootNode::~RootNode() {} + LoadParam param { + .stage = stage_arg.value(), + .filepath = std::u8string(filepath_arg.value()), + }; + SAFE_CALL_DELEGATE(this->load_delegate, param) + return {}; + } + Result Commander::UnloadBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->unload_delegate, UnloadParam{}) + return {}; + } + Result Commander::SaveBranch(ParamStack &stack) const { + auto filepath_arg = stack.Next(); + if (!filepath_arg.has_value()) return std::unexpected(Error::TooLessParam); - 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."); } + CHECK_NO_MORE_PARAM(stack) -#pragma endregion + SaveParam param { + .filepath = std::u8string(filepath_arg.value()), + }; + SAFE_CALL_DELEGATE(this->save_delegate, param) + return {}; + } + Result Commander::InfoBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->info_delegate, InfoParam{}) + return {}; + } + Result Commander::LsBranch(ParamStack &stack) const { + auto part_param = stack.Next(); + if (!part_param.has_value()) return std::unexpected(Error::TooLessParam); + auto part_arg = ParseLsPart(part_param.value()); + if (!part_arg.has_value()) return std::unexpected(Error::BadArg); -#pragma region Literal + auto page_param = stack.Next(); + if (!page_param.has_value()) return std::unexpected(Error::TooLessParam); + auto page_arg = yycc::num::parse::parse(page_param.value()); + if (!page_arg.has_value()) return std::unexpected(Error::BadArg); + if (page_arg.value() < 1) return std::unexpected(Error::BadArg); - Literal::Literal(const std::u8string_view& words) : - 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); + CHECK_NO_MORE_PARAM(stack) + + LsParam param { + .part = part_arg.value(), + .page = page_arg.value(), + }; + SAFE_CALL_DELEGATE(this->ls_delegate, param) + return {}; + } + Result Commander::DataBranch(ParamStack &stack) const { + auto part_param = stack.Next(); + if (!part_param.has_value()) return std::unexpected(Error::TooLessParam); + auto part_arg = ParseDataPart(part_param.value()); + if (!part_arg.has_value()) return std::unexpected(Error::BadArg); + + auto index_param = stack.Next(); + if (!index_param.has_value()) return std::unexpected(Error::TooLessParam); + auto index_arg = yycc::num::parse::parse(index_param.value()); + if (!index_arg.has_value()) return std::unexpected(Error::BadArg); + + CHECK_NO_MORE_PARAM(stack) + + DataParam param { + .part = part_arg.value(), + .index = index_arg.value(), + }; + SAFE_CALL_DELEGATE(this->data_delegate, param) + return {}; + } + Result Commander::ChunkBranch(ParamStack &stack) const { + auto part_param = stack.Next(); + if (!part_param.has_value()) return std::unexpected(Error::TooLessParam); + auto part_arg = ParseChunkPart(part_param.value()); + if (!part_arg.has_value()) return std::unexpected(Error::BadArg); + + auto index_param = stack.Next(); + if (!index_param.has_value()) return std::unexpected(Error::TooLessParam); + auto index_arg = yycc::num::parse::parse(index_param.value()); + if (!index_arg.has_value()) return std::unexpected(Error::BadArg); + + CHECK_NO_MORE_PARAM(stack) + + ChunkParam param { + .part = part_arg.value(), + .index = index_arg.value(), + }; + SAFE_CALL_DELEGATE(this->chunk_delegate, param) + return {}; + } + Result Commander::SearchBranch(ParamStack &stack) const { + auto part_param = stack.Next(); + if (!part_param.has_value()) return std::unexpected(Error::TooLessParam); + auto part_arg = ParseSearchPart(part_param.value()); + if (!part_arg.has_value()) return std::unexpected(Error::BadArg); + + auto mode_param = stack.Next(); + if (!mode_param.has_value()) return std::unexpected(Error::TooLessParam); + auto mode_arg = ParseSearchMode(mode_param.value()); + if (!mode_arg.has_value()) return std::unexpected(Error::BadArg); + + auto text_arg = stack.Next(); + if (!text_arg.has_value()) return std::unexpected(Error::TooLessParam); + + CHECK_NO_MORE_PARAM(stack) + + SearchParam param { + .part = part_arg.value(), + .mode = mode_arg.value(), + .text = std::u8string(text_arg.value()), + }; + SAFE_CALL_DELEGATE(this->search_delegate, param) + return {}; + } + Result Commander::ItemsBranch(ParamStack &stack) const { + auto count_param = stack.Next(); + if (!count_param.has_value()) return std::unexpected(Error::TooLessParam); + auto count_arg = yycc::num::parse::parse(count_param.value()); + if (!count_arg.has_value()) return std::unexpected(Error::BadArg); + if (count_arg.value() < 1) return std::unexpected(Error::BadArg); + + CHECK_NO_MORE_PARAM(stack) + + ItemsParam param { + .count = count_arg.value(), + }; + SAFE_CALL_DELEGATE(this->items_delegate, param) + return {}; + } + Result Commander::StyleBranch(ParamStack &stack) const { + auto level_param = stack.Next(); + if (!level_param.has_value()) return std::unexpected(Error::TooLessParam); + auto level_arg = ParseStyleLevel(level_param.value()); + if (!level_arg.has_value()) return std::unexpected(Error::BadArg); + + CHECK_NO_MORE_PARAM(stack) + + StyleParam param { + .level = level_arg.value(), + }; + SAFE_CALL_DELEGATE(this->style_delegate, param) + return {}; + } + Result Commander::EncodingBranch(ParamStack &stack) const { + std::vector collector; + while (true) { + auto param = stack.Next(); + if (param.has_value()) collector.emplace_back(param.value()); + else break; } - Literal::~Literal() {} + if (collector.empty()) return std::unexpected(Error::TooLessParam); - bool Literal::IsRootNode() { return false; } - bool Literal::IsArgument() { return false; } - 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) {} + EncodingParam param{ + .enc = std::move(collector), + }; + SAFE_CALL_DELEGATE(this->encoding_delegate, param) + return {}; + } + Result Commander::TempBranch(ParamStack &stack) const { + auto dirpath_arg = stack.Next(); + if (!dirpath_arg.has_value()) return std::unexpected(Error::TooLessParam); -#pragma endregion + CHECK_NO_MORE_PARAM(stack) -#pragma region Choice + TempParam param { + .dirpath = std::u8string(dirpath_arg.value()), + }; + SAFE_CALL_DELEGATE(this->temp_delegate, param) + return {}; + } + Result Commander::RscBranch(ParamStack &stack) const { + auto param = stack.Next(); + if (param.has_value()) { + const auto &verb = param.value(); - Choice::Choice(const std::u8string_view& argname, const std::initializer_list& vocabulary) : - AbstractNode(), - m_ChoiceName(argname), m_Vocabulary(vocabulary), - m_ConflictSet() { - // check argument - if (argname.empty()) - 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.AddArgument(m_ChoiceName); - for (const auto& word : m_Vocabulary) { - m_ConflictSet.AddLiteral(word); + if (verb == u8"clear") { + return this->RscClearBranch(stack); + } else if (verb == u8"add") { + return this->RscAddBranch(stack); + } else { + return std::unexpected(Error::BadVerb); } + } else { + return std::unexpected(Error::TooLessParam); } - Choice::~Choice() {} + } + Result Commander::RscClearBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->rsc_clear_delegate, RscClearParam{}) + return {}; + } + Result Commander::RscAddBranch(ParamStack &stack) const { + auto dirpath_arg = stack.Next(); + if (!dirpath_arg.has_value()) return std::unexpected(Error::TooLessParam); - bool Choice::IsRootNode() { return false; } - bool Choice::IsArgument() { return false; } - const ConflictSet& Choice::GetConflictSet() { return m_ConflictSet; } - std::u8string Choice::GetHelpSymbol() { - return YYCC::StringHelper::Printf(u8"[%s]", - YYCC::StringHelper::Join(m_Vocabulary.begin(), m_Vocabulary.end(), u8" | ").c_str() - ); - } - 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, i); - return true; - } - } - return false; - } - void Choice::EndConsume(ArgumentsMap& am) { am.Remove(m_ChoiceName); } - -#pragma endregion - -#pragma region Abstract Argument - - AbstractArgument::AbstractArgument(const std::u8string_view& argname) : - AbstractNode(), - 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() {} - - 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 Arithmetic Argument - - // It's a template class - // We are forced to implement it in header file. - -#pragma endregion - -#pragma region String Argument - - StringArgument::StringArgument(const std::u8string_view& argname, Constraint_t constraint) : - AbstractArgument(argname), m_Constraint(constraint) {} - - StringArgument::~StringArgument() {} - - bool StringArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { - // check constraint - if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(cur_cmd)) - return false; - // accept - am.Add(m_ArgumentName, cur_cmd); - return true; - } - -#pragma endregion + CHECK_NO_MORE_PARAM(stack) + RscAddParam param { + .dirpath = std::u8string(dirpath_arg.value()), + }; + SAFE_CALL_DELEGATE(this->rsc_add_delegate, param) + return {}; + } + Result Commander::TestBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->test_delegate, TestParam{}) + return {}; + } + Result Commander::VersionBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->version_delegate, VersionParam{}) + return {}; + } + Result Commander::HelpBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->help_delegate, HelpParam{}) + return {}; + } + Result Commander::ExitBranch(ParamStack &stack) const { + CHECK_NO_MORE_PARAM(stack) + SAFE_CALL_DELEGATE(this->exit_delegate, ExitParam{}) + return {}; } -#pragma region Command Parser +#undef SAFE_CALL_DELEGATE +#undef CHECK_NO_MORE_PARAM - CommandParser::CommandParser() {} +#pragma endregion - CommandParser::~CommandParser() {} +#pragma region Delegate Setter - bool CommandParser::Parse(const CmdSplitter::Result_t& cmds) { - // Create a copy of given command - CmdSplitter::Result_t al(cmds); - // Create argument map - ArgumentsMap am; - - // Call root node Consume function and return its result. - return m_RootNode.Consume(al, am); + void Commander::SetLoadDelegate(LoadDelegate &&delegate) { + this->load_delegate = std::move(delegate); } - - HelpDocument CommandParser::Help() { - // Create help docuemnt - HelpDocument doc; - // use node tree to fill help document. - m_RootNode.Help(doc); - // return result. - return doc; + void Commander::SetUnloadDelegate(UnloadDelegate &&delegate) { + this->unload_delegate = std::move(delegate); } - - Nodes::RootNode& CommandParser::GetRoot() { - return m_RootNode; + void Commander::SetSaveDelegate(SaveDelegate &&delegate) { + this->save_delegate = std::move(delegate); + } + void Commander::SetInfoDelegate(InfoDelegate &&delegate) { + this->info_delegate = std::move(delegate); + } + void Commander::SetLsDelegate(LsDelegate &&delegate) { + this->ls_delegate = std::move(delegate); + } + void Commander::SetDataDelegate(DataDelegate &&delegate) { + this->data_delegate = std::move(delegate); + } + void Commander::SetChunkDelegate(ChunkDelegate &&delegate) { + this->chunk_delegate = std::move(delegate); + } + void Commander::SetSearchDelegate(SearchDelegate &&delegate) { + this->search_delegate = std::move(delegate); + } + void Commander::SetItemsDelegate(ItemsDelegate &&delegate) { + this->items_delegate = std::move(delegate); + } + void Commander::SetStyleDelegate(StyleDelegate &&delegate) { + this->style_delegate = std::move(delegate); + } + void Commander::SetEncodingDelegate(EncodingDelegate &&delegate) { + this->encoding_delegate = std::move(delegate); + } + void Commander::SetTempDelegate(TempDelegate &&delegate) { + this->temp_delegate = std::move(delegate); + } + void Commander::SetRscClearDelegate(RscClearDelegate &&delegate) { + this->rsc_clear_delegate = std::move(delegate); + } + void Commander::SetRscAddDelegate(RscAddDelegate &&delegate) { + this->rsc_add_delegate = std::move(delegate); + } + void Commander::SetTestDelegate(TestDelegate &&delegate) { + this->test_delegate = std::move(delegate); + } + void Commander::SetVersionDelegate(VersionDelegate &&delegate) { + this->version_delegate = std::move(delegate); + } + void Commander::SetHelpDelegate(HelpDelegate &&delegate) { + this->help_delegate = std::move(delegate); + } + void Commander::SetExitDelegate(ExitDelegate &&delegate) { + this->exit_delegate = std::move(delegate); } #pragma endregion -} +#pragma endregion + +} // namespace Unvirt::CmdHelper diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index 9012a9a..ba99ea0 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -1,526 +1,241 @@ #pragma once -#include +#include +#include #include +#include #include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include namespace Unvirt::CmdHelper { - class CmdSplitter { +#pragma region Delegate Parameters + + enum class LoadStage { Shallow, Deep }; + + struct LoadParam { + LoadStage stage; + std::u8string filepath; + }; + + struct UnloadParam {}; + + struct SaveParam { + std::u8string filepath; + }; + + struct InfoParam {}; + + enum class LsPart { + Object, + Manager, + Search, + }; + + struct LsParam { + LsPart part; + size_t page; + }; + + enum class DataPart { + Object, + Manager, + }; + + struct DataParam { + DataPart part; + size_t index; + }; + + enum class ChunkPart { + Object, + Manager, + }; + + struct ChunkParam { + ChunkPart part; + size_t index; + }; + + enum class SearchPart { + Object, + Manager, + }; + + enum class SearchMode { + PlainText, + Regex, + }; + + struct SearchParam { + SearchPart part; + SearchMode mode; + std::u8string text; + }; + + struct ItemsParam { + size_t count; + }; + + enum class StyleLevel { + Full, + Simple, + }; + + struct StyleParam { + StyleLevel level; + }; + + struct EncodingParam { + std::vector enc; + }; + + struct TempParam { + std::u8string dirpath; + }; + + struct RscClearParam {}; + struct RscAddParam { + std::u8string dirpath; + }; + + struct TestParam {}; + + struct VersionParam {}; + + struct HelpParam {}; + + struct ExitParam {}; + +#pragma endregion + +#pragma region Delegates + + using LoadDelegate = std::function; + using UnloadDelegate = std::function; + using SaveDelegate = std::function; + using InfoDelegate = std::function; + using LsDelegate = std::function; + using DataDelegate = std::function; + using ChunkDelegate = std::function; + using SearchDelegate = std::function; + using ItemsDelegate = std::function; + using StyleDelegate = std::function; + using EncodingDelegate = std::function; + using TempDelegate = std::function; + using RscClearDelegate = std::function; + using RscAddDelegate = std::function; + using TestDelegate = std::function; + using VersionDelegate = std::function; + using HelpDelegate = std::function; + using ExitDelegate = std::function; + +#pragma endregion + +#pragma region Error Types + + enum class Error { + Lexer, ///< Fail to do lexer. + TooMuchParam, ///< The count of parameter is larger than expected. + TooLessParam, ///< The count of parameter is to less as expected. + BadArg, ///< The format of argument is wrong. + BadVerb, ///< Given verb do not match with any existing items. + }; + + template + using Result = std::expected; + +#pragma endregion + + class ParamStack { public: - using Result_t = std::deque; - private: - enum class StateType : int { - SPACE, - SINGLE, - DOUBLE, - ESCAPE, - NORMAL - }; + ParamStack(std::vector&& params); + ~ParamStack(); + YYCC_DEFAULT_COPY_MOVE(ParamStack) public: - CmdSplitter() : - m_CurrentChar(u8'\0'), m_Buffer(), m_Result(), m_ValidResult(false), - m_State(StateType::NORMAL), m_PrevState(StateType::NORMAL) {} - ~CmdSplitter() {} - YYCC_DEF_CLS_COPY_MOVE(CmdSplitter); - - bool Lex(const std::u8string& u8cmd); - const Result_t& GetResult() const; + std::optional Next(); private: - void ProcSpace(); - void ProcSingle(); - void ProcDouble(); - void ProcEscape(); - void ProcNormal(); - - char8_t m_CurrentChar; - std::u8string m_Buffer; - Result_t m_Result; - bool m_ValidResult; - StateType m_State, m_PrevState; + std::vector params; + size_t cursor; }; - namespace AMItems { - - class AbstractItem { - public: - AbstractItem() {} - virtual ~AbstractItem() {} - YYCC_DEF_CLS_COPY_MOVE(AbstractItem); - }; - - template, int> = 0> - class ArithmeticItem : public AbstractItem { - public: - ArithmeticItem(_Ty value) : AbstractItem(), m_Data(value) {} - virtual ~ArithmeticItem() {} - YYCC_DEF_CLS_COPY_MOVE(ArithmeticItem); - public: - _Ty Get() const { return m_Data; } - protected: - _Ty m_Data; - }; - - template, int> = 0> - class ArithmeticArrayItem : public AbstractItem { - public: - ArithmeticArrayItem(const std::vector<_Ty>& values) : AbstractItem(), m_Data(values) {} - virtual ~ArithmeticArrayItem() {} - YYCC_DEF_CLS_COPY_MOVE(ArithmeticArrayItem); - public: - const std::vector<_Ty>& Get() const { return m_Data; } - protected: - std::vector<_Ty> m_Data; - }; - - class StringItem : public AbstractItem { - public: - StringItem(const std::u8string_view& value) : AbstractItem(), m_Data(value) {} - virtual ~StringItem() {} - YYCC_DEF_CLS_COPY_MOVE(StringItem); - public: - const std::u8string& Get() const { return m_Data; } - protected: - std::u8string m_Data; - }; - - class StringArrayItem : public AbstractItem { - public: - StringArrayItem(const std::vector& value) : AbstractItem(), m_Data(value) {} - virtual ~StringArrayItem() {} - YYCC_DEF_CLS_COPY_MOVE(StringArrayItem); - public: - const std::vector& Get() const { return m_Data; } - protected: - std::vector m_Data; - }; - - } - - class ArgumentsMap { + class Commander { public: - ArgumentsMap(); - ~ArgumentsMap(); - YYCC_DEF_CLS_COPY_MOVE(ArgumentsMap); - - protected: - std::map> m_Data; + Commander(); + ~Commander(); + YYCC_DELETE_COPY_MOVE(Commander) public: - template, int> = 0> - void Add(const std::u8string_view& key, _Types&&... args) { - // check argument - if (key.empty()) - throw std::invalid_argument("argument key should not be empty"); - // insert into data - auto result = m_Data.try_emplace(std::u8string(key), std::make_shared<_Ty>(std::forward<_Types>(args)...)); - if (!result.second) - throw std::runtime_error("try to add an existing key."); - } - template, int> = 0> - const _Ty& Get(const std::u8string_view& key) const { - // check argument - if (key.empty()) - throw std::invalid_argument("argument key should not be empty"); - // find key first - auto finder = m_Data.find(std::u8string(key)); - if (finder == m_Data.end()) - throw std::runtime_error("try to get a non-existent key."); - // get stored value data - const _Ty* value = static_cast(finder->second.get()); - return *value; - } - void Remove(const std::u8string_view& key) { - // check argument - if (key.empty()) - throw std::invalid_argument("argument key should not be empty"); - // remove it from map and check whether remove item - if (m_Data.erase(std::u8string(key)) == 0u) - throw std::runtime_error("try to delete a non-existent key."); - } - }; - - // Forward declaration for Nodes::AbstractNode - namespace Nodes { class AbstractNode; } - - class HelpDocument { - friend class Nodes::AbstractNode; - public: - HelpDocument(); - ~HelpDocument(); - YYCC_DEF_CLS_COPY_MOVE(HelpDocument); - - public: - void Print(); - - // AbstractNode used - protected: - void Push(const std::u8string& arg_name, const std::u8string& arg_desc); - void Pop(); - void Terminate(std::u8string& command_desc); - - protected: - struct StackItem { - StackItem(); - StackItem(const std::u8string& name, const std::u8string& desc); - YYCC_DEF_CLS_COPY_MOVE(StackItem); - - std::u8string m_Name; - std::u8string m_Desc; - }; - std::deque m_Stack; - - struct ResultItem { - ResultItem(); - ResultItem(const std::u8string& cmd_desc, const std::deque& arg_desc); - YYCC_DEF_CLS_COPY_MOVE(ResultItem); - - std::u8string m_CmdDesc; - std::vector m_ArgDesc; - }; - 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 { - public: - using FctExecution_t = std::function; - - public: - AbstractNode(); - virtual ~AbstractNode(); - YYCC_DEF_CLS_COPY_MOVE(AbstractNode); - - protected: - std::vector> m_Nodes; - FctExecution_t m_Execution; - std::u8string m_ExecutionDesc; - std::u8string m_Comment; - - protected: - /** - * @brief The core function to generate help document by following hierarchy. - * @param[in] doc The generating help document. - */ - void Help(HelpDocument& doc); - /** - * @brief The core function to consume splitted commands by following hierarchy. - * @param[in] al The splitted commands deque. - * @param[in] am Argument map for operating. - * @return True if we reach a legal terminal, otherwise false. - * Once this function return true, there is no need to process other nodes. - * Because the final command processor has been executed when this function return true. - */ - 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, otherwise false. - * @remakrs - * \li Sub-class must implement this function. - * \li This function is used internally because when consuming nodes, - * we need consume literal and choice first, then consume argument. - */ - virtual bool IsArgument() = 0; - /** - * @brief Get a set of identifier used for checking node conflict. - * @return The set of identifier. - * @remarks - * Sub-class must implement this function. - * \par - * This function return the reference to the set. - * It means that sub-class need allocate some memory by themselves - * to store the value returned by this function. - * \par - * When adding new nodes, we use this function to check whether there is conflict. - * If the intersection between 2 sets coming from different nodes is not empty, - * it means that they have conflict, the process of adding will be aborted. - * \par - * In details: - * \li Literal: Put its literal in set directly. - * \li Choice: Put its vocabulary and associated argument name in set. - * \li Argument: Put its argument name in set. - */ - 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. - * @remarks Sub-class must implement this function. - * \li Literal: Return its literal directly. - * \li Choice: Join vocabulary with \c \| then brack it with square bracker. - * \li Argument: Bracket its argument name with sharp bracket. - */ - virtual std::u8string GetHelpSymbol() = 0; - /** - * @brief Try consume given command for this node. - * @param[in] cur_cmd The command hope to be accepted. - * @param[in] am Argument map for operating. - * @return True if this node accept this command. - * @remarks - * Sub-class must implement this function. - * \par - * For choice and argument, if given command is accepted, - * implementation should insert data into argument map. - * So that user can visit it. - */ - virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) = 0; - /** - * @brief End the consume of this node. - * @param[in] am Argument map for operating. - * @remarks - * Sub-class must implement this function. - * \par - * If BeginConsume() return false, this function will not be called. - * \par - * For choice and argument, if you have accepted one command, - * implementation should remove data from argument map. - * So that following nodes (not belongs to this tree) may add the argument with same name. - */ - virtual void EndConsume(ArgumentsMap& am) = 0; - - public: - /** - * @brief Add a new node as child nodes for this node. - * @tparam _Ty The child class type of AbstractNode. - * @param[in] node The node instance to be added. - * @return Return self for chain calling. - */ - template && !std::is_same_v, int> = 0> - AbstractNode& Then(AbstractNode& node) { - // create node first - auto new_node = std::make_shared<_Ty>(static_cast<_Ty&>(node)); - // get its abstract pointer for checking - AbstractNode* new_node_ptr = new_node.get(); - // check root node. - if (new_node_ptr->IsRootNode()) - throw std::invalid_argument("root node should not be inserted as child node."); - // check conflict - const auto& new_node_set = new_node_ptr->GetConflictSet(); - for (auto& child_node : m_Nodes) { - const auto& node_set = child_node->GetConflictSet(); - if (new_node_set.IsConflictWith(node_set)) - throw std::invalid_argument("try to add a conflict node. please check your code."); - } - // add into list - m_Nodes.emplace_back(std::move(new_node)); - return *this; - } - /** - * @brief Setup execution infomation for this node. - * @param[in] fct Associated execution function. nullptr is not allowed. - * @param[in] exec_desc Associated execution message presented in help message. - * @return Return self for chain calling. - * @remarks This function only can be called once for one node. - */ - AbstractNode& Executes(FctExecution_t fct, const std::u8string_view& exec_desc = u8""); - /** - * @brief Setup command for this node. - * @param[in] comment The command of current node. - * @return Return self for chain calling. - */ - AbstractNode& Comment(const std::u8string_view& comment = u8""); - - }; - - class RootNode : public AbstractNode { - friend class ::Unvirt::CmdHelper::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); - virtual ~Literal(); - YYCC_DEF_CLS_COPY_MOVE(Literal); - - 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; - - std::u8string m_Literal; - ConflictSet m_ConflictSet; - }; - - class Choice : public AbstractNode { - public: - 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 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; - ConflictSet m_ConflictSet; - }; - - class AbstractArgument : public AbstractNode { - public: - 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 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_ArgumentName; - ConflictSet m_ConflictSet; - }; - - template, int> = 0> - class ArithmeticArgument : public AbstractArgument { - public: - 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 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 ArgValue_t = AMItems::StringItem; - using Constraint_t = YYCC::Constraints::Constraint; - public: - StringArgument(const std::u8string_view& argname, Constraint_t constraint = Constraint_t {}); - virtual ~StringArgument(); - YYCC_DEF_CLS_COPY_MOVE(StringArgument); - - protected: - virtual bool BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) override; - Constraint_t m_Constraint; - }; - - } - - class CommandParser { - public: - CommandParser(); - ~CommandParser(); - - public: - bool Parse(const CmdSplitter::Result_t& cmds); - HelpDocument Help(); - Nodes::RootNode& GetRoot(); + Result Dispatch(const std::u8string_view &cmd) const; private: - Nodes::RootNode m_RootNode; + Result BuildStack(const std::u8string_view &cmd) const; + + private: + Result Branch(ParamStack &stack) const; + Result LoadBranch(ParamStack &stack) const; + Result UnloadBranch(ParamStack &stack) const; + Result SaveBranch(ParamStack &stack) const; + Result InfoBranch(ParamStack &stack) const; + Result LsBranch(ParamStack &stack) const; + Result DataBranch(ParamStack &stack) const; + Result ChunkBranch(ParamStack &stack) const; + Result SearchBranch(ParamStack &stack) const; + Result ItemsBranch(ParamStack &stack) const; + Result StyleBranch(ParamStack &stack) const; + Result EncodingBranch(ParamStack &stack) const; + Result TempBranch(ParamStack &stack) const; + Result RscBranch(ParamStack &stack) const; + Result RscClearBranch(ParamStack &stack) const; + Result RscAddBranch(ParamStack &stack) const; + Result TestBranch(ParamStack &stack) const; + Result VersionBranch(ParamStack &stack) const; + Result HelpBranch(ParamStack &stack) const; + Result ExitBranch(ParamStack &stack) const; + + public: + void SetLoadDelegate(LoadDelegate &&delegate); + void SetUnloadDelegate(UnloadDelegate &&delegate); + void SetSaveDelegate(SaveDelegate &&delegate); + void SetInfoDelegate(InfoDelegate &&delegate); + void SetLsDelegate(LsDelegate &&delegate); + void SetDataDelegate(DataDelegate &&delegate); + void SetChunkDelegate(ChunkDelegate &&delegate); + void SetSearchDelegate(SearchDelegate &&delegate); + void SetItemsDelegate(ItemsDelegate &&delegate); + void SetStyleDelegate(StyleDelegate &&delegate); + void SetEncodingDelegate(EncodingDelegate &&delegate); + void SetTempDelegate(TempDelegate &&delegate); + void SetRscClearDelegate(RscClearDelegate &&delegate); + void SetRscAddDelegate(RscAddDelegate &&delegate); + void SetTestDelegate(TestDelegate &&delegate); + void SetVersionDelegate(VersionDelegate &&delegate); + void SetHelpDelegate(HelpDelegate &&delegate); + void SetExitDelegate(ExitDelegate &&delegate); + + private: + LoadDelegate load_delegate; + UnloadDelegate unload_delegate; + SaveDelegate save_delegate; + InfoDelegate info_delegate; + LsDelegate ls_delegate; + DataDelegate data_delegate; + ChunkDelegate chunk_delegate; + SearchDelegate search_delegate; + ItemsDelegate items_delegate; + StyleDelegate style_delegate; + EncodingDelegate encoding_delegate; + TempDelegate temp_delegate; + RscClearDelegate rsc_clear_delegate; + RscAddDelegate rsc_add_delegate; + TestDelegate test_delegate; + VersionDelegate version_delegate; + HelpDelegate help_delegate; + ExitDelegate exit_delegate; }; -} +} // namespace Unvirt::CmdHelper diff --git a/Unvirt/Unvirt.cpp b/Unvirt/Unvirt.cpp index 24750f4..dcb0eaa 100644 --- a/Unvirt/Unvirt.cpp +++ b/Unvirt/Unvirt.cpp @@ -1,21 +1,10 @@ -#include #include "UnvirtContext.hpp" int main(int argc, char* argv[]) { - // register exception handler on windows release -#if defined(LIBCMO_BUILD_RELEASE) && (YYCC_OS == YYCC_OS_WINDOWS) - YYCC::ExceptionHelper::Register(); -#endif - // run core Unvirt::Context::UnvirtContext ctx; ctx.Run(); - // unregister exception handler on windows release -#if defined(LIBCMO_BUILD_RELEASE) && (YYCC_OS == YYCC_OS_WINDOWS) - YYCC::ExceptionHelper::Register(); -#endif - return 0; }