mainly theoratical work
This commit is contained in:
@@ -0,0 +1,134 @@
|
|||||||
|
# NGRA Regex 源生成手写样本
|
||||||
|
|
||||||
|
这个文件用于记录源生成器实现前需要手写的正则匹配样本。
|
||||||
|
|
||||||
|
流程:
|
||||||
|
|
||||||
|
1. 选一个表达式。
|
||||||
|
2. 手写理想匹配代码。
|
||||||
|
3. 生成优化后 IR。
|
||||||
|
4. 对照手写代码和 IR 段。
|
||||||
|
5. 标注每段 IR 应该生成的代码形状。
|
||||||
|
6. 抽取生成器规则。
|
||||||
|
|
||||||
|
选样原则:
|
||||||
|
|
||||||
|
1. 表达式必须是典型正则写法。
|
||||||
|
2. 全部样本合起来应覆盖计划支持的大部分 regex 语法。
|
||||||
|
3. 样本不以表达式结构优化为目标,避免依赖公共前缀提取、分支树重排等 AST 优化。
|
||||||
|
4. `TRY`/`CONDITION` 前缀化改写属于 IR 执行形态整理,不算表达式优化。
|
||||||
|
|
||||||
|
## 记录模板
|
||||||
|
|
||||||
|
```text
|
||||||
|
Regex:
|
||||||
|
|
||||||
|
覆盖形态:
|
||||||
|
|
||||||
|
期望代码形状:
|
||||||
|
|
||||||
|
Optimized IR:
|
||||||
|
|
||||||
|
IR -> Code 规则:
|
||||||
|
|
||||||
|
备注:
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第一批:基础线性匹配与转义
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `abc` | 单个 `MATCH_SEQ` | 最小长度检查、序列匹配、活动指针推进 |
|
||||||
|
| `a[abc]d` | `MATCH_SEQ` + `MATCH_SET` + `MATCH_SEQ` | set 后端展开前的调用边界 |
|
||||||
|
| `[abc]` | 单个 `MATCH_SET` | 字符集单字符匹配 |
|
||||||
|
| `[.]` | set 中的字面点号 | 确认 `[.]` 和 `.` 语义分离 |
|
||||||
|
| `.` | 任意字符匹配 | `MATCH_SET "."` 的特殊后端语义 |
|
||||||
|
| `\.` | 转义字面点号 | 确认 `\.` 进入 seq 系 IR |
|
||||||
|
| `a\*b` | 转义操作符字面量 | tokenizer 已消解转义语义 |
|
||||||
|
|
||||||
|
## 第二批:计数
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `a*` | `COUNT_SEQ`,`MIN:0 MAX:-1` | 贪婪计数、允许空匹配 |
|
||||||
|
| `a+` | `COUNT_SEQ`,`MIN:1 MAX:-1` | 至少一次的计数 |
|
||||||
|
| `a?` | `COUNT_SEQ`,`MIN:0 MAX:1` | 可选单元 |
|
||||||
|
| `a{3,5}` | 固定下限 + 可选上限 | 下限展开与剩余计数 |
|
||||||
|
| `[abc]*` | `COUNT_SET` | 字符集循环 |
|
||||||
|
| `.{11,13}` | 任意字符计数 | set 语义在后端展开 |
|
||||||
|
|
||||||
|
## 第三批:ALT 与前缀化
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `a|b` | 最小 ALT | region 缓冲、失败切换、成功短路 |
|
||||||
|
| `ab|cd|ef` | 多分支 ALT | 多个 case 的失败链 |
|
||||||
|
| `a|bc|def` | 不同长度 ALT | case 间活动指针恢复 |
|
||||||
|
| `(ab|[cd]|.)x` | 混合 case ALT | seq、set、任意字符分支后接公共后缀 |
|
||||||
|
|
||||||
|
## 第四批:复杂计数块
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `(ab){2,3}` | `COUNT` complex body | complex body 调用域、计数退出 |
|
||||||
|
| `(123(4|5)67){3,4}114514` | complex count + ALT + 后缀 | `CommandCount` 重算、region 复用、后续线性匹配 |
|
||||||
|
|
||||||
|
## 第五批:FIND 与 `.*`
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `.*abc` | 最远/最近搜索前缀 | `FIND_SEQ` 或 `FIND_COMPLEX` 到后续锚点 |
|
||||||
|
| `abc.*` | 后缀任意吞吐 | 尾部 `.*` 的快速成功策略 |
|
||||||
|
| `[.]*` | 字面点号计数 | 不应被当成原装 `.*` 优化 |
|
||||||
|
|
||||||
|
## 第六批:字符集语义
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `[123abc]` | 离散字符集 | set 后端展开为离散匹配 |
|
||||||
|
| `[1-9]` | 连续字符集 | set 后端展开为范围匹配 |
|
||||||
|
| `\w` | 转义字符集 | escape-set 后端语义 |
|
||||||
|
| `\w+` | 转义字符集计数 | set 计数循环 |
|
||||||
|
|
||||||
|
## 第七批:捕获组
|
||||||
|
|
||||||
|
| Regex | 覆盖形态 | 手写重点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `(abc)` | 普通捕获组 | 默认序号 group |
|
||||||
|
| `(?<name>abc)` | 简单命名组 | group 起止范围、`PACK` |
|
||||||
|
| `(?<name>.*)234` | group 内 `.*` + 后续锚点 | `FIND_COMPLEX FURTHEST -1` 与后续 `MATCH_SEQ` |
|
||||||
|
| `(?<name>.*)a` | group 内 `.*` + 单字符锚点 | 最远搜索回退到后缀匹配 |
|
||||||
|
| `(?<a>abcd|a(bc)c)` | group 内 ALT | group success 依赖 region |
|
||||||
|
| `a*.[abc]*(?<a>abcd|a(bc)c)(?<name>.*)234` | 综合样本 | count、set、ALT、group、find、后缀匹配 |
|
||||||
|
|
||||||
|
## 当前优先级
|
||||||
|
|
||||||
|
第一阶段只要求源生成器能稳定处理:
|
||||||
|
|
||||||
|
1. `MATCH_SEQ`
|
||||||
|
2. `MATCH_SET`
|
||||||
|
3. `COUNT_SEQ`
|
||||||
|
4. `COUNT_SET`
|
||||||
|
5. 简单 `ALT`
|
||||||
|
6. `PACK`
|
||||||
|
7. `FIND_SEQ`
|
||||||
|
8. `FIND_COMPLEX COUNT:-1`
|
||||||
|
|
||||||
|
低优先级:
|
||||||
|
|
||||||
|
1. 逐 region 最短长度断言。
|
||||||
|
2. 复杂字符集完全内联。
|
||||||
|
3. 公共前缀提取和代码树生成。
|
||||||
|
4. 对所有正则语义做完整兼容。
|
||||||
|
|
||||||
|
## 手写代码关注点
|
||||||
|
|
||||||
|
每个样本的手写代码都应尽量确认这些点:
|
||||||
|
|
||||||
|
1. 活动指针如何推进。
|
||||||
|
2. 失败时是否需要恢复活动指针。
|
||||||
|
3. region result 和 single-line result 是否必须分配。
|
||||||
|
4. `Observe_Line` 是否只读取目标行结果。
|
||||||
|
5. `Observe_Region` 是否只读取目标 region 结果。
|
||||||
|
6. `PACK` 使用哪一个成功结果作为捕获区间。
|
||||||
|
7. 最小长度检查是否值得在当前样本中生成。
|
||||||
@@ -64,10 +64,25 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
if (scan_win[0] is not RegexFindSeqIR find) {
|
if (scan_win[0] is not RegexFindSeqIR find) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
const string cur_ptr = "cur_ptr",
|
||||||
|
begin_ptr = "begin_ptr";
|
||||||
_ = main_emitter.AppendLine()
|
_ = main_emitter.AppendLine()
|
||||||
.AppendLine(@$"//Find sequence:{FormatStringLiteral(find.Pattern)} at any pos")
|
.AppendLine(@$"//Find sequence:{FormatStringLiteral(find.Pattern)} at any pos")
|
||||||
.AppendLine(@"do")
|
.AppendLine(@"do")
|
||||||
.AppendLine(@"{");
|
.AppendLine(@"{")
|
||||||
|
.AppendLine(@$"int {cur_ptr}, {begin_ptr} = {current_ptr};")
|
||||||
|
.AppendLine();
|
||||||
|
|
||||||
|
string finder = find.IsFirst ? "FirstIndexOf" : "LastIndexOf";
|
||||||
|
|
||||||
|
if (scan_win[1] is RegexMatchSeqIR seq)
|
||||||
|
{
|
||||||
|
string seq_pat = FormatStringLiteral(seq.Pattern);
|
||||||
|
_ = main_emitter.AppendLine(@$"//Match sequence:{seq_pat} at current pos")
|
||||||
|
.AppendLine(string.Empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = main_emitter.AppendLine("}").AppendLine(string.Empty);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
internal partial class TextRegexSourceGen
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
private const string current_ptr = "current_ptr";
|
private const string current_ptr = "cur_ptr_global";
|
||||||
private const string offset = "offset";
|
private const string offset = "offset";
|
||||||
private const string orig_text = "text";
|
private const string orig_text = "text";
|
||||||
|
|
||||||
@@ -39,12 +39,10 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
RegexInfo info = source.Info;
|
RegexInfo info = source.Info;
|
||||||
List<RegexIR> ir = source.IRs;
|
List<RegexIR> ir = source.IRs;
|
||||||
ReadOnlySpan<RegexIR> ir_sp = CollectionsMarshal.AsSpan(ir);
|
ReadOnlySpan<RegexIR> ir_sp = CollectionsMarshal.AsSpan(ir);
|
||||||
RegexIR[] window = new RegexIR[8];
|
|
||||||
Span<RegexIR> scan_win = window;
|
|
||||||
|
|
||||||
SequenceEmitter<string> match_e = new();
|
SequenceEmitter<string> match_e = new();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int end = FindRrgionEnd(ir_sp, index);
|
int end = FindRegionEnd(ir_sp, index);
|
||||||
ReadOnlySpan<RegexIR> region = ir_sp[0..end];
|
ReadOnlySpan<RegexIR> region = ir_sp[0..end];
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
@@ -55,7 +53,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
index = end;
|
index = end;
|
||||||
end = FindRrgionEnd(ir_sp, end);
|
end = FindRegionEnd(ir_sp, end);
|
||||||
region = ir_sp[index..end];
|
region = ir_sp[index..end];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +88,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int FindRrgionEnd(
|
private static int FindRegionEnd(
|
||||||
ReadOnlySpan<RegexIR> ir_sp,
|
ReadOnlySpan<RegexIR> ir_sp,
|
||||||
int current
|
int current
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,8 +32,14 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
private const string __remain_cur = "__remain_cur";
|
private const string __remain_cur = "__remain_cur";
|
||||||
private const string __find_result = "__find_result";
|
private const string __find_result = "__find_result";
|
||||||
|
|
||||||
private readonly Dictionary<string, int> _search_value_name = [];
|
private int _min_region_begin_index;
|
||||||
private int _min_search_value_index;
|
|
||||||
|
public string MakeNewRegionBegin()
|
||||||
|
{
|
||||||
|
string name = $"__region_begin_{_min_region_begin_index}";
|
||||||
|
_min_region_begin_index++;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public string CurChar
|
public string CurChar
|
||||||
{
|
{
|
||||||
@@ -44,19 +50,6 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int MakeNewSearchValue(
|
|
||||||
string seq
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (_search_value_name.TryGetValue(seq, out int idx)) {
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
idx = _min_search_value_index;
|
|
||||||
_min_search_value_index++;
|
|
||||||
_search_value_name.Add(seq, idx);
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FindResult
|
public string FindResult
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|||||||
Reference in New Issue
Block a user