a few changes on context

This commit is contained in:
Youmu Liu
2026-08-11 18:04:57 +08:00
parent dc9ccfc15a
commit a9835e0349
3 changed files with 125 additions and 24 deletions
@@ -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;
@@ -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<RegexIR> ir_sp = CollectionsMarshal.AsSpan(ir);
SequenceEmitter<string> match_e = new();
int index = 0;
int end = FindRegionEnd(ir_sp, index);
ReadOnlySpan<RegexIR> 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<string> _sub in sub_match_list)
{
_ = se.AppendLine() //
@@ -176,6 +171,82 @@ namespace StgSharp.RegularAnalysis.Text
return se;
}
/// <summary>
/// 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.
/// </summary>
/// <returns>
/// Total IR entries consumed, always <c> ir_span.Length </c>.
/// </returns>
private static int GenerateBlock(
int outer_level,
ReadOnlySpan<RegexIR> ir_span,
SourceGenContext sc,
in SequenceEmitter<string> code,
in List<SequenceEmitter<string>> local_func
)
{
int index = 0;
while (index < ir_span.Length)
{
ReadOnlySpan<RegexIR> 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;
}
/// <summary>
/// Processes exactly one carved region, or one ground instruction. Generators that own a
/// body re-enter <see cref="GenerateBlock" />, which is what closes the recursion.
/// </summary>
/// <returns>
/// IR entries consumed, at least 1.
/// </returns>
private static int GenerateRegion(
int outer_level,
ReadOnlySpan<RegexIR> region,
SourceGenContext sc,
in SequenceEmitter<string> code,
in List<SequenceEmitter<string>> 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<RegexIR> 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<RegexIR> 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<RegexIR> 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;
@@ -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 [