Compare commits

..
3 Commits
Author SHA1 Message Date
Youmu Liu 57587bec16 use new exit lable 2026-09-14 18:21:23 +08:00
Youmu Liu 9e9078ced1 meet difficult problem on COUNT node 2026-09-11 17:30:31 +08:00
Youmu Liu 5a3286dc21 meet difficult problem on COUNT node 2026-09-11 17:29:42 +08:00
8 changed files with 213 additions and 123 deletions
@@ -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;
@@ -221,7 +232,8 @@ 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,
in ExitLabel exit in ExitLabel exit,
ref ExitLabel backtrack
) )
{ {
if (min < 0 || (max >= 0 && max < min)) { if (min < 0 || (max >= 0 && max < min)) {
@@ -300,23 +312,54 @@ namespace Nitload.RegularAnalysis.Text
} }
int additional_limit = max < 0 ? -1 : max - min; int additional_limit = max < 0 ? -1 : max - min;
if (is_greedy && additional_limit != 0) if (additional_limit == 0) {
{ return;
_ = additional_limit < 0 ?
sc.AppendLine($"while (!{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal})")
.AppendLine("{")
.AppendLine($"{remain_span} = {remain_span}.Slice(1);")
.AppendLine("}") :
sc.AppendLine($"for (int {additional_count} = 0; {additional_count} < {additional_limit} && !{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal}; {additional_count}++)")
.AppendLine("{")
.AppendLine($"{remain_span} = {remain_span}.Slice(1);")
.AppendLine("}");
} else if (!is_greedy && additional_limit != 0)
{
// Lazy COUNT deliberately stops at Min. Its optional tail is consumed only when
// a future continuation attempt fails and requests another repetition.
_ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation.");
} }
// Save the optional portion once. Its counter is shared by scanning and retries.
string additional_entry = $"__additional_entry_{scratch_scope_index}";
ExitLabel retry = context.RequestExitLabel(ExitKind.CountRetry);
ExitLabel resume = context.RequestExitLabel(ExitKind.CountResume);
_ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};")
.AppendLine($"int {additional_count} = 0;");
if (is_greedy)
{
string limit = additional_limit < 0 ? string.Empty :
$"{additional_count} < {additional_limit} && ";
_ = sc.AppendLine($"while ({limit}!{remain_span}.IsEmpty && {remain_span}[0] == {expected_literal})")
.AppendLine("{")
.AppendLine($"{remain_span} = {remain_span}.Slice(1);")
.AppendLine($"{additional_count}++;")
.AppendLine("}");
}
// 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)
{
_ = sc.AppendLine($"if ({additional_count} == 0)")
.AppendLine("{")
.AppendLine($"{remain_span} = {additional_entry};")
.AppendLine($"goto {exit_label_node};")
.AppendLine("}")
.AppendLine($"{additional_count}--;");
} else
{
string limit = additional_limit < 0 ? string.Empty :
$"{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($"{remain_span} = {additional_entry}.Slice({additional_count});")
.AppendLine($"{resume.GetLabelName()}: {{}}");
backtrack = retry;
} }
private static void GenerateUnitSetCount( private static void GenerateUnitSetCount(
@@ -400,7 +443,8 @@ 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,
in ExitLabel exit in ExitLabel exit,
ref ExitLabel backtrack
) )
{ {
if (min < 0 || (max >= 0 && max < min)) { if (min < 0 || (max >= 0 && max < min)) {
@@ -510,27 +554,54 @@ namespace Nitload.RegularAnalysis.Text
} }
int additional_limit = max < 0 ? -1 : max - min; int additional_limit = max < 0 ? -1 : max - min;
if (is_greedy && additional_limit != 0) if (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}";
ExitLabel retry = context.RequestExitLabel(ExitKind.CountRetry);
ExitLabel resume = context.RequestExitLabel(ExitKind.CountResume);
_ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};")
.AppendLine($"int {additional_count} = 0;");
if (is_greedy)
{ {
if (additional_limit < 0) string limit = additional_limit < 0 ? string.Empty :
{ $"{additional_count} < {additional_limit} && ";
_ = sc.AppendLine($"while ({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});")
.AppendLine($"{additional_count}++;")
.AppendLine("}"); .AppendLine("}");
}
// 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)
{
_ = sc.AppendLine($"if ({additional_count} == 0)")
.AppendLine("{")
.AppendLine($"{remain_span} = {additional_entry};")
.AppendLine($"goto {exit_label_node};")
.AppendLine("}")
.AppendLine($"{additional_count}--;");
} else } else
{ {
_ = sc.AppendLine($"for (int {additional_count} = 0; {additional_count} < {additional_limit} && {remain_span}.{starts_with}({expected_literal}); {additional_count}++)") string limit = additional_limit < 0 ? string.Empty :
$"{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("{")
.AppendLine($"{remain_span} = {remain_span}.Slice({literal.Length});") .AppendLine($"{remain_span} = {additional_entry};")
.AppendLine("}"); .AppendLine($"goto {exit_label_node};")
} .AppendLine("}")
} else if (!is_greedy && additional_limit != 0) .AppendLine($"{additional_count}++;");
{
// Lazy COUNT deliberately stops at Min. Its optional tail is consumed only when
// a future continuation attempt fails and requests another repetition.
_ = sc.AppendLine("// TODO(NGRA): Retry the lazy COUNT tail from its continuation.");
} }
_ = sc.AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});")
.AppendLine($"{resume.GetLabelName()}: {{}}");
backtrack = retry;
} }
#endregion #endregion
@@ -552,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))
{ {
@@ -561,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(
@@ -569,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;
@@ -583,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;
@@ -601,14 +673,15 @@ 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
) )
{ {
if (!RegexAstNode.IsNullOrEmpty(node.Left)) { if (!RegexAstNode.IsNullOrEmpty(node.Left)) {
GenerateMethodSource(node.Left, sc, func_list, context, exit_label); GenerateMethodSource(node.Left, sc, func_list, context, local_fail, ref backtrack);
} }
if (!RegexAstNode.IsNullOrEmpty(node.Right)) { if (!RegexAstNode.IsNullOrEmpty(node.Right)) {
GenerateMethodSource(node.Right, sc, func_list, context, exit_label); GenerateMethodSource(node.Right, sc, func_list, context, local_fail, ref backtrack);
} }
} }
@@ -617,17 +690,29 @@ 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 value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left; RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left;
if (RegexAstNode.IsNullOrEmpty(value)) { if (RegexAstNode.IsNullOrEmpty(value)) {
throw new InvalidOperationException("COUNT requires an operand."); throw new InvalidOperationException("COUNT requires an operand.");
} }
// Inspect transparent CONCAT wrappers without rewriting the repetition body.
while (value.Label == RegexElementLabel.CONCAT)
{
bool left_empty = RegexAstNode.IsNullOrEmpty(value.Left);
bool right_empty = RegexAstNode.IsNullOrEmpty(value.Right);
if (left_empty == right_empty) {
break;
}
value = left_empty ? value.Right : value.Left;
}
RegexCountPayload payload = node.PayloadAs<RegexCountPayload>(); RegexCountPayload payload = node.PayloadAs<RegexCountPayload>();
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.
@@ -635,20 +720,20 @@ 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, context, exit_label); GenerateUnitCount(value, min, max, is_greedy, sc, context, failure, ref backtrack);
break; 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, context, exit_label); GenerateUnitSpanCount(value, min, max, is_greedy, sc, context, failure, ref backtrack);
break; return;
default: default:
// TODO(NGRA): Diagnose malformed COUNT children instead of emitting an // TODO(NGRA): Diagnose malformed COUNT children instead of emitting an
@@ -656,7 +741,6 @@ namespace Nitload.RegularAnalysis.Text
break; break;
} }
} }
} }
#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};");
@@ -187,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;
@@ -202,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;
} }
@@ -212,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()
+3 -12
View File
@@ -24,17 +24,8 @@ namespace Nitload
private static string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp"; private static string testExpPath = @"D:\Programmer\stepcode\data\ap203\ap203.exp";
internal static unsafe void Main( internal static unsafe void Main()
string[] args
)
{ {
// Select the managed-only emitter preview before initializing the normal runtime.
if (args.Length > 0 && args[0] == "--regex-preview")
{
Environment.ExitCode = TestRegexGenerate.Run(args[1..]);
return;
}
try try
{ {
World.Initialize(new() World.Initialize(new()
@@ -46,8 +37,8 @@ namespace Nitload
// .UseModule<GraphicModule>() // .UseModule<GraphicModule>()
.InitializeModule(); .InitializeModule();
// TestRegexAnalyse.Run(); TestRegexAnalyse.Run();
_ = TestRegexGenerate.Run("abc|123"); _ = TestRegexGenerate.Run("abc1{1,2}23");
// TestHlsf(); // TestHlsf();
+1 -1
View File
@@ -52,7 +52,7 @@ namespace Nitload
} }
} }
[GeneratedRegex(@"123.*\w123", RegexOptions.ExplicitCapture)] [GeneratedRegex(@"(a{2,4}bc)d", RegexOptions.ExplicitCapture)]
internal static partial Regex GetRegex(); internal static partial Regex GetRegex();
} }
+19 -22
View File
@@ -1,6 +1,6 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// file="TestRegexGenerate" // file="TestRegexGenerate"
// Project: Nitload // Project: StgSharp
// Copyright (c) Nitload. // Copyright (c) Nitload.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -16,29 +16,19 @@ namespace Nitload
{ {
internal static class TestRegexGenerate internal static class TestRegexGenerate
{ {
public static int Run(params string[] args)
public static int Run(
string arg
)
{ {
if (args.Length > 1) { Console.WriteLine($"Text regex to be tested: {arg} :\n\n\n");
throw new ArgumentException("Expected at most one regex pattern.", nameof(args)); Print(arg);
}
if (args.Length == 1)
{
Print(args[0]);
return 0; return 0;
} }
while (true) private static void Print(
{ string pattern
Console.Write("regex> "); )
string? pattern = Console.ReadLine();
if (pattern is null or ":exit") {
return 0;
}
Print(pattern);
}
}
private static void Print(string pattern)
{ {
TextRegexSource analysis = RegexAnalyzer.Analyze(pattern); TextRegexSource analysis = RegexAnalyzer.Analyze(pattern);
if (analysis.AnalyseException is not null) { if (analysis.AnalyseException is not null) {
@@ -48,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);
@@ -61,5 +57,6 @@ namespace Nitload
} }
} }
} }
} }
} }