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);
+ }
+ }
+ }
+}