diff --git a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.MultiIR.cs b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.MultiIR.cs index 9564aa62..0d8af8f5 100644 --- a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.MultiIR.cs +++ b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.MultiIR.cs @@ -227,7 +227,7 @@ namespace StgSharp.RegularAnalysis.Text * [011] 0003, Sccs, DEF , LINE, 0010, PACK SUCCESS GROUP: "name" * [012] 0000, DEF , DEF , NONE, 0000, MATCH_SEQ "234" */ - if (ir_span[0] is not RegexCountSetIR { IsDotStar: true } || + if (ir_span[0] is not RegexFindComplexIR { CommandCount: -1 } || ir_span[1] is not RegexPackIR pack) { return -1; diff --git a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs index 18ac24ce..007646b6 100644 --- a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs +++ b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenClass.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp; using StgSharp.RegularAnalysis.Abstraction; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -117,24 +118,13 @@ namespace StgSharp.RegularAnalysis.Text ReadOnlySpan ir_sp = CollectionsMarshal.AsSpan(ir); SequenceEmitter match_e = new(); - int index = 0; - int end = FindRegionEnd(ir_sp, index); - ReadOnlySpan region = ir_sp[0..end]; - while (true) - { - // GenerateRegion(region, sc, match_e, sub_match_list); - _ = TryGenerateAltChain(0, region, sc, match_e, sub_match_list); - _ = TryGenerateTryBlock(0, region, sc, match_e, sub_match_list); - _ = TryGenerateNamedCountAny(0, region, sc, match_e, sub_match_list); - if (end >= ir_sp.Length) - { - break; - } - index = end; - end = FindRegionEnd(ir_sp, end); - region = ir_sp[index..end]; - } + sc.RegionExtent = MeasureRegionExtents(ir_sp, info.RegionCount); + + // begin a very big do while(0) + _ = se.AppendLine(sc.BeginRegion()); + + _ = GenerateBlock(0, ir_sp, sc, match_e, sub_match_list); _ = se.AppendLine($@"public override MatchResult Match({ROS_char} text)")// .AppendLine(@"{"); @@ -165,6 +155,11 @@ namespace StgSharp.RegularAnalysis.Text _ = se.Append(match_e); + // end of the biggest region + _ = se.AppendLine(sc.CloseRegion()); + + // close the entire match result and return + foreach (SequenceEmitter _sub in sub_match_list) { _ = se.AppendLine() // @@ -176,6 +171,82 @@ namespace StgSharp.RegularAnalysis.Text return se; } + /// + /// Recursive dispatch entry. Walks one contiguous IR block and offers each window to the + /// generators in descending consumption order. Region bodies, count bodies and find + /// bodies recurse into this same method. + /// + /// + /// Total IR entries consumed, always ir_span.Length . + /// + private static int GenerateBlock( + int outer_level, + ReadOnlySpan ir_span, + SourceGenContext sc, + in SequenceEmitter code, + in List> local_func + ) + { + int index = 0; + while (index < ir_span.Length) + { + ReadOnlySpan rest = ir_span[index..]; + + // ground entries pass one by one, a foreign region id opens a mountain; + // carving always lands on a mountain head, so the extent is the carve length + int id = rest[0].Prefix.WorkingRegion; + int carve = id == outer_level ? 1 : sc.RegionExtent[id]; + Debug.Assert(id == outer_level || carve == FindRegionEnd(rest)); + + index += GenerateRegion(outer_level, rest[..carve], sc, code, local_func); + } + return index; + } + + /// + /// Processes exactly one carved region, or one ground instruction. Generators that own a + /// body re-enter , which is what closes the recursion. + /// + /// + /// IR entries consumed, at least 1. + /// + private static int GenerateRegion( + int outer_level, + ReadOnlySpan region, + SourceGenContext sc, + in SequenceEmitter code, + in List> local_func + ) + { + int consumed; + + // L1, several regions plus a tail + if ((consumed = TryGenerateAltChain(outer_level, region, sc, code, local_func)) > 0) { + return consumed; + } + if ((consumed = TryGenerateNamedCountAny(outer_level, region, sc, code, + local_func)) > 0) { + return consumed; + } + + // L2, one region + if ((consumed = TryGenerateTryBlock(outer_level, region, sc, code, local_func)) > 0) { + return consumed; + } + + // L4, single instruction fallback + if ((consumed = TryGenerateSingleIR(outer_level, region, sc, code, local_func)) > 0) { + return consumed; + } + if ((consumed = TryGeneratePack(outer_level, region, sc, code, local_func)) > 0) { + return consumed; + } + + // unclaimed window must break the build of the generated file + _ = code.AppendLine($@"#error NGRA has no source generation rule for {region[0]}"); + return 1; + } + #endregion private enum RegionExitMethod @@ -188,18 +259,45 @@ namespace StgSharp.RegularAnalysis.Text #region shared helpers + // backward scan; the final write per id happens at its first entry, leaving full extent + private static int[] MeasureRegionExtents( + ReadOnlySpan ir_sp, + int regionCount + ) + { + int[] last_seen = new int[regionCount + 1]; + int[] extent = new int[regionCount + 1]; + Array.Fill(last_seen, -1); + for (int i = ir_sp.Length - 1; i >= 0; i--) + { + int id = ir_sp[i].Prefix.WorkingRegion; + if (last_seen[id] < 0) { + last_seen[id] = i; + } + extent[id] = last_seen[id] - i + 1; + } + return extent; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FindRegionEnd( + ReadOnlySpan ir_sp + ) + { + return FindRegionEnd(ir_sp, ir_sp[0].Prefix.WorkingRegion); + } + + // exclusive bound of the mountain opened at ir_sp[0], sub peaks included [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int FindRegionEnd( ReadOnlySpan ir_sp, - int current + int level ) { - int cur_depth = ir_sp[current].Prefix.WorkingRegion; - int end = ir_sp.Length; - for (; end > 0; end--) + for (int end = ir_sp.Length - 1; end > 0; end--) { - if (ir_sp[end].Prefix.WorkingRegion == cur_depth) { - return end; + if (ir_sp[end].Prefix.WorkingRegion == level) { + return end + 1; } } return 1; diff --git a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs index 8c2abdb1..7525ea30 100644 --- a/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs +++ b/src/StgSharp.RegularAnalysis/Text/TextRegexSourceGen.GenContext.cs @@ -56,6 +56,9 @@ namespace StgSharp.RegularAnalysis.Text public string StepResult { get; } = _stepResult; + // region id -> entry count from first to last occurrence, filled once per pattern + public int[] RegionExtent { get; set; } = []; + public string[] BeginRegion() { return [