From 57587bec160025805c904536f840cc0d2c36864c Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Mon, 14 Sep 2026 18:21:23 +0800 Subject: [PATCH] use new exit lable --- .../Text/TextRegexSourceGen.ExitLabel.cs | 28 +- .../Text/TextRegexSourceGen.GenClass.cs | 246 +++++++++--------- .../Text/TextRegexSourceGen.GenContext.cs | 10 +- .../Text/TextRegexSourceGen.GenSpecial.cs | 62 +---- .../Text/TextRegexSourceGen.cs | 10 +- test/StgSharpDebug/TestRegexGenerate.cs | 10 +- 6 files changed, 169 insertions(+), 197 deletions(-) diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs index 766bd8e7..23a9b1db 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.ExitLabel.cs @@ -16,12 +16,15 @@ namespace Nitload.RegularAnalysis.Text internal partial class TextRegexSourceGen { - internal enum ExitMode + internal enum ExitKind { - Break, - Continue, - Goto, + MatchFailed, + MatchEnd, + CountRetry, + CountResume, + AltNext, + AltMatched, } @@ -29,25 +32,28 @@ namespace Nitload.RegularAnalysis.Text { public ExitLabel( - ExitMode mode, + ExitKind kind, int order ) { - Mode = mode; + Kind = kind; LabelOrder = order; } - public ExitMode Mode { get; init; } + public ExitKind Kind { get; init; } public int LabelOrder { get; init; } public string GetLabelName() { - return Mode switch + return Kind switch { - ExitMode.Break => $@"__break_{LabelOrder}", - ExitMode.Continue => $@"__continue_{LabelOrder}", - ExitMode.Goto => $@"__goto_{LabelOrder}", + ExitKind.MatchFailed => $@"__match_failed_{LabelOrder}", + ExitKind.MatchEnd => $@"__match_end_{LabelOrder}", + ExitKind.CountRetry => $@"__count_retry_{LabelOrder}", + ExitKind.CountResume => $@"__count_resume_{LabelOrder}", + ExitKind.AltNext => $@"__alt_next_{LabelOrder}", + ExitKind.AltMatched => $@"__alt_matched_{LabelOrder}", _ => throw new NotImplementedException(), }; } diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs index 6791bf37..87fd828b 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs @@ -14,20 +14,31 @@ namespace Nitload.RegularAnalysis.Text internal partial class TextRegexSourceGen { + /// + /// Emits a subtree and updates the retry target for the rest of the current path. + /// + /// + /// 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. + /// internal static void GenerateMethodSource( RegexAstNode ast, in SequenceEmitter sc, in List> func_list, in SourceGenContext context, - in ExitLabel exit_label = default, + in ExitLabel local_fail, + ref ExitLabel backtrack, 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; if (root.Label == RegexElementLabel.EPSILON) { return; } - if (TryGenerateSpecialSource(root, sc, func_list, context, exit_label)) { + if (TryGenerateSpecialSource(root, sc, func_list, context, local_fail, ref backtrack)) { return; } RegexElementLabel op = root.Label & (RegexElementLabel.VAST_OPERATOR); @@ -36,16 +47,16 @@ namespace Nitload.RegularAnalysis.Text switch (op) { case RegexElementLabel.CONCAT: - GenerateConcat(root, sc, func_list, context, exit_label); + GenerateConcat(root, sc, func_list, context, local_fail, ref backtrack); break; case RegexElementLabel.COUNT: - GenerateCount(root, sc, func_list, context, exit_label); + GenerateCount(root, sc, func_list, context, local_fail, ref backtrack); break; case RegexElementLabel.GROUP_BEGIN: - GenerateGroup(root, sc, func_list, context, exit_label); + GenerateGroup(root, sc, func_list, context, local_fail, ref backtrack); break; case RegexElementLabel.ALT: - GenerateAlt(root, sc, func_list, context, exit_label); + GenerateAlt(root, sc, func_list, context, backtrack); break; default: @@ -59,13 +70,13 @@ namespace Nitload.RegularAnalysis.Text switch (op) { case RegexElementLabel.UNIT: - GenerateUnit(root, sc, context, exit_label); + GenerateUnit(root, sc, context, backtrack); break; case RegexElementLabel.UNIT_SET: - GenerateUnitSet(root, sc, context, exit_label); + GenerateUnitSet(root, sc, context, backtrack); break; case RegexElementLabel.UNIT_SPAN: - GenerateUnitSpan(root, sc, context, exit_label); + GenerateUnitSpan(root, sc, context, backtrack); break; default: break; @@ -220,11 +231,9 @@ namespace Nitload.RegularAnalysis.Text int max, bool is_greedy, [NotNull] in SequenceEmitter sc, - in List> func_list, in SourceGenContext context, in ExitLabel exit, - IReadOnlyList? continuation = null, - int next = 0 + ref ExitLabel backtrack ) { if (min < 0 || (max >= 0 && max < min)) { @@ -303,61 +312,54 @@ namespace Nitload.RegularAnalysis.Text } int additional_limit = max < 0 ? -1 : max - min; - bool has_continuation = continuation is not null && next < continuation.Count; - bool can_retry = has_continuation && 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}"; - if (can_retry) { - _ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};"); - } - if (can_retry || (is_greedy && additional_limit > 0)) { - _ = sc.AppendLine($"int {additional_count} = 0;"); - } - if (is_greedy && additional_limit != 0) + 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);"); - if (can_retry || additional_limit > 0) { - _ = sc.AppendLine($"{additional_count}++;"); - } - _ = sc.AppendLine("}"); + .AppendLine($"{remain_span} = {remain_span}.Slice(1);") + .AppendLine($"{additional_count}++;") + .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. - ExitLabel retry = context.RequestExitLabel(ExitMode.Goto); - _ = sc.AppendLine("while (true)") + _ = sc.AppendLine($"if ({additional_count} == 0)") .AppendLine("{") - .AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count});"); - GenerateConcatTail(continuation!, next, sc, func_list, context, retry); - _ = sc.AppendLine("break;") - .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("}"); - } else if (has_continuation) { - GenerateConcatTail(continuation!, next, sc, func_list, context, exit); + .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( @@ -440,11 +442,9 @@ namespace Nitload.RegularAnalysis.Text int max, bool is_greedy, [NotNull] in SequenceEmitter sc, - in List> func_list, in SourceGenContext context, in ExitLabel exit, - IReadOnlyList? continuation = null, - int next = 0 + ref ExitLabel backtrack ) { if (min < 0 || (max >= 0 && max < min)) { @@ -554,61 +554,54 @@ namespace Nitload.RegularAnalysis.Text } int additional_limit = max < 0 ? -1 : max - min; - bool has_continuation = continuation is not null && next < continuation.Count; - bool can_retry = has_continuation && 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}"; - if (can_retry) { - _ = sc.AppendLine($"{ROS_char} {additional_entry} = {remain_span};"); - } - if (can_retry || (is_greedy && additional_limit > 0)) { - _ = sc.AppendLine($"int {additional_count} = 0;"); - } - if (is_greedy && additional_limit != 0) + 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}.{starts_with}({expected_literal}))") .AppendLine("{") - .AppendLine($"{remain_span} = {remain_span}.Slice({literal.Length});"); - if (can_retry || additional_limit > 0) { - _ = sc.AppendLine($"{additional_count}++;"); - } - _ = sc.AppendLine("}"); + .AppendLine($"{remain_span} = {remain_span}.Slice({literal.Length});") + .AppendLine($"{additional_count}++;") + .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. - ExitLabel retry = context.RequestExitLabel(ExitMode.Goto); - _ = sc.AppendLine("while (true)") + _ = sc.AppendLine($"if ({additional_count} == 0)") .AppendLine("{") - .AppendLine($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});"); - GenerateConcatTail(continuation!, next, sc, func_list, context, retry); - _ = sc.AppendLine("break;") - .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} * {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); + .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} * {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($"{remain_span} = {additional_entry}.Slice({additional_count} * {literal.Length});") + .AppendLine($"{resume.GetLabelName()}: {{}}"); + backtrack = retry; } #endregion @@ -630,7 +623,7 @@ namespace Nitload.RegularAnalysis.Text in ExitLabel exit_label ) { - ExitLabel success = context.RequestExitLabel(ExitMode.Goto); + ExitLabel success = context.RequestExitLabel(ExitKind.AltMatched); RegexAstNode current = node.Right; while (!RegexAstNode.IsNullOrEmpty(current)) { @@ -639,7 +632,7 @@ namespace Nitload.RegularAnalysis.Text } _ = sc.AppendLine($"goto {exit_label.GetLabelName()};") - .AppendLine($"{success.GetLabelName()}: ;"); + .AppendLine($"{success.GetLabelName()}: {{}}"); } private static void GenerateGroup( @@ -647,7 +640,8 @@ namespace Nitload.RegularAnalysis.Text [NotNull] in SequenceEmitter sc, in List> func_list, in SourceGenContext context, - in ExitLabel exit_label + in ExitLabel local_fail, + ref ExitLabel backtrack ) { 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. if (!RegexAstNode.IsNullOrEmpty(left)) { - GenerateMethodSource(left, sc, func_list, context, exit_label); + GenerateMethodSource(left, sc, func_list, context, local_fail, ref backtrack); } else 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; @@ -679,13 +673,16 @@ namespace Nitload.RegularAnalysis.Text [NotNull] in SequenceEmitter sc, in List> func_list, 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. - List sequence = []; - AppendConcatNodes(node, sequence); - GenerateConcatTail(sequence, 0, sc, func_list, context, exit_label); + if (!RegexAstNode.IsNullOrEmpty(node.Left)) { + GenerateMethodSource(node.Left, sc, func_list, context, local_fail, ref backtrack); + } + if (!RegexAstNode.IsNullOrEmpty(node.Right)) { + GenerateMethodSource(node.Right, sc, func_list, context, local_fail, ref backtrack); + } } private static void GenerateCount( @@ -693,9 +690,8 @@ namespace Nitload.RegularAnalysis.Text [NotNull] in SequenceEmitter sc, in List> func_list, in SourceGenContext context, - in ExitLabel exit_label, - IReadOnlyList? continuation = null, - int next = 0 + in ExitLabel local_fail, + ref ExitLabel backtrack ) { RegexAstNode value = RegexAstNode.IsNullOrEmpty(node.Left) ? node.Right : node.Left; @@ -716,6 +712,7 @@ namespace Nitload.RegularAnalysis.Text int min = payload.Min; int max = payload.Max; bool is_greedy = payload.IsGreedy; + ExitLabel failure = backtrack; // TODO(NGRA-NAMING): Reserve exclusive names for future repetition/rollback state // 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. // 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 { switch (value.Label & RegexElementLabel.SEQUENCE) { case RegexElementLabel.UNIT: - GenerateUnitCount(value, min, max, is_greedy, sc, func_list, context, - exit_label, continuation, next); + GenerateUnitCount(value, min, max, is_greedy, sc, context, failure, ref backtrack); return; case RegexElementLabel.UNIT_SET: - GenerateUnitSetCount(value, min, max, is_greedy, sc, context, exit_label); + GenerateUnitSetCount(value, min, max, is_greedy, sc, context, failure); break; case RegexElementLabel.UNIT_SPAN: - GenerateUnitSpanCount(value, min, max, is_greedy, sc, func_list, context, - exit_label, continuation, next); + GenerateUnitSpanCount(value, min, max, is_greedy, sc, context, failure, ref backtrack); return; default: @@ -746,9 +741,6 @@ namespace Nitload.RegularAnalysis.Text break; } } - if (continuation is not null) { - GenerateConcatTail(continuation, next, sc, func_list, context, exit_label); - } } #endregion diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs index b7642b11..c17f8dc4 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs @@ -36,7 +36,7 @@ namespace Nitload.RegularAnalysis.Text public const string _remain_span = "__remain"; - private int _max_exit_lable; + private int _exit_label_count; private int _scratch_scope_count; @@ -54,12 +54,12 @@ namespace Nitload.RegularAnalysis.Text } public ExitLabel RequestExitLabel( - ExitMode exitMode + ExitKind kind ) { - int index = _max_exit_lable; - _max_exit_lable++; - return new(exitMode, index); + int index = _exit_label_count; + _exit_label_count++; + return new(kind, index); } public int RequestScratchScopeIndex() diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs index d0724085..ffc61839 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.GenSpecial.cs @@ -17,7 +17,8 @@ namespace Nitload.RegularAnalysis.Text in SequenceEmitter sc, in List> func_list, 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 @@ -25,7 +26,7 @@ namespace Nitload.RegularAnalysis.Text return node.Label switch { RegexElementLabel.CONCAT => TryGenerateFindConcat(node, sc, func_list, context, - exit_label), + local_fail, ref backtrack), _ => false, }; } @@ -166,10 +167,12 @@ namespace Nitload.RegularAnalysis.Text in ExitLabel success ) { - ExitLabel failure = context.RequestExitLabel(ExitMode.Goto); + ExitLabel failure = context.RequestExitLabel(ExitKind.AltNext); string entry = $"__alt_entry_{context.RequestScratchScopeIndex()}"; _ = 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()};") .AppendLine($"{failure.GetLabelName()}:") .AppendLine($"{context.RemainSpan} = {entry};"); @@ -180,50 +183,6 @@ namespace Nitload.RegularAnalysis.Text #endregion - #region CONCAT continuation - - private static void AppendConcatNodes( - RegexAstNode node, - List 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 sequence, - int start, - in SequenceEmitter sc, - in List> 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 private static bool TryGenerateFindConcat( @@ -231,7 +190,8 @@ namespace Nitload.RegularAnalysis.Text in SequenceEmitter sc, in List> func_list, in SourceGenContext context, - in ExitLabel exit_label + in ExitLabel local_fail, + ref ExitLabel backtrack ) { 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 // 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; } @@ -256,7 +216,7 @@ namespace Nitload.RegularAnalysis.Text { // TODO(NGRA): Emit named FIND retry and capture the text preceding the // successful anchor candidate. - GenerateMethodSource(right, sc, func_list, context, exit_label, true); + GenerateMethodSource(right, sc, func_list, context, local_fail, ref backtrack, true); } else { // TODO(NGRA): Capture the remaining input for a terminal named dot-star. diff --git a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.cs b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.cs index bfe9cc54..eec6d7e3 100644 --- a/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.cs +++ b/src/Nitload.RegularAnalysis/Text/TextRegexSourceGen.cs @@ -172,7 +172,15 @@ namespace Nitload.RegularAnalysis.Text SequenceEmitter methodSource = new(); List> 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 localFunction in localFunctions) { _ = methodSource.AppendLine() .AppendLine() diff --git a/test/StgSharpDebug/TestRegexGenerate.cs b/test/StgSharpDebug/TestRegexGenerate.cs index 4e09804b..92c6277d 100644 --- a/test/StgSharpDebug/TestRegexGenerate.cs +++ b/test/StgSharpDebug/TestRegexGenerate.cs @@ -38,8 +38,14 @@ namespace Nitload SequenceEmitter body = new(); List> functions = []; SourceGenContext context = new(); - ExitLabel exit = context.RequestExitLabel(ExitMode.Goto); - GenerateMethodSource(analysis.Ast.Root, body, functions, context, exit); + ExitLabel local_fail = context.RequestExitLabel(ExitKind.MatchFailed); + 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()) { Console.WriteLine(line);