use new exit lable
This commit is contained in:
@@ -16,12 +16,15 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
internal partial class TextRegexSourceGen
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
internal enum ExitMode
|
internal enum ExitKind
|
||||||
{
|
{
|
||||||
|
|
||||||
Break,
|
MatchFailed,
|
||||||
Continue,
|
MatchEnd,
|
||||||
Goto,
|
CountRetry,
|
||||||
|
CountResume,
|
||||||
|
AltNext,
|
||||||
|
AltMatched,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,25 +32,28 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
|
|
||||||
public ExitLabel(
|
public ExitLabel(
|
||||||
ExitMode mode,
|
ExitKind kind,
|
||||||
int order
|
int order
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Mode = mode;
|
Kind = kind;
|
||||||
LabelOrder = order;
|
LabelOrder = order;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExitMode Mode { get; init; }
|
public ExitKind Kind { get; init; }
|
||||||
|
|
||||||
public int LabelOrder { get; init; }
|
public int LabelOrder { get; init; }
|
||||||
|
|
||||||
public string GetLabelName()
|
public string GetLabelName()
|
||||||
{
|
{
|
||||||
return Mode switch
|
return Kind switch
|
||||||
{
|
{
|
||||||
ExitMode.Break => $@"__break_{LabelOrder}",
|
ExitKind.MatchFailed => $@"__match_failed_{LabelOrder}",
|
||||||
ExitMode.Continue => $@"__continue_{LabelOrder}",
|
ExitKind.MatchEnd => $@"__match_end_{LabelOrder}",
|
||||||
ExitMode.Goto => $@"__goto_{LabelOrder}",
|
ExitKind.CountRetry => $@"__count_retry_{LabelOrder}",
|
||||||
|
ExitKind.CountResume => $@"__count_resume_{LabelOrder}",
|
||||||
|
ExitKind.AltNext => $@"__alt_next_{LabelOrder}",
|
||||||
|
ExitKind.AltMatched => $@"__alt_matched_{LabelOrder}",
|
||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,20 +14,31 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
internal partial class TextRegexSourceGen
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emits a subtree and updates the retry target for the rest of the current path.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// local_fail is the owning region's fallback. backtrack starts at that fallback and
|
||||||
|
/// advances when a COUNT registers a retry point. Returning from recursion does not
|
||||||
|
/// discard it. Mutually exclusive branches must use separate backtrack variables.
|
||||||
|
/// </remarks>
|
||||||
internal static void GenerateMethodSource(
|
internal static void GenerateMethodSource(
|
||||||
RegexAstNode ast,
|
RegexAstNode ast,
|
||||||
in SequenceEmitter<string> sc,
|
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_label = default,
|
in ExitLabel local_fail,
|
||||||
|
ref ExitLabel backtrack,
|
||||||
bool is_find = false
|
bool is_find = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// local_fail belongs to this region. COUNT updates only the current path's
|
||||||
|
// backtrack target; CONCAT passes that update on to every later sibling.
|
||||||
RegexAstNode root = ast;
|
RegexAstNode root = ast;
|
||||||
if (root.Label == RegexElementLabel.EPSILON) {
|
if (root.Label == RegexElementLabel.EPSILON) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (TryGenerateSpecialSource(root, sc, func_list, context, exit_label)) {
|
if (TryGenerateSpecialSource(root, sc, func_list, context, local_fail, ref backtrack)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR);
|
RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR);
|
||||||
@@ -36,16 +47,16 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case RegexElementLabel.CONCAT:
|
case RegexElementLabel.CONCAT:
|
||||||
GenerateConcat(root, sc, func_list, context, exit_label);
|
GenerateConcat(root, sc, func_list, context, local_fail, ref backtrack);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.COUNT:
|
case RegexElementLabel.COUNT:
|
||||||
GenerateCount(root, sc, func_list, context, exit_label);
|
GenerateCount(root, sc, func_list, context, local_fail, ref backtrack);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.GROUP_BEGIN:
|
case RegexElementLabel.GROUP_BEGIN:
|
||||||
GenerateGroup(root, sc, func_list, context, exit_label);
|
GenerateGroup(root, sc, func_list, context, local_fail, ref backtrack);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.ALT:
|
case RegexElementLabel.ALT:
|
||||||
GenerateAlt(root, sc, func_list, context, exit_label);
|
GenerateAlt(root, sc, func_list, context, backtrack);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
@@ -59,13 +70,13 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case RegexElementLabel.UNIT:
|
case RegexElementLabel.UNIT:
|
||||||
GenerateUnit(root, sc, context, exit_label);
|
GenerateUnit(root, sc, context, backtrack);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.UNIT_SET:
|
case RegexElementLabel.UNIT_SET:
|
||||||
GenerateUnitSet(root, sc, context, exit_label);
|
GenerateUnitSet(root, sc, context, backtrack);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.UNIT_SPAN:
|
case RegexElementLabel.UNIT_SPAN:
|
||||||
GenerateUnitSpan(root, sc, context, exit_label);
|
GenerateUnitSpan(root, sc, context, backtrack);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -220,11 +231,9 @@ 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 List<SequenceEmitter<string>> func_list,
|
|
||||||
in SourceGenContext context,
|
in SourceGenContext context,
|
||||||
in ExitLabel exit,
|
in ExitLabel exit,
|
||||||
IReadOnlyList<RegexAstNode>? continuation = null,
|
ref ExitLabel backtrack
|
||||||
int next = 0
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (min < 0 || (max >= 0 && max < min)) {
|
if (min < 0 || (max >= 0 && max < min)) {
|
||||||
@@ -303,61 +312,54 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
int additional_limit = max < 0 ? -1 : max - min;
|
int additional_limit = max < 0 ? -1 : max - min;
|
||||||
bool has_continuation = continuation is not null && next < continuation.Count;
|
if (additional_limit == 0) {
|
||||||
bool can_retry = has_continuation && additional_limit != 0;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the optional portion once. Its counter is shared by scanning and retries.
|
||||||
string additional_entry = $"__additional_entry_{scratch_scope_index}";
|
string additional_entry = $"__additional_entry_{scratch_scope_index}";
|
||||||
if (can_retry) {
|
ExitLabel retry = context.RequestExitLabel(ExitKind.CountRetry);
|
||||||
_ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};");
|
ExitLabel resume = context.RequestExitLabel(ExitKind.CountResume);
|
||||||
}
|
_ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};")
|
||||||
if (can_retry || (is_greedy && additional_limit > 0)) {
|
.AppendLine($"int {additional_count} = 0;");
|
||||||
_ = sc.AppendLine($"int {additional_count} = 0;");
|
if (is_greedy)
|
||||||
}
|
|
||||||
if (is_greedy && additional_limit != 0)
|
|
||||||
{
|
{
|
||||||
string limit = additional_limit < 0 ? string.Empty :
|
string limit = additional_limit < 0 ? string.Empty :
|
||||||
$"{additional_count} < {additional_limit} && ";
|
$"{additional_count} < {additional_limit} && ";
|
||||||
_ = sc.AppendLine($"while ({limit}!{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal})")
|
_ = sc.AppendLine($"while ({limit}!{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal})")
|
||||||
.AppendLine("{")
|
.AppendLine("{")
|
||||||
.AppendLine($"{remain_span} = {remain_span}.Slice(1);");
|
.AppendLine($"{remain_span} = {remain_span}.Slice(1);")
|
||||||
if (can_retry || additional_limit > 0) {
|
.AppendLine($"{additional_count}++;")
|
||||||
_ = sc.AppendLine($"{additional_count}++;");
|
.AppendLine("}");
|
||||||
}
|
|
||||||
_ = sc.AppendLine("}");
|
|
||||||
}
|
}
|
||||||
if (can_retry)
|
|
||||||
|
// Initial success skips retry handling. Later failures jump back here from anywhere
|
||||||
|
// in this path; exhaustion forwards to the target inherited before this COUNT.
|
||||||
|
_ = sc.AppendLine($"goto {resume.GetLabelName()};")
|
||||||
|
.AppendLine($"{retry.GetLabelName()}: {{}}");
|
||||||
|
if (is_greedy)
|
||||||
{
|
{
|
||||||
// The same optional count drives scanning and retries. Required units stay consumed.
|
_ = sc.AppendLine($"if ({additional_count} == 0)")
|
||||||
ExitLabel retry = context.RequestExitLabel(ExitMode.Goto);
|
|
||||||
_ = sc.AppendLine("while (true)")
|
|
||||||
.AppendLine("{")
|
.AppendLine("{")
|
||||||
.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count});");
|
.AppendLine($"{remain_span} = {additional_entry};")
|
||||||
GenerateConcatTail(continuation!, next, sc, func_list, context, retry);
|
.AppendLine($"goto {exit_label_node};")
|
||||||
_ = sc.AppendLine("break;")
|
.AppendLine("}")
|
||||||
.AppendLine($"{retry.GetLabelName()}: {{}}");
|
.AppendLine($"{additional_count}--;");
|
||||||
if (is_greedy)
|
} else
|
||||||
{
|
{
|
||||||
_ = sc.AppendLine($"if ({additional_count} == 0)")
|
string limit = additional_limit < 0 ? string.Empty :
|
||||||
.AppendLine("{")
|
$"{additional_count} == {additional_limit} || ";
|
||||||
.AppendLine($"{remain_span} = {additional_entry};")
|
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count});")
|
||||||
.AppendLine($"goto {exit_label_node};")
|
.AppendLine($"if ({limit}{remain_span}.IsEmpty || {remain_span}[0] != {expected_literal})")
|
||||||
.AppendLine("}")
|
.AppendLine("{")
|
||||||
.AppendLine($"{additional_count}--;");
|
.AppendLine($"{remain_span} = {additional_entry};")
|
||||||
} else
|
.AppendLine($"goto {exit_label_node};")
|
||||||
{
|
.AppendLine("}")
|
||||||
string limit = additional_limit < 0 ? string.Empty :
|
.AppendLine($"{additional_count}++;");
|
||||||
$"{additional_count} == {additional_limit} || ";
|
|
||||||
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count});")
|
|
||||||
.AppendLine($"if ({limit}{remain_span}.IsEmpty || {remain_span}[0] != {expected_literal})")
|
|
||||||
.AppendLine("{")
|
|
||||||
.AppendLine($"{remain_span} = {additional_entry};")
|
|
||||||
.AppendLine($"goto {exit_label_node};")
|
|
||||||
.AppendLine("}")
|
|
||||||
.AppendLine($"{additional_count}++;");
|
|
||||||
}
|
|
||||||
_ = sc.AppendLine("}");
|
|
||||||
} else if (has_continuation) {
|
|
||||||
GenerateConcatTail(continuation!, next, sc, func_list, context, exit);
|
|
||||||
}
|
}
|
||||||
|
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count});")
|
||||||
|
.AppendLine($"{resume.GetLabelName()}: {{}}");
|
||||||
|
backtrack = retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateUnitSetCount(
|
private static void GenerateUnitSetCount(
|
||||||
@@ -440,11 +442,9 @@ 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 List<SequenceEmitter<string>> func_list,
|
|
||||||
in SourceGenContext context,
|
in SourceGenContext context,
|
||||||
in ExitLabel exit,
|
in ExitLabel exit,
|
||||||
IReadOnlyList<RegexAstNode>? continuation = null,
|
ref ExitLabel backtrack
|
||||||
int next = 0
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (min < 0 || (max >= 0 && max < min)) {
|
if (min < 0 || (max >= 0 && max < min)) {
|
||||||
@@ -554,61 +554,54 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
int additional_limit = max < 0 ? -1 : max - min;
|
int additional_limit = max < 0 ? -1 : max - min;
|
||||||
bool has_continuation = continuation is not null && next < continuation.Count;
|
if (additional_limit == 0) {
|
||||||
bool can_retry = has_continuation && additional_limit != 0;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the optional portion once. Its counter is shared by scanning and retries.
|
||||||
string additional_entry = $"__additional_entry_{scratch_scope_index}";
|
string additional_entry = $"__additional_entry_{scratch_scope_index}";
|
||||||
if (can_retry) {
|
ExitLabel retry = context.RequestExitLabel(ExitKind.CountRetry);
|
||||||
_ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};");
|
ExitLabel resume = context.RequestExitLabel(ExitKind.CountResume);
|
||||||
}
|
_ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};")
|
||||||
if (can_retry || (is_greedy && additional_limit > 0)) {
|
.AppendLine($"int {additional_count} = 0;");
|
||||||
_ = sc.AppendLine($"int {additional_count} = 0;");
|
if (is_greedy)
|
||||||
}
|
|
||||||
if (is_greedy && additional_limit != 0)
|
|
||||||
{
|
{
|
||||||
string limit = additional_limit < 0 ? string.Empty :
|
string limit = additional_limit < 0 ? string.Empty :
|
||||||
$"{additional_count} < {additional_limit} && ";
|
$"{additional_count} < {additional_limit} && ";
|
||||||
_ = sc.AppendLine($"while ({limit}{remain_span}.{starts_with}({expected_literal}))")
|
_ = sc.AppendLine($"while ({limit}{remain_span}.{starts_with}({expected_literal}))")
|
||||||
.AppendLine("{")
|
.AppendLine("{")
|
||||||
.AppendLine($"{remain_span} = {remain_span}.Slice({literal.Length});");
|
.AppendLine($"{remain_span} = {remain_span}.Slice({literal.Length});")
|
||||||
if (can_retry || additional_limit > 0) {
|
.AppendLine($"{additional_count}++;")
|
||||||
_ = sc.AppendLine($"{additional_count}++;");
|
.AppendLine("}");
|
||||||
}
|
|
||||||
_ = sc.AppendLine("}");
|
|
||||||
}
|
}
|
||||||
if (can_retry)
|
|
||||||
|
// Initial success skips retry handling. Later failures jump back here from anywhere
|
||||||
|
// in this path; exhaustion forwards to the target inherited before this COUNT.
|
||||||
|
_ = sc.AppendLine($"goto {resume.GetLabelName()};")
|
||||||
|
.AppendLine($"{retry.GetLabelName()}: {{}}");
|
||||||
|
if (is_greedy)
|
||||||
{
|
{
|
||||||
// Each retry moves by a complete literal using the scan's own optional counter.
|
_ = sc.AppendLine($"if ({additional_count} == 0)")
|
||||||
ExitLabel retry = context.RequestExitLabel(ExitMode.Goto);
|
|
||||||
_ = sc.AppendLine("while (true)")
|
|
||||||
.AppendLine("{")
|
.AppendLine("{")
|
||||||
.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});");
|
.AppendLine($"{remain_span} = {additional_entry};")
|
||||||
GenerateConcatTail(continuation!, next, sc, func_list, context, retry);
|
.AppendLine($"goto {exit_label_node};")
|
||||||
_ = sc.AppendLine("break;")
|
.AppendLine("}")
|
||||||
.AppendLine($"{retry.GetLabelName()}: {{}}");
|
.AppendLine($"{additional_count}--;");
|
||||||
if (is_greedy)
|
} else
|
||||||
{
|
{
|
||||||
_ = sc.AppendLine($"if ({additional_count} == 0)")
|
string limit = additional_limit < 0 ? string.Empty :
|
||||||
.AppendLine("{")
|
$"{additional_count} == {additional_limit} || ";
|
||||||
.AppendLine($"{remain_span} = {additional_entry};")
|
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});")
|
||||||
.AppendLine($"goto {exit_label_node};")
|
.AppendLine($"if ({limit}!{remain_span}.{starts_with}({expected_literal}))")
|
||||||
.AppendLine("}")
|
.AppendLine("{")
|
||||||
.AppendLine($"{additional_count}--;");
|
.AppendLine($"{remain_span} = {additional_entry};")
|
||||||
} else
|
.AppendLine($"goto {exit_label_node};")
|
||||||
{
|
.AppendLine("}")
|
||||||
string limit = additional_limit < 0 ? string.Empty :
|
.AppendLine($"{additional_count}++;");
|
||||||
$"{additional_count} == {additional_limit} || ";
|
|
||||||
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});")
|
|
||||||
.AppendLine($"if ({limit}!{remain_span}.{starts_with}({expected_literal}))")
|
|
||||||
.AppendLine("{")
|
|
||||||
.AppendLine($"{remain_span} = {additional_entry};")
|
|
||||||
.AppendLine($"goto {exit_label_node};")
|
|
||||||
.AppendLine("}")
|
|
||||||
.AppendLine($"{additional_count}++;");
|
|
||||||
}
|
|
||||||
_ = sc.AppendLine("}");
|
|
||||||
} else if (has_continuation) {
|
|
||||||
GenerateConcatTail(continuation!, next, sc, func_list, context, exit);
|
|
||||||
}
|
}
|
||||||
|
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});")
|
||||||
|
.AppendLine($"{resume.GetLabelName()}: {{}}");
|
||||||
|
backtrack = retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -630,7 +623,7 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
in ExitLabel exit_label
|
in ExitLabel exit_label
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ExitLabel success = context.RequestExitLabel(ExitMode.Goto);
|
ExitLabel success = context.RequestExitLabel(ExitKind.AltMatched);
|
||||||
RegexAstNode current = node.Right;
|
RegexAstNode current = node.Right;
|
||||||
while (!RegexAstNode.IsNullOrEmpty(current))
|
while (!RegexAstNode.IsNullOrEmpty(current))
|
||||||
{
|
{
|
||||||
@@ -639,7 +632,7 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = sc.AppendLine($"goto {exit_label.GetLabelName()};")
|
_ = sc.AppendLine($"goto {exit_label.GetLabelName()};")
|
||||||
.AppendLine($"{success.GetLabelName()}: ;");
|
.AppendLine($"{success.GetLabelName()}: {{}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateGroup(
|
private static void GenerateGroup(
|
||||||
@@ -647,7 +640,8 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
[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_label
|
in ExitLabel local_fail,
|
||||||
|
ref ExitLabel backtrack
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
RegexAstNode left = node.Left;
|
RegexAstNode left = node.Left;
|
||||||
@@ -661,10 +655,10 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
// TODO(NGRA): Snapshot the group start cursor and restore it when the child fails.
|
// TODO(NGRA): Snapshot the group start cursor and restore it when the child fails.
|
||||||
if (!RegexAstNode.IsNullOrEmpty(left))
|
if (!RegexAstNode.IsNullOrEmpty(left))
|
||||||
{
|
{
|
||||||
GenerateMethodSource(left, sc, func_list, context, exit_label);
|
GenerateMethodSource(left, sc, func_list, context, local_fail, ref backtrack);
|
||||||
} else
|
} else
|
||||||
if (!RegexAstNode.IsNullOrEmpty(right)) {
|
if (!RegexAstNode.IsNullOrEmpty(right)) {
|
||||||
GenerateMethodSource(right, sc, func_list, context, exit_label);
|
GenerateMethodSource(right, sc, func_list, context, local_fail, ref backtrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
string group_name = config.Name;
|
string group_name = config.Name;
|
||||||
@@ -679,13 +673,16 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
[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_label = default
|
in ExitLabel local_fail,
|
||||||
|
ref ExitLabel backtrack
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Keep the complete suffix visible even when CONCAT is left-associated.
|
if (!RegexAstNode.IsNullOrEmpty(node.Left)) {
|
||||||
List<RegexAstNode> sequence = [];
|
GenerateMethodSource(node.Left, sc, func_list, context, local_fail, ref backtrack);
|
||||||
AppendConcatNodes(node, sequence);
|
}
|
||||||
GenerateConcatTail(sequence, 0, sc, func_list, context, exit_label);
|
if (!RegexAstNode.IsNullOrEmpty(node.Right)) {
|
||||||
|
GenerateMethodSource(node.Right, sc, func_list, context, local_fail, ref backtrack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateCount(
|
private static void GenerateCount(
|
||||||
@@ -693,9 +690,8 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
[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_label,
|
in ExitLabel local_fail,
|
||||||
IReadOnlyList<RegexAstNode>? continuation = null,
|
ref ExitLabel backtrack
|
||||||
int next = 0
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left;
|
RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left;
|
||||||
@@ -716,6 +712,7 @@ 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;
|
||||||
|
ExitLabel failure = backtrack;
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -723,21 +720,19 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
// TODO(NGRA): Emit min/max repetition, zero-width progress protection, greedy vs.
|
// TODO(NGRA): Emit min/max repetition, zero-width progress protection, greedy vs.
|
||||||
// lazy ordering, and per-attempt rollback for a complex COUNT body.
|
// lazy ordering, and per-attempt rollback for a complex COUNT body.
|
||||||
GenerateMethodSource(value, sc, func_list, context, exit_label);
|
GenerateMethodSource(value, sc, func_list, context, local_fail, ref backtrack);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
switch (value.Label & RegexElementLabel.SEQUENCE)
|
switch (value.Label & RegexElementLabel.SEQUENCE)
|
||||||
{
|
{
|
||||||
case RegexElementLabel.UNIT:
|
case RegexElementLabel.UNIT:
|
||||||
GenerateUnitCount(value, min, max, is_greedy, sc, func_list, context,
|
GenerateUnitCount(value, min, max, is_greedy, sc, context, failure, ref backtrack);
|
||||||
exit_label, continuation, next);
|
|
||||||
return;
|
return;
|
||||||
case RegexElementLabel.UNIT_SET:
|
case RegexElementLabel.UNIT_SET:
|
||||||
GenerateUnitSetCount(value, min, max, is_greedy, sc, context, exit_label);
|
GenerateUnitSetCount(value, min, max, is_greedy, sc, context, failure);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.UNIT_SPAN:
|
case RegexElementLabel.UNIT_SPAN:
|
||||||
GenerateUnitSpanCount(value, min, max, is_greedy, sc, func_list, context,
|
GenerateUnitSpanCount(value, min, max, is_greedy, sc, context, failure, ref backtrack);
|
||||||
exit_label, continuation, next);
|
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
@@ -746,9 +741,6 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (continuation is not null) {
|
|
||||||
GenerateConcatTail(continuation, next, sc, func_list, context, exit_label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
|
|
||||||
public const string _remain_span = "__remain";
|
public const string _remain_span = "__remain";
|
||||||
|
|
||||||
private int _max_exit_lable;
|
private int _exit_label_count;
|
||||||
|
|
||||||
private int _scratch_scope_count;
|
private int _scratch_scope_count;
|
||||||
|
|
||||||
@@ -54,12 +54,12 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ExitLabel RequestExitLabel(
|
public ExitLabel RequestExitLabel(
|
||||||
ExitMode exitMode
|
ExitKind kind
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int index = _max_exit_lable;
|
int index = _exit_label_count;
|
||||||
_max_exit_lable++;
|
_exit_label_count++;
|
||||||
return new(exitMode, index);
|
return new(kind, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int RequestScratchScopeIndex()
|
public int RequestScratchScopeIndex()
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
in SequenceEmitter<string> sc,
|
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_label
|
in ExitLabel local_fail,
|
||||||
|
ref ExitLabel backtrack
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// A special rule owns the entire matched subtree. Returning false leaves emission
|
// A special rule owns the entire matched subtree. Returning false leaves emission
|
||||||
@@ -25,7 +26,7 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
return node.Label switch
|
return node.Label switch
|
||||||
{
|
{
|
||||||
RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context,
|
RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context,
|
||||||
exit_label),
|
local_fail, ref backtrack),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -166,10 +167,12 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
in ExitLabel success
|
in ExitLabel success
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ExitLabel failure = context.RequestExitLabel(ExitMode.Goto);
|
ExitLabel failure = context.RequestExitLabel(ExitKind.AltNext);
|
||||||
string entry = $"__alt_entry_{context.RequestScratchScopeIndex()}";
|
string entry = $"__alt_entry_{context.RequestScratchScopeIndex()}";
|
||||||
_ = sc.AppendLine($"{ROS_char} {entry} = {context.RemainSpan};");
|
_ = sc.AppendLine($"{ROS_char} {entry} = {context.RemainSpan};");
|
||||||
GenerateMethodSource(node, sc, func_list, context, failure);
|
// Keep branch-local retries isolated from both siblings and the caller's path.
|
||||||
|
ExitLabel branch_backtrack = failure;
|
||||||
|
GenerateMethodSource(node, sc, func_list, context, failure, ref branch_backtrack);
|
||||||
_ = sc.AppendLine($"goto {success.GetLabelName()};")
|
_ = sc.AppendLine($"goto {success.GetLabelName()};")
|
||||||
.AppendLine($"{failure.GetLabelName()}:")
|
.AppendLine($"{failure.GetLabelName()}:")
|
||||||
.AppendLine($"{context.RemainSpan} = {entry};");
|
.AppendLine($"{context.RemainSpan} = {entry};");
|
||||||
@@ -180,50 +183,6 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region CONCAT continuation
|
|
||||||
|
|
||||||
private static void AppendConcatNodes(
|
|
||||||
RegexAstNode node,
|
|
||||||
List<RegexAstNode> sequence
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (RegexAstNode.IsNullOrEmpty(node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (node.Label == RegexElementLabel.CONCAT &&
|
|
||||||
!RegexAstNode.IsDotStar(node.Left) && !IsNamedFind(node))
|
|
||||||
{
|
|
||||||
AppendConcatNodes(node.Left, sequence);
|
|
||||||
AppendConcatNodes(node.Right, sequence);
|
|
||||||
} else {
|
|
||||||
sequence.Add(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateConcatTail(
|
|
||||||
IReadOnlyList<RegexAstNode> sequence,
|
|
||||||
int start,
|
|
||||||
in SequenceEmitter<string> sc,
|
|
||||||
in List<SequenceEmitter<string>> func_list,
|
|
||||||
in SourceGenContext context,
|
|
||||||
in ExitLabel exit_label
|
|
||||||
)
|
|
||||||
{
|
|
||||||
for (int i = start; i < sequence.Count; i++)
|
|
||||||
{
|
|
||||||
RegexAstNode node = sequence[i];
|
|
||||||
if (node.Label == RegexElementLabel.COUNT)
|
|
||||||
{
|
|
||||||
// COUNT owns both its repetition and the continuation that may retry it.
|
|
||||||
GenerateCount(node, sc, func_list, context, exit_label, sequence, i + 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GenerateMethodSource(node, sc, func_list, context, exit_label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region FIND concatenation
|
#region FIND concatenation
|
||||||
|
|
||||||
private static bool TryGenerateFindConcat(
|
private static bool TryGenerateFindConcat(
|
||||||
@@ -231,7 +190,8 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
in SequenceEmitter<string> sc,
|
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_label
|
in ExitLabel local_fail,
|
||||||
|
ref ExitLabel backtrack
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
RegexAstNode left = node.Left;
|
RegexAstNode left = node.Left;
|
||||||
@@ -246,7 +206,7 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
// TODO(NGRA): Emit the unnamed FIND candidate loop and retry the right subtree
|
// TODO(NGRA): Emit the unnamed FIND candidate loop and retry the right subtree
|
||||||
// until it succeeds or no anchor candidate remains.
|
// until it succeeds or no anchor candidate remains.
|
||||||
GenerateMethodSource(right, sc, func_list, context, exit_label, true);
|
GenerateMethodSource(right, sc, func_list, context, local_fail, ref backtrack, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +216,7 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
// TODO(NGRA): Emit named FIND retry and capture the text preceding the
|
// TODO(NGRA): Emit named FIND retry and capture the text preceding the
|
||||||
// successful anchor candidate.
|
// successful anchor candidate.
|
||||||
GenerateMethodSource(right, sc, func_list, context, exit_label, true);
|
GenerateMethodSource(right, sc, func_list, context, local_fail, ref backtrack, true);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// TODO(NGRA): Capture the remaining input for a terminal named dot-star.
|
// TODO(NGRA): Capture the remaining input for a terminal named dot-star.
|
||||||
|
|||||||
@@ -172,7 +172,15 @@ namespace Nitload.RegularAnalysis.Text
|
|||||||
SequenceEmitter<string> methodSource = new();
|
SequenceEmitter<string> methodSource = new();
|
||||||
List<SequenceEmitter<string>> localFunctions = [];
|
List<SequenceEmitter<string>> localFunctions = [];
|
||||||
|
|
||||||
GenerateMethodSource(root, methodSource, localFunctions, generationContext);
|
ExitLabel local_fail = generationContext.RequestExitLabel(ExitKind.MatchFailed);
|
||||||
|
ExitLabel backtrack = local_fail;
|
||||||
|
ExitLabel end = generationContext.RequestExitLabel(ExitKind.MatchEnd);
|
||||||
|
GenerateMethodSource(root, methodSource, localFunctions, generationContext,
|
||||||
|
local_fail, ref backtrack);
|
||||||
|
_ = methodSource.AppendLine($"goto {end.GetLabelName()};")
|
||||||
|
.AppendLine($"{local_fail.GetLabelName()}: {{}}")
|
||||||
|
.AppendLine("// TODO(NGRA): Produce the root failure result here.")
|
||||||
|
.AppendLine($"{end.GetLabelName()}: {{}}");
|
||||||
foreach (SequenceEmitter<string> localFunction in localFunctions) {
|
foreach (SequenceEmitter<string> localFunction in localFunctions) {
|
||||||
_ = methodSource.AppendLine()
|
_ = methodSource.AppendLine()
|
||||||
.AppendLine()
|
.AppendLine()
|
||||||
|
|||||||
@@ -38,8 +38,14 @@ namespace Nitload
|
|||||||
SequenceEmitter<string> body = new();
|
SequenceEmitter<string> body = new();
|
||||||
List<SequenceEmitter<string>> functions = [];
|
List<SequenceEmitter<string>> functions = [];
|
||||||
SourceGenContext context = new();
|
SourceGenContext context = new();
|
||||||
ExitLabel exit = context.RequestExitLabel(ExitMode.Goto);
|
ExitLabel local_fail = context.RequestExitLabel(ExitKind.MatchFailed);
|
||||||
GenerateMethodSource(analysis.Ast.Root, body, functions, context, exit);
|
ExitLabel backtrack = local_fail;
|
||||||
|
ExitLabel end = context.RequestExitLabel(ExitKind.MatchEnd);
|
||||||
|
GenerateMethodSource(analysis.Ast.Root, body, functions, context, local_fail, ref backtrack);
|
||||||
|
_ = body.AppendLine($"goto {end.GetLabelName()};")
|
||||||
|
.AppendLine($"{local_fail.GetLabelName()}: {{}}")
|
||||||
|
.AppendLine("// TODO(NGRA): Produce the root failure result here.")
|
||||||
|
.AppendLine($"{end.GetLabelName()}: {{}}");
|
||||||
|
|
||||||
foreach (string line in body.ToList()) {
|
foreach (string line in body.ToList()) {
|
||||||
Console.WriteLine(line);
|
Console.WriteLine(line);
|
||||||
|
|||||||
Reference in New Issue
Block a user