regex ir opt code on rewrite
This commit is contained in:
@@ -25,9 +25,12 @@
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
using StgSharp.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -37,154 +40,164 @@ namespace StgSharp.RegularAnalysis.Text
|
||||
public partial class RegexAnalyzer
|
||||
{
|
||||
|
||||
// this array is not thread safe so opt process must be done in single thread.
|
||||
private RegexIR[] _opt_buffer = new RegexIR[16];
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
internal RegexInfo ScanAndOptimizeIR(
|
||||
List<RegexIR> baseIR,
|
||||
out List<RegexIR> optIR
|
||||
internal static RegexInfo ScanAndOptimizeIR(
|
||||
List<RegexIR> baseIR,
|
||||
out List<RegexIR> optIR
|
||||
)
|
||||
{
|
||||
int index = 0;
|
||||
Span<RegexIR> origin_span = CollectionsMarshal.AsSpan(baseIR);
|
||||
List<RegexIR> optimizedIR = new(baseIR.Count);
|
||||
List<RegexIR> segment = [];
|
||||
List<RegexIR> opt_ir = new(baseIR.Count);
|
||||
CapacityFixedList<RegexIR> opt_window = new(8);
|
||||
Span<RegexIR> opt_win_sp = [.. opt_window];
|
||||
RegexInfo info = new();
|
||||
do
|
||||
{
|
||||
OptimizeIR(origin_span, ref index, segment, info);
|
||||
optimizedIR.AddRange(segment);
|
||||
index++;
|
||||
int process_count = 0;
|
||||
int last = int.Min(origin_span.Length, index + 8);
|
||||
origin_span[index..last].CopyTo(opt_win_sp);
|
||||
|
||||
do
|
||||
{
|
||||
// plus_expand:
|
||||
int expand_result = TryExpandPlus(opt_win_sp, opt_ir);
|
||||
if (expand_result == 0)
|
||||
{
|
||||
opt_win_sp[0] = opt_ir[^1];
|
||||
process_count++;
|
||||
|
||||
// goto dot_count_count:
|
||||
} else if (expand_result > 0)
|
||||
{
|
||||
// meets a count-complex
|
||||
RegexCountComplexIR cnt_cmp_ir = Unsafe.As<RegexCountComplexIR>(opt_win_sp[0]);
|
||||
if (TryExpandComplexPlus(cnt_cmp_ir.IsGreedy, origin_span[(index + 1)..(index + expand_result)], opt_ir))
|
||||
{
|
||||
process_count += expand_result + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// dot_count_count:
|
||||
if (TryOptDotCountCount(opt_win_sp, opt_ir))
|
||||
{
|
||||
process_count += expand_result == 0 ? 1 : 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (TryFlatternCount(opt_win_sp, opt_ir)) { }
|
||||
} while (false);
|
||||
|
||||
index += process_count;
|
||||
} while (index < baseIR.Count);
|
||||
optIR = optimizedIR;
|
||||
optIR = opt_ir;
|
||||
return info;
|
||||
}
|
||||
|
||||
private int ExpandCount(
|
||||
string pattern,
|
||||
int min,
|
||||
int max,
|
||||
bool is_greedy,
|
||||
in Span<RegexIR> span
|
||||
private static bool TryExpandComplexPlus(
|
||||
bool is_greedy,
|
||||
Span<RegexIR> complex,
|
||||
List<RegexIR> opt
|
||||
)
|
||||
{
|
||||
// expand count operation to less cycles.
|
||||
int count = complex.Length;
|
||||
opt.AddRange(complex);
|
||||
opt.Add(new RegexCountComplexIR(0, -1, is_greedy, count));
|
||||
return true;
|
||||
}
|
||||
|
||||
const int fast_seq_length = 32;
|
||||
#region expand plus
|
||||
|
||||
int fast_count = fast_seq_length / pattern.Length;
|
||||
fast_count = int.Min(fast_count, min);
|
||||
if (fast_count > 0)
|
||||
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)
|
||||
{
|
||||
string fast_pattern = string.Concat(Enumerable.Repeat(pattern, fast_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;
|
||||
}
|
||||
}
|
||||
|
||||
int fast_min = min / fast_count;
|
||||
int consumed_count = fast_min * fast_count;
|
||||
int remain_min = min - consumed_count;
|
||||
int remain_max = max - consumed_count;
|
||||
remain_max = int.Max(-1, remain_max); //for negative stands for infinitive
|
||||
if (remain_max == 0)
|
||||
{
|
||||
// a{2}
|
||||
RegexCountSeqIR fast = new(fast_min, fast_min, is_greedy, fast_pattern);
|
||||
span[0] = fast;
|
||||
return 1;
|
||||
} else
|
||||
{
|
||||
// a{2,5}
|
||||
RegexCountSeqIR fast = new(fast_min, fast_min, is_greedy, fast_pattern);
|
||||
RegexCountSeqIR tail = new(remain_min, remain_max, is_greedy, pattern);
|
||||
span[0] = fast;
|
||||
span[1] = tail;
|
||||
return 2;
|
||||
}
|
||||
} else if (fast_count == 0)
|
||||
#endregion
|
||||
|
||||
private static bool TryFlatternCount(
|
||||
Span<RegexIR> window,
|
||||
List<RegexIR> opt
|
||||
)
|
||||
{
|
||||
RegexIR ir = window[0];
|
||||
if (ir is not RegexCountSeqIR seq) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#region dot count count
|
||||
|
||||
private static int TryOptDotCountCount(
|
||||
Span<RegexIR> window,
|
||||
List<RegexIR> opt
|
||||
)
|
||||
{
|
||||
/* .* <any>*
|
||||
* convert to
|
||||
* .*
|
||||
*/
|
||||
RegexIR cur_ir = window[0];
|
||||
if (cur_ir is not RegexCountSetIR { IsDotStar: true } dot) {
|
||||
return -1;
|
||||
}
|
||||
if (window[1] is not IRegexCount { IsStar: true } after) {
|
||||
return -1;
|
||||
}
|
||||
if (dot.IsGreedy)
|
||||
{
|
||||
// least count is 0
|
||||
span[0] = new RegexCountSeqIR(0, max, is_greedy, pattern);
|
||||
return 1;
|
||||
opt.Add(window[0]);
|
||||
return 0;
|
||||
} else
|
||||
{
|
||||
// least count is infinitive
|
||||
span[0] = new RegexCountSeqIR(-1, -1, is_greedy, pattern);
|
||||
return 1;
|
||||
/*
|
||||
* .* ?a* is different
|
||||
* it is something like
|
||||
* FIND(a)?a* :.*
|
||||
*/
|
||||
RegexIRGenerator g = new();
|
||||
g.EmitTry(1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void OptimizeDot(
|
||||
RegexCountSetIR root,
|
||||
Span<RegexIR> ir,
|
||||
ref int index,
|
||||
List<RegexIR> optimizedIR,
|
||||
RegexInfo info
|
||||
)
|
||||
{
|
||||
int next = index + 1;
|
||||
optimizedIR.Clear();
|
||||
if (next >= ir.Length)
|
||||
{
|
||||
optimizedIR.Add(root);
|
||||
return;
|
||||
}
|
||||
RegexIR nextIR = ir[next];
|
||||
if (nextIR is RegexMatchSeqIR match_seq)
|
||||
{
|
||||
// opt .*abc style
|
||||
RegexFindSeqIR find = new(match_seq.Pattern);
|
||||
optimizedIR.Add(find);
|
||||
info.MinLength += find.Pattern.Length;
|
||||
return;
|
||||
} else if (nextIR is RegexCountSeqIR count_seq)
|
||||
{
|
||||
// opt .* (abc)* or .*(abc){2,5} style
|
||||
string pattern = count_seq.Sequence;
|
||||
int min = count_seq.Min;
|
||||
int max = count_seq.Max;
|
||||
switch ((min, max))
|
||||
{
|
||||
case (0, _):
|
||||
|
||||
break;
|
||||
default:
|
||||
RegexFindSeqIR find = new(pattern);
|
||||
|
||||
// RegexCountSeqIR
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OptimizeIR(
|
||||
Span<RegexIR> ir,
|
||||
ref int index,
|
||||
List<RegexIR> optimizedIR,
|
||||
RegexInfo info
|
||||
)
|
||||
{
|
||||
optimizedIR.Clear();
|
||||
|
||||
// TODO optimize here
|
||||
RegexIR root = ir[index];
|
||||
switch (root.Command)
|
||||
{
|
||||
case RegexIRCommand.Count_Set:
|
||||
RegexCountSetIR count = (root as RegexCountSetIR)!;
|
||||
if (count.CharSet == ".")
|
||||
{
|
||||
// opt for .+ / .* / .{} pattern
|
||||
OptimizeDot(count, ir, ref index, optimizedIR, info);
|
||||
}
|
||||
break;
|
||||
case RegexIRCommand.Match_Seq:
|
||||
info.MinLength += (root as RegexMatchSeqIR)!.Pattern.Length;
|
||||
optimizedIR.Add(root);
|
||||
break;
|
||||
default:
|
||||
optimizedIR.Add(root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal class RegexInfo
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user