From 5570c6f338c605f151ab9da58dd3a64ce5a39304 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Wed, 17 Jun 2026 18:32:55 +0800 Subject: [PATCH] add COUNT and CONCAT ir emitter --- .../TextRegex/RegexElementLabel.cs | 1 + StgSharp.RegularAnalysis/TextRegex/RegexIR.cs | 115 ++-- .../TextRegex/RegexIRGenerator.cs | 168 ++++++ .../TextRegex/RegexInterpreter.IRGenerate.cs | 202 +++++-- future/ngra-regex-ir.md | 2 + future/sample.cs | 501 ------------------ 6 files changed, 409 insertions(+), 580 deletions(-) create mode 100644 StgSharp.RegularAnalysis/TextRegex/RegexIRGenerator.cs delete mode 100644 future/sample.cs diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs b/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs index 67ee3515..dec57691 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs @@ -49,6 +49,7 @@ namespace StgSharp.RegularAnalysis.TextRegex SEQUENCE = UNIT | UNIT_SPAN | UNIT_SET, SINGLE = UNIT | UNIT_SET, OPERATOR = COUNT | ALT | CONCAT, + VAST_OPERATOR = OPERATOR | GROUP_BEGIN, ATOM_BEGIN = SEQUENCE | GROUP_BEGIN , ATOM_END = SEQUENCE | GROUP_END | COUNT, diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs b/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs index bd10f273..e1aa0871 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; @@ -36,12 +37,16 @@ namespace StgSharp.RegularAnalysis.TextRegex internal enum RegexIRCommand { - Search = 0, - Jump = 1, - Condition = 2, - Count = 3, - Try = 4, - Pop = 5, + Search_Seq = 0, + Search_Set = 1, + SearchChar = 2, + Jump = 3, + Condition = 4, + Try = 5, + Pop = 6, + Count = 7, + Count_Seq = 8, + Count_Set = 9 } @@ -50,28 +55,6 @@ namespace StgSharp.RegularAnalysis.TextRegex internal abstract RegexIRCommand Command { get; } - public static RegexIR Condition( - int success, - int fail - ) - { - return new RegexConditionIR(success, fail); - } - - public static RegexIR PopLevel( - bool isSuccess - ) - { - return new RegexPopIR(isSuccess); - } - - public static RegexIR Try( - int count - ) - { - return new RegexTryIR(count); - } - internal abstract void Execute(); } @@ -95,14 +78,14 @@ namespace StgSharp.RegularAnalysis.TextRegex /// /// Searches for a certain string pattern or charset from current position. /// - internal sealed class RegexSearchStringIR( + internal sealed class RegexSearchSeqIR( string pattern ) : RegexIR { public string Pattern { get; } = pattern; - internal override RegexIRCommand Command => RegexIRCommand.Search; + internal override RegexIRCommand Command => RegexIRCommand.Search_Seq; internal override void Execute() { @@ -111,30 +94,14 @@ namespace StgSharp.RegularAnalysis.TextRegex } - internal sealed class RegexSearchCharsetIR( + internal sealed class RegexMatchSetIR( string charset ) : RegexIR { public string Charset { get; } = charset; - internal override RegexIRCommand Command => RegexIRCommand.Search; - - internal override void Execute() - { - throw new NotImplementedException(); - } - - } - - internal sealed class RegexSearchCharSetIR( - string c - ) : RegexIR - { - - public string Target { get; } = c; - - internal override RegexIRCommand Command => RegexIRCommand.Search; + internal override RegexIRCommand Command => RegexIRCommand.Search_Set; internal override void Execute() { @@ -200,7 +167,57 @@ namespace StgSharp.RegularAnalysis.TextRegex public int OperationIndex { get; } = operation; - internal override RegexIRCommand Command => throw new NotImplementedException(); + internal override RegexIRCommand Command { get; } = RegexIRCommand.Count; + + internal override void Execute() + { + throw new NotImplementedException(); + } + + } + + internal sealed class RegexCountSeqIR( + int min, + int max, + bool isGreedy, + string seq + ) : RegexIR + { + + public bool IsGreedy { get; } = isGreedy; + + public int Min { get; } = min; + + public int Max { get; } = max; + + public string Sequence { get; } = seq; + + internal override RegexIRCommand Command { get; } = RegexIRCommand.Count_Seq; + + internal override void Execute() + { + throw new NotImplementedException(); + } + + } + + internal sealed class RegexCountSetIR( + int min, + int max, + bool isGreedy, + string set + ) : RegexIR + { + + public bool IsGreedy { get; } = isGreedy; + + public int Min { get; } = min; + + public int Max { get; } = max; + + public string CharSet { get; } = set; + + internal override RegexIRCommand Command => RegexIRCommand.Count_Set; internal override void Execute() { diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexIRGenerator.cs b/StgSharp.RegularAnalysis/TextRegex/RegexIRGenerator.cs new file mode 100644 index 00000000..e4268348 --- /dev/null +++ b/StgSharp.RegularAnalysis/TextRegex/RegexIRGenerator.cs @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="RegexIRGenerator" +// Project: StgSharp +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace StgSharp.RegularAnalysis.TextRegex +{ + internal class RegexIRGenerator + { + + private Node _head = new(); + private Node _tail; + + public RegexIRGenerator() + { + _tail = _head; + } + + public int Count { get; private set; } + + public void EmitCondition( + int success, + int fail + ) + { + _tail.Add(new RegexConditionIR(success, fail)); + Count++; + } + + public void EmitCount( + int min, + int max, + bool isGreedy, + int operation + ) + { + _tail.Add(new RegexCountIR(min, max, isGreedy, operation)); + Count++; + } + + public void EmitCountSeq( + int min, + int max, + bool isGreedy, + string seq + ) + { + _tail.Add(new RegexCountSeqIR(min, max, isGreedy, seq)); + Count++; + } + + public void EmitCountSet( + int min, + int max, + bool isGreedy, + string set + ) + { + _tail.Add(new RegexCountSetIR(min, max, isGreedy, set)); + Count++; + } + + public void EmitIRStream( + RegexIRGenerator stream + ) + { + if (stream is null || stream.Count == 0) { + return; + } + _tail.Next = stream._head; + _tail = stream._tail; + Count += stream.Count; + } + + public void EmitMatchSet( + string unit + ) + { + _tail.Add(new RegexMatchSetIR(unit)); + Count++; + } + + public void EmitMatchSeq( + string unit + ) + { + _tail.Add(new RegexMatchSeqIR(unit)); + Count++; + } + + public void EmitPopLevel( + bool isSuccess + ) + { + _tail.Add(new RegexPopIR(isSuccess)); + Count++; + } + + public void EmitTry( + int count + ) + { + _tail.Add(new RegexTryIR(count)); + Count++; + } + + public List ToList() + { + List result = new(Count); + Node current = _head; + while (current != null) + { + result.AddRange(current.IRList); + current = current.Next!; + } + return result; + } + + private class Node + { + + public Node? Next { get; set; } + + public int Count => IRList.Count; + + public List IRList { get; } = []; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add( + RegexIR ir + ) + { + IRList.Add(ir); + } + + } + + } +} diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs index b07bbe1a..04f06670 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs @@ -28,8 +28,12 @@ using StgSharp.RegularAnalysis.Abstraction; using System; using System.Collections.Generic; +using System.ComponentModel.Design; using System.Data; +using System.Diagnostics.Metrics; using System.Linq; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Xml; @@ -45,27 +49,68 @@ namespace StgSharp.RegularAnalysis.TextRegex AbstractSyntaxTree tree ) { - return GenerateIRRecursion(tree.Root, 0); + return GenerateIRRecursion(tree.Root, 0).ToList(); } - private List GenerateIRRecursion( - RegexAstNode node, - int depth + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EmitCount( + int curDepth, + RegexIRGenerator ir, + int min, + int max, + bool isGreedy, + RegexAstNode right ) { - if (depth > 1024) { + bool isRightOperator = !RegexAstNode.IsNullOrEmpty(right) && + (right.Source.Flag & RegexElementLabel.OPERATOR) != 0; + + if (isRightOperator) // seq to be count is complex + { + ir.EmitCount(min, max, isGreedy, 1); + RegexIRGenerator rightIR = GenerateIRRecursion(right, curDepth + 1); + ir.EmitTry(rightIR.Count); + ir.EmitIRStream(rightIR); + } else + { + RegexElementLabel count_source_mask = right.Source.Flag; + switch (count_source_mask) + { + case RegexElementLabel.UNIT: + ir.EmitCountSeq(min, max, isGreedy, right.Source.Value); + break; + case RegexElementLabel.UNIT_SET: + ir.EmitCountSet(min, max, isGreedy, right.Source.Value); + break; + case RegexElementLabel.UNIT_SPAN: + ir.EmitCountSet(min, max, isGreedy, right.Source.Value); + break; + default: + throw new InvalidOperationException($"Unexpected regex element type for count operator: {count_source_mask}"); + } + } + } + + private RegexIRGenerator GenerateIRRecursion( + RegexAstNode node, + int depth + ) + { +#pragma warning disable IDE0010 + if (depth > 1024) + { throw new OverflowException("Regex parse exceeds maximum recursion depth"); } int curDepth = depth; RegexAstNode left = node.Left; RegexAstNode right = node.Right; - List leftIR = EmptyIR; - List rightIR = EmptyIR; + RegexIRGenerator leftIR; + RegexIRGenerator rightIR; bool isLeftOperator = !RegexAstNode.IsNullOrEmpty(left) && - (left.Source.Flag & RegexElementLabel.OPERATOR) != 0; + (left.Source.Flag & RegexElementLabel.VAST_OPERATOR) != 0; bool isRightOperator = !RegexAstNode.IsNullOrEmpty(right) && - (right.Source.Flag & RegexElementLabel.OPERATOR) != 0; + (right.Source.Flag & RegexElementLabel.VAST_OPERATOR) != 0; Token token = node.Source; @@ -73,30 +118,127 @@ namespace StgSharp.RegularAnalysis.TextRegex { case RegexElementLabel.GROUP_BEGIN: string group_source = token.Value; - bool is_group = group_source.Length == 1; - string group_name = group_source.StartsWith(@"(?<", StringComparison.Ordinal) ? - group_source[3..^2] : - string.Empty; - List groupIR = []; + // process group type + bool is_group = group_source.Length == 1; // simple group: (pattern) + string group_name = + group_source.StartsWith(@"(?<", StringComparison.Ordinal) ? + group_source[3..^2] : /* named group (?) */ + string.Empty; /* unnamed group (?:) */ + + RegexIRGenerator groupIR = new(); rightIR = GenerateIRRecursion(right, curDepth + 1); - groupIR.Add(RegexIR.Try(rightIR.Count)); - groupIR.AddRange(rightIR); - groupIR.Add(RegexIR.Condition(2, 1)); - groupIR.Add(RegexIR.PopLevel(false)); - groupIR.Add(RegexIR.PopLevel(true)); - break; - case RegexElementLabel.COUNT: - break; - case RegexElementLabel.CONCAT: - break; - case RegexElementLabel.ALT: - break; - default: - break; - } + groupIR.EmitTry(rightIR.Count); + groupIR.EmitIRStream(rightIR); + groupIR.EmitCondition(2, 1); + groupIR.EmitPopLevel(false); - return []; + // a grouping op needed here, before popping + groupIR.EmitPopLevel(true); + return groupIR; + case RegexElementLabel.COUNT: + string count_source = token.Value; + bool isGreedy = !(count_source.Length > 1 && !count_source.EndsWith('?')); + ReadOnlySpan count_source_span = count_source; + RegexIRGenerator countIR = new(); + + if (count_source_span[0] == '*') // infinite count + { + EmitCount(curDepth, countIR, 0, int.MaxValue, isGreedy, right); + } else if (count_source_span[0] == '+') // at least one + { + EmitCount(curDepth, countIR, 1, int.MaxValue, isGreedy, right); + } else if (count_source_span[0] == '?') // zero or one + { + EmitCount(curDepth, countIR, 0, 1, isGreedy, right); + } else if (count_source_span[0] == '{') // zero or one, non-greedy + { + int comma_pos = count_source_span.IndexOf(','); + if (comma_pos == -1) + { + if (int.TryParse(count_source[1..(isGreedy ? ^3 : ^2)], out int count)) + { + EmitCount(curDepth, countIR, count, count, isGreedy, right); + } else + { + throw new InvalidCastException($"Invalid count format: {count_source}"); + } + } else + { + if (int.TryParse(count_source[1..comma_pos], out int min) && + int.TryParse(count_source[(comma_pos + 1)..(isGreedy ? ^3 : ^2)], out int max)) + { + EmitCount(curDepth, countIR, min, max, isGreedy, right); + } else + { + throw new InvalidCastException($"Invalid count format: {count_source}"); + } + } + } else + { + throw new InvalidOperationException(); + } // unknown count format + + return countIR; + case RegexElementLabel.CONCAT: + RegexIRGenerator concat = new(); + + // process left node + if (RegexAstNode.IsNullOrEmpty(left)) + { + // do nothing + } else if ((left.Source.Flag & RegexElementLabel.SEQUENCE) != 0) + { + switch (left.Source.Flag) + { + case RegexElementLabel.UNIT_SPAN: + goto case RegexElementLabel.UNIT_SPAN; + case RegexElementLabel.UNIT: + concat.EmitMatchSeq(left.Source.Value); + break; + case RegexElementLabel.UNIT_SET: + concat.EmitMatchSet(left.Source.Value); + break; + default: + break; + } + } else if ((left.Source.Flag & RegexElementLabel.VAST_OPERATOR) != 0) + { + leftIR = GenerateIRRecursion(left, curDepth + 1); + concat.EmitIRStream(leftIR); + } + + // process right node + if (RegexAstNode.IsNullOrEmpty(right)) + { + // do nothing + } else if ((right.Source.Flag & RegexElementLabel.SEQUENCE) != 0) + { + switch (right.Source.Flag) + { + case RegexElementLabel.UNIT_SPAN: + goto case RegexElementLabel.UNIT; + case RegexElementLabel.UNIT: + concat.EmitMatchSeq(right.Source.Value); + break; + case RegexElementLabel.UNIT_SET: + concat.EmitMatchSet(right.Source.Value); + break; + default: + break; + } + } else if ((right.Source.Flag & RegexElementLabel.VAST_OPERATOR) != 0) + { + rightIR = GenerateIRRecursion(right, curDepth + 1); + concat.EmitIRStream(rightIR); + } + return concat; + case RegexElementLabel.ALT: + return null!; + default: + return null!; + } +#pragma warning restore IDE0010 } } diff --git a/future/ngra-regex-ir.md b/future/ngra-regex-ir.md index 7de080ee..5e563832 100644 --- a/future/ngra-regex-ir.md +++ b/future/ngra-regex-ir.md @@ -22,3 +22,5 @@ 4. 条件指令(CONDITION %cmd_true %cmd_fail),比较前一条SEARCH指令是否成功,并指定跳转到指定的位置,如果某种条件不需要跳转,请使用0进行填充 5. 跳转执行指令(TRY %begin %end),尝试idx指向的IR块,并记住执行结果数据 6. 计数指令(COUNT %min %max %is_greedy %cmd),重复执行指令cmd,直到满足参数配置的数量为止 +7. 快速序列计数指令(COUNT_SEQ %min %max %is_greedy %seq),快速搜索一个确定性序列,直到满足参数配置的数量为止 +8. 快速字符集计数指令(COUNT_SET %min %max %is_greedy %set),快速搜索一个字符集,直到满足参数配置的数量为止 \ No newline at end of file diff --git a/future/sample.cs b/future/sample.cs deleted file mode 100644 index 0250f0d5..00000000 --- a/future/sample.cs +++ /dev/null @@ -1,501 +0,0 @@ -// -#nullable enable -#pragma warning disable - -namespace StgSharp -{ - partial class TestRegexAnalyse - { - /// - /// Pattern:
- /// a*.[abc]*(abcd|a(bc)c)+(?<name>abc)
- /// Explanation:
- /// - /// ○ Match 'a' greedily any number of times.
- /// ○ Match any character other than '\n'.
- /// ○ Match a character in the set [a-c] greedily any number of times.
- /// ○ Loop greedily at least once.
- /// ○ 1st capture group.
- /// ○ Match 'a'.
- /// ○ Match with 2 alternative expressions.
- /// ○ Match the string "bcd".
- /// ○ Match a sequence of expressions.
- /// ○ 2nd capture group.
- /// ○ Match the string "bc".
- /// ○ Match 'c'.
- /// ○ "name" capture group.
- /// ○ Match the string "abc".
- ///
- ///
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "10.0.14.27113")] - internal static partial global::System.Text.RegularExpressions.Regex GetRegex() => global::System.Text.RegularExpressions.Generated.GetRegex_0.Instance; - } -} - -namespace System.Text.RegularExpressions.Generated -{ - using System; - using System.Buffers; - using System.CodeDom.Compiler; - using System.Collections; - using System.ComponentModel; - using System.Globalization; - using System.Runtime.CompilerServices; - using System.Text.RegularExpressions; - using System.Threading; - - /// Custom -derived type for the GetRegex method. - [GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "10.0.14.27113")] - file sealed class GetRegex_0 : Regex - { - /// Cached, thread-safe singleton instance. - internal static readonly GetRegex_0 Instance = new(); - - /// Initializes the instance. - private GetRegex_0() - { - base.pattern = "a*.[abc]*(abcd|a(bc)c)+(?abc)"; - base.roptions = RegexOptions.None; - ValidateMatchTimeout(Utilities.s_defaultTimeout); - base.internalMatchTimeout = Utilities.s_defaultTimeout; - base.factory = new RunnerFactory(); - base.CapNames = new Hashtable { { "0", 0 } , { "1", 1 } , { "2", 2 } , { "name", 3 } }; - base.capslist = new string[] {"0", "1", "2", "name" }; - base.capsize = 4; - } - - /// Provides a factory for creating instances to be used by methods on . - private sealed class RunnerFactory : RegexRunnerFactory - { - /// Creates an instance of a used by methods on . - protected override RegexRunner CreateInstance() => new Runner(); - - /// Provides the runner that contains the custom logic implementing the specified regular expression. - private sealed class Runner : RegexRunner - { - /// Scan the starting from base.runtextstart for the next match. - /// The text being scanned by the regular expression. - protected override void Scan(ReadOnlySpan inputSpan) - { - // Search until we can't find a valid starting position, we find a match, or we reach the end of the input. - while (TryFindNextPossibleStartingPosition(inputSpan) && - !TryMatchAtCurrentPosition(inputSpan) && - base.runtextpos != inputSpan.Length) - { - base.runtextpos++; - if (Utilities.s_hasTimeout) - { - base.CheckTimeout(); - } - } - } - - /// Search starting from base.runtextpos for the next location a match could possibly start. - /// The text being scanned by the regular expression. - /// true if a possible match was found; false if no more matches are possible. - private bool TryFindNextPossibleStartingPosition(ReadOnlySpan inputSpan) - { - int pos = base.runtextpos; - - // Any possible match is at least 8 characters. - if (pos <= inputSpan.Length - 8) - { - // The pattern begins with a character in the set [^\n]. - // Find the next occurrence. If it can't be found, there's no match. - ReadOnlySpan span = inputSpan.Slice(pos); - for (int i = 0; i < span.Length; i++) - { - if ((span[i] != '\n')) - { - base.runtextpos = pos + i; - return true; - } - } - } - - // No match found. - base.runtextpos = inputSpan.Length; - return false; - } - - /// Determine whether at base.runtextpos is a match for the regular expression. - /// The text being scanned by the regular expression. - /// true if the regular expression matches at the current position; otherwise, false. - private bool TryMatchAtCurrentPosition(ReadOnlySpan inputSpan) - { - int pos = base.runtextpos; - int matchStart = pos; - int capture_starting_pos2 = 0; - int charloop_capture_pos = 0; - int charloop_capture_pos1 = 0; - int charloop_starting_pos = 0, charloop_ending_pos = 0; - int charloop_starting_pos1 = 0, charloop_ending_pos1 = 0; - int loop_iteration = 0; - int stackpos = 0; - ReadOnlySpan slice = inputSpan.Slice(pos); - - // Match 'a' greedily any number of times. - //{ - charloop_starting_pos = pos; - - int iteration = slice.IndexOfAnyExcept('a'); - if (iteration < 0) - { - iteration = slice.Length; - } - - slice = slice.Slice(iteration); - pos += iteration; - - charloop_ending_pos = pos; - goto CharLoopEnd; - - CharLoopBacktrack: - UncaptureUntil(charloop_capture_pos); - - if (Utilities.s_hasTimeout) - { - base.CheckTimeout(); - } - - if (charloop_starting_pos >= charloop_ending_pos || - (charloop_ending_pos = inputSpan.Slice(charloop_starting_pos, charloop_ending_pos - charloop_starting_pos).LastIndexOfAnyExcept('\n')) < 0) - { - UncaptureUntil(0); - return false; // The input didn't match. - } - charloop_ending_pos += charloop_starting_pos; - pos = charloop_ending_pos; - slice = inputSpan.Slice(pos); - - CharLoopEnd: - charloop_capture_pos = base.Crawlpos(); - //} - - // Advance the next matching position. - if (base.runtextpos < pos) - { - base.runtextpos = pos; - } - - // Match any character other than '\n'. - if (slice.IsEmpty || slice[0] == '\n') - { - goto CharLoopBacktrack; - } - - // Match a character in the set [a-c] greedily any number of times. - //{ - pos++; - slice = inputSpan.Slice(pos); - charloop_starting_pos1 = pos; - - int iteration1 = slice.IndexOfAnyExceptInRange('a', 'c'); - if (iteration1 < 0) - { - iteration1 = slice.Length; - } - - slice = slice.Slice(iteration1); - pos += iteration1; - - charloop_ending_pos1 = pos; - goto CharLoopEnd1; - - CharLoopBacktrack1: - UncaptureUntil(charloop_capture_pos1); - - if (Utilities.s_hasTimeout) - { - base.CheckTimeout(); - } - - if (charloop_starting_pos1 >= charloop_ending_pos1 || - (charloop_ending_pos1 = inputSpan.Slice(charloop_starting_pos1, charloop_ending_pos1 - charloop_starting_pos1).LastIndexOf('a')) < 0) - { - goto CharLoopBacktrack; - } - charloop_ending_pos1 += charloop_starting_pos1; - pos = charloop_ending_pos1; - slice = inputSpan.Slice(pos); - - CharLoopEnd1: - charloop_capture_pos1 = base.Crawlpos(); - //} - - // Loop greedily at least once. - //{ - loop_iteration = 0; - - LoopBody: - Utilities.StackPush(ref base.runstack!, ref stackpos, base.Crawlpos(), pos); - - loop_iteration++; - - // 1st capture group. - //{ - int capture_starting_pos = pos; - - // Match 'a'. - if (slice.IsEmpty || slice[0] != 'a') - { - goto LoopIterationNoMatch; - } - - // Match with 2 alternative expressions. - //{ - int alternation_starting_pos = pos; - int alternation_starting_capturepos = base.Crawlpos(); - - // Branch 0 - //{ - // Match the string "bcd". - if (!slice.Slice(1).StartsWith("bcd")) - { - goto AlternationBranch; - } - - Utilities.StackPush(ref base.runstack!, ref stackpos, 0, alternation_starting_pos, alternation_starting_capturepos); - pos += 4; - slice = inputSpan.Slice(pos); - goto AlternationMatch; - - AlternationBranch: - pos = alternation_starting_pos; - slice = inputSpan.Slice(pos); - UncaptureUntil(alternation_starting_capturepos); - //} - - // Branch 1 - //{ - // 2nd capture group. - { - pos++; - slice = inputSpan.Slice(pos); - int capture_starting_pos1 = pos; - - // Match the string "bc". - if (!slice.StartsWith("bc")) - { - goto LoopIterationNoMatch; - } - - pos += 2; - slice = inputSpan.Slice(pos); - base.Capture(2, capture_starting_pos1, pos); - } - - // Match 'c'. - if (slice.IsEmpty || slice[0] != 'c') - { - goto LoopIterationNoMatch; - } - - Utilities.StackPush(ref base.runstack!, ref stackpos, 1, alternation_starting_pos, alternation_starting_capturepos); - pos++; - slice = inputSpan.Slice(pos); - goto AlternationMatch; - //} - - AlternationBacktrack: - if (Utilities.s_hasTimeout) - { - base.CheckTimeout(); - } - - Utilities.StackPop(base.runstack!, ref stackpos, out alternation_starting_capturepos, out alternation_starting_pos); - switch (base.runstack![--stackpos]) - { - case 0: - goto AlternationBranch; - case 1: - goto LoopIterationNoMatch; - } - - AlternationMatch:; - //} - - base.Capture(1, capture_starting_pos, pos); - - Utilities.StackPush(ref base.runstack!, ref stackpos, capture_starting_pos); - goto CaptureSkipBacktrack; - - CaptureBacktrack: - capture_starting_pos = base.runstack![--stackpos]; - goto AlternationBacktrack; - - CaptureSkipBacktrack:; - //} - - - // The loop has no upper bound. Continue iterating greedily. - goto LoopBody; - - // The loop iteration failed. Put state back to the way it was before the iteration. - LoopIterationNoMatch: - if (--loop_iteration < 0) - { - // Unable to match the remainder of the expression after exhausting the loop. - goto CharLoopBacktrack1; - } - pos = base.runstack![--stackpos]; - UncaptureUntil(base.runstack![--stackpos]); - slice = inputSpan.Slice(pos); - if (loop_iteration == 0) - { - // No iterations have been matched to backtrack into. Fail the loop. - goto CharLoopBacktrack1; - } - - goto LoopEnd; - - LoopBacktrack: - if (Utilities.s_hasTimeout) - { - base.CheckTimeout(); - } - - if (loop_iteration == 0) - { - // No iterations of the loop remain to backtrack into. Fail the loop. - goto CharLoopBacktrack1; - } - goto CaptureBacktrack; - LoopEnd:; - //} - - // "name" capture group. - { - capture_starting_pos2 = pos; - - // Match the string "abc". - if (!slice.StartsWith("abc")) - { - goto LoopBacktrack; - } - - pos += 3; - slice = inputSpan.Slice(pos); - base.Capture(3, capture_starting_pos2, pos); - } - - // The input matched. - base.runtextpos = pos; - base.Capture(0, matchStart, pos); - return true; - - // Undo captures until it reaches the specified capture position. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void UncaptureUntil(int capturePosition) - { - while (base.Crawlpos() > capturePosition) - { - base.Uncapture(); - } - } - } - } - } - - } - - /// Helper methods used by generated -derived implementations. - [GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "10.0.14.27113")] - file static class Utilities - { - /// Default timeout value set in , or if none was set. - internal static readonly TimeSpan s_defaultTimeout = AppContext.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") is TimeSpan timeout ? timeout : Regex.InfiniteMatchTimeout; - - /// Whether is non-infinite. - internal static readonly bool s_hasTimeout = s_defaultTimeout != Regex.InfiniteMatchTimeout; - - /// Pops 2 values from the backtracking stack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void StackPop(int[] stack, ref int pos, out int arg0, out int arg1) - { - arg0 = stack[--pos]; - arg1 = stack[--pos]; - } - - /// Pushes 1 value onto the backtracking stack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void StackPush(ref int[] stack, ref int pos, int arg0) - { - // If there's space available for the value, store it. - int[] s = stack; - int p = pos; - if ((uint)p < (uint)s.Length) - { - s[p] = arg0; - pos++; - return; - } - - // Otherwise, resize the stack to make room and try again. - WithResize(ref stack, ref pos, arg0); - - // Resize the backtracking stack array and push 1 value onto the stack. - [MethodImpl(MethodImplOptions.NoInlining)] - static void WithResize(ref int[] stack, ref int pos, int arg0) - { - Array.Resize(ref stack, (pos + 0) * 2); - StackPush(ref stack, ref pos, arg0); - } - } - - /// Pushes 2 values onto the backtracking stack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void StackPush(ref int[] stack, ref int pos, int arg0, int arg1) - { - // If there's space available for all 2 values, store them. - int[] s = stack; - int p = pos; - if ((uint)(p + 1) < (uint)s.Length) - { - s[p] = arg0; - s[p + 1] = arg1; - pos += 2; - return; - } - - // Otherwise, resize the stack to make room and try again. - WithResize(ref stack, ref pos, arg0, arg1); - - // Resize the backtracking stack array and push 2 values onto the stack. - [MethodImpl(MethodImplOptions.NoInlining)] - static void WithResize(ref int[] stack, ref int pos, int arg0, int arg1) - { - Array.Resize(ref stack, (pos + 1) * 2); - StackPush(ref stack, ref pos, arg0, arg1); - } - } - - /// Pushes 3 values onto the backtracking stack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void StackPush(ref int[] stack, ref int pos, int arg0, int arg1, int arg2) - { - // If there's space available for all 3 values, store them. - int[] s = stack; - int p = pos; - if ((uint)(p + 2) < (uint)s.Length) - { - s[p] = arg0; - s[p + 1] = arg1; - s[p + 2] = arg2; - pos += 3; - return; - } - - // Otherwise, resize the stack to make room and try again. - WithResize(ref stack, ref pos, arg0, arg1, arg2); - - // Resize the backtracking stack array and push 3 values onto the stack. - [MethodImpl(MethodImplOptions.NoInlining)] - static void WithResize(ref int[] stack, ref int pos, int arg0, int arg1, int arg2) - { - Array.Resize(ref stack, (pos + 2) * 2); - StackPush(ref stack, ref pos, arg0, arg1, arg2); - } - } - } -}