text regex source gen framework is briefly done. Going on ir2code
This commit is contained in:
+2
-2
@@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.14.36705.20
|
VisualStudioVersion = 17.14.36705.20
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateGL", "StgSharp.GenerateGL\GenerateGL.csproj", "{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateGL", "test\StgSharp.GenerateGL\GenerateGL.csproj", "{9FD27EE6-E94D-4E64-A13B-5A349DEA215A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StgSharp.Common", "StgSharp.Common\StgSharp.Common.csproj", "{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StgSharp.Common", "src\StgSharp.Common\StgSharp.Common.csproj", "{5D646C0E-CCF8-B5B4-7DE8-1C37542E7B4D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
private readonly Node _head;
|
private readonly Node _head;
|
||||||
private Node _tail;
|
private Node _tail;
|
||||||
|
|
||||||
|
private SequenceEmitter(
|
||||||
|
Node node
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_isRoot = true;
|
||||||
|
_head = node;
|
||||||
|
_tail = _head;
|
||||||
|
}
|
||||||
|
|
||||||
public SequenceEmitter()
|
public SequenceEmitter()
|
||||||
{
|
{
|
||||||
_isRoot = true;
|
_isRoot = true;
|
||||||
@@ -57,8 +66,6 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
|
|
||||||
public int Count { get; private set; }
|
public int Count { get; private set; }
|
||||||
|
|
||||||
private static bool IsString { get; } = typeof(T) == typeof(string);
|
|
||||||
|
|
||||||
public SequenceEmitter<T> Append(
|
public SequenceEmitter<T> Append(
|
||||||
T unit
|
T unit
|
||||||
)
|
)
|
||||||
@@ -92,6 +99,16 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SequenceEmitter<T> Clone()
|
||||||
|
{
|
||||||
|
if (!_isRoot) {
|
||||||
|
throw new InvalidOperationException("This emitter has been appended to another emitter. It is now readonly.");
|
||||||
|
}
|
||||||
|
SequenceEmitter<T> clone = new();
|
||||||
|
Node current = _head;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
public Enumerator GetEnumerator()
|
public Enumerator GetEnumerator()
|
||||||
{
|
{
|
||||||
return _isRoot ?
|
return _isRoot ?
|
||||||
@@ -173,6 +190,18 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
private sealed class Node
|
private sealed class Node
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public Node()
|
||||||
|
{
|
||||||
|
IRList = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node(
|
||||||
|
int capacity
|
||||||
|
)
|
||||||
|
{
|
||||||
|
IRList = new(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
public T this[
|
public T this[
|
||||||
int index
|
int index
|
||||||
]
|
]
|
||||||
@@ -185,7 +214,7 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
|
|
||||||
public int Count => IRList.Count;
|
public int Count => IRList.Count;
|
||||||
|
|
||||||
public List<T> IRList { get; } = [];
|
public List<T> IRList { get; }
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Add(
|
public void Add(
|
||||||
@@ -195,6 +224,13 @@ namespace StgSharp.RegularAnalysis.Abstraction
|
|||||||
IRList.Add(ir);
|
IRList.Add(ir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddRange(
|
||||||
|
IEnumerable<T> irs
|
||||||
|
)
|
||||||
|
{
|
||||||
|
IRList.AddRange(irs);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
// -----------------------------------------------------------------------------
|
|
||||||
// file="RegexAnalyzer.SourceGen.CharSetEmitter"
|
|
||||||
// Project: StgSharp
|
|
||||||
// Copyright (c) Nitload.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace StgSharp.RegularAnalysis.Text
|
|
||||||
{
|
|
||||||
internal enum CharSetType
|
|
||||||
{
|
|
||||||
|
|
||||||
None,
|
|
||||||
Single,
|
|
||||||
Range,
|
|
||||||
Set
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal record struct RegexCharSet(
|
|
||||||
string value,
|
|
||||||
CharSetType t,
|
|
||||||
bool accept
|
|
||||||
)
|
|
||||||
{
|
|
||||||
|
|
||||||
public bool IsAcceptable { get; set; } = accept;
|
|
||||||
|
|
||||||
public CharSetType Type { get; set; } = t;
|
|
||||||
|
|
||||||
public string Value { get; set; } = value;
|
|
||||||
|
|
||||||
public override readonly string ToString()
|
|
||||||
{
|
|
||||||
return $"{{value:{value}, type:{Type}, accept:{IsAcceptable}}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static partial class RegexAnalyzerBackEnd
|
|
||||||
{
|
|
||||||
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,13 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
category:"NGRA.TextRegex",
|
category:"NGRA.TextRegex",
|
||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
|
|
||||||
|
private static readonly DiagnosticDescriptor CompileFailDiag = new(id:"NGRA005",
|
||||||
|
title:"Unexpected exception when compiling",
|
||||||
|
messageFormat:"Unexpected exception handles when compiling expression \"{0}\":{1}",
|
||||||
|
defaultSeverity:DiagnosticSeverity.Error,
|
||||||
|
category:"NGRA.TextRegex",
|
||||||
|
isEnabledByDefault:true);
|
||||||
|
|
||||||
private static readonly DiagnosticDescriptor IncorrectContainingTypeDiag = new(id:"NGRA005",
|
private static readonly DiagnosticDescriptor IncorrectContainingTypeDiag = new(id:"NGRA005",
|
||||||
title:$"Containing type can only be struct or class",
|
title:$"Containing type can only be struct or class",
|
||||||
messageFormat:$"Containing type: {{0}} is a {{1}}, but not class or struct",
|
messageFormat:$"Containing type: {{0}} is a {{1}}, but not class or struct",
|
||||||
|
|||||||
+12
-9
@@ -1,26 +1,26 @@
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// file="RegexAnalyzer.SourceGen"
|
// file="TextRegexSourceGen.GenClass"
|
||||||
// Project: StgSharp
|
// Project: StgSharp
|
||||||
// Copyright (c) Nitload.
|
// Copyright (c) Nitload.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using StgSharp.RegularAnalysis.Abstraction;
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace StgSharp.RegularAnalysis.Text
|
namespace StgSharp.RegularAnalysis.Text
|
||||||
{
|
{
|
||||||
public partial class RegexAnalyzer
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
internal static SequenceEmitter<string> GenerateSource(TextRegexSource source)
|
internal static SequenceEmitter<string> GenerateSource(
|
||||||
|
TextRegexSource source
|
||||||
|
)
|
||||||
{
|
{
|
||||||
SequenceEmitter<string> se = new();
|
SequenceEmitter<string> se = new();
|
||||||
List<SequenceEmitter<string>> sub_match_list = [];
|
List<SequenceEmitter<string>> sub_match_list = [];
|
||||||
@@ -33,7 +33,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
const string current_ptr = "current_ptr";
|
const string current_ptr = "current_ptr";
|
||||||
|
|
||||||
_ = se.AppendLine(@"public override MatchResult Match(ReadOnlySpan<char> text)")//
|
_ = se.AppendLine($@"public override MatchResult Match({ROS_char} text)")//
|
||||||
.AppendLine(@"{");
|
.AppendLine(@"{");
|
||||||
|
|
||||||
int region_count = info.RegionCount;
|
int region_count = info.RegionCount;
|
||||||
@@ -51,6 +51,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
int count = int.Min(8, ir_sp.Length - index);
|
int count = int.Min(8, ir_sp.Length - index);
|
||||||
ir_sp.Slice(index, count).CopyTo(scan_win);
|
ir_sp.Slice(index, count).CopyTo(scan_win);
|
||||||
|
if (TryGenerateMatch(scan_win, se) > 0) { }
|
||||||
} while (index < ir.Count);
|
} while (index < ir.Count);
|
||||||
|
|
||||||
foreach (SequenceEmitter<string> _sub in sub_match_list)
|
foreach (SequenceEmitter<string> _sub in sub_match_list)
|
||||||
@@ -63,14 +64,17 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
return se;
|
return se;
|
||||||
/**/
|
/**/
|
||||||
int TryGenerateMatch(Span<RegexIR> scan_win, SequenceEmitter<string> sb)
|
int TryGenerateMatch(
|
||||||
|
Span<RegexIR> scan_win,
|
||||||
|
SequenceEmitter<string> sb
|
||||||
|
)
|
||||||
{
|
{
|
||||||
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;
|
||||||
int length = pattern.Length;
|
int length = pattern.Length;
|
||||||
_ = sb.Append(string.Empty)
|
_ = sb.AppendLine()
|
||||||
.AppendLine($@"if(text[{current_ptr}..({current_ptr} + {length})] == {pattern})")
|
.AppendLine($@"if(text[{current_ptr}..({current_ptr} + {length})] == {pattern})")
|
||||||
.AppendLine($@"{{")
|
.AppendLine($@"{{")
|
||||||
.AppendLine($@"{current_ptr} += {length};")
|
.AppendLine($@"{current_ptr} += {length};")
|
||||||
@@ -83,4 +87,3 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// file="TextRegexSourceGen.GenFile"
|
||||||
|
// Project: StgSharp
|
||||||
|
// Copyright (c) Nitload.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace StgSharp.RegularAnalysis.Text
|
||||||
|
{
|
||||||
|
internal partial class TextRegexSourceGen
|
||||||
|
{
|
||||||
|
|
||||||
|
public const string ROS_char = "global::System.ReadOnlySpan<char>";
|
||||||
|
public const string Text_Regex = "global::StgSharp.RegularAnalysis.Text.TextRegex";
|
||||||
|
|
||||||
|
private static readonly SymbolDisplayFormat TypeSourceFormat =
|
||||||
|
SymbolDisplayFormat.FullyQualifiedFormat
|
||||||
|
.AddMiscellaneousOptions(
|
||||||
|
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
|
||||||
|
|
||||||
|
private static string AsSource<T>()
|
||||||
|
{
|
||||||
|
Type t = typeof(T);
|
||||||
|
return t.IsGenericType ? throw new InvalidOperationException() : $"global::{t.FullName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateRegexSourceFile(
|
||||||
|
SourceProductionContext context,
|
||||||
|
TextRegexBinding binding
|
||||||
|
)
|
||||||
|
{
|
||||||
|
StringBuilder file_name_sb = new();
|
||||||
|
SequenceEmitter<string> ce = new();
|
||||||
|
SourceRecord gen_source = binding.Source;
|
||||||
|
TypeSource type = binding.Source.ContainingType;
|
||||||
|
|
||||||
|
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $"{gen_source.NamespaceName}.");
|
||||||
|
_ = ce.AppendLine($@"// <auto-generated/>")
|
||||||
|
.AppendLine($@"#nullable enable")
|
||||||
|
.AppendLine($@"using StgSharp.RegularAnalysis.Text;")
|
||||||
|
.AppendLine($@"using StgSharp.RegularAnalysis.Abstraction;")
|
||||||
|
.AppendLine($@"using System;")
|
||||||
|
.AppendLine($@"using System.Text;")
|
||||||
|
.AppendLine($@"using System.Threading.Tasks;")
|
||||||
|
.AppendLine()
|
||||||
|
.AppendLine($@"namespace {gen_source.NamespaceName};");
|
||||||
|
|
||||||
|
string static_modifier;
|
||||||
|
|
||||||
|
int brace_count = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $@"{type.TypeName}.");
|
||||||
|
string declare_type = type.DeclType switch
|
||||||
|
{
|
||||||
|
TypeKind.Class => "class",
|
||||||
|
TypeKind.Struct => "struct",
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type kind: {type.DeclType}"),
|
||||||
|
};
|
||||||
|
static_modifier = type.IsStatic ? "static" : string.Empty;
|
||||||
|
_ = ce.AppendLine($@"{ToKeyword(type.Accessibility)} {static_modifier} partial {declare_type} {type.TypeName}")
|
||||||
|
.AppendLine("{");
|
||||||
|
brace_count++;
|
||||||
|
type = type.InnerType!;
|
||||||
|
} while (type is not null);
|
||||||
|
|
||||||
|
static_modifier = gen_source.IsMethodStatic ? "static" : string.Empty;
|
||||||
|
_ = ce.AppendLine(@$"{ToKeyword(gen_source.MethodAccessibility)} {static_modifier} {Text_Regex} {gen_source.MethodName}()")
|
||||||
|
.AppendLine("{")
|
||||||
|
/**/ //TODO return single instance of the TextRegex object
|
||||||
|
.AppendLine("}");
|
||||||
|
|
||||||
|
// TODO inject method source code here
|
||||||
|
|
||||||
|
for (int i = 0; i < brace_count; i++)
|
||||||
|
{
|
||||||
|
_ = ce.AppendLine("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder code_source = new();
|
||||||
|
brace_count = 0;
|
||||||
|
foreach (string line in ce)
|
||||||
|
{
|
||||||
|
bool is_right_brace = line == "}";
|
||||||
|
brace_count -= is_right_brace ? 1 : 0;
|
||||||
|
for (int i = 0; i < brace_count; i++) {
|
||||||
|
_ = code_source.Append(" ");
|
||||||
|
}
|
||||||
|
_ = code_source.AppendLine(line);
|
||||||
|
brace_count += is_right_brace ? 0 : (line == "{" ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $@"{binding.Source.MethodName}.g.cs");
|
||||||
|
string file_name = file_name_sb.ToString();
|
||||||
|
context.AddSource(file_name, code_source.ToString());
|
||||||
|
|
||||||
|
foreach (Diagnostic diag in binding.RegexDiagnostic) {
|
||||||
|
context.ReportDiagnostic(diag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToKeyword(
|
||||||
|
Accessibility accessibility
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return accessibility switch
|
||||||
|
{
|
||||||
|
Accessibility.Public => "public",
|
||||||
|
Accessibility.Private => "private",
|
||||||
|
Accessibility.Internal => "internal",
|
||||||
|
Accessibility.Protected => "protected",
|
||||||
|
|
||||||
|
Accessibility.ProtectedAndInternal => "private protected",
|
||||||
|
|
||||||
|
Accessibility.ProtectedOrInternal => "protected internal",
|
||||||
|
|
||||||
|
_ => throw new InvalidOperationException(
|
||||||
|
$"Unexpected accessibility: {accessibility}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -18,19 +19,21 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
internal partial class TextRegexSourceGen
|
internal partial class TextRegexSourceGen
|
||||||
{
|
{
|
||||||
|
|
||||||
private record struct SourceRecord(
|
private readonly record struct SourceRecord(
|
||||||
|
|
||||||
bool IsClassStatic,
|
bool IsClassStatic,
|
||||||
bool IsMethodStatic,
|
bool IsMethodStatic,
|
||||||
string Pattern,
|
string Pattern,
|
||||||
string MethodName,
|
string MethodName,
|
||||||
string NamespaceName,
|
string NamespaceName,
|
||||||
Accessibility MethodAccessibility,
|
Accessibility MethodAccessibility,
|
||||||
TypeSource ContainingType
|
TypeSource ContainingType,
|
||||||
|
Location PatternDefineLocation
|
||||||
);
|
);
|
||||||
|
|
||||||
private sealed record TypeSource(
|
private sealed record TypeSource(
|
||||||
bool IsStatic,
|
bool IsStatic,
|
||||||
|
string TypeDisplayName,
|
||||||
string TypeName,
|
string TypeName,
|
||||||
TypeKind DeclType,
|
TypeKind DeclType,
|
||||||
Accessibility Accessibility,
|
Accessibility Accessibility,
|
||||||
@@ -46,6 +49,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
public bool IsMethodStatic { get; set; }
|
public bool IsMethodStatic { get; set; }
|
||||||
|
|
||||||
|
public Location PatternDefineLocation { get; set; }
|
||||||
|
|
||||||
public string NameSpaceName { get; set; }
|
public string NameSpaceName { get; set; }
|
||||||
|
|
||||||
public string Pattern { get; set; }
|
public string Pattern { get; set; }
|
||||||
@@ -57,7 +62,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
public readonly SourceRecord AsRecord()
|
public readonly SourceRecord AsRecord()
|
||||||
{
|
{
|
||||||
return new SourceRecord(IsClassStatic, IsMethodStatic, Pattern, MethodName,
|
return new SourceRecord(IsClassStatic, IsMethodStatic, Pattern, MethodName,
|
||||||
NameSpaceName, MethodAccessibility, ContainingType);
|
NameSpaceName, MethodAccessibility, ContainingType,
|
||||||
|
PatternDefineLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -67,10 +73,28 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
private readonly List<Diagnostic> _diagnostics = [];
|
private readonly List<Diagnostic> _diagnostics = [];
|
||||||
|
|
||||||
|
private TextRegexBinding(
|
||||||
|
List<Diagnostic> diagnostics,
|
||||||
|
SequenceEmitter<string> generatedSource,
|
||||||
|
SourceRecord source
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_diagnostics = diagnostics ?? [];
|
||||||
|
GeneratedSource = generatedSource ?? new();
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextRegexBinding()
|
||||||
|
{
|
||||||
|
_diagnostics = [];
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsValid { get; private set; } = true;
|
public bool IsValid { get; private set; } = true;
|
||||||
|
|
||||||
public IReadOnlyList<Diagnostic> RegexDiagnostic => _diagnostics;
|
public IReadOnlyList<Diagnostic> RegexDiagnostic => _diagnostics;
|
||||||
|
|
||||||
|
public SequenceEmitter<string> GeneratedSource { get; set; }
|
||||||
|
|
||||||
public SourceRecord Source { get; set; }
|
public SourceRecord Source { get; set; }
|
||||||
|
|
||||||
public void AddDiagnostic(
|
public void AddDiagnostic(
|
||||||
@@ -89,16 +113,15 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
{
|
{
|
||||||
Diagnostic diag = Diagnostic.Create(descriptor, location, messageArgs);
|
Diagnostic diag = Diagnostic.Create(descriptor, location, messageArgs);
|
||||||
_diagnostics.Add(diag);
|
_diagnostics.Add(diag);
|
||||||
IsValid = IsValid && (diag.DefaultSeverity != DiagnosticSeverity.Error);
|
IsValid = IsValid && (diag.Severity != DiagnosticSeverity.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextRegexBinding Clone()
|
public TextRegexBinding Clone()
|
||||||
{
|
{
|
||||||
TextRegexBinding binding = new()
|
SequenceEmitter<string> code = GeneratedSource.Clone();
|
||||||
{
|
List<Diagnostic> diag = new(_diagnostics.Count);
|
||||||
IsValid = this.IsValid,
|
diag.AddRange(this._diagnostics);
|
||||||
};
|
TextRegexBinding binding = new(diag, code, Source with { });
|
||||||
binding._diagnostics.AddRange(this._diagnostics);
|
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,12 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Security.AccessControl;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -21,7 +23,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
internal partial class TextRegexSourceGen : IIncrementalGenerator
|
internal partial class TextRegexSourceGen : IIncrementalGenerator
|
||||||
{
|
{
|
||||||
|
|
||||||
private static readonly string TextRegexTypeName = typeof(TextRegex).FullName ??
|
private static readonly string TextRegexTypeName = typeof(TextRegexAttribute).FullName ??
|
||||||
string.Empty;
|
string.Empty;
|
||||||
|
|
||||||
public void Initialize(
|
public void Initialize(
|
||||||
@@ -37,6 +39,12 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
) => true, BindTextRegexCandidate);
|
) => true, BindTextRegexCandidate);
|
||||||
|
|
||||||
IncrementalValuesProvider<TextRegexBinding> match = RegisterDiagnosticBypass(context, provider);
|
IncrementalValuesProvider<TextRegexBinding> match = RegisterDiagnosticBypass(context, provider);
|
||||||
|
|
||||||
|
IncrementalValuesProvider<TextRegexBinding> compile = match.Select(CompileRegexMethodSource);
|
||||||
|
|
||||||
|
IncrementalValuesProvider<TextRegexBinding> compile_success = RegisterDiagnosticBypass(context, compile);
|
||||||
|
|
||||||
|
context.RegisterSourceOutput(compile_success, GenerateRegexSourceFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TextRegexBinding BindTextRegexCandidate(
|
private static TextRegexBinding BindTextRegexCandidate(
|
||||||
@@ -84,7 +92,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
INamedTypeSymbol contain = method_symbol.ContainingType;
|
INamedTypeSymbol contain = method_symbol.ContainingType;
|
||||||
INamespaceSymbol namespace_symbol = contain.ContainingNamespace;
|
INamespaceSymbol namespace_symbol = contain.ContainingNamespace;
|
||||||
TypeSource type_source = null;
|
TypeSource type_source = null!;
|
||||||
|
|
||||||
while (contain is not null)
|
while (contain is not null)
|
||||||
{
|
{
|
||||||
@@ -98,22 +106,53 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
if (!bind.IsValid) {
|
if (!bind.IsValid) {
|
||||||
return bind;
|
return bind;
|
||||||
}
|
}
|
||||||
TypeSource t = new(contain.IsStatic, contain.Name, contain.TypeKind,
|
string display = contain.ToDisplayString(TypeSourceFormat);
|
||||||
|
TypeSource t = new(contain.IsStatic, display, contain.Name, contain.TypeKind,
|
||||||
contain.DeclaredAccessibility, type_source);
|
contain.DeclaredAccessibility, type_source);
|
||||||
type_source = t;
|
type_source = t;
|
||||||
contain = contain.BaseType!;
|
contain = contain.ContainingType!;
|
||||||
}
|
}
|
||||||
Source s = new()
|
Source s = new()
|
||||||
{
|
{
|
||||||
MethodName = method_symbol.Name,
|
MethodName = method_symbol.Name,
|
||||||
NameSpaceName = namespace_symbol.Name,
|
NameSpaceName = namespace_symbol.ToDisplayString(),
|
||||||
|
PatternDefineLocation = method_decl.GetLocation(),
|
||||||
|
IsMethodStatic = method_decl.Modifiers.Any(SyntaxKind.StaticKeyword),
|
||||||
|
ContainingType = type_source,
|
||||||
|
MethodAccessibility = method_symbol.DeclaredAccessibility,
|
||||||
};
|
};
|
||||||
|
foreach (KeyValuePair<string, TypedConstant> item in attribute.NamedArguments)
|
||||||
|
{
|
||||||
|
if (item.Key == nameof(TextRegexAttribute.Pattern)) {
|
||||||
|
s.Pattern = item.Value.Value as string ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bind.Source = s.AsRecord();
|
bind.Source = s.AsRecord();
|
||||||
return bind;
|
return bind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TextRegexBinding CompileRegexMethodSource(
|
||||||
|
TextRegexBinding source,
|
||||||
|
CancellationToken ct
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
TextRegexBinding result = source.Clone();
|
||||||
|
string pattern = result.Source.Pattern;
|
||||||
|
TextRegexSource analyze_result = RegexAnalyzer.Analyze(pattern);
|
||||||
|
if (analyze_result.AnalyseException is not null)
|
||||||
|
{
|
||||||
|
result.AddDiagnostic(CompileFailDiag, null, analyze_result.AnalyseException.Message);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
SequenceEmitter<string> method_source = GenerateSource(analyze_result);
|
||||||
|
result.GeneratedSource = method_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static IncrementalValuesProvider<TextRegexBinding> RegisterDiagnosticBypass(
|
private static IncrementalValuesProvider<TextRegexBinding> RegisterDiagnosticBypass(
|
||||||
IncrementalGeneratorInitializationContext context,
|
IncrementalGeneratorInitializationContext context,
|
||||||
IncrementalValuesProvider<TextRegexBinding> bindings
|
IncrementalValuesProvider<TextRegexBinding> bindings
|
||||||
|
|||||||
Reference in New Issue
Block a user