regex ir opt code rewriting, use over-again opt method

This commit is contained in:
Youmu Liu
2026-06-29 17:14:52 +08:00
parent 347d4b5a47
commit e60eb0ada6
6 changed files with 188 additions and 411 deletions
@@ -1,30 +1,27 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="RegexInterpreter.IROptimize.cs"
// file="RegexInterpreter.IROptimize"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
@@ -44,13 +41,17 @@ namespace StgSharp.RegularAnalysis.Text
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static RegexInfo ScanAndOptimizeIR(List<RegexIR> baseIR, out List<RegexIR> optIR)
internal static RegexInfo ScanAndOptimizeIR(
List<RegexIR> baseIR,
out List<RegexIR> optIR
)
{
int index = 0;
List<RegexIR> origin_ir = baseIR;
Span<RegexIR> origin_span = CollectionsMarshal.AsSpan(baseIR);
List<RegexIR> opt_ir = new(baseIR.Count);
CapacityFixedList<RegexIR> opt_window = new(8);
Span<RegexIR> opt_win_sp = [ .. opt_window ];
Span<RegexIR> opt_win_sp = [.. opt_window];
RegexInfo info = new();
do
{
@@ -60,7 +61,23 @@ namespace StgSharp.RegularAnalysis.Text
do
{
if (true) { }
int result = 0;
if ((result = TryExpandPlus(opt_win_sp, opt_ir)) >= 0)
{
process_count++;
if (result > 0)
{
RegexCountComplexIR cc = Unsafe.As<RegexCountComplexIR>(opt_win_sp[0]);
Span<RegexIR> sp = origin_span.Slice(index + 1, result);
TryExpandComplexPlus(cc.IsGreedy, sp, opt_ir);
process_count += result;
}
} else if ((result = TryOptDotCountCount(opt_win_sp, opt_ir)) >= 0)
{
process_count++;
} else if (TryFlattenCount(opt_win_sp, opt_ir)) {
process_count++;
}
} while (false);
index += process_count;
@@ -69,67 +86,63 @@ namespace StgSharp.RegularAnalysis.Text
return info;
}
private void OptimizeRange(List<RegexIR> origin, List<RegexIR> target, int begin, int end) { }
private static bool TryExpandComplexPlus(bool is_greedy, Span<RegexIR> complex, List<RegexIR> opt)
private static RegexInfo ScanAndOptimizeIR(
Span<RegexIR> baseIR,
List<RegexIR> opt_ir
)
{
int count = complex.Length;
opt.AddRange(complex);
opt.Add(new RegexCountComplexIR(0, -1, is_greedy, count));
return true;
List<RegexIR> originList = new(baseIR.Length);
CollectionsMarshal.SetCount(originList, baseIR.Length);
baseIR.CopyTo(CollectionsMarshal.AsSpan(originList));
return ScanAndOptimizeIR(originList, out opt_ir);
}
#region expand plus
private static int TryExpandPlus(Span<RegexIR> window, List<RegexIR> opt)
{
/* a+
* convert to
* aa*
*/
RegexIR ir = window[0];
if (ir is not IRegexCount { IsPlus: true } count) {
return -1;
}
bool is_greedy = count.IsGreedy;
string pattern;
switch (ir.Command & RegexIRCommand.Count)
{
case RegexIRCommand.Count_Complex:
RegexCountComplexIR complex = Unsafe.As<RegexCountComplexIR>(ir);
return complex.OperationCount;
case RegexIRCommand.Count_Seq:
RegexCountSeqIR seq = Unsafe.As<RegexCountSeqIR>(ir);
pattern = seq.Sequence;
opt.Add(new RegexMatchSeqIR(pattern));
opt.Add(new RegexCountSeqIR(0, -1, is_greedy, pattern));
return 0;
case RegexIRCommand.Count_Set:
RegexCountSeqIR set = Unsafe.As<RegexCountSeqIR>(ir);
pattern = set.Sequence;
opt.Add(new RegexMatchSeqIR(pattern));
opt.Add(new RegexCountSeqIR(0, -1, is_greedy, pattern));
return 0;
default:
return -1;
}
}
#endregion
private static bool TryFlattenCount(Span<RegexIR> window, List<RegexIR> opt)
private static bool TryFlattenCount(
Span<RegexIR> window,
List<RegexIR> opt
)
{
const int falttern_size = 32;
RegexIR ir = window[0];
if (ir is not RegexCountSeqIR seq) {
return false;
}
string pattern = seq.Sequence;
int pattern_size = pattern.Length;
if (pattern_size > falttern_size) {
opt.Add(ir);
}
int min = seq.Min;
int max = seq.Max;
int min_expand = falttern_size / pattern_size;
min_expand = int.Min(min_expand, min);
string expand = string.Concat(Enumerable.Repeat(pattern, min_expand));
int new_min = min / min_expand;
int new_rest = min % min_expand;
if (new_min == 1)
{
opt.Add(new RegexMatchSeqIR(pattern));
} else
{
opt.Add(new RegexCountSeqIR(new_min, new_min, seq.IsGreedy, pattern));
}
if (new_rest > 0)
{
string expand_tail = string.Concat(Enumerable.Repeat(pattern, new_rest));
opt.Add(new RegexMatchSeqIR(expand_tail));
}
int rest_count = max - min;
rest_count = int.Max(rest_count, -1);
opt.Add(new RegexCountSeqIR(0, rest_count, seq.IsGreedy, pattern));
return true;
}
#region dot count count
private static int TryOptDotCountCount(Span<RegexIR> window, List<RegexIR> opt)
private static int TryOptDotCountCount(
Span<RegexIR> window,
List<RegexIR> opt
)
{
/* .* <any>*
* convert to
@@ -146,15 +159,6 @@ namespace StgSharp.RegularAnalysis.Text
{
opt.Add(window[0]);
return 0;
} else
{
/*
* .* ?a* is different
* it is something like
* FIND(a)?a* :.*
*/
RegexIRGenerator g = new();
g.EmitTry(1);
}
return -1;
}
@@ -172,5 +176,78 @@ namespace StgSharp.RegularAnalysis.Text
}
#region expand plus
private static bool TryExpandComplexPlus(
bool is_greedy,
Span<RegexIR> complex,
List<RegexIR> opt
)
{
int count = complex.Length;
List<RegexIR> part_opt = new(count);
RegexInfo info = ScanAndOptimizeIR(complex, part_opt);
opt.AddRange(part_opt);
RegexCountComplexIR c_ir = new(0, -1, is_greedy, part_opt.Count)
{
IsFullyOptimized = true
};
opt.Add(c_ir);
opt.AddRange(part_opt);
return true;
}
/// <summary>
///
/// </summary>
/// <param name="window">
///
/// </param>
/// <param name="opt">
///
/// </param>
/// <returns>
/// Negative if cannot opt, 0 if match sucessfully, positive if meets an IRgexComplex
/// interface, need to call TryExpandComplexPlus
/// </returns>
private static int TryExpandPlus(
Span<RegexIR> window,
List<RegexIR> opt
)
{
/* a+
* convert to
* aa*
*/
RegexIR ir = window[0];
if (ir is not IRegexCount { IsPlus: true } count) {
return -1;
}
bool is_greedy = count.IsGreedy;
string pattern;
switch (ir.Command & RegexIRCommand.Count)
{
case RegexIRCommand.Count_Complex:
RegexCountComplexIR complex = Unsafe.As<RegexCountComplexIR>(ir);
return complex.CommandCount;
case RegexIRCommand.Count_Seq:
RegexCountSeqIR seq = Unsafe.As<RegexCountSeqIR>(ir);
pattern = seq.Sequence;
opt.Add(new RegexMatchSeqIR(pattern));
opt.Add(new RegexCountSeqIR(0, -1, is_greedy, pattern));
return 0;
case RegexIRCommand.Count_Set:
RegexCountSeqIR set = Unsafe.As<RegexCountSeqIR>(ir);
pattern = set.Sequence;
opt.Add(new RegexMatchSeqIR(pattern));
opt.Add(new RegexCountSeqIR(0, -1, is_greedy, pattern));
return 0;
default:
return -1;
}
}
#endregion
}
}