remove useless node from source gen pipeline. add source gen logic
This commit is contained in:
@@ -225,7 +225,7 @@ allocator.Free(handle);
|
|||||||
|
|
||||||
### Technical Documentation
|
### 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 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
|
- [Native Library Naming](src/StgSharp.Native/naming.md) - Native library file naming conventions
|
||||||
|
|
||||||
|
|||||||
@@ -124,24 +124,6 @@ namespace StgSharp.Collections
|
|||||||
_index += span.Length;
|
_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(
|
public bool TryPop(
|
||||||
out T value
|
out T value
|
||||||
)
|
)
|
||||||
@@ -157,6 +139,18 @@ namespace StgSharp.Collections
|
|||||||
return true;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal Span<T> GetValueLayout()
|
internal Span<T> GetValueLayout()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,16 +41,16 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
_Capacity = count;
|
_Capacity = count;
|
||||||
_recycle = new((int)_Capacity);
|
_recycle = new((int)_Capacity);
|
||||||
_recycle.PushRange(init, (int)count);
|
_recycle.FillRange(init, buffer);
|
||||||
_currentIndex = _Capacity;
|
_currentIndex = _Capacity;
|
||||||
|
|
||||||
static void init(
|
static void init(
|
||||||
Span<nuint> arr,
|
Span<nuint> arr,
|
||||||
int _
|
nuint buffer
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < arr.Length; i++) {
|
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 EventHandler? _refreshHandler, _missHandler;
|
||||||
private int _size, _id;
|
private int _size, _id;
|
||||||
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
|
private long _pendingSpans; // Number of unconsumed span ticks since last Wait.
|
||||||
|
|
||||||
private readonly ManualResetEventSlim _event = new();
|
|
||||||
private readonly object _participantObject = new();
|
private readonly object _participantObject = new();
|
||||||
|
|
||||||
public event EventHandler MissTimeSpanRefreshed
|
public event EventHandler MissTimeSpanRefreshed
|
||||||
@@ -57,15 +55,17 @@ namespace StgSharp.Timing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
|
public long PendingSpanCount => Volatile.Read(ref _pendingSpans);
|
||||||
|
|
||||||
internal ManualResetEventSlim AwaitingEvent => _event;
|
internal ManualResetEventSlim ResetEvent { get; } = new();
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CA1816
|
||||||
// mark provider as ended so external users can observe terminal state
|
// mark provider as ended so external users can observe terminal state
|
||||||
Volatile.Write(ref _endedFlag, 1);
|
Volatile.Write(ref _endedFlag, 1);
|
||||||
s_unusedID.Push(TokenID);
|
s_unusedID.Push(TokenID);
|
||||||
_event.Set();
|
ResetEvent.Set();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
#pragma warning restore CA1816
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
@@ -84,7 +84,7 @@ namespace StgSharp.Timing
|
|||||||
|
|
||||||
public void WaitNextSpan()
|
public void WaitNextSpan()
|
||||||
{
|
{
|
||||||
_event.Wait();
|
ResetEvent.Wait();
|
||||||
|
|
||||||
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
|
// Consume one span slot (clamp at 0 so multiple subscribers won't make it negative).
|
||||||
while (true)
|
while (true)
|
||||||
@@ -99,7 +99,7 @@ namespace StgSharp.Timing
|
|||||||
if (cur - 1 == 0)
|
if (cur - 1 == 0)
|
||||||
{
|
{
|
||||||
// Last consumer resets the gate for the next tick.
|
// Last consumer resets the gate for the next tick.
|
||||||
_event.Reset();
|
ResetEvent.Reset();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ namespace StgSharp.Timing
|
|||||||
|
|
||||||
public async Task WaitNextSpanAsync()
|
public async Task WaitNextSpanAsync()
|
||||||
{
|
{
|
||||||
await Task.Run(() => _event.Wait()).ConfigureAwait(false);
|
await Task.Run(ResetEvent.Wait).ConfigureAwait(false);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long cur = Volatile.Read(ref _pendingSpans);
|
long cur = Volatile.Read(ref _pendingSpans);
|
||||||
@@ -120,7 +120,7 @@ namespace StgSharp.Timing
|
|||||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||||
{
|
{
|
||||||
if (cur - 1 == 0) {
|
if (cur - 1 == 0) {
|
||||||
_event.Reset();
|
ResetEvent.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -138,7 +138,7 @@ namespace StgSharp.Timing
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Block the current thread until the event is signaled or cancellation requested.
|
// 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)
|
// consume one pending span (same logic as non-cancellable variant)
|
||||||
while (true)
|
while (true)
|
||||||
@@ -152,7 +152,7 @@ namespace StgSharp.Timing
|
|||||||
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
|
||||||
{
|
{
|
||||||
if (cur - 1 == 0) {
|
if (cur - 1 == 0) {
|
||||||
_event.Reset();
|
ResetEvent.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -203,7 +203,7 @@ namespace StgSharp.Timing
|
|||||||
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
|
if (Interlocked.CompareExchange(ref _pendingSpans, toIssue, 0) == 0)
|
||||||
{
|
{
|
||||||
// Open the gate for all current waiters (up to `_size`).
|
// Open the gate for all current waiters (up to `_size`).
|
||||||
_event.Set();
|
ResetEvent.Set();
|
||||||
_refreshHandler?.Invoke(this, EventArgs.Empty);
|
_refreshHandler?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace StgSharp.Timing
|
|||||||
}
|
}
|
||||||
TokenID = id;
|
TokenID = id;
|
||||||
_size = maxWaitCount > 0 ? maxWaitCount : 1;
|
_size = maxWaitCount > 0 ? maxWaitCount : 1;
|
||||||
_event = new ManualResetEventSlim(initialState:false, spinCount:0);
|
ResetEvent = new ManualResetEventSlim(initialState:false, spinCount:0);
|
||||||
_pendingSpans = 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(
|
public RegexAstNode(
|
||||||
Token<RegexElementLabel> source
|
Token<RegexElementLabel> source
|
||||||
)
|
)
|
||||||
: this(RegexCommonPayload.FromToken(source))
|
: this(RegexCommonPayload.FromToken(source)) { }
|
||||||
{ }
|
|
||||||
|
|
||||||
public RegexAstNode(
|
public RegexAstNode(
|
||||||
RegexAstPayload payload
|
RegexAstPayload payload
|
||||||
@@ -32,6 +31,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
public int EnumState { get; set; }
|
public int EnumState { get; set; }
|
||||||
|
|
||||||
|
public ISyntaxPayload<RegexElementLabel> Source => Payload;
|
||||||
|
|
||||||
public long NodeFlag => (long)Label;
|
public long NodeFlag => (long)Label;
|
||||||
|
|
||||||
public static RegexAstNode Empty { get; }
|
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 EqualityTypeConvert => Label;
|
||||||
|
|
||||||
public RegexElementLabel Label => Payload.Flag;
|
public RegexElementLabel Label => Payload.Flag;
|
||||||
@@ -80,13 +83,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
_ => Payload.Source
|
_ => Payload.Source
|
||||||
};
|
};
|
||||||
|
|
||||||
public RegexAstPayload Payload { get; private protected set; }
|
public string SourceCode => Source.Source;
|
||||||
|
|
||||||
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 void AppendNode(
|
public void AppendNode(
|
||||||
RegexAstNode nextToken
|
RegexAstNode nextToken
|
||||||
@@ -106,7 +103,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
right &= RegexElementLabel.OPERATOR;
|
right &= RegexElementLabel.OPERATOR;
|
||||||
int v_left = (int)left, v_right = (int)right;
|
int v_left = (int)left, v_right = (int)right;
|
||||||
if (v_left == 0 || v_right == 0) {
|
if (v_left == 0 || v_right == 0) {
|
||||||
return (false,0);
|
return (false, 0);
|
||||||
}
|
}
|
||||||
return (true,v_left - v_right);
|
return (true,v_left - v_right);
|
||||||
}
|
}
|
||||||
@@ -148,6 +145,14 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
return new EmptyRegexAstNode();
|
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(
|
internal void RemoveAndAsChild(
|
||||||
bool useLeft
|
bool useLeft
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
category:"NGRA.TextRegex",
|
category:"NGRA.TextRegex",
|
||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
|
|
||||||
private static readonly DiagnosticDescriptor CompileFailDiag = new(id:"NGRA005",
|
private static readonly DiagnosticDescriptor CompileFailDiag = new(id:"NGRA006",
|
||||||
title:"Unexpected exception when compiling",
|
title:"Regular expression compilation failed",
|
||||||
messageFormat:"Unexpected exception handles when compiling expression \"{0}\":{1}",
|
messageFormat:"Failed to compile expression \"{0}\": {1}",
|
||||||
defaultSeverity:DiagnosticSeverity.Error,
|
defaultSeverity:DiagnosticSeverity.Error,
|
||||||
category:"NGRA.TextRegex",
|
category:"NGRA.TextRegex",
|
||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
@@ -39,8 +39,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
|
|
||||||
private static readonly DiagnosticDescriptor IncorrectReturnTypeDiag = new(id:"NGRA003",
|
private static readonly DiagnosticDescriptor IncorrectReturnTypeDiag = new(id:"NGRA003",
|
||||||
title:"Text regex binding must be partial",
|
title:$"Returning type must be {typeof(TextRegex).FullName}",
|
||||||
messageFormat:"Method {0} must be partial to receive generated code",
|
messageFormat:$"Method {{0}} must return {typeof(TextRegex).FullName}",
|
||||||
defaultSeverity:DiagnosticSeverity.Error,
|
defaultSeverity:DiagnosticSeverity.Error,
|
||||||
category:"NGRA.TextRegex",
|
category:"NGRA.TextRegex",
|
||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
@@ -53,8 +53,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
|
|
||||||
private static readonly DiagnosticDescriptor NotPartialDiag = new(id:"NGRA001",
|
private static readonly DiagnosticDescriptor NotPartialDiag = new(id:"NGRA001",
|
||||||
title:$"Returning type must be {typeof(TextRegex).FullName}",
|
title:"Text regex binding must be partial",
|
||||||
messageFormat:$"Method {{0}} must return {typeof(TextRegex).FullName}",
|
messageFormat:"Method {0} must be partial to receive generated code",
|
||||||
defaultSeverity:DiagnosticSeverity.Error,
|
defaultSeverity:DiagnosticSeverity.Error,
|
||||||
category:"NGRA.TextRegex",
|
category:"NGRA.TextRegex",
|
||||||
isEnabledByDefault:true);
|
isEnabledByDefault:true);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
RegexAstNode ast,
|
RegexAstNode ast,
|
||||||
in SequenceEmitter<string> sc,
|
in SequenceEmitter<string> sc,
|
||||||
in List<SequenceEmitter<string>> func_list,
|
in List<SequenceEmitter<string>> func_list,
|
||||||
|
in SourceGenContext context,
|
||||||
bool is_find = false
|
bool is_find = false
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -38,16 +39,16 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case RegexElementLabel.CONCAT:
|
case RegexElementLabel.CONCAT:
|
||||||
GenerateConcat(root, sc, func_list);
|
GenerateConcat(root, sc, func_list, context);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.COUNT:
|
case RegexElementLabel.COUNT:
|
||||||
GenerateCount(root, sc, func_list);
|
GenerateCount(root, sc, func_list, context);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.GROUP_BEGIN:
|
case RegexElementLabel.GROUP_BEGIN:
|
||||||
GenerateGroup(root, sc, func_list);
|
GenerateGroup(root, sc, func_list, context);
|
||||||
break;
|
break;
|
||||||
case RegexElementLabel.ALT:
|
case RegexElementLabel.ALT:
|
||||||
GenerateAlt(root, sc, func_list);
|
GenerateAlt(root, sc, func_list, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@@ -67,6 +68,20 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
#region supporting method
|
#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(
|
private static bool IsNamedFind(
|
||||||
RegexAstNode node
|
RegexAstNode node
|
||||||
)
|
)
|
||||||
@@ -99,32 +114,48 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
private static void GenerateAlt(
|
private static void GenerateAlt(
|
||||||
RegexAstNode node,
|
RegexAstNode node,
|
||||||
[NotNull] in SequenceEmitter<string> sc,
|
[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 current = node.Right;
|
||||||
RegexAstNode right = node.Right;
|
string exit_label = context.RequestExitAlt();
|
||||||
|
|
||||||
RegexAstNode current = left;
|
|
||||||
|
|
||||||
while (RegexAstNode.IsNullOrEmpty(current))
|
while (RegexAstNode.IsNullOrEmpty(current))
|
||||||
{
|
{
|
||||||
GenerateMethodSource(current, sc, func_list);
|
RegexElementLabel op = current.Label & RegexElementLabel.SEQUENCE;
|
||||||
current = current.Next;
|
if (op != 0)
|
||||||
}
|
{
|
||||||
current = right;
|
string code = current.SourceCode;
|
||||||
|
switch (op)
|
||||||
while (RegexAstNode.IsNullOrEmpty(current))
|
{
|
||||||
{
|
case RegexElementLabel.UNIT:
|
||||||
GenerateMethodSource(current, sc, func_list);
|
_ = sc.AppendLine(@$"if({context.RemainSpan}[0] == {FormatCharLiteral(code[0])})")
|
||||||
current = current.Next;
|
.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(
|
private static void GenerateGroup(
|
||||||
RegexAstNode node,
|
RegexAstNode node,
|
||||||
[NotNull] in SequenceEmitter<string> sc,
|
[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 left = node.Left;
|
||||||
@@ -134,10 +165,10 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
if (left is not null)
|
if (left is not null)
|
||||||
{
|
{
|
||||||
GenerateMethodSource(left, sc, func_list);
|
GenerateMethodSource(left, sc, func_list, context);
|
||||||
} else
|
} else
|
||||||
if (right is not null) {
|
if (right is not null) {
|
||||||
GenerateMethodSource(right, sc, func_list);
|
GenerateMethodSource(right, sc, func_list, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
string group_name = config.Name;
|
string group_name = config.Name;
|
||||||
@@ -147,7 +178,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
private static void GenerateConcat(
|
private static void GenerateConcat(
|
||||||
RegexAstNode node,
|
RegexAstNode node,
|
||||||
[NotNull] in SequenceEmitter<string> sc,
|
[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 left = node.Left;
|
||||||
@@ -162,40 +194,41 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
right is not null)
|
right is not null)
|
||||||
{
|
{
|
||||||
// TODO use find
|
// TODO use find
|
||||||
GenerateMethodSource(right, sc, func_list, true);
|
GenerateMethodSource(right, sc, func_list, context, true);
|
||||||
} else if (IsNamedFind(node))
|
} else if (IsNamedFind(node))
|
||||||
{
|
{
|
||||||
if (right is not null)
|
if (right is not null)
|
||||||
{
|
{
|
||||||
// TODO named find
|
// TODO named find
|
||||||
GenerateMethodSource(right, sc, func_list, true);
|
GenerateMethodSource(right, sc, func_list, context, true);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// TODO pack rest of string to group
|
// TODO pack rest of string to group
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
GenerateMethodSource(left, sc, func_list);
|
GenerateMethodSource(left, sc, func_list, context);
|
||||||
if (right is not null) {
|
if (right is not null) {
|
||||||
GenerateMethodSource(right, sc, func_list);
|
GenerateMethodSource(right, sc, func_list, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (right is not null) {
|
if (right is not null) {
|
||||||
GenerateMethodSource(right, sc, func_list);
|
GenerateMethodSource(right, sc, func_list, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GenerateCount(
|
private static void GenerateCount(
|
||||||
RegexAstNode node,
|
RegexAstNode node,
|
||||||
[NotNull] in SequenceEmitter<string> sc,
|
[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;
|
RegexAstNode value = node.Left ?? node.Right;
|
||||||
if ((value.Label & RegexElementLabel.SEQUENCE) == 0)
|
if ((value.Label & RegexElementLabel.SEQUENCE) == 0)
|
||||||
{
|
{
|
||||||
GenerateMethodSource(value, sc, func_list);
|
GenerateMethodSource(value, sc, func_list, context);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
RegexCountPayload payload = value.PayloadAs<RegexCountPayload>();
|
RegexCountPayload payload = value.PayloadAs<RegexCountPayload>();
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using StgSharp.PipeLine;
|
||||||
using StgSharp.RegularAnalysis.Abstraction;
|
using StgSharp.RegularAnalysis.Abstraction;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
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
|
/// 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.
|
/// always declare its own without colliding with an enclosing one.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private sealed class SourceGenContext
|
internal sealed class SourceGenContext
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
public const string __input_span = "input";
|
||||||
/// Name of the match method parameter holding the input sequence.
|
|
||||||
/// </summary>
|
|
||||||
public const string _inputSpan = "text";
|
|
||||||
|
|
||||||
/// <summary>
|
public const string _remain_span = "__remain";
|
||||||
/// Result of the instruction emitted immediately before the current one.
|
|
||||||
/// </summary>
|
|
||||||
public const string _stepResult = "__ok";
|
|
||||||
|
|
||||||
private readonly Dictionary<string, int> _groupIndex = [];
|
private int _exit_alt_count;
|
||||||
private readonly HashSet<int> _observedLines = [];
|
|
||||||
|
|
||||||
private int _anonymousGroup;
|
public string RemainSpan { get; } = _remain_span;
|
||||||
|
|
||||||
private int _count_idx = 0;
|
public string FirstOfRemain { get; } = $@"{_remain_span}[0]";
|
||||||
private int _scratchIndex;
|
|
||||||
private readonly SortedSet<int> _lineSlots = [];
|
|
||||||
private readonly SortedSet<int> _regionSlots = [];
|
|
||||||
|
|
||||||
public string RemainingSpan { get; } = "__remaining_span";
|
public string InputString { get; } = __input_span;
|
||||||
|
|
||||||
public string InputSpan { get; } = _inputSpan;
|
public SequenceEmitter<string> DeclareGlobalFields()
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
return [
|
SequenceEmitter<string> sc = new();
|
||||||
"do",
|
|
||||||
"{"
|
return sc.AppendLine($@"{ROS_char} {RemainSpan};");
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] CloseRegion()
|
public string RequestExitAlt()
|
||||||
{
|
{
|
||||||
return [
|
string code = $@"_exit_alt_{_exit_alt_count}";
|
||||||
"} while(true);"
|
_exit_alt_count++;
|
||||||
];
|
return code;
|
||||||
}
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
SequenceEmitter<string> ce = new();
|
SequenceEmitter<string> ce = new();
|
||||||
SourceRecord gen_source = binding.Source;
|
SourceRecord gen_source = binding.Source;
|
||||||
TypeSource type = binding.Source.ContainingType;
|
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}.");
|
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $"{gen_source.NamespaceName}.");
|
||||||
_ = ce.AppendLine($@"// <auto-generated/>")
|
_ = ce.AppendLine($@"// <auto-generated/>")
|
||||||
@@ -75,15 +77,12 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
} while (type is not null);
|
} while (type is not null);
|
||||||
|
|
||||||
static_modifier = gen_source.IsMethodStatic ? "static" : string.Empty;
|
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("{")
|
.AppendLine("{")
|
||||||
/**/ //TODO return single instance of the TextRegex object
|
.Append(method_body)
|
||||||
.AppendLine("}");
|
.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("}");
|
_ = ce.AppendLine("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,10 +103,6 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $@"{binding.Source.MethodName}.g.cs");
|
_ = file_name_sb.Append(CultureInfo.InvariantCulture, $@"{binding.Source.MethodName}.g.cs");
|
||||||
string file_name = file_name_sb.ToString();
|
string file_name = file_name_sb.ToString();
|
||||||
context.AddSource(file_name, code_source.ToString());
|
context.AddSource(file_name, code_source.ToString());
|
||||||
|
|
||||||
foreach (Diagnostic diag in binding.RegexDiagnostic) {
|
|
||||||
context.ReportDiagnostic(diag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ToKeyword(
|
private static string ToKeyword(
|
||||||
|
|||||||
@@ -76,12 +76,17 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
private TextRegexBinding(
|
private TextRegexBinding(
|
||||||
List<Diagnostic> diagnostics,
|
List<Diagnostic> diagnostics,
|
||||||
SequenceEmitter<string> generatedSource,
|
SequenceEmitter<string> generatedSource,
|
||||||
SourceRecord source
|
SourceRecord source,
|
||||||
|
SourceGenContext sourceContext,
|
||||||
|
TextRegexSource? analyzedSource
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_diagnostics = diagnostics ?? [];
|
_diagnostics = diagnostics ?? [];
|
||||||
GeneratedSource = generatedSource ?? new();
|
GeneratedSource = generatedSource ?? new();
|
||||||
Source = source;
|
Source = source;
|
||||||
|
AnalyzedSource = analyzedSource;
|
||||||
|
IsValid = !_diagnostics.Exists(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error);
|
||||||
|
SourceContext = sourceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextRegexBinding()
|
public TextRegexBinding()
|
||||||
@@ -89,11 +94,15 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
_diagnostics = [];
|
_diagnostics = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextRegexSource? AnalyzedSource { get; set; }
|
||||||
|
|
||||||
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 SequenceEmitter<string> GeneratedSource { get; set; } = new();
|
||||||
|
|
||||||
|
public SourceGenContext SourceContext { get; set; } = null!;
|
||||||
|
|
||||||
public SourceRecord Source { get; set; }
|
public SourceRecord Source { get; set; }
|
||||||
|
|
||||||
@@ -121,7 +130,8 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
SequenceEmitter<string> code = GeneratedSource.Clone();
|
SequenceEmitter<string> code = GeneratedSource.Clone();
|
||||||
List<Diagnostic> diag = new(_diagnostics.Count);
|
List<Diagnostic> diag = new(_diagnostics.Count);
|
||||||
diag.AddRange(this._diagnostics);
|
diag.AddRange(this._diagnostics);
|
||||||
TextRegexBinding binding = new(diag, code, Source with { });
|
TextRegexBinding binding = new(diag, code, Source with { }, SourceContext,
|
||||||
|
AnalyzedSource);
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,8 @@ 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 StgSharp.RegularAnalysis.Abstraction;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Security.AccessControl;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace StgSharp.RegularAnalysis.Text
|
namespace StgSharp.RegularAnalysis.Text
|
||||||
{
|
{
|
||||||
@@ -30,21 +24,54 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
IncrementalGeneratorInitializationContext context
|
IncrementalGeneratorInitializationContext context
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IncrementalValuesProvider<TextRegexBinding> provider = context.SyntaxProvider
|
IncrementalValuesProvider<TextRegexBinding> candidates =
|
||||||
.ForAttributeWithMetadataName<TextRegexBinding>(
|
|
||||||
|
//
|
||||||
|
context.SyntaxProvider
|
||||||
|
.ForAttributeWithMetadataName<TextRegexBinding>(
|
||||||
TextRegexTypeName,
|
TextRegexTypeName,
|
||||||
static(
|
static(
|
||||||
_,
|
_,
|
||||||
_
|
_
|
||||||
) => true, BindTextRegexCandidate);
|
) => 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(
|
private static TextRegexBinding BindTextRegexCandidate(
|
||||||
@@ -71,7 +98,7 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
|
|
||||||
TextRegexBinding bind = new();
|
TextRegexBinding bind = new();
|
||||||
if (!method_decl.Modifiers.Any(SyntaxKind.PartialKeyword)) {
|
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) {
|
if (method_decl.ParameterList.Parameters.Count != 0) {
|
||||||
bind.AddDiagnostic(NotParameterLessDiag, method_decl.GetLocation(), method_decl.GetText());
|
bind.AddDiagnostic(NotParameterLessDiag, method_decl.GetLocation(), method_decl.GetText());
|
||||||
@@ -97,9 +124,9 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
while (contain is not null)
|
while (contain is not null)
|
||||||
{
|
{
|
||||||
if (contain.IsRecord) {
|
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(),
|
bind.AddDiagnostic(IncorrectContainingTypeDiag, method_decl.GetLocation(),
|
||||||
method_symbol.Name, contain.TypeKind);
|
method_symbol.Name, contain.TypeKind);
|
||||||
}
|
}
|
||||||
@@ -133,23 +160,28 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static TextRegexBinding CompileRegexMethodSource(
|
private static TextRegexBinding CompileRegexMethodSource(
|
||||||
TextRegexBinding source,
|
TextRegexBinding binding,
|
||||||
CancellationToken ct
|
CancellationToken cancellationToken
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ct.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
TextRegexBinding result = source.Clone();
|
|
||||||
string pattern = result.Source.Pattern;
|
TextRegexBinding result = binding.Clone();
|
||||||
TextRegexSource analyze_result = RegexAnalyzer.Analyze(pattern);
|
RegexAstNode root = result.AnalyzedSource!.Ast.Root;
|
||||||
if (analyze_result.AnalyseException is not null)
|
SourceGenContext generationContext = new();
|
||||||
{
|
SequenceEmitter<string> methodSource = new();
|
||||||
result.AddDiagnostic(CompileFailDiag, null, analyze_result.AnalyseException.Message);
|
List<SequenceEmitter<string>> localFunctions = [];
|
||||||
} else
|
|
||||||
{
|
GenerateMethodSource(root, methodSource, localFunctions, generationContext);
|
||||||
SequenceEmitter<string> method_source = /**/ new()/*GenerateSource(analyze_result)/**/;
|
foreach (SequenceEmitter<string> localFunction in localFunctions) {
|
||||||
result.GeneratedSource = method_source;
|
_ = methodSource.AppendLine()
|
||||||
|
.AppendLine()
|
||||||
|
.AppendLine("// local method for matching")
|
||||||
|
.Append(localFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.SourceContext = generationContext;
|
||||||
|
result.GeneratedSource = methodSource;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,19 +190,18 @@ namespace StgSharp.RegularAnalysis.Text
|
|||||||
IncrementalValuesProvider<TextRegexBinding> bindings
|
IncrementalValuesProvider<TextRegexBinding> bindings
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IncrementalValuesProvider<Diagnostic> error_path = bindings.Where(static binding => !binding.IsValid)
|
IncrementalValuesProvider<Diagnostic> errorPath =
|
||||||
.SelectMany(static(
|
bindings.Where(static binding => !binding.IsValid)
|
||||||
binding,
|
.SelectMany(static(
|
||||||
_
|
binding,
|
||||||
) => binding.RegexDiagnostic);
|
_
|
||||||
IncrementalValuesProvider<TextRegexBinding> correct_path = bindings.Where(static binding => binding.IsValid);
|
) => binding.RegexDiagnostic);
|
||||||
|
context.RegisterSourceOutput(errorPath, static(
|
||||||
|
productionContext,
|
||||||
|
diagnostic
|
||||||
|
) => productionContext.ReportDiagnostic(diagnostic));
|
||||||
|
|
||||||
context.RegisterSourceOutput(error_path, (
|
return bindings.Where(static binding => binding.IsValid);
|
||||||
c,
|
|
||||||
diag
|
|
||||||
) => c.ReportDiagnostic(diag));
|
|
||||||
|
|
||||||
return correct_path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user