do some source gen work

This commit is contained in:
Youmu Liu
2026-07-15 18:05:38 +08:00
parent 4d2a4eeb18
commit ee7cbd869c
10 changed files with 276 additions and 106 deletions
@@ -8,6 +8,7 @@
using StgSharp.RegularAnalysis.Abstraction;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -18,12 +19,16 @@ namespace StgSharp.RegularAnalysis.Text
internal partial class TextRegexSourceGen
{
private const string current_ptr = "current_ptr";
private const string orig_text = "text";
internal static SequenceEmitter<string> GenerateSource(
TextRegexSource source
)
{
SequenceEmitter<string> se = new();
List<SequenceEmitter<string>> sub_match_list = [];
VarNameGenerator ng = new();
RegexInfo info = source.Info;
List<RegexIR> ir = source.IRs;
@@ -31,8 +36,6 @@ namespace StgSharp.RegularAnalysis.Text
RegexIR[] window = new RegexIR[8];
Span<RegexIR> scan_win = window;
const string current_ptr = "current_ptr";
_ = se.AppendLine($@"public override MatchResult Match({ROS_char} text)")//
.AppendLine(@"{");
@@ -49,9 +52,18 @@ namespace StgSharp.RegularAnalysis.Text
int index = 0;
do
{
int result;
int count = int.Min(8, ir_sp.Length - index);
ir_sp.Slice(index, count).CopyTo(scan_win);
if (TryGenerateMatch(scan_win, se) > 0) { }
if (TryGenerateMatchSeq(scan_win, se, ng) > 0)
{
count++;
} else if (TryGenerateMatchSet(scan_win, se, ng) > 0)
{
count++;
} else { }
index += count;
} while (index < ir.Count);
foreach (SequenceEmitter<string> _sub in sub_match_list)
@@ -63,27 +75,203 @@ namespace StgSharp.RegularAnalysis.Text
_ = se.Append(@"}");
return se;
/**/
int TryGenerateMatch(
Span<RegexIR> scan_win,
SequenceEmitter<string> sb
)
{
if (scan_win[0] is not RegexMatchSeqIR seq) {
return -1;
}
string pattern = seq.Pattern;
int length = pattern.Length;
_ = sb.AppendLine()
.AppendLine($@"if(text[{current_ptr}..({current_ptr} + {length})] == {pattern})")
.AppendLine($@"{{")
.AppendLine($@"{current_ptr} += {length};")
.AppendLine($@"}}");
return 1;
}
/**/
}
#region var naming
private class VarNameGenerator
{
private int char_counter;
public string GenerateCharName()
{
string name = $"__char_{char_counter}";
char_counter++;
return name;
}
}
#endregion
#region sub generator
private static int TryGenerateMatchSeq(
Span<RegexIR> scan_win,
SequenceEmitter<string> se,
VarNameGenerator ng
)
{
if (scan_win[0] is not RegexMatchSeqIR seq) {
return -1;
}
string pattern = seq.Pattern;
int length = pattern.Length;
_ = se.AppendLine()
.AppendLine($@"if(text[{current_ptr}..({current_ptr} + {length})] == {pattern})")
.AppendLine($@"{{")
.AppendLine($@"{current_ptr} += {length};")
.AppendLine($@"}}");
return 1;
}
private static int TryGenerateMatchSet(
Span<RegexIR> scan_win,
SequenceEmitter<string> se,
VarNameGenerator ng
)
{
if (scan_win[0] is not RegexMatchSeqIR seq) {
return -1;
}
string pattern = seq.Pattern;
RegexCharSet[] set_pattern = FigureCharSetType(pattern);
string var_char = ng.GenerateCharName();
_ = se.AppendLine($@"// match charset of {pattern}")
.AppendLine($@"char {var_char} = {orig_text}[{current_ptr}];")
.AppendLine();
bool accept = set_pattern[0].Accept;
_ = se.AppendLine("if(");
RegexCharSet p = set_pattern[0];
string _case = GenerateCase(p);
string _case_begin = accept ? string.Empty : "!(";
_ = se.AppendLine($@"if ({_case_begin}{_case}||");
for (int i = 1; i < set_pattern.Length - 1; i++)
{
p = set_pattern[i];
_case = GenerateCase(p);
if (string.IsNullOrEmpty(_case))
{
continue;
}
_ = se.AppendLine($@" {_case} ||");
}
p = set_pattern[^1];
_case = GenerateCase(p);
string _case_end = accept ? string.Empty : ")";
_ = se.AppendLine($@"{_case}{_case_end})")
.AppendLine("{")
.AppendLine($@"{current_ptr} += 1;")
.AppendLine("}");
return 1;
string GenerateCase(
RegexCharSet c
)
{
return c.Type switch
{
// Handle single character
CharSetType.Single => $@"{var_char} == '{c.Value}'",
// Handle range of characters
CharSetType.Range => $@"({var_char} >= '{c.Value[0]}' && {var_char} <= '{c.Value[1]}')",
// Handle set of characters
CharSetType.Set => c.Value switch
{
"s" => $@"char.IsWhiteSpace({var_char})",
"w" => $@"char.IsLetterOrDigit({var_char}) || {var_char} == '_'",
"d" => $@"char.IsDigit({var_char})",
_ => throw new InvalidCastException(),
},
CharSetType.None => string.Empty,
_ => string.Empty,
};
}
}
#endregion
#region charset config
internal enum CharSetType
{
None,
Single,
Range,
Set
}
internal record struct RegexCharSet(
string Value,
CharSetType Type,
bool Accept
)
{
public override readonly string ToString()
{
return $"{{value:{Value}, type:{Type}, accept:{Accept}}}";
}
}
public static RegexCharSet[] FigureCharSetType(
ReadOnlySpan<char> source
)
{
// range or single
bool accept;
if (source[0] == '[')
{
int idx;
accept = source[1] != '^';
idx = accept ? 1 : 2;
ReadOnlySpan<char> seq = source.Slice(idx, source.Length - idx - 1);
if (seq.IndexOf('-') == -1)
{
return [new RegexCharSet(seq.ToString(), CharSetType.Single, accept)];
} else
{
List<RegexCharSet> list = [];
for (; seq.Length > 0; )
{
int pos = seq.IndexOf('-');
if (pos == -1)
{
RegexCharSet single = new(seq.ToString(), CharSetType.Single, accept);
list.Add(single);
break;
} else
if (pos > 1) {
list.Add(new(seq[0..(pos - 1)].ToString(), CharSetType.Single, accept));
}
list.Add(new($"{seq[pos - 1]}{seq[pos + 1]}", CharSetType.Range, accept));
seq = seq[(pos + 2)..];
}
return [.. list];
}
} else if (source[0] == '\\')
{
// set
accept = char.IsAsciiLetterUpper(source[0]);
return char.ToLower(source[0], CultureInfo.InvariantCulture)
switch
{
's' => [new("s", CharSetType.Set, accept)],
'w' => [new("w", CharSetType.Set, accept)],
'd' => [new("d", CharSetType.Set, accept)],
_ => throw new NotSupportedException()
};
} else
{
// unknown, throw
throw new InvalidCastException($"Unknown char set: {source}");
}
}
#endregion
}
}