feat: 简易的任务表达式运算

This commit is contained in:
zzyyyl
2023-02-22 11:00:43 +08:00
parent 81e8e73184
commit d681bafeee
2 changed files with 413 additions and 89 deletions

View File

@@ -44,7 +44,7 @@ std::shared_ptr<asst::TaskInfo> asst::TaskData::get(std::string_view name)
return it->second;
}
return expend_sharp_task(name, get_raw(name)).value_or(nullptr);
return expend_task(name, get_raw(name)).value_or(nullptr);
}
bool asst::TaskData::parse(const json::value& json)
@@ -142,7 +142,7 @@ bool asst::TaskData::parse(const json::value& json)
// 生成 # 型任务
for (const auto& [name, old_task] : m_raw_all_tasks_info) {
expend_sharp_task(name, old_task);
expend_task(name, old_task);
}
}
@@ -197,97 +197,416 @@ bool asst::TaskData::parse(const json::value& json)
return true;
}
std::optional<asst::TaskData::taskptr_t> asst::TaskData::expend_sharp_task(std::string_view name, taskptr_t old_task)
std::optional<asst::TaskData::tasklistptr_t> asst::TaskData::perform_op(std::string_view task_name,
std::string_view subtask_name, char op,
tasklistptr_t x, tasklistptr_t y)
{
auto ret = std::make_shared<tasklist_t>();
switch (op) {
case '+':
ranges::copy(*x, std::back_inserter(*ret));
ranges::copy(*y, std::back_inserter(*ret));
break;
case '^':
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 '*': {
if (y->size() != 1) {
Log.error("There is more than one y:", *y, "while perform op", op, "in task:", subtask_name,
"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", op, "in task:", subtask_name,
"of task:", task_name);
return std::nullopt;
}
for (int i = 0; i < times; ++i) {
ranges::copy(*x, std::back_inserter(*ret));
}
break;
}
case '@': {
for (std::string_view s : *x) {
ranges::copy(append_prefix(*y, s), std::back_inserter(*ret));
}
break;
}
case '#': {
if (x->size() != 1) {
// TODO: 这个限制可以去掉,例如 "(A+B)#next" 可以是 [ "A#next", "B#next" ]
Log.error("There is more than one x:", *x, "while perform op", op, "in task:", subtask_name,
"of task:", task_name);
return std::nullopt;
}
if (y->size() != 1) {
Log.error("There is more than one y:", *y, "while perform op", op, "in task:", subtask_name,
"of task:", task_name);
return std::nullopt;
}
std::string_view type = y->front();
if (type == "self") {
ret->emplace_back(task_name);
break;
}
else if (type == "back") {
// "A#back" === "A", "B@A#back" === "B@A", "#back" === null
if (!x->front().empty()) {
ret->emplace_back(x->front());
}
break;
}
taskptr_t other_task_info_ptr = x->front().empty() ? default_task_info_ptr : get_raw(x->front());
// 都进 perform_op 了task_change 必为 true
#define ASST_TASKDATA_PERFORM_OP_IF_BRANCH(t, m) \
else if (type == #t) \
{ \
bool task_changed = false; \
std::unordered_set<std::string_view> tasks_set {}; \
if (!explain_tasks(*ret, other_task_info_ptr->t, task_name, task_changed, tasks_set, m)) { \
Log.error("Explain task", x->front() + "->" #t, "failed while perform op", op, "in task:", subtask_name, \
"of task:", task_name); \
return std::nullopt; \
} \
}
if (other_task_info_ptr == nullptr) [[unlikely]] {
Log.error("Task", x->front(), "not found while perform op", op, "in task:", subtask_name,
"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 sharp type", type, "while perform op", op, "in task:", subtask_name,
"of task:", task_name);
return std::nullopt;
}
#undef ASST_TASKDATA_PERFORM_OP_IF_BRANCH
break;
}
default:
Log.error("Unknown op", op, "in task:", subtask_name, "of task:", task_name);
return std::nullopt;
}
return ret;
}
// new_tasks 是目的任务列表
// raw_tasks 是源任务表达式列表
// task_name 是任务名
// task_changed 记录 raw_tasks 在转换为 new_tasks 时是否发生变化
// tasks_set 记录任务列表中已有的任务(内容元素与 new_tasks 基本一致)
// multi 表示是否允许重复
bool asst::TaskData::explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view task_name,
bool& task_changed, [[maybe_unused]] std::unordered_set<std::string_view>& tasks_set,
[[maybe_unused]] bool multi)
{
for (std::string_view subtask_name : raw_tasks) {
if (subtask_name.empty()) {
task_changed = true;
continue;
}
if (tasks_set.contains(subtask_name) && !multi) {
task_changed = true;
continue;
}
// 记号表中预分配一些保留字
std::vector<std::string> symbols_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
};
[[maybe_unused]] constexpr size_t symbl_end = 0;
[[maybe_unused]] constexpr size_t symbl_lambda_task_sep = 1;
[[maybe_unused]] constexpr size_t symbl_lparen = 2;
[[maybe_unused]] constexpr size_t symbl_rparen = 3;
[[maybe_unused]] constexpr size_t symbl_lbrace = 4;
[[maybe_unused]] constexpr size_t symbl_rbrace = 5;
[[maybe_unused]] constexpr size_t symbl_at = 6;
[[maybe_unused]] constexpr size_t symbl_sharp = 7;
[[maybe_unused]] constexpr size_t symbl_mul = 8;
[[maybe_unused]] constexpr size_t symbl_add = 9;
[[maybe_unused]] constexpr size_t symbl_sub = 10;
[[maybe_unused]] constexpr size_t symbl_name_start = 11;
[[maybe_unused]] constexpr size_t symbl_name_sub = 11;
[[maybe_unused]] constexpr size_t symbl_name_next = 12;
[[maybe_unused]] constexpr size_t symbl_name_on_error_next = 13;
[[maybe_unused]] constexpr size_t symbl_name_exceeded_next = 14;
[[maybe_unused]] constexpr size_t symbl_name_reduce_other_times = 15;
[[maybe_unused]] constexpr size_t symbl_name_self = 16;
[[maybe_unused]] constexpr size_t symbl_name_back = 17;
[[maybe_unused]] constexpr size_t symbl_name_none = 18;
auto is_symbl_name = [&](size_t x) {
return x >= symbl_name_start;
};
auto is_symbl_subtask_type = [&](size_t x) {
return x == symbl_name_sub || x == symbl_name_next || x == symbl_name_on_error_next ||
x == symbl_name_exceeded_next || x == symbl_name_reduce_other_times;
};
auto is_symbl_sharp_type = [&](size_t x) {
return is_symbl_subtask_type(x) || x == symbl_name_self || x == symbl_name_back || x == symbl_name_none;
};
std::unordered_map<std::string, size_t> symbols_id = {};
for (size_t i = 0; i != symbols_table.size(); ++i) {
symbols_id.emplace(symbols_table[i], i);
}
std::vector<size_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)) {
size_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 y_begin = subtask_name.begin();
for (auto p = subtask_name.begin(); p != subtask_name.end(); ++p) {
switch (*p) {
case ' ':
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;
if (*p != ' ') {
symbol_stream.emplace_back(symbols_id[std::string(1, *p)]);
}
}
}
emplace_symbol_if_not_empty(y_begin, subtask_name.end());
symbol_stream.emplace_back(symbl_end); // 结束符
// Log.debug(symbol_stream | views::transform([&](size_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 // 推迟实现 3
$subtask = $subtask_type ( $tasks )
$lambda_task = { $subtask , ... } // 推迟实现 1
| { $tasks } // 推迟实现 1不写 subtask_type 默认为 sub
$parens = # $sharp_type
| $name
| $name $lambda_task // 推迟实现 1
| ( $tasks )
$top_tasks = $top_tasks @ $parens // 推迟实现 2
| $top_tasks # $sharp_type
| $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<std::optional<tasklistptr_t>()>;
decode_func_t decode_name, decode_tasks, decode_multasks, decode_vtasks, decode_parens;
std::vector<size_t>::const_iterator cur = symbol_stream.cbegin();
decode_name = [&]() -> std::optional<tasklistptr_t> {
if (*cur >= symbl_name_start) {
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) {
if (*cur == symbl_add) {
++cur;
auto r = decode_multasks();
if (!r) return std::nullopt;
l = perform_op(task_name, subtask_name, '+', *l, *r);
}
if (*cur == symbl_sub) {
++cur;
auto r = decode_multasks();
if (!r) return std::nullopt;
l = perform_op(task_name, subtask_name, '^', *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) {
if (*cur == symbl_mul) {
++cur;
auto r = decode_name();
if (!r) return std::nullopt;
l = perform_op(task_name, subtask_name, '*', *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) {
if (*cur == symbl_at) {
++cur;
auto r = decode_parens();
if (!r) return std::nullopt;
l = perform_op(task_name, subtask_name, '@', *l, *r);
}
if (*cur == symbl_sharp) {
++cur;
auto r = decode_name();
if (!r) return std::nullopt;
l = perform_op(task_name, subtask_name, '#', *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) {
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_name, subtask_name, '#', std::make_shared<tasklist_t>(tasklist_t { "" }), *r);
}
if (*cur >= symbl_name_start) {
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.value()->empty()) continue;
for (const auto& task : **opt) {
if (task.empty()) {
task_changed = true;
continue;
}
if (!tasks_set.contains(task)) {
tasks_set.emplace(task_name_view(task));
}
else if (!multi) {
task_changed = true;
continue;
}
new_tasks.emplace_back(task);
}
tasks_set.emplace(task_name_view(subtask_name));
}
return true;
}
std::optional<asst::TaskData::taskptr_t> asst::TaskData::expend_task(std::string_view name, taskptr_t old_task)
{
if (old_task == nullptr) [[unlikely]] {
return std::nullopt;
}
bool task_changed = false;
auto task_info = _generate_task_info(old_task);
auto expend_sharp_task_list = [&](tasklist_t& new_task_list, const tasklist_t& task_list,
std::string_view list_type) -> bool {
new_task_list.clear();
std::function<bool(const tasklist_t&)> generate_tasks;
std::unordered_set<std::string_view> tasks_set {};
generate_tasks = [&](const tasklist_t& raw_tasks) {
for (std::string_view task : raw_tasks) {
if (task.empty()) {
Log.error("Task", name, "has a empty", list_type);
return false;
}
if (tasks_set.contains(task)) [[unlikely]] {
task_changed = true;
continue;
}
tasks_set.emplace(task_name_view(task));
size_t pos = task.rfind('#');
if (pos == std::string_view::npos) [[likely]] {
new_task_list.emplace_back(task);
continue;
}
#ifdef ASST_DEBUG
if (pos == 0) [[unlikely]] {
Log.trace("Task", name, "has a virtual", list_type, ":", (std::string("`") += task) += '`');
}
#endif // ASST_DEBUG
task_changed = true;
std::string_view type = task.substr(pos + 1);
if (type == "self") {
new_task_list.emplace_back(name);
continue;
}
taskptr_t other_task_info_ptr = pos ? get_raw(task.substr(0, pos)) : default_task_info_ptr;
#define ASST_TASKDATA_GENERATE_TASKS(t) \
else if (type == #t) \
{ \
if (!generate_tasks(other_task_info_ptr->t)) { \
return false; \
} \
auto task_info = _generate_task_info(old_task); // 复制一份
bool task_changed = false; // 任务列表是否发生变化
std::unordered_set<std::string_view> tasks_set {};
#define ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(type, m) \
task_info->type.clear(); \
tasks_set.clear(); \
if (!explain_tasks(task_info->type, old_task->type, name, task_changed, tasks_set, m)) [[unlikely]] { \
Log.error("Generate task_list", std::string(name) + "->" #type, "failed."); \
return std::nullopt; \
}
if (other_task_info_ptr == nullptr) [[unlikely]] {
Log.error("Task", task, "not found");
return false;
}
else if (type == "back") {
// "A#back" === "A", "B@A#back" === "B@A", "#back" === null
if (pos) {
new_task_list.emplace_back(task.substr(0, pos));
}
}
ASST_TASKDATA_GENERATE_TASKS(next)
ASST_TASKDATA_GENERATE_TASKS(sub)
ASST_TASKDATA_GENERATE_TASKS(on_error_next)
ASST_TASKDATA_GENERATE_TASKS(exceeded_next)
ASST_TASKDATA_GENERATE_TASKS(reduce_other_times)
else [[unlikely]]
{
Log.error("Unknown type", type, "in", task);
return false;
}
#undef ASST_TASKDATA_GENERATE_TASKS
}
return true;
};
if (!generate_tasks(task_list)) [[unlikely]] {
Log.error("Generate task_list", (std::string(name) += "->") += list_type, "failed.");
return false;
}
return true;
};
#define ASST_TASKDATA_GENERATE_SHARP_TASK(type) \
if (!expend_sharp_task_list(task_info->type, old_task->type, #type)) { \
return std::nullopt; \
}
ASST_TASKDATA_GENERATE_SHARP_TASK(next);
ASST_TASKDATA_GENERATE_SHARP_TASK(sub);
ASST_TASKDATA_GENERATE_SHARP_TASK(exceeded_next);
ASST_TASKDATA_GENERATE_SHARP_TASK(on_error_next);
ASST_TASKDATA_GENERATE_SHARP_TASK(reduce_other_times);
#undef ASST_TASKDATA_GENERATE_SHARP_TASK
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(next, false);
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(sub, true);
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(exceeded_next, false);
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(on_error_next, false);
ASST_TASKDATA_EXPEND_TASK_IF_BRANCH(reduce_other_times, true);
#undef ASST_TASKDATA_EXPEND_TASK_IF_BRANCH
// tasks 个数超过上限时不再 emplace返回临时值
constexpr size_t MAX_TASKS_SIZE = 65535;

View File

@@ -17,6 +17,7 @@ namespace asst
{
private:
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();
@@ -141,7 +142,11 @@ namespace asst
{
return m_all_tasks_info.insert_or_assign(task_name_view(task_name), task_info_ptr);
}
std::optional<taskptr_t> expend_sharp_task(std::string_view name, taskptr_t old_task);
std::optional<tasklistptr_t> perform_op(std::string_view task_name, std::string_view subtask_name, char op,
tasklistptr_t x, tasklistptr_t y);
bool explain_tasks(tasklist_t& new_tasks, const tasklist_t& raw_tasks, std::string_view name,
bool& task_changed, std::unordered_set<std::string_view>& tasks_set, bool multi);
std::optional<taskptr_t> expend_task(std::string_view name, taskptr_t old_task);
#ifdef ASST_DEBUG
bool syntax_check(const std::string& task_name, const json::value& task_json);
#endif