remove useless node from source gen pipeline. add source gen logic

This commit is contained in:
Youmu Liu
2026-08-21 18:31:31 +08:00
parent 968a6afe1a
commit f0b6a4038e
15 changed files with 219 additions and 334 deletions
+1 -1
View File
@@ -225,7 +225,7 @@ allocator.Free(handle);
### Technical Documentation
- [TLSF Allocator](src/StgSharp.Common/HighPerformance/Memory/InroductionToTLSF.md) - Arena layout, allocation behavior, and benchmark methodology
- [TLSF Allocator](doc/InroductionToTLSF.md) - Arena layout, allocation behavior, and benchmark methodology
- [Native Library Build](src/StgSharp.Native/README.md) - Native build and source-layout notes
- [Native Library Naming](src/StgSharp.Native/naming.md) - Native library file naming conventions
@@ -124,24 +124,6 @@ namespace StgSharp.Collections
_index += span.Length;
}
public void PushRange(
SpanAction<T, int> filling,
int count
)
{
if (count == 0) {
return;
}
if (_index + count > _values.Length) {
throw new InvalidOperationException("Not enough space in the stack to push the entire span.");
}
if (filling is not null)
{
filling(_values.AsSpan()[_index..(_index + count)], count);
_index += count;
}
}
public bool TryPop(
out T value
)
@@ -157,6 +139,18 @@ namespace StgSharp.Collections
return true;
}
internal void FillRange(
SpanAction<T, nuint> filling,
nuint buffer
)
{
if (filling is not null)
{
filling(_values.AsSpan()[_index..], buffer);
_index = _values.Length;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Span<T> GetValueLayout()
{
@@ -41,16 +41,16 @@ namespace StgSharp.HighPerformance.Memory
_Capacity = count;
_recycle = new((int)_Capacity);
_recycle.PushRange(init, (int)count);
_recycle.FillRange(init, buffer);
_currentIndex = _Capacity;
static void init(
Span<nuint> arr,
int _
nuint buffer
)
{
for (int i = 0; i < arr.Length; i++) {
arr[i] = (nuint)i;
arr[i] = buffer + ((nuint)i * (nuint)Unsafe.SizeOf<T>());
}
}
}
@@ -25,8 +25,6 @@ namespace StgSharp.Timing
private EventHandler? _refreshHandler, _missHandler;
private int _size, _id;
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
private readonly ManualResetEventSlim _event = new();
private readonly object _participantObject = new();
public event EventHandler MissTimeSpanRefreshed
@@ -57,15 +55,17 @@ namespace StgSharp.Timing
/// </summary>
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
internal ManualResetEventSlim AwaitingEvent => _event;
internal ManualResetEventSlim ResetEvent { get; } = new();
public void Dispose()
{
#pragma warning disable CA1816
// mark provider as ended so external users can observe terminal state
Volatile.Write(ref _endedFlag, 1);
s_unusedID.Push(TokenID);
_event.Set();
ResetEvent.Set();
GC.SuppressFinalize(this);
#pragma warning restore CA1816
}
public override int GetHashCode()
@@ -84,7 +84,7 @@ namespace StgSharp.Timing
public void WaitNextSpan()
{
_event.Wait();
ResetEvent.Wait();
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
while (true)
@@ -99,7 +99,7 @@ namespace StgSharp.Timing
if (cur - 1 == 0)
{
// Last consumer resets the gate for the next tick.
_event.Reset();
ResetEvent.Reset();
}
return;
}
@@ -108,7 +108,7 @@ namespace StgSharp.Timing
public async Task WaitNextSpanAsync()
{
await Task.Run(() => _event.Wait()).ConfigureAwait(false);
await Task.Run(ResetEvent.Wait).ConfigureAwait(false);
while (true)
{
long cur = Volatile.Read(ref _pendingSpans);
@@ -120,7 +120,7 @@ namespace StgSharp.Timing
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
ResetEvent.Reset();
}
break;
@@ -138,7 +138,7 @@ namespace StgSharp.Timing
)
{
// Block the current thread until the event is signaled or cancellation requested.
_event.Wait(ct);
ResetEvent.Wait(ct);
// consume one pending span (same logic as non-cancellable variant)
while (true)
@@ -152,7 +152,7 @@ namespace StgSharp.Timing
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
ResetEvent.Reset();
}
break;
@@ -203,7 +203,7 @@ namespace StgSharp.Timing
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
{
// Open the gate for all current waiters (up to `_size`).
_event.Set();
ResetEvent.Set();
_refreshHandler?.Invoke(this, EventArgs.Empty);
}
} else
@@ -47,7 +47,7 @@ namespace StgSharp.Timing
}
TokenID = id;
_size = maxWaitCount > 0 ? maxWaitCount : 1;
_event = new ManualResetEventSlim(initialState:false, spinCount:0);
ResetEvent = new ManualResetEventSlim(initialState:false, spinCount:0);
_pendingSpans = 0;
}
@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Common.Timing
{
internal class TimeThreadProfile
{
}
}
@@ -20,8 +20,7 @@ namespace StgSharp.RegularAnalysis.Text
public RegexAstNode(
Token<RegexElementLabel> source
)
: this(RegexCommonPayload.FromToken(source))
{ }
: this(RegexCommonPayload.FromToken(source)) { }
public RegexAstNode(
RegexAstPayload payload
@@ -32,6 +31,8 @@ namespace StgSharp.RegularAnalysis.Text
public int EnumState { get; set; }
public ISyntaxPayload<RegexElementLabel> Source => Payload;
public long NodeFlag => (long)Label;
public static RegexAstNode Empty { get; }
@@ -69,6 +70,8 @@ namespace StgSharp.RegularAnalysis.Text
}
}
public RegexAstPayload Payload { get; private protected set; }
public RegexElementLabel EqualityTypeConvert => Label;
public RegexElementLabel Label => Payload.Flag;
@@ -80,13 +83,7 @@ namespace StgSharp.RegularAnalysis.Text
_ => Payload.Source
};
public RegexAstPayload Payload { get; private protected set; }
public ISyntaxPayload<RegexElementLabel> Source => Payload;
internal TPayload PayloadAs<TPayload>() where TPayload : RegexAstPayload =>
Payload as TPayload ?? throw new InvalidOperationException(
$"Node {Label} does not carry {typeof(TPayload).Name}.");
public string SourceCode => Source.Source;
public void AppendNode(
RegexAstNode nextToken
@@ -106,7 +103,7 @@ namespace StgSharp.RegularAnalysis.Text
right &= RegexElementLabel.OPERATOR;
int v_left = (int)left, v_right = (int)right;
if (v_left == 0 || v_right == 0) {
return (false,0);
return (false, 0);
}
return (true,v_left - v_right);
}
@@ -148,6 +145,14 @@ namespace StgSharp.RegularAnalysis.Text
return new EmptyRegexAstNode();
}
internal TPayload PayloadAs<TPayload>() where TPayload : RegexAstPayload
{
return
Payload as TPayload ??
throw new InvalidOperationException(
$"Node {Label} does not carry {typeof(TPayload).Name}.");
}
internal void RemoveAndAsChild(
bool useLeft
)
@@ -24,9 +24,9 @@ namespace StgSharp.RegularAnalysis.Text
category:"NGRA.TextRegex",
isEnabledByDefault:true);
private static readonly DiagnosticDescriptor CompileFailDiag = new(id:"NGRA005",
title:"Unexpected exception when compiling",
messageFormat:"Unexpected exception handles when compiling expression \"{0}\":{1}",
private static readonly DiagnosticDescriptor CompileFailDiag = new(id:"NGRA006",
title:"Regular expression compilation failed",
messageFormat:"Failed to compile expression \"{0}\": {1}",
defaultSeverity:DiagnosticSeverity.Error,
category:"NGRA.TextRegex",
isEnabledByDefault:true);
@@ -39,8 +39,8 @@ namespace StgSharp.RegularAnalysis.Text
isEnabledByDefault:true);
private static readonly DiagnosticDescriptor IncorrectReturnTypeDiag = new(id:"NGRA003",
title:"Text regex binding must be partial",
messageFormat:"Method {0} must be partial to receive generated code",
title:$"Returning type must be {typeof(TextRegex).FullName}",
messageFormat:$"Method {{0}} must return {typeof(TextRegex).FullName}",
defaultSeverity:DiagnosticSeverity.Error,
category:"NGRA.TextRegex",
isEnabledByDefault:true);
@@ -53,8 +53,8 @@ namespace StgSharp.RegularAnalysis.Text
isEnabledByDefault:true);
private static readonly DiagnosticDescriptor NotPartialDiag = new(id:"NGRA001",
title:$"Returning type must be {typeof(TextRegex).FullName}",
messageFormat:$"Method {{0}} must return {typeof(TextRegex).FullName}",
title:"Text regex binding must be partial",
messageFormat:"Method {0} must be partial to receive generated code",
defaultSeverity:DiagnosticSeverity.Error,
category:"NGRA.TextRegex",
isEnabledByDefault:true);
@@ -24,6 +24,7 @@ namespace StgSharp.RegularAnalysis.Text
RegexAstNode ast,
in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context,
bool is_find = false
)
@@ -38,16 +39,16 @@ namespace StgSharp.RegularAnalysis.Text
switch (op)
{
case RegexElementLabel.CONCAT:
GenerateConcat(root, sc, func_list);
GenerateConcat(root, sc, func_list, context);
break;
case RegexElementLabel.COUNT:
GenerateCount(root, sc, func_list);
GenerateCount(root, sc, func_list, context);
break;
case RegexElementLabel.GROUP_BEGIN:
GenerateGroup(root, sc, func_list);
GenerateGroup(root, sc, func_list, context);
break;
case RegexElementLabel.ALT:
GenerateAlt(root, sc, func_list);
GenerateAlt(root, sc, func_list, context);
break;
}
} else
@@ -67,6 +68,20 @@ namespace StgSharp.RegularAnalysis.Text
#region supporting method
private static string FormatStringLiteral(
string value
)
{
return SyntaxFactory.Literal(value).Text;
}
private static string FormatCharLiteral(
char value
)
{
return SyntaxFactory.Literal(value).Text;
}
private static bool IsNamedFind(
RegexAstNode node
)
@@ -99,32 +114,48 @@ namespace StgSharp.RegularAnalysis.Text
private static void GenerateAlt(
RegexAstNode node,
[NotNull] in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context
)
{
RegexAstNode left = node.Left;
RegexAstNode right = node.Right;
RegexAstNode current = left;
RegexAstNode current = node.Right;
string exit_label = context.RequestExitAlt();
while (RegexAstNode.IsNullOrEmpty(current))
{
GenerateMethodSource(current, sc, func_list);
current = current.Next;
}
current = right;
while (RegexAstNode.IsNullOrEmpty(current))
RegexElementLabel op = current.Label & RegexElementLabel.SEQUENCE;
if (op != 0)
{
GenerateMethodSource(current, sc, func_list);
string code = current.SourceCode;
switch (op)
{
case RegexElementLabel.UNIT:
_ = sc.AppendLine(@$"if({context.RemainSpan}[0] == {FormatCharLiteral(code[0])})")
.AppendLine("{")
.AppendLine($@"goto {exit_label};")
.AppendLine("}");
break;
case RegexElementLabel.UNIT_SET:
IReadOnlyList<RegexCharSet> split = current.PayloadAs<RegexCharSetPayload>()
.Set;
RegexCharSet first_set = split[0];
for (int i = 1; i < split.Count - 1; i++) { }
RegexCharSet last_set = split[^1];
break;
}
} else
{
GenerateMethodSource(current, sc, func_list, context);
current = current.Next;
}
}
_ = sc.AppendLine($@"{exit_label}: {{ }}");
}
private static void GenerateGroup(
RegexAstNode node,
[NotNull] in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context
)
{
RegexAstNode left = node.Left;
@@ -134,10 +165,10 @@ namespace StgSharp.RegularAnalysis.Text
if (left is not null)
{
GenerateMethodSource(left, sc, func_list);
GenerateMethodSource(left, sc, func_list, context);
} else
if (right is not null) {
GenerateMethodSource(right, sc, func_list);
GenerateMethodSource(right, sc, func_list, context);
}
string group_name = config.Name;
@@ -147,7 +178,8 @@ namespace StgSharp.RegularAnalysis.Text
private static void GenerateConcat(
RegexAstNode node,
[NotNull] in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context
)
{
RegexAstNode left = node.Left;
@@ -162,40 +194,41 @@ namespace StgSharp.RegularAnalysis.Text
right is not null)
{
// TODO use find
GenerateMethodSource(right, sc, func_list, true);
GenerateMethodSource(right, sc, func_list, context, true);
} else if (IsNamedFind(node))
{
if (right is not null)
{
// TODO named find
GenerateMethodSource(right, sc, func_list, true);
GenerateMethodSource(right, sc, func_list, context, true);
} else
{
// TODO pack rest of string to group
}
} else
{
GenerateMethodSource(left, sc, func_list);
GenerateMethodSource(left, sc, func_list, context);
if (right is not null) {
GenerateMethodSource(right, sc, func_list);
GenerateMethodSource(right, sc, func_list, context);
}
}
}
if (right is not null) {
GenerateMethodSource(right, sc, func_list);
GenerateMethodSource(right, sc, func_list, context);
}
}
private static void GenerateCount(
RegexAstNode node,
[NotNull] in SequenceEmitter<string> sc,
in List<SequenceEmitter<string>> func_list
in List<SequenceEmitter<string>> func_list,
in SourceGenContext context
)
{
RegexAstNode value = node.Left ?? node.Right;
if ((value.Label & RegexElementLabel.SEQUENCE) == 0)
{
GenerateMethodSource(value, sc, func_list);
GenerateMethodSource(value, sc, func_list, context);
} else
{
RegexCountPayload payload = value.PayloadAs<RegexCountPayload>();
@@ -6,9 +6,11 @@
// -----------------------------------------------------------------------------
using Microsoft.CodeAnalysis.Text;
using StgSharp.PipeLine;
using StgSharp.RegularAnalysis.Abstraction;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -27,206 +29,33 @@ namespace StgSharp.RegularAnalysis.Text
/// a block scoped scratch variable. Scratch names are never reused, so a nested block can
/// always declare its own without colliding with an enclosing one.
/// </remarks>
private sealed class SourceGenContext
internal sealed class SourceGenContext
{
/// <summary>
/// Name of the match method parameter holding the input sequence.
/// </summary>
public const string _inputSpan = "text";
public const string __input_span = "input";
/// <summary>
/// Result of the instruction emitted immediately before the current one.
/// </summary>
public const string _stepResult = "__ok";
public const string _remain_span = "__remain";
private readonly Dictionary<string, int> _groupIndex = [];
private readonly HashSet<int> _observedLines = [];
private int _exit_alt_count;
private int _anonymousGroup;
public string RemainSpan { get; } = _remain_span;
private int _count_idx = 0;
private int _scratchIndex;
private readonly SortedSet<int> _lineSlots = [];
private readonly SortedSet<int> _regionSlots = [];
public string FirstOfRemain { get; } = $@"{_remain_span}[0]";
public string RemainingSpan { get; } = "__remaining_span";
public string InputString { get; } = __input_span;
public string InputSpan { get; } = _inputSpan;
public string StepResult { get; } = _stepResult;
// region id -> entry count from first to last occurrence, filled once per pattern
public int[] RegionExtent { get; set; } = [];
public string[] BeginRegion()
public SequenceEmitter<string> DeclareGlobalFields()
{
return [
"do",
"{"
];
SequenceEmitter<string> sc = new();
return sc.AppendLine($@"{ROS_char} {RemainSpan};");
}
public string[] CloseRegion()
public string RequestExitAlt()
{
return [
"} while(true);"
];
}
/// <summary>
/// Emits declarations for every slot requested during body generation.
/// </summary>
/// <remarks>
/// Must be called after the method body has been generated, but the produced lines
/// have to be placed before it.
/// </remarks>
public SequenceEmitter<string> EmitDeclarationsTo(
SequenceEmitter<string> emitter
)
{
if (emitter is null) {
return null!;
}
_ = emitter.AppendLine($@"{ROS_char} {RemainingSpan} = {_inputSpan};")
.AppendLine($@"bool {_stepResult} = true;");
foreach (int region in _regionSlots) {
_ = emitter.AppendLine($@"int {RegionAnchor(region)} = 0;")
.AppendLine($@"bool {RegionResult(region)} = false;");
}
foreach (int line in _lineSlots) {
_ = emitter.AppendLine($@"bool {LineResult(line)} = false;");
}
foreach (int group in _groupIndex.Values) {
_ = emitter.AppendLine($@"int __g{group}_begin = -1;")
.AppendLine($@"int __g{group}_end = -1;");
}
return emitter;
}
public string GetVarGroupBegin(
string name
)
{
return $"__g{ResolveGroup(name)}_begin";
}
public string GetVarGroupEnd(
string name
)
{
return $"__g{ResolveGroup(name)}_end";
}
/// <summary>
/// Indicates whether the instruction at line <paramref name="line" /> has to persist
/// its result into a line slot.
/// </summary>
public bool IsLineObserved(
int line
)
{
return _observedLines.Contains(line);
}
/// <summary>
/// Name of the slot holding the match result of the instruction at line <paramref
/// name="line" />. Only lines actually referenced by an <see
/// cref="RegexIRPrefixMask.Observe_Line" /> prefix get a slot.
/// </summary>
public string LineResult(
int line
)
{
_ = _lineSlots.Add(line);
return $"__l{line}_ok";
}
public string MakeRemainOffset(
string offset_name
)
{
return @$"{RemainingSpan} = {RemainingSpan}[{offset_name}..];";
}
public string MakeRemainOffset(
int offset
)
{
return @$"{RemainingSpan} = {RemainingSpan}[{offset}..];";
}
/// <summary>
/// Records that some prefix observes the result of the instruction at line <paramref
/// name="line" />.
/// </summary>
public void MarkLineObserved(
int line
)
{
_ = _observedLines.Add(line);
}
/// <summary>
/// Allocates a fresh block scoped scratch variable name.
/// </summary>
/// <param name="tag">
/// Short role marker, for example <c> n </c> for a counter or <c> ch </c> for a
/// character.
/// </param>
public string NewScratch(
string tag
)
{
return $"__{tag}{_scratchIndex++}";
}
/// <summary>
/// Name of the slot holding the cursor value captured when region <paramref
/// name="region" /> was entered.
/// </summary>
public string RegionAnchor(
int region
)
{
_ = _regionSlots.Add(region);
return $"__r{region}_begin";
}
/// <summary>
/// Name of the slot holding the match result of region <paramref name="region" />.
/// </summary>
public string RegionResult(
int region
)
{
_ = _regionSlots.Add(region);
return $"__r{region}_ok";
}
public string RequireNewCounter()
{
string counter_name = $"__count{_count_idx}";
_count_idx++;
return counter_name;
}
private int ResolveGroup(
string name
)
{
string key = string.IsNullOrEmpty(name) ? $"#{_anonymousGroup++}" : name;
if (_groupIndex.TryGetValue(key, out int index)) {
return index;
}
index = _groupIndex.Count;
_groupIndex[key] = index;
return index;
string code = $@"_exit_alt_{_exit_alt_count}";
_exit_alt_count++;
return code;
}
}
@@ -43,6 +43,8 @@ namespace StgSharp.RegularAnalysis.Text
SequenceEmitter<string> ce = new();
SourceRecord gen_source = binding.Source;
TypeSource type = binding.Source.ContainingType;
SequenceEmitter<string> method_body = binding.GeneratedSource;
SourceGenContext regex_gen_context = binding.SourceContext;
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $"{gen_source.NamespaceName}.");
_ = ce.AppendLine($@"// <auto-generated/>")
@@ -75,15 +77,12 @@ namespace StgSharp.RegularAnalysis.Text
} 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}()")
_ = ce.AppendLine(@$"{ToKeyword(gen_source.MethodAccessibility)} {static_modifier} {Text_Regex} {gen_source.MethodName}({ROS_char} {regex_gen_context.InputString})")
.AppendLine("{")
/**/ //TODO return single instance of the TextRegex object
.Append(method_body)
.AppendLine("}");
// TODO inject method source code here
for (int i = 0; i < brace_count; i++)
{
for (int i = 0; i < brace_count; i++) {
_ = ce.AppendLine("}");
}
@@ -104,10 +103,6 @@ namespace StgSharp.RegularAnalysis.Text
_ = 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(
@@ -76,12 +76,17 @@ namespace StgSharp.RegularAnalysis.Text
private TextRegexBinding(
List<Diagnostic> diagnostics,
SequenceEmitter<string> generatedSource,
SourceRecord source
SourceRecord source,
SourceGenContext sourceContext,
TextRegexSource? analyzedSource
)
{
_diagnostics = diagnostics ?? [];
GeneratedSource = generatedSource ?? new();
Source = source;
AnalyzedSource = analyzedSource;
IsValid = !_diagnostics.Exists(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error);
SourceContext = sourceContext;
}
public TextRegexBinding()
@@ -89,11 +94,15 @@ namespace StgSharp.RegularAnalysis.Text
_diagnostics = [];
}
public TextRegexSource? AnalyzedSource { get; set; }
public bool IsValid { get; private set; } = true;
public IReadOnlyList<Diagnostic> RegexDiagnostic => _diagnostics;
public SequenceEmitter<string> GeneratedSource { get; set; }
public SequenceEmitter<string> GeneratedSource { get; set; } = new();
public SourceGenContext SourceContext { get; set; } = null!;
public SourceRecord Source { get; set; }
@@ -121,7 +130,8 @@ namespace StgSharp.RegularAnalysis.Text
SequenceEmitter<string> code = GeneratedSource.Clone();
List<Diagnostic> diag = new(_diagnostics.Count);
diag.AddRange(this._diagnostics);
TextRegexBinding binding = new(diag, code, Source with { });
TextRegexBinding binding = new(diag, code, Source with { }, SourceContext,
AnalyzedSource);
return binding;
}
@@ -9,14 +9,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using StgSharp.RegularAnalysis.Abstraction;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.RegularAnalysis.Text
{
@@ -30,7 +24,10 @@ namespace StgSharp.RegularAnalysis.Text
IncrementalGeneratorInitializationContext context
)
{
IncrementalValuesProvider<TextRegexBinding> provider = context.SyntaxProvider
IncrementalValuesProvider<TextRegexBinding> candidates =
//
context.SyntaxProvider
.ForAttributeWithMetadataName<TextRegexBinding>(
TextRegexTypeName,
static(
@@ -38,13 +35,43 @@ namespace StgSharp.RegularAnalysis.Text
_
) => true, BindTextRegexCandidate);
IncrementalValuesProvider<TextRegexBinding> match = RegisterDiagnosticBypass(context, provider);
IncrementalValuesProvider<TextRegexBinding> validCandidates =
RegisterDiagnosticBypass(context, candidates);
IncrementalValuesProvider<TextRegexBinding> compile = match.Select(CompileRegexMethodSource);
IncrementalValuesProvider<TextRegexBinding> analyzed =
validCandidates.Select(AnalyzeRegexMethod);
IncrementalValuesProvider<TextRegexBinding> compile_success = RegisterDiagnosticBypass(context, compile);
IncrementalValuesProvider<TextRegexBinding> validAnalyzed =
RegisterDiagnosticBypass(context, analyzed);
context.RegisterSourceOutput(compile_success, GenerateRegexSourceFile);
IncrementalValuesProvider<TextRegexBinding> generated =
validAnalyzed.Select(CompileRegexMethodSource);
IncrementalValuesProvider<TextRegexBinding> validGenerated =
RegisterDiagnosticBypass(context, generated);
context.RegisterSourceOutput(validGenerated, GenerateRegexSourceFile);
}
private static TextRegexBinding AnalyzeRegexMethod(
TextRegexBinding binding,
CancellationToken cancellationToken
)
{
cancellationToken.ThrowIfCancellationRequested();
if (!binding.IsValid) {
return binding;
}
TextRegexBinding result = binding.Clone();
string pattern = result.Source.Pattern;
TextRegexSource analyzedSource = RegexAnalyzer.Analyze(pattern);
result.AnalyzedSource = analyzedSource;
if (analyzedSource.AnalyseException is not null) {
result.AddDiagnostic(CompileFailDiag, result.Source.PatternDefineLocation, pattern,
analyzedSource.AnalyseException.Message);
}
return result;
}
private static TextRegexBinding BindTextRegexCandidate(
@@ -71,7 +98,7 @@ namespace StgSharp.RegularAnalysis.Text
TextRegexBinding bind = new();
if (!method_decl.Modifiers.Any(SyntaxKind.PartialKeyword)) {
bind.AddDiagnostic(NotPartialDiag, method_decl.GetLocation(), method_decl.GetText());
bind.AddDiagnostic(NotPartialDiag, method_decl.GetLocation(), method_symbol.Name);
}
if (method_decl.ParameterList.Parameters.Count != 0) {
bind.AddDiagnostic(NotParameterLessDiag, method_decl.GetLocation(), method_decl.GetText());
@@ -97,9 +124,9 @@ namespace StgSharp.RegularAnalysis.Text
while (contain is not null)
{
if (contain.IsRecord) {
bind.AddDiagnostic(CanNotBeRecordDiag, method_decl.GetLocation(), method_symbol.Name);
bind.AddDiagnostic(CanNotBeRecordDiag, method_decl.GetLocation(), contain.Name);
}
if (contain.TypeKind is not TypeKind.Class or TypeKind.Struct) {
if (contain.TypeKind is not (TypeKind.Class or TypeKind.Struct)) {
bind.AddDiagnostic(IncorrectContainingTypeDiag, method_decl.GetLocation(),
method_symbol.Name, contain.TypeKind);
}
@@ -133,23 +160,28 @@ namespace StgSharp.RegularAnalysis.Text
}
private static TextRegexBinding CompileRegexMethodSource(
TextRegexBinding source,
CancellationToken ct
TextRegexBinding binding,
CancellationToken cancellationToken
)
{
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 = /**/ new()/*GenerateSource(analyze_result)/**/;
result.GeneratedSource = method_source;
cancellationToken.ThrowIfCancellationRequested();
TextRegexBinding result = binding.Clone();
RegexAstNode root = result.AnalyzedSource!.Ast.Root;
SourceGenContext generationContext = new();
SequenceEmitter<string> methodSource = new();
List<SequenceEmitter<string>> localFunctions = [];
GenerateMethodSource(root, methodSource, localFunctions, generationContext);
foreach (SequenceEmitter<string> localFunction in localFunctions) {
_ = methodSource.AppendLine()
.AppendLine()
.AppendLine("// local method for matching")
.Append(localFunction);
}
result.SourceContext = generationContext;
result.GeneratedSource = methodSource;
return result;
}
@@ -158,19 +190,18 @@ namespace StgSharp.RegularAnalysis.Text
IncrementalValuesProvider<TextRegexBinding> bindings
)
{
IncrementalValuesProvider<Diagnostic> error_path = bindings.Where(static binding => !binding.IsValid)
IncrementalValuesProvider<Diagnostic> errorPath =
bindings.Where(static binding => !binding.IsValid)
.SelectMany(static(
binding,
_
) => binding.RegexDiagnostic);
IncrementalValuesProvider<TextRegexBinding> correct_path = bindings.Where(static binding => binding.IsValid);
context.RegisterSourceOutput(errorPath, static(
productionContext,
diagnostic
) => productionContext.ReportDiagnostic(diagnostic));
context.RegisterSourceOutput(error_path, (
c,
diag
) => c.ReportDiagnostic(diag));
return correct_path;
return bindings.Where(static binding => binding.IsValid);
}
}