do some source gen work, findseq, matchseq, matchseq framework done, on find set
This commit is contained in:
@@ -38,6 +38,15 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
emitter.Append(string.IsNullOrEmpty(line) ? string.Empty : line);
|
emitter.Append(string.IsNullOrEmpty(line) ? string.Empty : line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SequenceEmitter<string> AppendLine(
|
||||||
|
this SequenceEmitter<string> emitter,
|
||||||
|
string line,
|
||||||
|
bool condition
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return emitter is null ? null! : condition ? emitter.Append(line) : emitter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class SequenceEmitter<T>
|
public sealed class SequenceEmitter<T>
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexFindComplexIR(bool nearest, int count) : RegexIR(RegexIRCommand.Find_Complex), IRegexComplex
|
internal sealed class RegexFindComplexIR(
|
||||||
|
bool nearest,
|
||||||
|
int count
|
||||||
|
) : RegexIR(RegexIRCommand.Find_Complex), IRegexComplex
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsFullyOptimized { get; set; }
|
public bool IsFullyOptimized { get; set; }
|
||||||
@@ -53,7 +56,10 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexFindSeqIR(string pattern, bool isFirst) : RegexIR(RegexIRCommand.Find_Seq), IRegexSequence
|
internal sealed class RegexFindSeqIR(
|
||||||
|
string pattern,
|
||||||
|
bool isFirst
|
||||||
|
) : RegexIR(RegexIRCommand.Find_Seq), IRegexSequence
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsFirst { get; } = isFirst;
|
public bool IsFirst { get; } = isFirst;
|
||||||
@@ -70,7 +76,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches for a certain string pattern or charset from current position.
|
/// Searches for a certain string pattern or charset from current position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class RegexMatchSeqIR(string pattern) : RegexIR(RegexIRCommand.Match_Seq), IRegexSequence
|
internal sealed class RegexMatchSeqIR(
|
||||||
|
string pattern
|
||||||
|
) : RegexIR(RegexIRCommand.Match_Seq), IRegexSequence
|
||||||
{
|
{
|
||||||
|
|
||||||
public string Pattern { get; } = pattern;
|
public string Pattern { get; } = pattern;
|
||||||
@@ -82,7 +90,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexMatchSetIR(string charset) : RegexIR(RegexIRCommand.Match_Set)
|
internal sealed class RegexMatchSetIR(
|
||||||
|
string charset
|
||||||
|
) : RegexIR(RegexIRCommand.Match_Set)
|
||||||
{
|
{
|
||||||
|
|
||||||
public string Charset { get; } = charset;
|
public string Charset { get; } = charset;
|
||||||
@@ -94,7 +104,10 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexConditionIR(int success, int fail) : RegexIR(RegexIRCommand.Condition)
|
internal sealed class RegexConditionIR(
|
||||||
|
int success,
|
||||||
|
int fail
|
||||||
|
) : RegexIR(RegexIRCommand.Condition)
|
||||||
{
|
{
|
||||||
|
|
||||||
public int SuccessCase { get; } = success;
|
public int SuccessCase { get; } = success;
|
||||||
@@ -108,7 +121,10 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexPackIR(bool condition, string name = "") : RegexIR(RegexIRCommand.Pack)
|
internal sealed class RegexPackIR(
|
||||||
|
bool condition,
|
||||||
|
string name = ""
|
||||||
|
) : RegexIR(RegexIRCommand.Pack)
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool Condition { get; } = condition;
|
public bool Condition { get; } = condition;
|
||||||
@@ -122,7 +138,12 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexCountComplexIR(int min, int max, bool isGreedy, int operation) : RegexIR(RegexIRCommand.Count_Complex), IRegexCount, IRegexComplex
|
internal sealed class RegexCountComplexIR(
|
||||||
|
int min,
|
||||||
|
int max,
|
||||||
|
bool isGreedy,
|
||||||
|
int operation
|
||||||
|
) : RegexIR(RegexIRCommand.Count_Complex), IRegexCount, IRegexComplex
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsGreedy { get; } = isGreedy;
|
public bool IsGreedy { get; } = isGreedy;
|
||||||
@@ -146,7 +167,12 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexCountSeqIR(int min, int max, bool isGreedy, string seq) : RegexIR(RegexIRCommand.Count_Seq), IRegexCount, IRegexSequence
|
internal sealed class RegexCountSeqIR(
|
||||||
|
int min,
|
||||||
|
int max,
|
||||||
|
bool isGreedy,
|
||||||
|
string seq
|
||||||
|
) : RegexIR(RegexIRCommand.Count_Seq), IRegexCount, IRegexSequence
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsGreedy { get; } = isGreedy;
|
public bool IsGreedy { get; } = isGreedy;
|
||||||
@@ -168,7 +194,12 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexCountSetIR(int min, int max, bool isGreedy, string set) : RegexIR(RegexIRCommand.Count_Set), IRegexCount
|
internal sealed class RegexCountSetIR(
|
||||||
|
int min,
|
||||||
|
int max,
|
||||||
|
bool isGreedy,
|
||||||
|
string set
|
||||||
|
) : RegexIR(RegexIRCommand.Count_Set), IRegexCount
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsGreedy { get; } = isGreedy;
|
public bool IsGreedy { get; } = isGreedy;
|
||||||
@@ -192,7 +223,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexTryIR(int count) : RegexIR(RegexIRCommand.Try), IRegexComplex
|
internal sealed class RegexTryIR(
|
||||||
|
int count
|
||||||
|
) : RegexIR(RegexIRCommand.Try), IRegexComplex
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsFullyOptimized { get; set; }
|
public bool IsFullyOptimized { get; set; }
|
||||||
@@ -206,7 +239,26 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RegexPopIR(bool isSuccess) : RegexIR(RegexIRCommand.Pop)
|
internal sealed class RegexFindSetIR(
|
||||||
|
string charset,
|
||||||
|
bool isFirst
|
||||||
|
) : RegexIR(RegexIRCommand.Find_Set)
|
||||||
|
{
|
||||||
|
|
||||||
|
public bool IsFirst { get; } = isFirst;
|
||||||
|
|
||||||
|
public string Charset { get; } = charset;
|
||||||
|
|
||||||
|
protected override string AsCommand()
|
||||||
|
{
|
||||||
|
return $"FIND_SET \"{Charset}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class RegexPopIR(
|
||||||
|
bool isSuccess
|
||||||
|
) : RegexIR(RegexIRCommand.Pop)
|
||||||
{
|
{
|
||||||
|
|
||||||
public bool IsSuccess { get; } = isSuccess;
|
public bool IsSuccess { get; } = isSuccess;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using StgSharp.RegularAnalysis.Abstraction;
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -20,6 +21,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
|
|
||||||
private const string current_ptr = "current_ptr";
|
private const string current_ptr = "current_ptr";
|
||||||
|
private const string offset = "offset";
|
||||||
private const string orig_text = "text";
|
private const string orig_text = "text";
|
||||||
|
|
||||||
internal static SequenceEmitter<string> GenerateSource(
|
internal static SequenceEmitter<string> GenerateSource(
|
||||||
@@ -28,7 +30,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
SequenceEmitter<string> se = new();
|
SequenceEmitter<string> se = new();
|
||||||
List<SequenceEmitter<string>> sub_match_list = [];
|
List<SequenceEmitter<string>> sub_match_list = [];
|
||||||
VarNameGenerator ng = new();
|
SourceGenContext ng = new();
|
||||||
|
|
||||||
RegexInfo info = source.Info;
|
RegexInfo info = source.Info;
|
||||||
List<RegexIR> ir = source.IRs;
|
List<RegexIR> ir = source.IRs;
|
||||||
@@ -36,6 +38,28 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
RegexIR[] window = new RegexIR[8];
|
RegexIR[] window = new RegexIR[8];
|
||||||
Span<RegexIR> scan_win = window;
|
Span<RegexIR> scan_win = window;
|
||||||
|
|
||||||
|
SequenceEmitter<string> match_e = new();
|
||||||
|
int index = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
int count = int.Min(8, ir_sp.Length - index);
|
||||||
|
ir_sp.Slice(index, count).CopyTo(scan_win);
|
||||||
|
if (TryGenerateMatchSeq(scan_win, match_e, ng) > 0)
|
||||||
|
{
|
||||||
|
count += 1;
|
||||||
|
} else if (TryGenerateMatchSet(scan_win, match_e, ng) > 0)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
} else if (TryGenerateFindSeq(scan_win, match_e, ng) > 0)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
} else { }
|
||||||
|
index += count;
|
||||||
|
} while (index < ir.Count);
|
||||||
|
|
||||||
|
|
||||||
_ = se.AppendLine($@"public override MatchResult Match({ROS_char} text)")//
|
_ = se.AppendLine($@"public override MatchResult Match({ROS_char} text)")//
|
||||||
.AppendLine(@"{");
|
.AppendLine(@"{");
|
||||||
|
|
||||||
@@ -47,24 +71,13 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
.AppendLine($@"bool region_{i}_success;");
|
.AppendLine($@"bool region_{i}_success;");
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = se.AppendLine($@"int {current_ptr} = 0;");
|
_ = se.AppendLine($@"int {current_ptr} = 0;")
|
||||||
|
.AppendLine($@"int {offset} = 0;")
|
||||||
|
.AppendLine();
|
||||||
|
|
||||||
int index = 0;
|
_ = ng.GenVarDefine(se);
|
||||||
do
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
|
|
||||||
int count = int.Min(8, ir_sp.Length - index);
|
_ = se.Append(match_e);
|
||||||
ir_sp.Slice(index, count).CopyTo(scan_win);
|
|
||||||
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)
|
foreach (SequenceEmitter<string> _sub in sub_match_list)
|
||||||
{
|
{
|
||||||
@@ -77,66 +90,171 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
return se;
|
return se;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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
|
#region sub generator
|
||||||
|
|
||||||
private static int TryGenerateMatchSeq(
|
private static int TryGenerateFindSeq(
|
||||||
Span<RegexIR> scan_win,
|
Span<RegexIR> scan_win,
|
||||||
SequenceEmitter<string> se,
|
SequenceEmitter<string> se,
|
||||||
VarNameGenerator ng
|
SourceGenContext ng
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (scan_win[0] is not RegexMatchSeqIR seq) {
|
if (scan_win[0] is not RegexFindSeqIR seq) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
string pattern = seq.Pattern;
|
string pattern = seq.Pattern;
|
||||||
int length = pattern.Length;
|
int length = pattern.Length;
|
||||||
|
string index_caller = seq.IsFirst ? "IndexOf" : "LastIndexOf";
|
||||||
|
|
||||||
_ = se.AppendLine()
|
_ = se.AppendLine()
|
||||||
.AppendLine($@"if(text[{current_ptr}..({current_ptr} + {length})] == {pattern})")
|
.AppendLine($@"if(({offset} = [{current_ptr}..].{index_caller}({pattern})) != -1)")
|
||||||
.AppendLine($@"{{")
|
.AppendLine($@"{{")
|
||||||
.AppendLine($@"{current_ptr} += {length};")
|
.AppendLine($@"{current_ptr} += {offset} + {length};")
|
||||||
.AppendLine($@"}}");
|
.AppendLine($@"}}");
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int TryGenerateMatchSeq(
|
||||||
|
Span<RegexIR> scan_win,
|
||||||
|
SequenceEmitter<string> se,
|
||||||
|
SourceGenContext ng
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (scan_win[0] is not RegexFindSeqIR seq) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
string pattern = seq.Pattern;
|
||||||
|
int length = pattern.Length;
|
||||||
|
|
||||||
|
_ = se.AppendLine()
|
||||||
|
.AppendLine($@"if([{current_ptr}..({current_ptr} + {length})] == {pattern})")
|
||||||
|
.AppendLine($@"{{")
|
||||||
|
.AppendLine($@"{current_ptr} += {offset} + {length};")
|
||||||
|
.AppendLine($@"}}");
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int TryGenerateFindSet(
|
||||||
|
Span<RegexIR> scan_win,
|
||||||
|
SequenceEmitter<string> se,
|
||||||
|
SourceGenContext ng
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (scan_win[0] is not RegexFindSetIR seq) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
string pattern = seq.Charset;
|
||||||
|
|
||||||
|
RegexCharSet[] set_pattern = FigureCharSetType(pattern, out bool accept);
|
||||||
|
|
||||||
|
bool first = seq.IsFirst;
|
||||||
|
|
||||||
|
string remain = ng.CurRemain;
|
||||||
|
string offset = ng.FindResult;
|
||||||
|
_ = se.AppendLine($@"// match charset of {pattern}")
|
||||||
|
.AppendLine($@"{remain} = {orig_text}[{current_ptr}..];")
|
||||||
|
.AppendLine();
|
||||||
|
|
||||||
|
_ = se.AppendLine("if(");
|
||||||
|
RegexCharSet p = set_pattern[0];
|
||||||
|
string _case = accept ? GenerateAcceptCase(p) : GenerateDenyCase(p);
|
||||||
|
_ = se.AppendLine($@"if ({_case}||");
|
||||||
|
|
||||||
|
for (int i = 1; i < set_pattern.Length - 1; i++)
|
||||||
|
{
|
||||||
|
p = set_pattern[i];
|
||||||
|
_case = accept ? GenerateAcceptCase(p) : GenerateDenyCase(p);
|
||||||
|
if (string.IsNullOrEmpty(_case))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ = se.AppendLine($@" {_case} ||");
|
||||||
|
}
|
||||||
|
|
||||||
|
p = set_pattern[^1];
|
||||||
|
_case = accept ? GenerateAcceptCase(p) : GenerateDenyCase(p);
|
||||||
|
_ = se.AppendLine($@"{_case})")
|
||||||
|
.AppendLine("{")
|
||||||
|
.AppendLine($@"{current_ptr} += 1;")
|
||||||
|
.AppendLine("}");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
||||||
|
string GenerateAcceptCase(
|
||||||
|
RegexCharSet c
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string caller;
|
||||||
|
switch (c.Type)
|
||||||
|
{
|
||||||
|
// Handle single character
|
||||||
|
case CharSetType.Single:
|
||||||
|
caller = first ? "IndexOfAny" : "LastIndexOfAny";
|
||||||
|
return $@"({offset} = {remain}.{caller}(""{c.Value}"") != -1)";
|
||||||
|
|
||||||
|
// Handle range of characters
|
||||||
|
case CharSetType.Range:
|
||||||
|
caller = first ? "IndexOfAnyInRange" : "LastIndexOfAnyInRange";
|
||||||
|
return $@"({offset} = {remain}.{caller}('{c.Value[0]}', '{c.Value[1]}') != -1)";
|
||||||
|
|
||||||
|
// Handle set of characters
|
||||||
|
case CharSetType.Set:
|
||||||
|
switch (c.Value)
|
||||||
|
{
|
||||||
|
case "s":
|
||||||
|
caller = first ? "IndexOfAnyWhiteSpace" : "LastIndexOfAnyWhiteSpace";
|
||||||
|
return $@"({offset} = {remain}.{caller}() != -1)";
|
||||||
|
case "w":
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CharSetType.None:
|
||||||
|
return string.Empty;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string GenerateDenyCase(
|
||||||
|
RegexCharSet c
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (c.Type)
|
||||||
|
{
|
||||||
|
// Handle single character
|
||||||
|
case CharSetType.Single:
|
||||||
|
|
||||||
|
// Handle range of characters
|
||||||
|
case CharSetType.Range:
|
||||||
|
|
||||||
|
// Handle set of characters
|
||||||
|
case CharSetType.Set:
|
||||||
|
case CharSetType.None:
|
||||||
|
return string.Empty;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int TryGenerateMatchSet(
|
private static int TryGenerateMatchSet(
|
||||||
Span<RegexIR> scan_win,
|
Span<RegexIR> scan_win,
|
||||||
SequenceEmitter<string> se,
|
SequenceEmitter<string> se,
|
||||||
VarNameGenerator ng
|
SourceGenContext ng
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (scan_win[0] is not RegexMatchSeqIR seq) {
|
if (scan_win[0] is not RegexMatchSeqIR seq) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
string pattern = seq.Pattern;
|
string pattern = seq.Pattern;
|
||||||
RegexCharSet[] set_pattern = FigureCharSetType(pattern);
|
RegexCharSet[] set_pattern = FigureCharSetType(pattern, out bool accept);
|
||||||
|
|
||||||
string var_char = ng.GenerateCharName();
|
string var_char = ng.CurChar;
|
||||||
_ = se.AppendLine($@"// match charset of {pattern}")
|
_ = se.AppendLine($@"// match charset of {pattern}")
|
||||||
.AppendLine($@"char {var_char} = {orig_text}[{current_ptr}];")
|
.AppendLine($@"char {var_char} = {orig_text}[{current_ptr}];")
|
||||||
.AppendLine();
|
.AppendLine();
|
||||||
|
|
||||||
bool accept = set_pattern[0].Accept;
|
|
||||||
|
|
||||||
_ = se.AppendLine("if(");
|
_ = se.AppendLine("if(");
|
||||||
RegexCharSet p = set_pattern[0];
|
RegexCharSet p = set_pattern[0];
|
||||||
string _case = GenerateCase(p);
|
string _case = GenerateCase(p);
|
||||||
@@ -164,29 +282,63 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
||||||
string GenerateCase(
|
string GenerateCase(
|
||||||
RegexCharSet c
|
RegexCharSet c
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return c.Type switch
|
switch (c.Type)
|
||||||
{
|
{
|
||||||
// Handle single character
|
// Handle single character
|
||||||
CharSetType.Single => $@"{var_char} == '{c.Value}'",
|
case CharSetType.Single:
|
||||||
|
string set = c.Value;
|
||||||
|
if (set.Length == 1)
|
||||||
|
{
|
||||||
|
return $@"{var_char} == '{c.Value}'";
|
||||||
|
} else if (set.Length < 5)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
_ = sb.Append('(');
|
||||||
|
foreach (char item in set) {
|
||||||
|
_ = sb.Append(CultureInfo.InvariantCulture, $@"item == '{item}' or ");
|
||||||
|
}
|
||||||
|
_ = sb.Append(')');
|
||||||
|
return sb.ToString();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return $@"""{c.Value}"".AsSpan().Contains({var_char})";
|
||||||
|
}
|
||||||
|
|
||||||
// Handle range of characters
|
// Handle range of characters
|
||||||
CharSetType.Range => $@"({var_char} >= '{c.Value[0]}' && {var_char} <= '{c.Value[1]}')",
|
case CharSetType.Range:
|
||||||
|
return $@"({var_char} >= '{c.Value[0]}' && {var_char} <= '{c.Value[1]}')";
|
||||||
|
|
||||||
// Handle set of characters
|
// Handle set of characters
|
||||||
CharSetType.Set => c.Value switch
|
case CharSetType.Set:
|
||||||
{
|
if (c.Accept)
|
||||||
"s" => $@"char.IsWhiteSpace({var_char})",
|
{
|
||||||
"w" => $@"char.IsLetterOrDigit({var_char}) || {var_char} == '_'",
|
return c.Value switch
|
||||||
"d" => $@"char.IsDigit({var_char})",
|
{
|
||||||
_ => throw new InvalidCastException(),
|
"s" => $@"char.IsWhiteSpace({var_char})",
|
||||||
},
|
"w" => $@"char.IsLetterOrDigit({var_char}) || {var_char} == '_'",
|
||||||
CharSetType.None => string.Empty,
|
"d" => $@"char.IsDigit({var_char})",
|
||||||
_ => string.Empty,
|
_ => throw new InvalidCastException(),
|
||||||
};
|
};
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return c.Value switch
|
||||||
|
{
|
||||||
|
"s" => $@"!char.IsWhiteSpace({var_char})",
|
||||||
|
"w" => $@"!char.IsLetterOrDigit({var_char}) && {var_char} != '_'",
|
||||||
|
"d" => $@"!char.IsDigit({var_char})",
|
||||||
|
_ => throw new InvalidCastException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case CharSetType.None:
|
||||||
|
return string.Empty;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,12 +370,12 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RegexCharSet[] FigureCharSetType(
|
private static RegexCharSet[] FigureCharSetType(
|
||||||
ReadOnlySpan<char> source
|
ReadOnlySpan<char> source,
|
||||||
|
out bool accept
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// range or single
|
// range or single
|
||||||
bool accept;
|
|
||||||
if (source[0] == '[')
|
if (source[0] == '[')
|
||||||
{
|
{
|
||||||
int idx;
|
int idx;
|
||||||
@@ -232,7 +384,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
ReadOnlySpan<char> seq = source.Slice(idx, source.Length - idx - 1);
|
ReadOnlySpan<char> seq = source.Slice(idx, source.Length - idx - 1);
|
||||||
if (seq.IndexOf('-') == -1)
|
if (seq.IndexOf('-') == -1)
|
||||||
{
|
{
|
||||||
return [new RegexCharSet(seq.ToString(), CharSetType.Single, accept)];
|
return [ new RegexCharSet(seq.ToString(), CharSetType.Single, true) ];
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
List<RegexCharSet> list = [];
|
List<RegexCharSet> list = [];
|
||||||
@@ -241,17 +393,18 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
int pos = seq.IndexOf('-');
|
int pos = seq.IndexOf('-');
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
{
|
{
|
||||||
RegexCharSet single = new(seq.ToString(), CharSetType.Single, accept);
|
// has no range
|
||||||
list.Add(single);
|
SplitSet(seq, list);
|
||||||
break;
|
break;
|
||||||
} else
|
} else if (pos > 1)
|
||||||
if (pos > 1) {
|
{
|
||||||
list.Add(new(seq[0..(pos - 1)].ToString(), CharSetType.Single, accept));
|
// has other sequence before range
|
||||||
|
SplitSet(seq[0..(pos - 1)], list);
|
||||||
}
|
}
|
||||||
list.Add(new($"{seq[pos - 1]}{seq[pos + 1]}", CharSetType.Range, accept));
|
list.Add(new($"{seq[pos - 1]}{seq[pos + 1]}", CharSetType.Range, true));
|
||||||
seq = seq[(pos + 2)..];
|
seq = seq[(pos + 2)..];
|
||||||
}
|
}
|
||||||
return [.. list];
|
return [ .. list ];
|
||||||
}
|
}
|
||||||
} else if (source[0] == '\\')
|
} else if (source[0] == '\\')
|
||||||
{
|
{
|
||||||
@@ -260,9 +413,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
return char.ToLower(source[0], CultureInfo.InvariantCulture)
|
return char.ToLower(source[0], CultureInfo.InvariantCulture)
|
||||||
switch
|
switch
|
||||||
{
|
{
|
||||||
's' => [new("s", CharSetType.Set, accept)],
|
's' => [ new("s", CharSetType.Set, accept) ],
|
||||||
'w' => [new("w", CharSetType.Set, accept)],
|
'w' => [ new("w", CharSetType.Set, accept) ],
|
||||||
'd' => [new("d", CharSetType.Set, accept)],
|
'd' => [ new("d", CharSetType.Set, accept) ],
|
||||||
_ => throw new NotSupportedException()
|
_ => throw new NotSupportedException()
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
@@ -270,6 +423,40 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
// unknown, throw
|
// unknown, throw
|
||||||
throw new InvalidCastException($"Unknown char set: {source}");
|
throw new InvalidCastException($"Unknown char set: {source}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SplitSet(
|
||||||
|
ReadOnlySpan<char> seq,
|
||||||
|
List<RegexCharSet> sets
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (seq.Length == 0 || sets == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int pos;
|
||||||
|
ReadOnlySpan<char> remain = seq;
|
||||||
|
while ((pos = remain.IndexOf('\\')) != -1 || remain.Length > 0)
|
||||||
|
{
|
||||||
|
if (pos != 0)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<char> singles = remain[0..pos];
|
||||||
|
sets.Add(new RegexCharSet(singles.ToString(), CharSetType.Single, true));
|
||||||
|
}
|
||||||
|
bool accept = char.IsAsciiLetterUpper(remain[pos + 1]);
|
||||||
|
RegexCharSet set_set = char.ToLower(remain[pos + 1], CultureInfo.InvariantCulture)
|
||||||
|
switch
|
||||||
|
{
|
||||||
|
's' => new("s", CharSetType.Set, accept),
|
||||||
|
'w' => new("w", CharSetType.Set, accept),
|
||||||
|
'd' => new("d", CharSetType.Set, accept),
|
||||||
|
_ => throw new NotSupportedException()
|
||||||
|
};
|
||||||
|
sets.Add(set_set);
|
||||||
|
remain = remain[(pos + 2)..];
|
||||||
|
}
|
||||||
|
if (remain.Length > 0) {
|
||||||
|
sets.Add(new RegexCharSet(remain.ToString(), CharSetType.Single, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// file="TextRegexSourceGen.GenContext"
|
||||||
|
// Project: StgSharp
|
||||||
|
// Copyright (c) Nitload.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace StgSharp.RegularAnalysis.Text
|
||||||
|
{
|
||||||
|
internal partial class TextRegexSourceGen
|
||||||
|
{
|
||||||
|
|
||||||
|
#region var naming
|
||||||
|
|
||||||
|
private class SourceGenContext
|
||||||
|
{
|
||||||
|
|
||||||
|
public bool Need_char_cur { get; set; }
|
||||||
|
|
||||||
|
public bool Need_remain_cur { get; set; }
|
||||||
|
|
||||||
|
public bool Need_find_result { get; set; }
|
||||||
|
|
||||||
|
private const string __char_cur = "__char_cur";
|
||||||
|
private const string __remain_cur = "__remain_cur";
|
||||||
|
private const string __find_result = "__find_result";
|
||||||
|
|
||||||
|
private readonly Dictionary<string, int> _search_value_name = [];
|
||||||
|
private int _min_search_value_index;
|
||||||
|
|
||||||
|
public string CurChar
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Need_char_cur = true;
|
||||||
|
return __char_cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MakeNewSearchValue(
|
||||||
|
string seq
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_search_value_name.TryGetValue(seq, out int idx)) {
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
idx = _min_search_value_index;
|
||||||
|
_min_search_value_index++;
|
||||||
|
_search_value_name.Add(seq, idx);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FindResult
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Need_find_result = true;
|
||||||
|
return __find_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CurRemain
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Need_remain_cur = true;
|
||||||
|
return __remain_cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SequenceEmitter<string> GenVarDefine(
|
||||||
|
SequenceEmitter<string> se
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (se is null) {
|
||||||
|
return null!;
|
||||||
|
}
|
||||||
|
_ = se.AppendLine($@"int {__char_cur}", Need_char_cur)
|
||||||
|
.AppendLine($@"int {__remain_cur}", Need_remain_cur);
|
||||||
|
|
||||||
|
return se;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[GeneratedRegex(@".*a*b", RegexOptions.ExplicitCapture)]
|
[GeneratedRegex(@"", RegexOptions.ExplicitCapture)]
|
||||||
internal static partial Regex GetRegex();
|
internal static partial Regex GetRegex();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user