refactor: 从 TaskData 中分离 TaskDataSymbol, TaskDataSymbolStream;增加 TaskDataTypes

fix: 用 to_string 和 string 加法取代 std::format 以通过非 win 平台编译

asst::ResultOrError 是 std::expected 的下位替代(同时又类似于 std::optional)
(cherry picked from commit 55875a2eb5)
This commit is contained in:
zzyyyl
2023-11-02 18:48:59 +08:00
parent 8f145041d0
commit d86e5dc770
9 changed files with 671 additions and 385 deletions

View File

@@ -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::RawCompileResult> asst::TaskData::compile_raw_tasklist(
const tasklist_t& raw_tasks, std::string_view self_name, std::function<taskptr_t(std::string_view)> get_raw,
bool allow_duplicate)
{
RawCompileResult ret { .task_changed = false, .symbols = {} };
std::unordered_set<std::string_view> 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<std::string> 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<asst::TaskData::tasklistptr_t> {
auto ret = std::make_shared<tasklist_t>();
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>(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<std::string> symbols_table = symbl_table;
std::unordered_map<std::string, symbl_t> 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<symbl_t> 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<std::optional<tasklistptr_t>()>;
decode_func_t decode_name, decode_tasks, decode_multasks, decode_vtasks, decode_parens;
decode_name = [&]() -> std::optional<tasklistptr_t> {
if (is_symbl_name(*cur)) {
return std::make_shared<tasklist_t>(tasklist_t { symbols_table[*(cur++)] });
}
return std::nullopt;
};
decode_tasks = [&]() -> std::optional<tasklistptr_t> {
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<tasklistptr_t> {
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<tasklistptr_t> {
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<tasklistptr_t> {
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::CompileResult> 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<TaskDataSymbol> 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<std::string_view> 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

View File

@@ -8,6 +8,7 @@
#include <unordered_set>
#include "Common/AsstTypes.h"
#include "TaskData/TaskDataSymbol.h"
namespace asst
{
@@ -15,21 +16,22 @@ namespace asst
class TaskData final : public SingletonHolder<TaskData>, public AbstractConfigWithTempl
{
private:
public:
using tasklist_t = std::vector<std::string>;
using tasklistptr_t = std::shared_ptr<tasklist_t>;
using taskptr_t = std::shared_ptr<TaskInfo>;
std::shared_ptr<MatchTaskInfo> _default_match_task_info();
std::shared_ptr<OcrTaskInfo> _default_ocr_task_info();
std::shared_ptr<HashTaskInfo> _default_hash_task_info();
taskptr_t _default_task_info();
private:
static std::shared_ptr<MatchTaskInfo> _default_match_task_info();
static std::shared_ptr<OcrTaskInfo> _default_ocr_task_info();
static std::shared_ptr<HashTaskInfo> _default_hash_task_info();
static taskptr_t _default_task_info();
// 从模板任务生成
const std::shared_ptr<MatchTaskInfo> default_match_task_info_ptr = _default_match_task_info();
const std::shared_ptr<OcrTaskInfo> default_ocr_task_info_ptr = _default_ocr_task_info();
const std::shared_ptr<HashTaskInfo> 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<MatchTaskInfo> default_match_task_info_ptr = _default_match_task_info();
static inline const std::shared_ptr<OcrTaskInfo> default_ocr_task_info_ptr = _default_ocr_task_info();
static inline const std::shared_ptr<HashTaskInfo> 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<RawCompileResult> compile_raw_tasklist(const tasklist_t& raw_tasks,
std::string_view self_name,
std::function<taskptr_t(std::string_view)> get_raw,
bool allow_duplicate);
struct CompileResult
{
bool task_changed;
tasklist_t tasks;
};
ResultOrError<CompileResult> 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<std::string> m_task_names;
static inline std::unordered_set<std::string> m_task_names;
std::unordered_set<std::string> m_templ_required;
std::unordered_map<std::string_view, TaskStatus> m_task_status;
std::unordered_map<std::string_view, json::object> m_json_all_tasks_info; // 原始的 json 信息

View File

@@ -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::shared_ptr<TaskInfo>(std::string_view)> get_raw,
std::function<SymbolsOrError(const std::vector<std::string>&)> 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" };
}

View File

@@ -0,0 +1,136 @@
#pragma once
#include <string>
#include <string_view>
#include <unordered_map>
#include <variant>
#include "TaskDataTypes.hpp"
#include "Common/AsstTypes.h"
#include "Utils/Logger.hpp"
namespace asst
{
class TaskDataSymbol final
{
public:
using Symbols = std::vector<TaskDataSymbol>;
using SymbolsOrError = ResultOrError<Symbols>;
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<std::string_view, Type> 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<Type, std::string> 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 <typename StringT>
requires(std::constructible_from<std::string, StringT>)
TaskDataSymbol(StringT&& name) : m_symbol(Name), m_name(std::forward<StringT>(name))
{}
static bool is_sharp_type(Type type) noexcept
{
static std::unordered_set<Type> 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::shared_ptr<TaskInfo>(std::string_view)> get_raw,
std::function<SymbolsOrError(const std::vector<std::string>&)> 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

View File

@@ -0,0 +1,266 @@
#include "TaskDataSymbolStream.h"
asst::ResultOrError<bool> 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<SymbolsOrError()>;
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<int, true>(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;
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <optional>
#include <vector>
#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<SymbolsOrError(const Symbol&, const Symbol&)>;
private:
Symbols m_symbolstream;
public:
ResultOrError<bool> parse(std::string_view task_expr);
SymbolsOrError decode(AppendPrefixFunc append_prefix, std::string_view self_name) const;
};
} // namespace asst

View File

@@ -0,0 +1,45 @@
#pragma once
#include <optional>
#include <string>
#include <variant>
namespace asst
{
template <typename ResultT>
class ResultOrError
{
using _variant_t = std::variant<ResultT, std::string>;
bool m_success;
_variant_t m_result_or_error;
public:
template <typename... Args>
requires std::constructible_from<ResultT, Args...>
constexpr ResultOrError(Args&&... args)
: m_success(true), m_result_or_error(std::in_place_index<0>, std::forward<Args>(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

View File

@@ -48,6 +48,9 @@
<ClInclude Include="Config\Miscellaneous\OcrConfig.h" />
<ClInclude Include="Config\Miscellaneous\SSSCopilotConfig.h" />
<ClInclude Include="Config\OnnxSessions.h" />
<ClInclude Include="Config\TaskData\TaskDataSymbol.h" />
<ClInclude Include="Config\TaskData\TaskDataSymbolStream.h" />
<ClInclude Include="Config\TaskData\TaskDataTypes.hpp" />
<ClInclude Include="Controller\adb-lite\client.hpp" />
<ClInclude Include="Controller\adb-lite\protocol.hpp" />
<ClInclude Include="Controller\Controller.h" />
@@ -229,6 +232,8 @@
<ClCompile Include="Config\Miscellaneous\OcrConfig.cpp" />
<ClCompile Include="Config\Miscellaneous\SSSCopilotConfig.cpp" />
<ClCompile Include="Config\OnnxSessions.cpp" />
<ClCompile Include="Config\TaskData\TaskDataSymbol.cpp" />
<ClCompile Include="Config\TaskData\TaskDataSymbolStream.cpp" />
<ClCompile Include="Controller\adb-lite\client.cpp" />
<ClCompile Include="Controller\adb-lite\protocol.cpp" />
<ClCompile Include="Controller\Controller.cpp" />

View File

@@ -95,6 +95,9 @@
<Filter Include="Source\Vision\Config">
<UniqueIdentifier>{bf83678b-e9d7-4cf1-8616-aba2009072b6}</UniqueIdentifier>
</Filter>
<Filter Include="Source\Resource\TaskData">
<UniqueIdentifier>{8d436f0b-8dad-42b2-9549-f9073b808104}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="..\..\resource\config.json">
@@ -681,6 +684,15 @@
<ClInclude Include="Task\Roguelike\AbstractRoguelikeTaskPlugin.h">
<Filter>Source\Task\Roguelike</Filter>
</ClInclude>
<ClInclude Include="Config\TaskData\TaskDataSymbol.h">
<Filter>Source\Resource\TaskData</Filter>
</ClInclude>
<ClInclude Include="Config\TaskData\TaskDataSymbolStream.h">
<Filter>Source\Resource\TaskData</Filter>
</ClInclude>
<ClInclude Include="Config\TaskData\TaskDataTypes.hpp">
<Filter>Source\Resource\TaskData</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Vision\VisionHelper.cpp">
@@ -1136,5 +1148,11 @@
<ClCompile Include="Task\Roguelike\AbstractRoguelikeTaskPlugin.cpp">
<Filter>Source\Task\Roguelike</Filter>
</ClCompile>
<ClCompile Include="Config\TaskData\TaskDataSymbol.cpp">
<Filter>Source\Resource\TaskData</Filter>
</ClCompile>
<ClCompile Include="Config\TaskData\TaskDataSymbolStream.cpp">
<Filter>Source\Resource\TaskData</Filter>
</ClCompile>
</ItemGroup>
</Project>