Compare commits

...

3 Commits

4 changed files with 76 additions and 44 deletions

View File

@@ -184,21 +184,6 @@ JSON 文件是不支持注释的,文本中的注释仅用于演示,请勿直
}
```
## 表达式计算
任务列表类型字段(`sub`, `next`, `onErrorNext`, `exceededNext`, `reduceOtherTimes`)支持表达式计算。
| 符号 | 含义 | 实例 |
| :---------: | :------------------------------------------------------: | :------------------------------------: |
| `@` | `@` 型任务 | `Fight@ReturnTo` |
| `#`(单目) | 虚任务 | `#self` |
| `#`(双目) | 虚任务 | `StartUpThemes#next` |
| `*` | 重复多个任务 | `(ClickCornerAfterPRTS+ClickCorner)*5` |
| `+` | 任务列表合并(在 next 系列字段中同名任务只保留最靠前者) | `A+B` |
| `^` | 任务列表差(在前者但不在后者,顺序不变) | `(A+A+B+C)^(A+B+D)`(结果为 `C` |
运算符 `@`, `#`, `*`, `+`, `^` 有优先级:`#`(单目)> `@` = `#`(双目)> `*` > `+` = `^`
## 特殊任务类型
### 模板任务
@@ -229,25 +214,6 @@ JSON 文件是不支持注释的,文本中的注释仅用于演示,请勿直
3. 若字段 `algorithm` 与基任务不同,则派生类参数不继承(只继承 `TaskInfo` 定义的参数);
4. 其余字段的默认值均为**基任务对应字段**。
### 虚任务(`#` 型任务)
虚任务即型如 `"#{sharp_type}"``"B#{sharp_type}"` 的任务,其中 `{sharp_type}` 可以是 `none`, `self`, `back`, `next`, `sub`, `on_error_next`, `exceeded_next`, `reduce_other_times`
可以将虚任务分为**指令虚任务**`#none` / `#self` / `#back`)和**字段虚任务**`#next` 等)。
| 虚任务类型 | 含义 | 简单示例 |
| :----------: | :----------------: | :--------------------------------------------------------------------------------------------------------------------------------------: |
| none | 空任务 | 直接跳过<sup>1</sup><br>`"A": {"next": ["#none", "T1"]}` 被解释为 `"A": {"next": ["T1"]}`<br>`"A#none + T1"` 被解释为 `"T1"` |
| self | 当前任务名 | `"A": {"next": ["#self"]}` 中的 `"#self"` 被解释为 `"A"`<br>`"B": {"next": ["A@B@C#self"]}` 中的 `"A@B@C#self"` 被解释为 `"B"`<sup>2</sup> |
| back | # 前面的任务名 | `"A@B#back"` 被解释为 `"A@B"`<br>`"#back"` 直接出现则会被跳过<sup>3</sup> |
| next, sub 等 | # 前任务名对应字段 | 以 `next` 为例:<br>`"A#next"` 被解释为 `Task.get("A")->next`<br>`"#next"` 直接出现则会被跳过 |
*Note<sup>1</sup>: `"#none"` 一般配合模板任务增加前缀的特性使用,或用在字段 `baseTask` 中避免多文件继承不必要的字段。*
*Note<sup>2</sup>: `"XXX#self"` 与 `"#self"` 含义相同。*
*Note<sup>3</sup>: 当几个任务有 `"next": [ "#back" ]` 时,`"T1@T2@T3"` 代表依次执行 `T3`, `T2`, `T1`。*
### 多文件任务
如果后加载的任务文件 (例如外服 `tasks.json`; 下称文件二) 中定义的任务在先加载的任务文件 (例如国服 `tasks.json`; 下称文件一) 中也定义了同名任务,那么:
@@ -255,6 +221,79 @@ JSON 文件是不支持注释的,文本中的注释仅用于演示,请勿直
- 如果文件二中任务没有 `baseTask` 字段,则直接继承文件一中同名任务的字段。
- 如果文件二中任务有 `baseTask` 字段,则不继承文件一中同名任务的字段,而是直接覆盖。特别地,在没有模板任务时你可以使用 `"baseTask": "#none"` 来避免继承不必要的字段。
## 表达式计算
任务列表类型字段(`sub`, `next`, `onErrorNext`, `exceededNext`, `reduceOtherTimes`)支持表达式计算,我们提供四种二元运算:`@`, `*`, `+`, `^`
运算 `*``+``^` 的**左元**和运算 `@``+``^` 的**右元**为包含任意个以下元素的列表:
1. 任务名字符串(例如:`TaskA`, `Base@TaskB` 等);
2. [虚任务](#虚任务)。
运算 `@` 的左元必须为包含任务名字符串的列表;运算 `*` 的右元必须为一个正整数。
| 符号 | 含义 | 实例 |
| :---------: | :------------------------------------------------------: | :------------------------------------: |
| `@` | `@` 型任务 | `Fight@ReturnTo` |
| `*` | 重复多个任务 | `(ClickCornerAfterPRTS+ClickCorner)*5` |
| `+` | 任务列表合并(在 next 系列字段中同名任务只保留最靠前者) | `A+B` |
| `^` | 任务列表差(在前者但不在后者,顺序不变) | `(A+A+B+C)^(A+B+D)`(结果为 `C` |
运算符 `@`, `*`, `+`, `^` 均是左结合的,有优先级:`@` > `*` > `+` = `^`
### `@` 运算
运算 `@` 在左右元均为普通任务时,会将左元作为前缀,右元作为基任务,运算结果为一个 `@` 型任务:
- `A` `@` `B` = `A@B`
运算 `@` 的元为任务列表时,会按照类似满足右分配律的顺序运算:
- `[A1, A2, ..., An]` `@` `[B1, B2, ..., Bm]`
= `A1` `@` `[B1, B2, ..., Bm]` + `A2` `@` `[B1, B2, ..., Bm]` + ... + `An` `@` `[B1, B2, ..., Bm]`
= `[A1@B1, A1@B2, ..., A1@Bm, A2@B1, ..., An@Bm]`
### 虚任务
为使表达式计算支持更多功能,我们引入了虚任务。虚任务即型如 `"#{sharp_type}"` 的任务,其中 `{sharp_type}` 可以是 `none`, `self`, `back`, `next`, `sub`, `on_error_next`, `exceeded_next`, `reduce_other_times`
从功能上可以将虚任务分为**指令虚任务**`#none` / `#self` / `#back`)和**字段虚任务**`#next` 等)。
虚任务在 `@` 运算中有额外的规则。
当虚任务作为 `@` 运算的右元时,`@` 可以省略。例如,`A@#next` 等价于 `A#next`。(不要将其理解为二元运算 `#`
虚任务不能作为 `@` 运算的左元。例如 `#self@A``#back@B` 均是非法的,但 `A@#back@B` 是合法的。
#### SharpNone
SharpNone 即 `#none`。运行时会被解释为空任务列表。它满足:
- `A` `@` `#none` = `#none`
#### SharpSelf
SharpSelf 即 `#self`。运行时会被解释为当前任务名。例如 `"A": {"next": ["#self"]}` 中的 `#self` 被解释为 `A`。它满足:
- `A` `@` `#self` = `#self`
#### SharpBack
SharpBack 即 `#back`。运行时会被解释为空任务列表。它满足:
- `A` `@` `#back` = `A`
将他作为右元进行 `@` 运算可以解释为“返回”左元所指代的任务。当几个任务有 `"next": [ "#back" ]` 时,`"T1@T2@T3"` 代表依次执行 `T3`, `T2`, `T1`
#### 字段虚任务
字段虚任务在运行时会被解释为空任务列表。以 `#next` 为例,它满足:
- `A` `@` `#next` = Task.get_raw("A")->next<sup>\*</sup>
将他作为右元进行 `@` 运算结果为左元所指代的任务的对应字段。
*Note<sup>\*</sup>: 注意这里是 get_raw 而非 get。*
### 使用示例
- 派生任务示例(字段 `baseTask`
@@ -385,7 +424,7 @@ JSON 文件是不支持注释的,文本中的注释仅用于演示,请勿直
## 运行时修改任务
- `Task.lazy_parse()` 可以在运行时加载 json 任务配置文件。 lazy_parse 规则与[多文件任务](#多文件任务)相同。
- `Task.set_task_base()` 可以修改任务的 `baseTask` 字段。
- `Task.set_task_base()` 可以修改任务的 `baseTask` 字段。*不推荐使用此特性。*
### 使用示例

View File

@@ -863,7 +863,6 @@ asst::ResultOrError<asst::TaskData::RawCompileResult> asst::TaskData::compile_ra
return TaskDataSymbol::append_prefix(
symbol,
prefix,
self_name,
get_raw,
[&](const TaskList& raw_or_empty) -> TaskDataSymbol::SymbolsOrError {
if (auto opt = compile_raw_tasklist(raw_or_empty, self_name, get_raw, allow_duplicate)) {

View File

@@ -3,20 +3,15 @@
asst::TaskDataSymbol::SymbolsOrError asst::TaskDataSymbol::append_prefix(
const TaskDataSymbol& symbol,
const TaskDataSymbol& prefix,
std::string_view self_name,
std::function<TaskDerivedConstPtr(std::string_view)> get_raw,
std::function<SymbolsOrError(const TaskList&)> compile_tasklist)
{
// 注意A@#self 是 A 而不是 A@self_name, #self@A 是 self_name@A 而不是 A
std::string_view prefix_name;
if (prefix == SharpSelf) {
prefix_name = self_name;
}
else if (prefix.is_name()) {
if (prefix.is_name()) {
prefix_name = prefix.name();
}
else [[unlikely]] {
return { std::nullopt, "prefix " + prefix.name() + " is not name or self" };
return { std::nullopt, "prefix " + prefix.name() + " is not name" };
}
if (prefix_name.empty()) {
return std::vector { symbol };

View File

@@ -136,7 +136,6 @@ public:
static SymbolsOrError append_prefix(
const TaskDataSymbol& symbol,
const TaskDataSymbol& prefix,
std::string_view self_name,
std::function<TaskDerivedConstPtr(std::string_view)> get_raw,
std::function<SymbolsOrError(const TaskList&)> compile_tasklist);