From d6a75552bac715e60e40cb6761a19ecd13c6eba5 Mon Sep 17 00:00:00 2001 From: zzyyyl Date: Thu, 2 Nov 2023 18:48:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BB=8E=20TaskData=20=E4=B8=AD?= =?UTF-8?q?=E5=88=86=E7=A6=BB=20TaskDataSymbol,=20TaskDataSymbolStream?= =?UTF-8?q?=EF=BC=9B=E5=A2=9E=E5=8A=A0=20TaskDataTypes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: 用 to_string 和 string 加法取代 std::format 以通过非 win 平台编译 asst::ResultOrError 是 std::expected 的下位替代(同时又类似于 std::optional) --- src/MaaCore/Config/TaskData.cpp | 457 ++++-------------- src/MaaCore/Config/TaskData.h | 46 +- .../Config/TaskData/TaskDataSymbol.cpp | 56 +++ src/MaaCore/Config/TaskData/TaskDataSymbol.h | 136 ++++++ .../Config/TaskData/TaskDataSymbolStream.cpp | 266 ++++++++++ .../Config/TaskData/TaskDataSymbolStream.h | 27 ++ src/MaaCore/Config/TaskData/TaskDataTypes.hpp | 45 ++ src/MaaCore/MaaCore.vcxproj | 5 + src/MaaCore/MaaCore.vcxproj.filters | 18 + 9 files changed, 671 insertions(+), 385 deletions(-) create mode 100644 src/MaaCore/Config/TaskData/TaskDataSymbol.cpp create mode 100644 src/MaaCore/Config/TaskData/TaskDataSymbol.h create mode 100644 src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp create mode 100644 src/MaaCore/Config/TaskData/TaskDataSymbolStream.h create mode 100644 src/MaaCore/Config/TaskData/TaskDataTypes.hpp diff --git a/src/MaaCore/Config/TaskData.cpp b/src/MaaCore/Config/TaskData.cpp index 84e85815d5..7e2ec2abb3 100644 --- a/src/MaaCore/Config/TaskData.cpp +++ b/src/MaaCore/Config/TaskData.cpp @@ -9,6 +9,8 @@ #include "Common/AsstTypes.h" #include "GeneralConfig.h" +#include "TaskData/TaskDataSymbolStream.h" +#include "TaskData/TaskDataTypes.hpp" #include "TemplResource.h" #include "Utils/JsonMisc.hpp" #include "Utils/Logger.hpp" @@ -56,11 +58,14 @@ asst::TaskData::taskptr_t asst::TaskData::get(std::string_view name) return nullptr; } bool changed = false; // 任务列表是否发生变化(是否包含虚任务) -#define ASST_TASKDATA_GET_IF_BRANCH(list, m) \ - task->list.clear(); \ - if (!compile_tasklist(task->list, raw_task->list, name, changed, m)) [[unlikely]] { \ - Log.error("Generate task_list", std::string(name) + "->" #list, "failed."); \ - return nullptr; \ +#define ASST_TASKDATA_GET_IF_BRANCH(list, m) \ + if (auto opt = compile_tasklist(raw_task->list, name, m); !opt) [[unlikely]] { \ + Log.error("Generate task_list", std::string(name) + "->" #list, "failed.", opt.what()); \ + return nullptr; \ + } \ + else { \ + changed |= opt.value().task_changed; \ + task->list = std::move(opt.value().tasks); \ } // 展开五个任务列表中的虚任务 ASST_TASKDATA_GET_IF_BRANCH(next, false); @@ -531,394 +536,102 @@ asst::TaskData::taskptr_t asst::TaskData::_default_task_info() return task_info_ptr; } -// new_tasks 是目的任务列表 -// raw_tasks 是源任务表达式列表 -// task_name 是任务名 -// task_changed 记录 raw_tasks 在转换为 new_tasks 时是否发生变化 -// allow_duplicate 是否允许重复 -bool asst::TaskData::compile_tasklist(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view task_name, - bool& task_changed, bool allow_duplicate) +asst::ResultOrError asst::TaskData::compile_raw_tasklist( + const tasklist_t& raw_tasks, std::string_view self_name, std::function get_raw, + bool allow_duplicate) { + RawCompileResult ret { .task_changed = false, .symbols = {} }; std::unordered_set tasks_set; // 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致) - using symbl_t = size_t; - [[maybe_unused]] constexpr symbl_t symbl_end = 0; - [[maybe_unused]] constexpr symbl_t symbl_lambda_task_sep = 1; - [[maybe_unused]] constexpr symbl_t symbl_lparen = 2; - [[maybe_unused]] constexpr symbl_t symbl_rparen = 3; - [[maybe_unused]] constexpr symbl_t symbl_lbrace = 4; - [[maybe_unused]] constexpr symbl_t symbl_rbrace = 5; - [[maybe_unused]] constexpr symbl_t symbl_at = 6; - [[maybe_unused]] constexpr symbl_t symbl_sharp = 7; - [[maybe_unused]] constexpr symbl_t symbl_mul = 8; - [[maybe_unused]] constexpr symbl_t symbl_add = 9; - [[maybe_unused]] constexpr symbl_t symbl_sub = 10; - - [[maybe_unused]] constexpr symbl_t symbl_name_start = 11; - [[maybe_unused]] constexpr symbl_t symbl_name_sub = 11; - [[maybe_unused]] constexpr symbl_t symbl_name_next = 12; - [[maybe_unused]] constexpr symbl_t symbl_name_on_error_next = 13; - [[maybe_unused]] constexpr symbl_t symbl_name_exceeded_next = 14; - [[maybe_unused]] constexpr symbl_t symbl_name_reduce_other_times = 15; - [[maybe_unused]] constexpr symbl_t symbl_name_self = 16; - [[maybe_unused]] constexpr symbl_t symbl_name_back = 17; - [[maybe_unused]] constexpr symbl_t symbl_name_none = 18; - - static const std::vector symbl_table = { - "__END__", // 0 - ",", // 1 - "(", // 2 - ")", // 3 - "{", // 4 - "}", // 5 - "@", // 6 - "#", // 7 - "*", // 8 - "+", // 9 - "^", // 10 - "sub", // 11 - "next", // 12 - "on_error_next", // 13 - "exceeded_next", // 14 - "reduce_other_times", // 15 - "self", // 16 - "back", // 17 - "none", // 18 - }; - - auto is_symbl_name = [&](symbl_t x) { return x >= symbl_name_start; }; - auto is_symbl_subtask_type = [&](symbl_t x) { - switch (x) { - case symbl_name_sub: - case symbl_name_next: - case symbl_name_on_error_next: - case symbl_name_exceeded_next: - case symbl_name_reduce_other_times: - return true; - default: - return false; - } - }; - [[maybe_unused]] auto is_symbl_sharp_type = [&](symbl_t x) { - switch (x) { - case symbl_name_self: - case symbl_name_back: - case symbl_name_none: - return true; - default: - return is_symbl_subtask_type(x); - } - }; - - // perform_op 的结果不保证符合参数 multi 的要求 - auto perform_op = [&](std::string_view task_expr, symbl_t op, tasklistptr_t x, - tasklistptr_t y) -> std::optional { - auto ret = std::make_shared(); - - switch (op) { - case symbl_add: - ranges::copy(*x, std::back_inserter(*ret)); - ranges::copy(*y, std::back_inserter(*ret)); - break; - case symbl_sub: - for (std::string_view sx : *x) { - bool flag = true; - for (std::string_view sy : *y) { - if (sx == sy) { - flag = false; - break; - } - } - if (flag) ret->emplace_back(sx); - } - break; - case symbl_mul: { - if (y->size() != 1) { - Log.error("There is more than one y:", *y, "while perform op", symbl_table[op], "in", task_expr, - "of task:", task_name); - return std::nullopt; - } - int times = 0; - try { - times = std::stoi(y->front()); - } - catch (...) { - Log.error("y:", y->front(), "is not number while perform op", symbl_table[op], "in", task_expr, - "of task:", task_name); - return std::nullopt; - } - for (int i = 0; i < times; ++i) { - ranges::copy(*x, std::back_inserter(*ret)); - } - break; - } - case symbl_at: { - if (y->empty()) { // A@#none = A - ranges::copy(*x, std::back_inserter(*ret)); - } - else if (x->empty()) { // #none@A = A - ranges::copy(*y, std::back_inserter(*ret)); - } - else { // (A+B)@(C+D) = A@C + A@D + B@C + B@D - ranges::for_each( - *x, [&](std::string_view s) { ranges::copy(append_prefix(*y, s), std::back_inserter(*ret)); }); - } - break; - } - case symbl_sharp: { - if (y->empty()) { - Log.error("There is no sharp_type while perform op", symbl_table[op], "in", task_expr, - "of task:", task_name); - return std::nullopt; - } - for (std::string_view type : *y) { - if (!x || x->empty()) { // unary - x = std::make_shared(tasklist_t { "" }); - } - for (const auto& task : *x) { - if (type == "self") { - ret->emplace_back(task_name); - continue; - } - if (type == "none") { - continue; - } - if (type == "back") { - // "A#back" === "A", "B@A#back" === "B@A", "#back" === null - if (!task.empty()) ret->emplace_back(task); - continue; - } - taskptr_t other_task_info_ptr = task.empty() ? default_task_info_ptr : get_raw(task); - -#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, m) \ - else if (type == #t) \ - { \ - bool task_changed = false; \ - if (!compile_tasklist(*ret, other_task_info_ptr->t, task_name, task_changed, m)) { \ - Log.error("Failed to compile task", task + "->" #t, "while perform op", symbl_table[op], "in", task_expr, \ - "of task:", task_name); \ - return std::nullopt; \ - } \ - } - if (other_task_info_ptr == nullptr) [[unlikely]] { - Log.error("Task", task, "not found while perform op", symbl_table[op], "in", task_expr, - "of task:", task_name); - return std::nullopt; - } - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(next, false) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(sub, true) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(on_error_next, false) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(exceeded_next, false) - ASST_TASKDATA_PERFORM_OP_IF_BRANCH(reduce_other_times, true) - else [[unlikely]] - { - Log.error("Unknown symbol", type, "while perform op", symbl_table[op], "in", task_expr, - "of task:", task_name); - return std::nullopt; - } -#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH - } - } - - break; - } - default: - Log.error("Unknown op", symbl_table[op], "in", task_expr, "of task:", task_name); - return std::nullopt; - } - return ret; - }; - for (std::string_view task_expr : raw_tasks) { if (task_expr.empty() || (!allow_duplicate && tasks_set.contains(task_expr))) { - task_changed = true; + ret.task_changed = true; continue; } - std::vector symbols_table = symbl_table; - - std::unordered_map symbols_id = {}; - for (symbl_t i = 0; i != symbols_table.size(); ++i) { - symbols_id.emplace(symbols_table[i], i); + TaskDataSymbolStream symbol_stream; + if (auto opt = symbol_stream.parse(task_expr); opt) [[likely]] { + ret.task_changed = ret.task_changed || opt.value(); + } + else { + return { std::nullopt, std::move(opt.what()) }; } - std::vector symbol_stream; - auto emplace_symbol_if_not_empty = [&](const auto l, const auto r) { - if (l < r) { - auto symbol = std::string(l, r); - if (!symbols_id.contains(symbol)) { - symbl_t id = symbols_table.size(); - symbols_id.emplace(symbol, id); - symbols_table.emplace_back(symbol); - symbol_stream.emplace_back(id); - } - else { - symbol_stream.emplace_back(symbols_id[symbol]); - } - } - }; + auto opt = symbol_stream.decode( + [&](const TaskDataSymbol& symbol, const TaskDataSymbol& prefix) -> TaskDataSymbol::SymbolsOrError { + return TaskDataSymbol::append_prefix( + symbol, prefix, self_name, get_raw, + [&](const tasklist_t& raw_or_empty) -> TaskDataSymbol::SymbolsOrError { + if (auto opt = compile_raw_tasklist(raw_or_empty, self_name, get_raw, allow_duplicate)) { + return opt.value().symbols; + } + return { std::nullopt, "" }; + }); + }, + self_name); - // 记号流分析 - auto y_begin = task_expr.begin(); - for (auto p = task_expr.begin(); p != task_expr.end(); ++p) { - switch (*p) { - case ' ': - case '\t': - case '\r': - case '\n': - emplace_symbol_if_not_empty(y_begin, p); - y_begin = p + 1; - break; - case ',': - case '(': - case ')': - case '{': - case '}': - case '#': - case '*': - case '+': - case '^': - task_changed = true; - [[fallthrough]]; - case '@': - emplace_symbol_if_not_empty(y_begin, p); - y_begin = p + 1; - symbol_stream.emplace_back(symbols_id[std::string(1, *p)]); - break; - default: - break; - } - } - emplace_symbol_if_not_empty(y_begin, task_expr.end()); - symbol_stream.emplace_back(symbl_end); // 结束符 - // Log.debug(symbol_stream | views::transform([&](symbl_t id) { return symbols_table[id]; })); - // Log.debug(symbol_stream); - - /* - $name = 至少一位的任务名 - $numbers = 至少一位的数字 - - $subtask_type = sub - | next - | on_error_next - | exceeded_next - | reduce_other_times - - $sharp_type = $subtask_type - | self - | back - | none - - $subtask = $subtask_type ( $tasks ) - - $lambda_task = { $subtask , ... } // 暂未实现 - | { $tasks } // 暂未实现;不写 subtask_type 默认为 sub - - $parens = # $sharp_type - | $name - | $name $lambda_task // 暂未实现 - | ( $tasks ) - - $top_tasks = $top_tasks @ $parens - | $top_tasks # $sharp_type - | $parens - - $mul_tasks = $top_tasks * $numbers - | $top_tasks - - $tasks = $mul_tasks + $tasks - | $mul_tasks ^ $tasks // 删除 $mul_tasks 中所有在 $tasks 中的任务 - | $mul_tasks - */ // 记号流处理 - auto cur = symbol_stream.cbegin(); - using decode_func_t = std::function()>; - decode_func_t decode_name, decode_tasks, decode_multasks, decode_vtasks, decode_parens; - decode_name = [&]() -> std::optional { - if (is_symbl_name(*cur)) { - return std::make_shared(tasklist_t { symbols_table[*(cur++)] }); - } - return std::nullopt; - }; - decode_tasks = [&]() -> std::optional { - auto l = decode_multasks(); - if (!l) return std::nullopt; - while (*cur == symbl_add || *cur == symbl_sub) { - symbl_t op = *(cur++); - auto r = decode_multasks(); - if (!r) return std::nullopt; - l = perform_op(task_expr, op, *l, *r); - if (!l) return std::nullopt; - } - return l; - }; - decode_multasks = [&]() -> std::optional { - auto l = decode_vtasks(); - if (!l) return std::nullopt; - while (*cur == symbl_mul) { - symbl_t op = *(cur++); - auto r = decode_name(); - if (!r) return std::nullopt; - l = perform_op(task_expr, op, *l, *r); - if (!l) return std::nullopt; - } - return l; - }; - decode_vtasks = [&]() -> std::optional { - auto l = decode_parens(); - if (!l) return std::nullopt; - while (*cur == symbl_at || *cur == symbl_sharp) { - symbl_t op = *(cur++); - auto r = decode_parens(); - if (!r) return std::nullopt; - l = perform_op(task_expr, op, *l, *r); - if (!l) return std::nullopt; - } - return l; - }; - decode_parens = [&]() -> std::optional { - if (*cur == symbl_lparen) { - ++cur; - auto l = decode_tasks(); - if (!l) return std::nullopt; - if (*cur != symbl_rparen) [[unlikely]] { - Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream, - "(should be ')')"); - return std::nullopt; - } - ++cur; - return l; - } - if (*cur == symbl_sharp) { - ++cur; - auto r = decode_name(); - if (!r) return std::nullopt; - return perform_op(task_expr, symbl_sharp, nullptr, *r); - } - if (is_symbl_name(*cur)) { - return decode_name(); - } - Log.error("Invalid symbol", *cur, "at", cur - symbol_stream.cbegin(), "in", symbol_stream); - return std::nullopt; - }; - auto opt = decode_tasks(); - if (!opt || (*cur != symbl_end && cur != symbol_stream.cend())) { - return false; + if (!opt) { + return { std::nullopt, std::move(opt.what()) }; } - for (const auto& task : **opt) { - if (task.empty() || (!allow_duplicate && tasks_set.contains(task))) { - task_changed = true; + for (const auto& task : *opt) { + if (task.name().empty() || (!allow_duplicate && tasks_set.contains(task.name()))) { + ret.task_changed = true; continue; } - new_tasks.emplace_back(task); - tasks_set.emplace(task_name_view(task)); + tasks_set.emplace(ret.symbols.emplace_back(task).name()); } tasks_set.emplace(task_name_view(task_expr)); } - return true; + return ret; +} + +asst::ResultOrError asst::TaskData::compile_tasklist(const tasklist_t& raw_tasks, + std::string_view self_name, + bool allow_duplicate) +{ + CompileResult ret { .task_changed = false, .tasks = {} }; + std::vector new_symbols; + if (auto opt = compile_raw_tasklist( + raw_tasks, self_name, [&](std::string_view name) { return get_raw(name); }, allow_duplicate); + !opt) { + return { std::nullopt, std::move(opt.what()) }; + } + else { + new_symbols = std::move(opt.value().symbols); + ret.task_changed = opt.value().task_changed; + } + std::unordered_set tasks_set; + for (const auto& task : new_symbols) { + std::string_view task_name; + if (task == TaskDataSymbol::SharpSelf) { + task_name = self_name; + ret.task_changed = true; + } + else if (task.is_name()) { + task_name = task.name(); + } + else { + ret.task_changed = true; + continue; + } + if (task_name.empty()) { + Log.error("Empty task name in", self_name); + ret.task_changed = true; + continue; + } + if (!allow_duplicate && tasks_set.contains(task_name)) { + ret.task_changed = true; + continue; + } + ret.tasks.emplace_back(task_name_view(task_name)); + tasks_set.emplace(task_name); + } + + return ret; } #ifdef ASST_DEBUG diff --git a/src/MaaCore/Config/TaskData.h b/src/MaaCore/Config/TaskData.h index 79c02c2d49..4b8a99a21d 100644 --- a/src/MaaCore/Config/TaskData.h +++ b/src/MaaCore/Config/TaskData.h @@ -8,6 +8,7 @@ #include #include "Common/AsstTypes.h" +#include "TaskData/TaskDataSymbol.h" namespace asst { @@ -15,21 +16,22 @@ namespace asst class TaskData final : public SingletonHolder, public AbstractConfigWithTempl { - private: + public: using tasklist_t = std::vector; using tasklistptr_t = std::shared_ptr; using taskptr_t = std::shared_ptr; - std::shared_ptr _default_match_task_info(); - std::shared_ptr _default_ocr_task_info(); - std::shared_ptr _default_hash_task_info(); - taskptr_t _default_task_info(); + private: + static std::shared_ptr _default_match_task_info(); + static std::shared_ptr _default_ocr_task_info(); + static std::shared_ptr _default_hash_task_info(); + static taskptr_t _default_task_info(); // 从模板任务生成 - const std::shared_ptr default_match_task_info_ptr = _default_match_task_info(); - const std::shared_ptr default_ocr_task_info_ptr = _default_ocr_task_info(); - const std::shared_ptr default_hash_task_info_ptr = _default_hash_task_info(); - const taskptr_t default_task_info_ptr = _default_task_info(); + static inline const std::shared_ptr default_match_task_info_ptr = _default_match_task_info(); + static inline const std::shared_ptr default_ocr_task_info_ptr = _default_ocr_task_info(); + static inline const std::shared_ptr default_hash_task_info_ptr = _default_hash_task_info(); + static inline const taskptr_t default_task_info_ptr = _default_task_info(); // 这是下面几个函数的封装 taskptr_t generate_task_info(std::string_view name, const json::value&, taskptr_t default_ptr, @@ -129,7 +131,10 @@ namespace asst return task_list; } - std::string_view task_name_view(std::string_view task_name) { return *m_task_names.emplace(task_name).first; } + static std::string_view task_name_view(std::string_view task_name) + { + return *m_task_names.emplace(task_name).first; + } decltype(auto) insert_or_assign_raw_task(std::string_view task_name, taskptr_t task_info_ptr) { return m_raw_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr); @@ -138,8 +143,23 @@ namespace asst { return m_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr); } - bool compile_tasklist(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view name, - bool& task_changed, bool multi); + + struct RawCompileResult + { + bool task_changed; + TaskDataSymbol::Symbols symbols; + }; + static ResultOrError compile_raw_tasklist(const tasklist_t& raw_tasks, + std::string_view self_name, + std::function get_raw, + bool allow_duplicate); + struct CompileResult + { + bool task_changed; + tasklist_t tasks; + }; + ResultOrError compile_tasklist(const tasklist_t& raw_tasks, std::string_view self_name, + bool allow_duplicate); bool generate_task_and_its_base(std::string_view name, bool must_true); #ifdef ASST_DEBUG bool syntax_check(std::string_view task_name, const json::value& task_json); @@ -187,7 +207,7 @@ namespace asst virtual bool parse(const json::value& json) override; - std::unordered_set m_task_names; + static inline std::unordered_set m_task_names; std::unordered_set m_templ_required; std::unordered_map m_task_status; std::unordered_map m_json_all_tasks_info; // 原始的 json 信息 diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbol.cpp b/src/MaaCore/Config/TaskData/TaskDataSymbol.cpp new file mode 100644 index 0000000000..b19cb90c03 --- /dev/null +++ b/src/MaaCore/Config/TaskData/TaskDataSymbol.cpp @@ -0,0 +1,56 @@ +#include "TaskDataSymbol.h" + +asst::TaskDataSymbol::SymbolsOrError asst::TaskDataSymbol::append_prefix( + const TaskDataSymbol& symbol, const TaskDataSymbol& prefix, std::string_view self_name, + std::function(std::string_view)> get_raw, + std::function&)> compile_tasklist) +{ + // !!!注意: + // A@#self 是 A 而不是 A@self_name, #self@A 是 selfname@A 而不是 A + std::string_view prefix_name; + if (prefix == SharpSelf) { + prefix_name = self_name; + } + else if (prefix.is_name()) { + prefix_name = prefix.name(); + } + else [[unlikely]] { + return { std::nullopt, "prefix " + prefix.name() + " is not name or self" }; + } + if (prefix_name.empty()) { + return std::vector { symbol }; + } + if (symbol == SharpNone) { + return std::vector { symbol }; + } + if (symbol == SharpSelf) { + return std::vector { symbol }; + } + if (symbol.is_name()) { + if (symbol.name().empty()) { + return { std::nullopt, "name is empty" }; + } + return std::vector { TaskDataSymbol(std::string(prefix_name) + '@' + symbol.name()) }; + } + if (symbol == SharpBack) { + return std::vector { TaskDataSymbol(prefix_name) }; + } + auto other_task_info_ptr = get_raw(prefix_name); +#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, s, m) \ + if (symbol == s) { \ + if (auto opt = compile_tasklist(other_task_info_ptr->t)) { \ + return opt.value(); \ + } \ + return { std::nullopt, "failed to compile tasklist" }; \ + } + if (other_task_info_ptr == nullptr) [[unlikely]] { + return { std::nullopt, "task not found" }; + } + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(next, SharpNext, false) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(sub, SharpSub, true) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(on_error_next, SharpOnErrorNext, false) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(exceeded_next, SharpExceededNext, false) + ASST_TASKDATA_PERFORM_OP_IF_BRANCH(reduce_other_times, SharpReduceOtherTimes, true) +#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH + return { std::nullopt, "unknown symbol" }; +} diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbol.h b/src/MaaCore/Config/TaskData/TaskDataSymbol.h new file mode 100644 index 0000000000..7363d2b951 --- /dev/null +++ b/src/MaaCore/Config/TaskData/TaskDataSymbol.h @@ -0,0 +1,136 @@ +#pragma once + +#include +#include +#include +#include + +#include "TaskDataTypes.hpp" + +#include "Common/AsstTypes.h" +#include "Utils/Logger.hpp" + +namespace asst +{ + class TaskDataSymbol final + { + public: + using Symbols = std::vector; + using SymbolsOrError = ResultOrError; + + enum Type + { + End, + LambdaTaskSep, + LParen, + RParen, + LBrace, + RBrace, + At, + Sharp, + Mul, + Add, + Sub, + SharpSub, + SharpNext, + SharpOnErrorNext, + SharpExceededNext, + SharpReduceOtherTimes, + SharpSelf, + SharpBack, + SharpNone, + Name, + }; + + static const inline std::unordered_map symbol_repr_to_type = { + { "__END__", End }, + { ",", LambdaTaskSep }, + { "(", LParen }, + { ")", RParen }, + { "{", LBrace }, + { "}", RBrace }, + { "@", At }, + { "#", Sharp }, + { "*", Mul }, + { "+", Add }, + { "^", Sub }, + { "sub", SharpSub }, + { "next", SharpNext }, + { "on_error_next", SharpOnErrorNext }, + { "exceeded_next", SharpExceededNext }, + { "reduce_other_times", SharpReduceOtherTimes }, + { "self", SharpSelf }, + { "back", SharpBack }, + { "none", SharpNone }, + { "__name__", Name }, + }; + static const inline std::unordered_map symbol_type_to_repr = { + { End, "__END__" }, + { LambdaTaskSep, "," }, + { LParen, "(" }, + { RParen, ")" }, + { LBrace, "{" }, + { RBrace, "}" }, + { At, "@" }, + { Sharp, "#" }, + { Mul, "*" }, + { Add, "+" }, + { Sub, "^" }, + { SharpSub, "sub" }, + { SharpNext, "next" }, + { SharpOnErrorNext, "on_error_next" }, + { SharpExceededNext, "exceeded_next" }, + { SharpReduceOtherTimes, "reduce_other_times" }, + { SharpSelf, "self" }, + { SharpBack, "back" }, + { SharpNone, "none" }, + { Name, "__name__" }, + }; + + Type m_symbol; + std::string m_name; + + public: + bool operator==(Type other) const noexcept { return m_symbol == other; } + bool operator==(std::string_view other) const noexcept { return m_symbol == Name && m_name == other; } + bool operator==(const TaskDataSymbol& other) const noexcept + { + return m_symbol == other.m_symbol && (m_symbol != Name || m_name == other.m_name); + } + TaskDataSymbol(Type symbol) : m_symbol(symbol) {} + template + requires(std::constructible_from) + TaskDataSymbol(StringT&& name) : m_symbol(Name), m_name(std::forward(name)) + {} + + static bool is_sharp_type(Type type) noexcept + { + static std::unordered_set sharp_types = { + SharpSub, SharpNext, SharpOnErrorNext, SharpExceededNext, SharpReduceOtherTimes, + SharpSelf, SharpBack, SharpNone, + }; + return sharp_types.contains(type); + } + static const std::string& repr(Type type) + { + const auto pos = symbol_type_to_repr.find(type); + return pos == symbol_type_to_repr.end() ? symbol_type_to_repr.at(Name) : pos->second; + } + static Type type(std::string_view repr) + { + const auto pos = symbol_repr_to_type.find(repr); + return pos == symbol_repr_to_type.end() ? Name : pos->second; + } + + static SymbolsOrError append_prefix( + const TaskDataSymbol& symbol, const TaskDataSymbol& prefix, std::string_view self_name, + std::function(std::string_view)> get_raw, + std::function&)> compile_tasklist); + + bool is_name() const noexcept { return m_symbol == Name; } + bool is_sharp_type() const noexcept { return is_sharp_type(m_symbol); } + Type type() const noexcept { return m_symbol; } + // const std::string& repr() const& noexcept { return repr(m_symbol); } + const std::string& name() const& noexcept { return is_name() ? m_name : repr(m_symbol); } + }; +} // namespace asst diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp b/src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp new file mode 100644 index 0000000000..ceccb50f48 --- /dev/null +++ b/src/MaaCore/Config/TaskData/TaskDataSymbolStream.cpp @@ -0,0 +1,266 @@ +#include "TaskDataSymbolStream.h" + +asst::ResultOrError asst::TaskDataSymbolStream::parse(std::string_view task_expr) +{ + bool task_changed = false; + auto emplace_symbol_if_not_empty = [&](const auto l, const auto r) { + if (l < r) { + auto symbol = std::string_view(l, r); + auto symbol_type = Symbol::type(symbol); + if (Symbol::is_sharp_type(symbol_type)) { + if (!m_symbolstream.empty() && m_symbolstream.back() == Symbol::Sharp) { + m_symbolstream.pop_back(); + if (!m_symbolstream.empty() && + (m_symbolstream.back().is_name() || m_symbolstream.back().is_sharp_type() || + m_symbolstream.back() == Symbol::RParen)) { + m_symbolstream.emplace_back(Symbol::At); + } + m_symbolstream.emplace_back(symbol_type); + } + else { + m_symbolstream.emplace_back(symbol); + } + } + else if (symbol_type != Symbol::Name) { + m_symbolstream.emplace_back(symbol_type); + } + else { + m_symbolstream.emplace_back(symbol); + } + } + }; + + // 记号流分析 + auto y_begin = task_expr.cbegin(); + for (auto p = task_expr.cbegin(); p != task_expr.cend(); ++p) { + switch (*p) { + case ' ': + case '\t': + case '\r': + case '\n': + task_changed = true; + emplace_symbol_if_not_empty(y_begin, p); + y_begin = p + 1; + break; + case ',': + case '(': + case ')': + case '{': + case '}': + case '#': + case '*': + case '+': + case '^': + task_changed = true; + [[fallthrough]]; + case '@': { + emplace_symbol_if_not_empty(y_begin, p); + y_begin = p + 1; + auto symbol = TaskDataSymbol::type(std::string_view { std::addressof(*p), 1 }); + if (symbol == TaskDataSymbol::Name) [[unlikely]] { + // should not happen + return { std::nullopt, std::string("Error when decode symbol ") + *p + " at " + + std::to_string(p - task_expr.cbegin()) + " in " += task_expr }; + } + m_symbolstream.emplace_back(symbol); + break; + } + default: + break; + } + } + emplace_symbol_if_not_empty(y_begin, task_expr.cend()); + m_symbolstream.emplace_back(TaskDataSymbol::End); // 结束符 + + return task_changed; +} + +asst::TaskDataSymbolStream::SymbolsOrError asst::TaskDataSymbolStream::decode(AppendPrefixFunc append_prefix, + std::string_view self_name) const +{ + /* + $name = 至少一位的任务名 + $numbers = 至少一位的数字 + + $subtask_type = # sub + | # next + | # on_error_next + | # exceeded_next + | # reduce_other_times + + $sharp_type = $subtask_type + | # self + | # back + | # none + + $subtask = $subtask_type ( $tasks ) + + $lambda_task = { $subtask , ... } // 暂未实现 + | { $tasks } // 暂未实现;不写 subtask_type 默认为 sub + + $parens = $sharp_type + | $name + | $name $lambda_task // 暂未实现 + | ( $tasks ) + + $top_tasks = $top_tasks @ $parens + | $parens + + $mul_tasks = $top_tasks * $numbers + | $top_tasks + + $tasks = $mul_tasks + $tasks + | $mul_tasks ^ $tasks // 删除 $mul_tasks 中所有在 $tasks 中的任务 + | $mul_tasks + */ + using decode_func_t = std::function; + + auto cur = m_symbolstream.cbegin(); + decode_func_t decode_name_or_vtask, decode_tasks, decode_multasks, decode_vtasks, decode_parens; + decode_name_or_vtask = [&]() -> SymbolsOrError { + if (cur->is_name() || cur->is_sharp_type()) { + return Symbols { *(cur++) }; + } + return { std::nullopt, "expect name or sharp type, got " + cur->name() }; + }; + decode_tasks = [&]() -> SymbolsOrError { + Symbols x = {}, y = {}; + if (auto opt = decode_multasks(); !opt) { + return opt; + } + else { + x = *opt; + } + while (*cur == Symbol::Add || *cur == Symbol::Sub) { + Symbol op = *(cur++); + if (auto opt = decode_multasks(); !opt) { + return opt; + } + else { + y = *opt; + } + if (op == Symbol::Add) { + // x = x + y + ranges::move(y, std::back_inserter(x)); + } + else { + // x = x - y + // WARNING: + // self_name + #self ^ #self = #none + // self_name + #self ^ self_name = #self + if (ranges::any_of(y, [&](const auto& sy) { return sy == Symbol::SharpSelf; })) { + auto remove_ret = ranges::remove(x, self_name); + x.erase(remove_ret.begin(), remove_ret.end()); + } + auto remove_ret = ranges::remove_if( + x, [&](const auto& sx) { return ranges::any_of(y, [&](const auto& sy) { return sx == sy; }); }); + x.erase(remove_ret.begin(), remove_ret.end()); + } + } + return x; + }; + decode_multasks = [&]() -> SymbolsOrError { + Symbols x = {}, y = {}; + if (auto opt = decode_vtasks(); !opt) { + return opt; + } + else { + x = *opt; + } + while (*cur == Symbol::Mul) { + Symbol op = *(cur++); + if (auto opt = decode_name_or_vtask(); !opt) { + return opt; + } + else { + y = *opt; + } + if (y.size() != 1) { + return { std::nullopt, "too many y" }; + } + int times = 0; + if (const auto s = y.front(); !s.is_name() || !utils::chars_to_number(s.name(), times)) { + return { std::nullopt, "y is not number" }; + } + if (times < 0) { + return { std::nullopt, "y is negative" }; + } + if (times == 0) { + x = {}; + Log.warn("y:", times, "is zero"); + continue; + } + if (times == 1) { + continue; + } + Symbols x_copy = x; + for (int i = 0; i < times - 1; ++i) { + ranges::copy(x_copy, std::back_inserter(x)); + } + } + return x; + }; + decode_vtasks = [&]() -> SymbolsOrError { + Symbols x = {}, y = {}; + if (auto opt = decode_parens(); !opt) { + return opt; + } + else { + x = *opt; + } + while (*cur == Symbol::At) { + Symbol op = *(cur++); + if (auto opt = decode_parens(); !opt) { + return opt; + } + else { + y = *opt; + } + // (A+B)@(C+D) = A@C + A@D + B@C + B@D + Symbols x_copy = x; + x.clear(); + for (const auto& sx : x_copy) { + for (const auto& sy : y) { + auto opt = append_prefix(sy, sx); + if (!opt) { + return { std::nullopt, + "decode_vtasks: failed while " + sx.name() + " @ " + sy.name() + ", " + opt.what() }; + } + ranges::copy(*opt, std::back_inserter(x)); + } + } + } + return x; + }; + decode_parens = [&]() -> SymbolsOrError { + if (*cur == Symbol::LParen) { + ++cur; + Symbols x = {}; + if (auto opt = decode_tasks(); !opt) { + return opt; + } + else { + x = *opt; + } + if (*cur != Symbol::RParen) [[unlikely]] { + return { std::nullopt, "decode_parens: expected ')', got " + cur->name() }; + } + ++cur; + return x; + } + if (cur->is_name() || cur->is_sharp_type()) { + return decode_name_or_vtask(); + } + return { std::nullopt, "decode_parens: unexpected symbol " + cur->name() }; + }; + + auto opt = decode_tasks(); + if (!opt) { + return opt; + } + if (cur != m_symbolstream.cend() && *cur != TaskDataSymbol::End) { + return { std::nullopt, "decode: did not reach end, got " + cur->name() }; + } + + return opt; +} diff --git a/src/MaaCore/Config/TaskData/TaskDataSymbolStream.h b/src/MaaCore/Config/TaskData/TaskDataSymbolStream.h new file mode 100644 index 0000000000..4dd58fdb37 --- /dev/null +++ b/src/MaaCore/Config/TaskData/TaskDataSymbolStream.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include "Config/TaskData.h" +#include "TaskDataSymbol.h" +#include "TaskDataTypes.hpp" + +namespace asst +{ + class TaskDataSymbolStream final + { + public: + using Symbol = TaskDataSymbol; + using Symbols = TaskDataSymbol::Symbols; + using SymbolsOrError = TaskDataSymbol::SymbolsOrError; + using AppendPrefixFunc = std::function; + + private: + Symbols m_symbolstream; + + public: + ResultOrError parse(std::string_view task_expr); + SymbolsOrError decode(AppendPrefixFunc append_prefix, std::string_view self_name) const; + }; +} // namespace asst diff --git a/src/MaaCore/Config/TaskData/TaskDataTypes.hpp b/src/MaaCore/Config/TaskData/TaskDataTypes.hpp new file mode 100644 index 0000000000..98fd6478b8 --- /dev/null +++ b/src/MaaCore/Config/TaskData/TaskDataTypes.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +namespace asst +{ + template + class ResultOrError + { + using _variant_t = std::variant; + bool m_success; + _variant_t m_result_or_error; + + public: + template + requires std::constructible_from + constexpr ResultOrError(Args&&... args) + : m_success(true), m_result_or_error(std::in_place_index<0>, std::forward(args)...) + {} + + constexpr ResultOrError([[maybe_unused]] std::nullopt_t nullopt, std::string result) + : m_success(false), m_result_or_error(std::in_place_index<1>, std::move(result)) + {} + + constexpr operator bool() const noexcept { return m_success; } + constexpr bool has_value() const noexcept { return m_success; } + + constexpr ResultT& operator*() & { return std::get<0>(m_result_or_error); } + constexpr const ResultT& operator*() const& { return std::get<0>(m_result_or_error); } + constexpr ResultT&& operator*() && { return std::move(std::get<0>(m_result_or_error)); } + constexpr const ResultT&& operator*() const&& { return std::move(std::get<0>(m_result_or_error)); } + + constexpr ResultT& value() & { return std::get<0>(m_result_or_error); } + constexpr const ResultT& value() const& { return std::get<0>(m_result_or_error); } + constexpr ResultT&& value() && { return std::move(std::get<0>(m_result_or_error)); } + constexpr const ResultT&& value() const&& { return std::move(std::get<0>(m_result_or_error)); } + + constexpr std::string& what() & { return std::get<1>(m_result_or_error); } + constexpr const std::string& what() const& { return std::get<1>(m_result_or_error); } + constexpr std::string&& what() && { return std::move(std::get<1>(m_result_or_error)); } + constexpr const std::string&& what() const&& { return std::move(std::get<1>(m_result_or_error)); } + }; +} // namespace asst diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj index 99dcffe697..c2f827c378 100644 --- a/src/MaaCore/MaaCore.vcxproj +++ b/src/MaaCore/MaaCore.vcxproj @@ -48,6 +48,9 @@ + + + @@ -229,6 +232,8 @@ + + diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters index b65b19442a..141af70b06 100644 --- a/src/MaaCore/MaaCore.vcxproj.filters +++ b/src/MaaCore/MaaCore.vcxproj.filters @@ -95,6 +95,9 @@ {bf83678b-e9d7-4cf1-8616-aba2009072b6} + + {8d436f0b-8dad-42b2-9549-f9073b808104} + @@ -681,6 +684,15 @@ Source\Task\Roguelike + + Source\Resource\TaskData + + + Source\Resource\TaskData + + + Source\Resource\TaskData + @@ -1136,5 +1148,11 @@ Source\Task\Roguelike + + Source\Resource\TaskData + + + Source\Resource\TaskData + \ No newline at end of file