diff --git a/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs b/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs index af9dfc19..072faeb9 100644 --- a/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs +++ b/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs @@ -106,6 +106,8 @@ namespace StgSharp.RegularAnalysis.Abstraction ) : this(tree.Root) { } + public int Depth => _stack.Count; + public TNode Current { get diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs index 522bfa28..e3a381a5 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs @@ -56,11 +56,11 @@ namespace StgSharp.RegularAnalysis.TextRegex public static RegexAstNode Empty { get; } = new EmptyRegexAstNode(); - public virtual RegexAstNode Previous { get; set; } + public RegexAstNode Previous { get; set; } - public virtual RegexAstNode Next { get; set; } + public RegexAstNode Next { get; set; } - public virtual RegexAstNode Parent { get; set; } + public RegexAstNode Parent { get; set; } public RegexAstNode Left { @@ -69,7 +69,9 @@ namespace StgSharp.RegularAnalysis.TextRegex { field?.Parent = Empty; field = value; - value.Parent = this; + if (!IsNullOrEmpty(value)) { + value.Parent = this; + } } } @@ -80,7 +82,9 @@ namespace StgSharp.RegularAnalysis.TextRegex { field?.Parent = Empty; field = value; - value.Parent = this; + if (!IsNullOrEmpty(value)) { + value.Parent = this; + } } } @@ -93,7 +97,7 @@ namespace StgSharp.RegularAnalysis.TextRegex public string CodeConvertTemplate { get; } - public Token Source { get; private set; } + public Token Source { get; private protected set; } public void AppendNode( RegexAstNode nextToken @@ -123,7 +127,7 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode node ) { - return node is null || node is EmptyRegexAstNode; + return node is null || ReferenceEquals(node, Empty); } /**/ @@ -154,6 +158,23 @@ namespace StgSharp.RegularAnalysis.TextRegex { return new EmptyRegexAstNode(); } + + internal void RemoveAndAsChild( + bool useLeft + ) + { + if (useLeft) + { + Source = Left.Source; + Left.Parent = Empty; + Left = Empty; + } else + { + Source = Right.Source; + Right.Parent = Empty; + Right = Empty; + } + } /**/ internal void ReplaceBy( @@ -163,10 +184,10 @@ namespace StgSharp.RegularAnalysis.TextRegex Source = node.Source; Left = node.Left; Right = node.Right; - if (IsNullOrEmpty(Left)) { + if (!IsNullOrEmpty(Left)) { Left.Parent = this; } - if (IsNullOrEmpty(Right)) { + if (!IsNullOrEmpty(Right)) { Right.Parent = this; } Previous = node.Previous; @@ -188,18 +209,6 @@ namespace StgSharp.RegularAnalysis.TextRegex public EmptyRegexAstNode() : base(Token.Empty) { } - public override RegexAstNode Next - { - get; - set { field = Empty; } - } - - public override RegexAstNode Previous - { - get; - set { field = Empty; } - } - } } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs index 7da0f8f7..4e422cb9 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.Optimize.cs @@ -35,14 +35,15 @@ using System.Runtime.CompilerServices; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; namespace StgSharp.RegularAnalysis.TextRegex { public partial class RegexInterpreter { - private static List FlattenAlt( - RegexAstNode root + private static RegexAstNode FlattenAlt( + RegexAstNode root ) { TreeEnumerator enumerator = new(root); @@ -59,7 +60,13 @@ namespace StgSharp.RegularAnalysis.TextRegex } current.Parent.RemoveChild(current); } - return cases; + for (int i = 0; i < cases.Count - 1; i++) + { + cases[i].AppendNode(cases[i + 1]); + cases[i].Parent = root; + } + cases[^1].Parent = root; + return cases[0]; } private static bool MergeConcat( @@ -69,6 +76,25 @@ namespace StgSharp.RegularAnalysis.TextRegex if (root.EqualityTypeConvert != RegexElementLabel.CONCAT) { return false; } + + /* + * concat → 'a' + * ╱ + * 'a' + */ + if ((root.Left.EqualityTypeConvert & RegexElementLabel.SEQUENCE) != 0 && + RegexAstNode.IsNullOrEmpty(root.Right)) + { + root.RemoveAndAsChild(true); + return true; + } + if ((root.Right.EqualityTypeConvert & RegexElementLabel.SEQUENCE) != 0 && + RegexAstNode.IsNullOrEmpty(root.Left)) + { + root.RemoveAndAsChild(false); + return true; + } + /* * concat → concat * ╱ ╲ / @@ -184,7 +210,9 @@ namespace StgSharp.RegularAnalysis.TextRegex ) { UnionAlt union = new(0, cases); - return union.AsNormal(); + RegexAstNode root = union.AsNormal(); + + return root; } private class UnionAlt : RegexAstNode @@ -196,11 +224,21 @@ namespace StgSharp.RegularAnalysis.TextRegex int level, RegexAstNode node ) - : base(new Token(string.Empty, 0, 0, 0)) + : base(node.Source) { Level = level; Cases = new List(); - Cases.Add(node.Right ?? Empty); + + RegexAstNode right = IsNullOrEmpty(node.Right) ? + new RegexAstNode( + new Token(node.Value, 0, node.Source.Column, + RegexElementLabel.UNIT) + ) : + node.Right; + Cases.Add(right); + + node.PrependNode(Empty); + node.AppendNode(Empty); UnionBegin = node; UnionEnd = node; } @@ -209,7 +247,7 @@ namespace StgSharp.RegularAnalysis.TextRegex int level, List c ) - : base(new Token(string.Empty, 0, 0, 0)) + : base(c[0].Source) { Cases = c; Level = level; @@ -233,6 +271,17 @@ namespace StgSharp.RegularAnalysis.TextRegex List buffer = []; for (int i = 0; i < Cases.Count; i++) { + if ((Cases[i].EqualityTypeConvert & RegexElementLabel.SEQUENCE) != 0) + { + RegexAstNode concat = new RegexAstNode(new Token(string.Empty, + 0, + Cases[i].Source.Column, + RegexElementLabel.CONCAT)); + concat.Left = Cases[i]; + Cases[i].Parent = concat; + concat.Right = Empty; + Cases[i] = concat; + } RegexAstNode current = Cases[i]; int j = 0; for (; j < buffer.Count; j++) @@ -258,11 +307,11 @@ namespace StgSharp.RegularAnalysis.TextRegex buffer.Add(current); } } - Cases = buffer; + if (Cases.Count != buffer.Count) { + Cases = buffer; + } - // TODO problem1 没处理case链表 - // TODO problem2 没有处理立刻返回策略 - if (Cases.Count == 1) + if (buffer.Count == 1) { // if there is only one case, // then we suppose that its contained cases has a same beginning, @@ -276,6 +325,7 @@ namespace StgSharp.RegularAnalysis.TextRegex { alt.UnionBegin.Parent = UnionEnd; UnionEnd.Right = alt.UnionBegin; + UnionEnd = alt.UnionBegin; } Cases = alt.Cases; @@ -291,18 +341,34 @@ namespace StgSharp.RegularAnalysis.TextRegex Cases[i] = union.AsNormal(); } } - RegexAstNode branch = new RegexAstNode(new Token(string.Empty, 0, - UnionEnd.Source.Column, - RegexElementLabel.ALT)); - UnionEnd.Right = branch; - branch.Parent = UnionEnd; - UnionEnd = branch; + + RegexAstNode branch; + if (IsNullOrEmpty(UnionEnd)) + { + 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, + RegexElementLabel.ALT)); + UnionEnd.Right = branch; + branch.Parent = UnionEnd; + UnionEnd = branch; + } + for (int i = 0; i < Cases.Count - 1; i++) { + Cases[i] ??= NewEmptyNode(); Cases[i].AppendNode(Cases[i + 1]); Cases[i].Parent = UnionEnd; } UnionEnd.Right = Cases[0]; + TreeEnumerator enumerator = new TreeEnumerator(UnionBegin); + return UnionBegin; } @@ -310,8 +376,12 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode _case ) { - RegexAstNode right = _case.Right; - right ??= Empty; + RegexAstNode right = IsNullOrEmpty(_case.Right) ? + new RegexAstNode( + new Token(_case.Value, 0, _case.Source.Column, + RegexElementLabel.UNIT) + ) : + _case.Right; Cases.Add(right); } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs index 7794cd64..be2a6053 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs @@ -170,23 +170,40 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode current; // phase1: ALT flatten - // phase2: then ALT union // phase3: CONCAT normalize int i = 0; + List altNodes = []; while (enumerator.MoveNext()) { current = enumerator.Current; - RegexAstNode cur = current.Next; if (current.EqualityTypeConvert == RegexElementLabel.ALT) { - List list = FlattenAlt(current); - RegexAstNode union = MergeAlt(list); - current.ReplaceBy(union); + RegexAstNode union = FlattenAlt(current); + altNodes.Add(current); + current.Right = union; + current.Left = RegexAstNode.Empty; } else if (current.EqualityTypeConvert == RegexElementLabel.CONCAT) { RotateConcat(current); } } + List cases = []; + + // phase2: then ALT union + foreach (RegexAstNode item in altNodes) + { + RegexAstNode _case = item.Right; + do + { + cases.Add(_case); + _case = _case.Next; + } while (!RegexAstNode.IsNullOrEmpty(_case)); + RegexAstNode newAlt = MergeAlt(cases); + item.ReplaceBy(newAlt); + } + + // return; + // phase4: CONCAT merge enumerator.Reset(); while (enumerator.MoveNext()) @@ -207,7 +224,6 @@ namespace StgSharp.RegularAnalysis.TextRegex RegexAstNode op ) { - Console.WriteLine($"Processing a {op.Source.Flag} operator"); if (op.EqualityTypeConvert == RegexElementLabel.CONCAT) { if (_stack.TryPopOperand(out RegexAstNode? _1) && diff --git a/StgSharpDebug/TestRegexAnalyse.cs b/StgSharpDebug/TestRegexAnalyse.cs index c258a697..c707df55 100644 --- a/StgSharpDebug/TestRegexAnalyse.cs +++ b/StgSharpDebug/TestRegexAnalyse.cs @@ -38,16 +38,29 @@ namespace StgSharp public class TestRegexAnalyse { - private const string regex = @"abc|abd|abe|abf"; + private static string[] regexSource = [ + @"http|https", + @"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" +]; public static void Run() { - RegexInterpreter interpreter = RegexInterpreter.Analyze(regex); - AbstractSyntaxTree tree = interpreter.Tree; - Console.WriteLine(); - Console.WriteLine(); - foreach (RegexAstNode node in tree) { - Console.WriteLine($"{node.Source.Value}\t{node.Source.Flag}"); + foreach (string regex in regexSource) + { + RegexInterpreter interpreter = RegexInterpreter.Analyze(regex); + AbstractSyntaxTree tree = interpreter.Tree; + Console.WriteLine($"result of \"{regex}\""); + TreeEnumerator enumerator = new TreeEnumerator(tree); + while (enumerator.MoveNext()) + { + RegexAstNode node = enumerator.Current; + Console.Write(new string('|', enumerator.Depth)); + Console.WriteLine($"\t{node.Source.Value}\t{node.Source.Flag}"); + } + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); } }