diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs index e843196c..9b6de712 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs @@ -167,6 +167,15 @@ namespace StgSharp.RegularAnalysis.TextRegex } /**/ + /// + /// Replace the current node with another node, and update the parent and sibling + /// references accordingly. This is implemented by copying metadata and nearby + /// environment of the given node to the current node, and then update the nearby nodes to + /// point to the current node. + /// + /// + /// The node to replace the current node with. + /// internal void ReplaceBy( RegexAstNode node ) diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs b/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs index 78bf5a25..bd10f273 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexIR.cs @@ -38,13 +38,10 @@ namespace StgSharp.RegularAnalysis.TextRegex Search = 0, Jump = 1, - Compare = 2, - Check = 3, - Uncheck = 4, - Span = 5, - Define = 6, - Assign = 7, - Back = 8, + Condition = 2, + Count = 3, + Try = 4, + Pop = 5, } @@ -53,10 +50,48 @@ 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(); } + internal sealed class RegexPopIR( + bool isSuccess + ) : RegexIR + { + + public bool IsSuccess { get; } = isSuccess; + + internal override RegexIRCommand Command => RegexIRCommand.Pop; + + internal override void Execute() + { + throw new NotImplementedException(); + } + + } + /// /// Searches for a certain string pattern or charset from current position. /// @@ -92,12 +127,12 @@ namespace StgSharp.RegularAnalysis.TextRegex } - internal sealed class RegexSearchCharIR( - char c + internal sealed class RegexSearchCharSetIR( + string c ) : RegexIR { - public char target { get; } = c; + public string Target { get; } = c; internal override RegexIRCommand Command => RegexIRCommand.Search; @@ -130,23 +165,7 @@ namespace StgSharp.RegularAnalysis.TextRegex } - internal sealed class RegexBackIR( - int index - ) : RegexIR - { - - public int Target { get; } = index; - - internal override RegexIRCommand Command => RegexIRCommand.Back; - - internal override void Execute() - { - throw new NotImplementedException(); - } - - } - - internal sealed class RegexCompareIR( + internal sealed class RegexConditionIR( int success, int fail ) : RegexIR @@ -156,7 +175,7 @@ namespace StgSharp.RegularAnalysis.TextRegex public int FailCase { get; } = fail; - internal override RegexIRCommand Command => RegexIRCommand.Compare; + internal override RegexIRCommand Command => RegexIRCommand.Condition; internal override void Execute() { @@ -165,38 +184,23 @@ namespace StgSharp.RegularAnalysis.TextRegex } - internal sealed class RegexCheckIR : RegexIR - { - - internal override RegexIRCommand Command => RegexIRCommand.Check; - - internal override void Execute() - { - throw new NotImplementedException(); - } - - } - - internal sealed class RegexUncheckIR : RegexIR - { - - internal override RegexIRCommand Command => RegexIRCommand.Check; - - internal override void Execute() - { - throw new NotImplementedException(); - } - - } - - internal sealed class RegexSpanIR( - string name + internal sealed class RegexCountIR( + int min, + int max, + bool isGreedy, + int operation ) : RegexIR { - public string Name { get; } = name; + public bool IsGreedy { get; } = isGreedy; - internal override RegexIRCommand Command => RegexIRCommand.Span; + public int Min { get; } = min; + + public int Max { get; } = max; + + public int OperationIndex { get; } = operation; + + internal override RegexIRCommand Command => throw new NotImplementedException(); internal override void Execute() { @@ -205,33 +209,14 @@ namespace StgSharp.RegularAnalysis.TextRegex } - internal sealed class RegexDefineCommand( - string name + internal sealed class RegexTryIR( + int count ) : RegexIR { - public string Name { get; } = name; + public int CountOfCommand { get; } = count; - internal override RegexIRCommand Command => RegexIRCommand.Define; - - internal override void Execute() - { - throw new NotImplementedException(); - } - - } - - internal sealed class RegexAssignCommand( - string name, - T value - ) : RegexIR - { - - public string Name { get; } = name; - - public T Value { get; } = value; - - internal override RegexIRCommand Command => RegexIRCommand.Assign; + internal override RegexIRCommand Command => RegexIRCommand.Try; internal override void Execute() { diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs index fa5fff99..b07bbe1a 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.IRGenerate.cs @@ -28,23 +28,72 @@ using StgSharp.RegularAnalysis.Abstraction; using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace StgSharp.RegularAnalysis.TextRegex { public partial class RegexInterpreter { + private List EmptyIR { get; } = []; + internal List GenerateIR( AbstractSyntaxTree tree ) { - using TreeEnumerator enumerator = new(tree); - while (enumerator.MoveNext()) + return GenerateIRRecursion(tree.Root, 0); + } + + private List GenerateIRRecursion( + RegexAstNode node, + int depth + ) + { + 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; + + bool isLeftOperator = !RegexAstNode.IsNullOrEmpty(left) && + (left.Source.Flag & RegexElementLabel.OPERATOR) != 0; + bool isRightOperator = !RegexAstNode.IsNullOrEmpty(right) && + (right.Source.Flag & RegexElementLabel.OPERATOR) != 0; + + Token token = node.Source; + + switch (node.Source.Flag) { - if (true) { } + 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 = []; + 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; } return []; diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs index 05eb015e..6e7bbb80 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs @@ -2,8 +2,8 @@ // ----------------------------------------------------------------------- // file="RegexInterpreter.Optimize" // Project: StgSharp -// AuthorGroup: Nitload Space -// Copyright (c) Nitload Space. All rights reserved. +// 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 @@ -42,7 +42,9 @@ namespace StgSharp.RegularAnalysis.TextRegex public partial class RegexInterpreter { - private static RegexAstNode FlattenAlt(RegexAstNode root) + private static RegexAstNode FlattenAlt( + RegexAstNode root + ) { TreeEnumerator enumerator = new(root); List cases = []; @@ -67,7 +69,9 @@ namespace StgSharp.RegularAnalysis.TextRegex return cases[0]; } - private static bool MergeConcat(RegexAstNode root) + private static bool MergeConcat( + RegexAstNode root + ) { if (root.EqualityTypeConvert != RegexElementLabel.CONCAT) { return false; @@ -109,7 +113,8 @@ namespace StgSharp.RegularAnalysis.TextRegex string str = left.Value; buffer.Add(str); length += str.Length; - while (!RegexAstNode.IsNullOrEmpty(right) && right.EqualityTypeConvert == RegexElementLabel.CONCAT) + while (!RegexAstNode.IsNullOrEmpty(right) && + right.EqualityTypeConvert == RegexElementLabel.CONCAT) { left = right.Left; if (left.EqualityTypeConvert is not RegexElementLabel.UNIT_SPAN and @@ -132,7 +137,10 @@ namespace StgSharp.RegularAnalysis.TextRegex buffer.Add(right.Value); length += right.Value.Length; } - str = string.Create(length, buffer, static(span, buffer) => { + str = string.Create(length, buffer, static( + span, + buffer + ) => { int offset = 0; for (int i = 0; i < buffer.Count; i++) { @@ -140,17 +148,21 @@ namespace StgSharp.RegularAnalysis.TextRegex offset += buffer[i].Length; } }); - root.Left = new RegexAstNode(new Token(str, left.Source.Line, left.Source.Column, + root.Left = new RegexAstNode(new Token(str, left.Source.Line, + left.Source.Column, str.Length == 1 ? RegexElementLabel.UNIT : RegexElementLabel.UNIT_SPAN)); return true; } - private static void RotateConcat(RegexAstNode root) + private static void RotateConcat( + RegexAstNode root + ) { RegexAstNode left = root.Left; - while (!RegexAstNode.IsNullOrEmpty(left) && left.EqualityTypeConvert == RegexElementLabel.CONCAT) + while (!RegexAstNode.IsNullOrEmpty(left) && + left.EqualityTypeConvert == RegexElementLabel.CONCAT) { /* * root → root @@ -193,7 +205,9 @@ namespace StgSharp.RegularAnalysis.TextRegex /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static RegexAstNode MergeAlt(List cases) + private static RegexAstNode MergeAlt( + List cases + ) { UnionAlt union = new(0, cases); RegexAstNode root = union.AsNormal(); @@ -205,7 +219,10 @@ namespace StgSharp.RegularAnalysis.TextRegex private const int MaxLevel = 8; - private UnionAlt(int level, RegexAstNode node) + private UnionAlt( + int level, + RegexAstNode node + ) : base(node.Source) { Level = level; @@ -213,7 +230,8 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode right = IsNullOrEmpty(node.Right) ? new RegexAstNode( - new Token(node.Value, 0, node.Source.Column, RegexElementLabel.UNIT) + new Token(node.Value, 0, node.Source.Column, + RegexElementLabel.UNIT) ) : node.Right; Cases.Add(right); @@ -224,7 +242,10 @@ namespace StgSharp.RegularAnalysis.TextRegex UnionEnd = node; } - public UnionAlt(int level, List c) + public UnionAlt( + int level, + List c + ) : base(c[0].Source) { Cases = c; @@ -251,7 +272,8 @@ namespace StgSharp.RegularAnalysis.TextRegex { if ((Cases[i].EqualityTypeConvert & RegexElementLabel.SEQUENCE) != 0) { - RegexAstNode concat = new(new Token(string.Empty, 0, Cases[i].Source.Column, + RegexAstNode concat = new(new Token(string.Empty, 0, + Cases[i].Source.Column, RegexElementLabel.CONCAT)) { Left = Cases[i] @@ -323,13 +345,15 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode branch; if (IsNullOrEmpty(UnionEnd)) { - branch = new RegexAstNode(new Token(string.Empty, 0, Source.Column, + branch = new RegexAstNode(new Token(string.Empty, 0, + Source.Column, RegexElementLabel.ALT)); UnionBegin = branch; UnionEnd = branch; } else { - branch = new RegexAstNode(new Token(string.Empty, 0, UnionEnd.Source.Column, + branch = new RegexAstNode(new Token(string.Empty, 0, + UnionEnd.Source.Column, RegexElementLabel.ALT)); UnionEnd.Right = branch; branch.Parent = UnionEnd; @@ -346,17 +370,23 @@ namespace StgSharp.RegularAnalysis.TextRegex return UnionBegin; } - private void AddCase(RegexAstNode _case) + private void AddCase( + RegexAstNode _case + ) { RegexAstNode right = IsNullOrEmpty(_case.Right) ? new RegexAstNode( - new Token(_case.Value, 0, _case.Source.Column, RegexElementLabel.UNIT) + new Token(_case.Value, 0, _case.Source.Column, + RegexElementLabel.UNIT) ) : _case.Right; Cases.Add(right); } - private static bool CanMerge(RegexAstNode origin, RegexAstNode target) + private static bool CanMerge( + RegexAstNode origin, + RegexAstNode target + ) { // suppose that origin is not a union, if (target is UnionAlt alt) diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs index 7ec250ea..0a5cfbdc 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs @@ -2,8 +2,8 @@ // ----------------------------------------------------------------------- // file="RegexInterpreter" // Project: StgSharp -// AuthorGroup: Nitload Space -// Copyright (c) Nitload Space. All rights reserved. +// 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 @@ -58,7 +58,9 @@ namespace StgSharp.RegularAnalysis.TextRegex } } - public static RegexInterpreter Analyze(string regex) + public static RegexInterpreter Analyze( + string regex + ) { RegexInterpreter interpreter = new(); interpreter._source = regex; @@ -160,8 +162,7 @@ namespace StgSharp.RegularAnalysis.TextRegex throw new InvalidOperationException("Invalid regular expression syntax."); } - private void OptimizeTree( - ) + private void OptimizeTree() { // phase0: scan container and remove no parent nodes _ = _tree.AllNodes.RemoveWhere(node => node.Parent == null && node != _tree.Root); @@ -169,7 +170,7 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode current; // phase1: ALT flatten - // phase3: CONCAT normalize + // phase2: CONCAT normalize int i = 0; List altNodes = []; while (enumerator.MoveNext()) @@ -177,30 +178,10 @@ namespace StgSharp.RegularAnalysis.TextRegex current = enumerator.Current; if (current.EqualityTypeConvert == RegexElementLabel.ALT) { - if (current.Left.EqualityTypeConvert == RegexElementLabel.UNIT && - current.Right.EqualityTypeConvert == RegexElementLabel.UNIT) - { - if (string.IsNullOrEmpty(current.Left.Value)) - { - // TODO replace the alt node with a count node - /* - RegexAstNode parent = current.Parent; - parent.Next = new RegexAstNode(); - /**/ - } else if (string.IsNullOrEmpty(current.Right.Value)) - { - // TODO replace the alt node with a count node - } else - { - // TODO replace the alt node with a charset node - } - } else - { - RegexAstNode union = FlattenAlt(current); - altNodes.Add(current); - current.Right = union; - current.Left = RegexAstNode.Empty; - } + RegexAstNode union = FlattenAlt(current); + altNodes.Add(current); + current.Right = union; + current.Left = RegexAstNode.Empty; } else if (current.EqualityTypeConvert == RegexElementLabel.CONCAT) { RotateConcat(current); } @@ -208,7 +189,7 @@ namespace StgSharp.RegularAnalysis.TextRegex List cases = []; - // phase2: then ALT union + // phase3: then ALT union foreach (RegexAstNode item in altNodes) { RegexAstNode _case = item.Right; @@ -237,13 +218,18 @@ namespace StgSharp.RegularAnalysis.TextRegex } } } + + // TODO enum AST again , do ALT tail merge and simple ALT front merge } - private void ProcessOperator(RegexAstNode op) + private void ProcessOperator( + RegexAstNode op + ) { if (op.EqualityTypeConvert == RegexElementLabel.CONCAT) { - if (_stack.TryPopOperand(out RegexAstNode? _1) && _stack.TryPopOperand(out RegexAstNode? _2)) + if (_stack.TryPopOperand(out RegexAstNode? _1) && + _stack.TryPopOperand(out RegexAstNode? _2)) { op.Left = _2; op.Right = _1; @@ -269,7 +255,8 @@ namespace StgSharp.RegularAnalysis.TextRegex } } else if (op.EqualityTypeConvert == RegexElementLabel.ALT) { - if (_stack.TryPopOperand(out RegexAstNode? _1) && _stack.TryPopOperand(out RegexAstNode? _2)) + if (_stack.TryPopOperand(out RegexAstNode? _1) && + _stack.TryPopOperand(out RegexAstNode? _2)) { op.Left = _2; op.Right = _1; diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs b/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs index bfd0a23c..d57f3518 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs @@ -148,7 +148,7 @@ namespace StgSharp.RegularAnalysis.TextRegex } throw new InvalidOperationException("Unclosed count specifier"); case '*' or '+': - int count = _source[pos + 1] == '?' ? 2 : 1; + int count = pos + 1 >= _source.Length ? 1 : (_source[pos + 1] == '?' ? 2 : 1); position = pos + count; return new Token(_source[(pos)..(pos + count)], 0, pos, RegexElementLabel.COUNT); @@ -157,6 +157,7 @@ namespace StgSharp.RegularAnalysis.TextRegex return new Token(_source[(pos)..(pos + 1)], 0, pos, RegexElementLabel.COUNT); case '.': + position = pos + 1; return new Token(_source[(pos)..(pos + 1)], 0, pos, RegexElementLabel.UNIT); default: @@ -287,7 +288,7 @@ namespace StgSharp.RegularAnalysis.TextRegex } // Consume "(?" - int consumedLen = (i - start) + 1; // include '>' + int consumedLen = i - start + 1; // include '>' string text = _source.Substring(start, consumedLen); position = start + consumedLen; diff --git a/StgSharpDebug/TestRegexAnalyse.cs b/StgSharpDebug/TestRegexAnalyse.cs index b4d4ae83..88bbac82 100644 --- a/StgSharpDebug/TestRegexAnalyse.cs +++ b/StgSharpDebug/TestRegexAnalyse.cs @@ -31,11 +31,12 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace StgSharp { - public class TestRegexAnalyse + public partial class TestRegexAnalyse { private static string[] regexSource = [ @@ -43,7 +44,7 @@ namespace StgSharp @"abc[0-9]x|abc[0-9]y|abd[0-9]x|abd[0-9]y|xyz", @"get_name|get_value|get_type|set_name|set_value|set_type", @"helix|hello|help|hero|hers|her", - @"http|https|ftp|ftps" + @"a*.[abc]*(abcd|a(bc)c)+(?abc)" ]; public static void Run() @@ -66,5 +67,8 @@ namespace StgSharp } } + [GeneratedRegex(@"a*.[abc]*(abcd|a(bc)c)+(?abc)", RegexOptions.ExplicitCapture)] + internal static partial Regex GetRegex(); + } } diff --git a/future/ngra-regex-ir.md b/future/ngra-regex-ir.md index ee2641e1..7de080ee 100644 --- a/future/ngra-regex-ir.md +++ b/future/ngra-regex-ir.md @@ -16,12 +16,9 @@ ## 算子 -1. 搜索算子(SEARCH %str; SEARCH %unicode;SERCH %set_expression),向后搜索确定长度序列或者满足规则的字符集,更新活动指针,不更新锚点指针,搜索结果保存在全局上下文中 -2. 跳转算子(JMP %cmd_idx),跳转到指定的IR指令之前,不变化任何指针, -3. 回到(BACK %cmd_idx %count)跳转到一个跳自身位置之前的位置,并更新跳转次数,不变化任何的指针,%count应当是使用DEFINE指令定义好的符号 -4. 比较指令(CONDITION %cmd_true %cmd_fail),比较前一条SEARCH指令是否成功,并指定跳转到指定的位置,如果某种条件不需要跳转,请使用0进行填充 -5. 检查点指令(CHECK),将锚点指针强制拉取到活动指针的位置 -6. 弹出检查点(UNCHECK),将一个检查点撤销并销毁 -7. 划分区间指令(SPAN %name),将锚点指针和活动指针之间的部分划分为一个Span<>,并弹出对应的检查点 -8. 定义指令(DEFINE %symbol)定义一个符号 -9. 赋值指令(ASSIGN %symbol %value)将符号设定为值 +1. 搜索算子(SEARCH %str; SEARCH %unicode),向后搜索确定长度序列或者满足规则的字符集,更新活动指针,不更新锚点指针,搜索结果保存在全局上下文中 +2. 搜索算子(SEARCH %set_expression),向后匹配一个满足字符集要求的字符,更新活动指针,不更新锚点指针,搜索结果保存在全局上下文中 +3. 跳转算子(JMP %cmd_idx),跳转到指定的IR指令之前,不变化任何指针,cmd_idx为相对当前位置的偏移量 +4. 条件指令(CONDITION %cmd_true %cmd_fail),比较前一条SEARCH指令是否成功,并指定跳转到指定的位置,如果某种条件不需要跳转,请使用0进行填充 +5. 跳转执行指令(TRY %begin %end),尝试idx指向的IR块,并记住执行结果数据 +6. 计数指令(COUNT %min %max %is_greedy %cmd),重复执行指令cmd,直到满足参数配置的数量为止 diff --git a/future/sample.cs b/future/sample.cs new file mode 100644 index 00000000..0250f0d5 --- /dev/null +++ b/future/sample.cs @@ -0,0 +1,501 @@ +// +#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); + } + } + } +}