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
|
||||
|
||||
- [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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user