Small changeds on text regex source gen

This commit is contained in:
Youmu Liu
2026-09-08 18:10:36 +08:00
parent 90c7538639
commit 8641fd4e08
3 changed files with 40 additions and 25 deletions
@@ -41,6 +41,17 @@ namespace Nitload.RegularAnalysis.Text
public int LabelOrder { get; init; } public int LabelOrder { get; init; }
public string GetLabelName()
{
return Mode switch
{
ExitMode.Break => $@"__break_{LabelOrder}",
ExitMode.Continue => $@"__continue_{LabelOrder}",
ExitMode.Goto => $@"__goto_{LabelOrder}",
_ => throw new NotImplementedException(),
};
}
} }
} }
@@ -183,9 +183,10 @@ namespace Nitload.RegularAnalysis.Text
) )
{ {
RegexAstNode current = node.Right; RegexAstNode current = node.Right;
// TODO(NGRA-NAMING): Reserve exclusive names for future ALT cursor/capture snapshots; // TODO(NGRA-NAMING): Reserve exclusive names for future ALT cursor/capture snapshots;
// their lifetime extends through retry labels, not just the candidate's emitted block. // their lifetime extends through retry labels, not just the candidate's emitted block.
string exit_label = context.RequestExitLabel(); ExitLabel exit_label = context.RequestExitLabel(ExitMode.Goto);
// TODO(NGRA): Walk every normalized ALT candidate exactly once. The final traversal // TODO(NGRA): Walk every normalized ALT candidate exactly once. The final traversal
// shape and failure continuation are not wired yet. // shape and failure continuation are not wired yet.
@@ -350,7 +351,7 @@ namespace Nitload.RegularAnalysis.Text
bool is_greedy, bool is_greedy,
[NotNull] in SequenceEmitter<string> sc, [NotNull] in SequenceEmitter<string> sc,
in SourceGenContext context, in SourceGenContext context,
string exit_label in ExitLabel exit
) )
{ {
if (min < 0 || (max >= 0 && max < min)) { if (min < 0 || (max >= 0 && max < min)) {
@@ -366,9 +367,11 @@ namespace Nitload.RegularAnalysis.Text
char expected = literal[0]; char expected = literal[0];
string expected_literal = FormatCharLiteral(expected); string expected_literal = FormatCharLiteral(expected);
string remain_span = context.RemainSpan; string remain_span = context.RemainSpan;
// TODO(NGRA-NAMING): Allocate required_rest, required_matched, required_chunk_index, // TODO(NGRA-NAMING): Allocate required_rest, required_matched, required_chunk_index,
// and additional_count through the exclusive-name pool, including loop-local names. // and additional_count through the exclusive-name pool, including loop-local names.
int scratch_scope_index = context.RequestScratchScopeIndex(); int scratch_scope_index = context.RequestScratchScopeIndex();
string exit_label_node = exit.GetLabelName();
string required_rest = $"__required_rest_{scratch_scope_index}"; string required_rest = $"__required_rest_{scratch_scope_index}";
string required_matched = $"__required_matched_{scratch_scope_index}"; string required_matched = $"__required_matched_{scratch_scope_index}";
string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}"; string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}";
@@ -384,7 +387,7 @@ namespace Nitload.RegularAnalysis.Text
string required_prefix = new(expected, min); string required_prefix = new(expected, min);
_ = sc.AppendLine($"if (!{remain_span}.{nameof(MemoryExtensions.StartsWith)}({FormatStringLiteral(required_prefix)}))") _ = sc.AppendLine($"if (!{remain_span}.{nameof(MemoryExtensions.StartsWith)}({FormatStringLiteral(required_prefix)}))")
.AppendLine("{") .AppendLine("{")
.AppendLine($"goto {exit_label};") .AppendLine($"goto {exit_label_node};")
.AppendLine("}") .AppendLine("}")
.AppendLine($"{remain_span} = {remain_span}.Slice({min});"); .AppendLine($"{remain_span} = {remain_span}.Slice({min});");
} }
@@ -399,7 +402,7 @@ namespace Nitload.RegularAnalysis.Text
_ = sc.AppendLine($"if ({remain_span}.Length < {min})") _ = sc.AppendLine($"if ({remain_span}.Length < {min})")
.AppendLine("{") .AppendLine("{")
.AppendLine($"goto {exit_label};") .AppendLine($"goto {exit_label_node};")
.AppendLine("}") .AppendLine("}")
.AppendLine($"{ROS_char} {required_rest} = {remain_span};") .AppendLine($"{ROS_char} {required_rest} = {remain_span};")
.AppendLine($"bool {required_matched} = true;") .AppendLine($"bool {required_matched} = true;")
@@ -424,7 +427,7 @@ namespace Nitload.RegularAnalysis.Text
_ = sc.AppendLine($"if (!{required_matched})") _ = sc.AppendLine($"if (!{required_matched})")
.AppendLine("{") .AppendLine("{")
.AppendLine($"goto {exit_label};") .AppendLine($"goto {exit_label_node};")
.AppendLine("}") .AppendLine("}")
.AppendLine($"{remain_span} = {remain_span}.Slice({min});"); .AppendLine($"{remain_span} = {remain_span}.Slice({min});");
} }
@@ -451,7 +454,6 @@ namespace Nitload.RegularAnalysis.Text
// a future continuation attempt fails and requests another repetition. // a future continuation attempt fails and requests another repetition.
_ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation."); _ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation.");
} }
} }
private static void GenerateUnitSetCount( private static void GenerateUnitSetCount(
@@ -460,7 +462,8 @@ namespace Nitload.RegularAnalysis.Text
int max, int max,
bool is_greedy, bool is_greedy,
[NotNull] in SequenceEmitter<string> sc, [NotNull] in SequenceEmitter<string> sc,
in SourceGenContext context in SourceGenContext context,
in ExitLabel exit_label
) )
{ {
if (min < 0 || (max >= 0 && max < min)) { if (min < 0 || (max >= 0 && max < min)) {
@@ -477,6 +480,7 @@ namespace Nitload.RegularAnalysis.Text
IReadOnlyList<RegexCharSet> charset = payload.Set; IReadOnlyList<RegexCharSet> charset = payload.Set;
string remain_span = context.RemainSpan; string remain_span = context.RemainSpan;
// TODO(NGRA-NAMING): Allocate required_matched and additional_count through the // TODO(NGRA-NAMING): Allocate required_matched and additional_count through the
// exclusive-name pool. Reserve required_rest/required_chunk_index if emitted later. // exclusive-name pool. Reserve required_rest/required_chunk_index if emitted later.
int scratch_scope_index = context.RequestScratchScopeIndex(); int scratch_scope_index = context.RequestScratchScopeIndex();
@@ -533,7 +537,7 @@ namespace Nitload.RegularAnalysis.Text
bool is_greedy, bool is_greedy,
[NotNull] in SequenceEmitter<string> sc, [NotNull] in SequenceEmitter<string> sc,
in SourceGenContext context, in SourceGenContext context,
string exit_label in ExitLabel exit
) )
{ {
if (min < 0 || (max >= 0 && max < min)) { if (min < 0 || (max >= 0 && max < min)) {
@@ -548,10 +552,12 @@ namespace Nitload.RegularAnalysis.Text
string expected_literal = FormatStringLiteral(literal); string expected_literal = FormatStringLiteral(literal);
string remain_span = context.RemainSpan; string remain_span = context.RemainSpan;
// TODO(NGRA-NAMING): Allocate required_rest, required_matched, required_chunk_index, // TODO(NGRA-NAMING): Allocate required_rest, required_matched, required_chunk_index,
// required_count, and additional_count through the exclusive-name pool. Flattened // required_count, and additional_count through the exclusive-name pool. Flattened
// sibling COUNT nodes share the declaration space even after their exit labels. // sibling COUNT nodes share the declaration space even after their exit labels.
int scratch_scope_index = context.RequestScratchScopeIndex(); int scratch_scope_index = context.RequestScratchScopeIndex();
string exit_label_node = exit.GetLabelName();
string required_rest = $"__required_rest_{scratch_scope_index}"; string required_rest = $"__required_rest_{scratch_scope_index}";
string required_matched = $"__required_matched_{scratch_scope_index}"; string required_matched = $"__required_matched_{scratch_scope_index}";
string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}"; string required_chunk_index = $"__required_chunk_index_{scratch_scope_index}";
@@ -575,7 +581,7 @@ namespace Nitload.RegularAnalysis.Text
.AppendLine("}") .AppendLine("}")
.AppendLine($"if ({required_count} != {min})") .AppendLine($"if ({required_count} != {min})")
.AppendLine("{") .AppendLine("{")
.AppendLine($"goto {exit_label};") .AppendLine($"goto {exit_label_node};")
.AppendLine("}") .AppendLine("}")
.AppendLine($"{remain_span} = {required_rest};"); .AppendLine($"{remain_span} = {required_rest};");
} else if (min <= flattened_unit_count_threshold / literal.Length) } else if (min <= flattened_unit_count_threshold / literal.Length)
@@ -589,7 +595,7 @@ namespace Nitload.RegularAnalysis.Text
string required_prefix = required_prefix_span.ToString(); string required_prefix = required_prefix_span.ToString();
_ = sc.AppendLine($"if (!{remain_span}.{starts_with}({FormatStringLiteral(required_prefix)}))") _ = sc.AppendLine($"if (!{remain_span}.{starts_with}({FormatStringLiteral(required_prefix)}))")
.AppendLine("{") .AppendLine("{")
.AppendLine($"goto {exit_label};") .AppendLine($"goto {exit_label_node};")
.AppendLine("}") .AppendLine("}")
.AppendLine($"{remain_span} = {remain_span}.Slice({min * literal.Length});"); .AppendLine($"{remain_span} = {remain_span}.Slice({min * literal.Length});");
} }
@@ -638,7 +644,7 @@ namespace Nitload.RegularAnalysis.Text
_ = sc.AppendLine($"if (!{required_matched})") _ = sc.AppendLine($"if (!{required_matched})")
.AppendLine("{") .AppendLine("{")
.AppendLine($"goto {exit_label};") .AppendLine($"goto {exit_label_node};")
.AppendLine("}") .AppendLine("}")
.AppendLine($"{remain_span} = {required_rest};"); .AppendLine($"{remain_span} = {required_rest};");
} }
@@ -665,14 +671,14 @@ namespace Nitload.RegularAnalysis.Text
// a future continuation attempt fails and requests another repetition. // a future continuation attempt fails and requests another repetition.
_ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation."); _ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation.");
} }
} }
private static void GenerateCount( private static void GenerateCount(
RegexAstNode node, RegexAstNode node,
[NotNull] in SequenceEmitter<string> sc, [NotNull] in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list, in List<SequenceEmitter<string>> func_list,
in SourceGenContext context in SourceGenContext context,
in ExitLabel exit
) )
{ {
RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left; RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left;
@@ -683,9 +689,10 @@ namespace Nitload.RegularAnalysis.Text
int min = payload.Min; int min = payload.Min;
int max = payload.Max; int max = payload.Max;
bool is_greedy = payload.IsGreedy; bool is_greedy = payload.IsGreedy;
// TODO(NGRA-NAMING): Reserve exclusive names for future repetition/rollback state // TODO(NGRA-NAMING): Reserve exclusive names for future repetition/rollback state
// across complex COUNT retries; recursive children must use the same method pool. // across complex COUNT retries; recursive children must use the same method pool.
string exit_label = context.RequestExitLabel(); ExitLabel exit_label = context.RequestExitLabel(ExitMode.Goto);
if ((value.Label & RegexElementLabel.SEQUENCE) == 0) if ((value.Label & RegexElementLabel.SEQUENCE) == 0)
{ {
// TODO(NGRA): Emit min/max repetition, zero-width progress protection, greedy vs. // TODO(NGRA): Emit min/max repetition, zero-width progress protection, greedy vs.
@@ -699,10 +706,7 @@ namespace Nitload.RegularAnalysis.Text
GenerateUnitCount(value, min, max, is_greedy, sc, context, exit_label); GenerateUnitCount(value, min, max, is_greedy, sc, context, exit_label);
break; break;
case RegexElementLabel.UNIT_SET: case RegexElementLabel.UNIT_SET:
GenerateUnitSetCount(value, min, max, is_greedy, sc, context); GenerateUnitSetCount(value, min, max, is_greedy, sc, context, exit_label);
// TODO(NGRA): Emit a bounded/unbounded charset counting loop, preferably
// using IndexOfAnyExcept where the charset shape permits it.
break; break;
case RegexElementLabel.UNIT_SPAN: case RegexElementLabel.UNIT_SPAN:
GenerateUnitSpanCount(value, min, max, is_greedy, sc, context, exit_label); GenerateUnitSpanCount(value, min, max, is_greedy, sc, context, exit_label);
@@ -36,7 +36,7 @@ namespace Nitload.RegularAnalysis.Text
public const string _remain_span = "__remain"; public const string _remain_span = "__remain";
private int _exit_alt_count; private int _max_exit_lable;
private int _scratch_scope_count; private int _scratch_scope_count;
@@ -53,13 +53,13 @@ namespace Nitload.RegularAnalysis.Text
return sc.AppendLine($@"{ROS_char} {RemainSpan};"); return sc.AppendLine($@"{ROS_char} {RemainSpan};");
} }
public string RequestExitLabel() public ExitLabel RequestExitLabel(
ExitMode exitMode
)
{ {
// TODO(NGRA-NAMING): Route ALT and COUNT labels through the method-wide label pool. int index = _max_exit_lable;
// Keep every emitted label exclusive until method generation is complete. _max_exit_lable++;
string code = $@"_exit_alt_{_exit_alt_count}"; return new(exitMode, index);
_exit_alt_count++;
return code;
} }
public int RequestScratchScopeIndex() public int RequestScratchScopeIndex()