Working on tree optimizer
This commit is contained in:
@@ -33,6 +33,7 @@ using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace StgSharp.RegularAnalysis.TextRegex
|
||||
{
|
||||
@@ -52,10 +53,8 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
|
||||
public long NodeFlag => (long)Source.Flag;
|
||||
|
||||
public static RegexAstNode Empty
|
||||
{
|
||||
get => new EmptyRegexAstNode();
|
||||
}
|
||||
public static RegexAstNode Empty { get; }
|
||||
= new EmptyRegexAstNode();
|
||||
|
||||
public RegexAstNode Previous { get; set; }
|
||||
|
||||
@@ -68,7 +67,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
get;
|
||||
set
|
||||
{
|
||||
field?.Parent = null;
|
||||
field?.Parent = Empty;
|
||||
field = value;
|
||||
value.Parent = this;
|
||||
}
|
||||
@@ -79,7 +78,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
get;
|
||||
set
|
||||
{
|
||||
field?.Parent = null;
|
||||
field?.Parent = Empty;
|
||||
field = value;
|
||||
value.Parent = this;
|
||||
}
|
||||
@@ -90,13 +89,19 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
get => Source.Flag;
|
||||
}
|
||||
|
||||
public string Value => Source.Value;
|
||||
|
||||
public string CodeConvertTemplate { get; }
|
||||
|
||||
public Token<RegexElementLabel> Source { get; }
|
||||
|
||||
public void AppendNode(
|
||||
RegexAstNode nextToken
|
||||
) { }
|
||||
)
|
||||
{
|
||||
Next = nextToken;
|
||||
nextToken.Previous = this;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static (bool Valid,int Value) ComparePrecedence(
|
||||
@@ -113,9 +118,33 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
return (true,v_left - v_right);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrEmpty(
|
||||
RegexAstNode node
|
||||
)
|
||||
{
|
||||
return node is null || node.Equals(Empty);
|
||||
}
|
||||
|
||||
public void PrependNode(
|
||||
RegexAstNode previousNode
|
||||
) { }
|
||||
)
|
||||
{
|
||||
Previous = previousNode;
|
||||
previousNode.Next = this;
|
||||
}
|
||||
|
||||
public void RemoveChild(
|
||||
RegexAstNode node
|
||||
)
|
||||
{
|
||||
if (Left == node)
|
||||
{
|
||||
Left = Empty;
|
||||
} else if (Right == node) {
|
||||
Right = Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryMergeUnit(
|
||||
RegexAstNode prev,
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
using StgSharp.RegularAnalysis.Abstraction;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -43,9 +45,118 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
{
|
||||
TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new(root);
|
||||
List<RegexAstNode> cases = [];
|
||||
RegexAstNode current;
|
||||
_ = enumerator.MoveNext(); // skip root
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
RegexAstNode current = enumerator.Current;
|
||||
current = enumerator.Current;
|
||||
current.Parent.RemoveChild(current);
|
||||
current.Parent = RegexAstNode.Empty;
|
||||
if (current.Parent.EqualityTypeConvert == RegexElementLabel.ALT)
|
||||
{
|
||||
continue;
|
||||
} else
|
||||
{
|
||||
cases.Add(current);
|
||||
enumerator.SkipNode(current);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cases.Count - 1; i++)
|
||||
{
|
||||
cases[i].AppendNode(cases[i + 1]);
|
||||
cases[i].Parent = root;
|
||||
}
|
||||
return cases[0];
|
||||
}
|
||||
|
||||
private static void MergeConcat(
|
||||
RegexAstNode root
|
||||
)
|
||||
{
|
||||
if (root.EqualityTypeConvert != RegexElementLabel.CONCAT) {
|
||||
return;
|
||||
}
|
||||
List<string> buffer = [];
|
||||
int length = 0;
|
||||
RegexAstNode right = root.Right;
|
||||
RegexAstNode left = root.Left;
|
||||
if (left.EqualityTypeConvert != RegexElementLabel.UNIT_SPAN &&
|
||||
left.EqualityTypeConvert != RegexElementLabel.UNIT) {
|
||||
return;
|
||||
}
|
||||
string str = left.Value;
|
||||
buffer.Add(str);
|
||||
length += str.Length;
|
||||
while (RegexAstNode.IsNullOrEmpty(right) &&
|
||||
right.EqualityTypeConvert == RegexElementLabel.CONCAT)
|
||||
{
|
||||
left = right.Left;
|
||||
if (left.EqualityTypeConvert != RegexElementLabel.UNIT_SPAN &&
|
||||
left.EqualityTypeConvert != RegexElementLabel.UNIT)
|
||||
{
|
||||
break;
|
||||
}
|
||||
str = left.Value;
|
||||
buffer.Add(str);
|
||||
length += str.Length;
|
||||
root.Right = right.Right;
|
||||
|
||||
// TODO: remove right from tree
|
||||
right = root.Right;
|
||||
}
|
||||
str = string.Create(length, buffer, static(
|
||||
span,
|
||||
buffer
|
||||
) => {
|
||||
int offset = 0;
|
||||
for (int i = 0; i < buffer.Count; i++)
|
||||
{
|
||||
buffer[i].AsSpan().CopyTo(span[offset..]);
|
||||
offset += buffer[i].Length;
|
||||
}
|
||||
});
|
||||
left = root.Left;
|
||||
root.Left = new RegexAstNode(new Token<RegexElementLabel>(str, left.Source.Line,
|
||||
left.Source.Column,
|
||||
str.Length == 1 ?
|
||||
RegexElementLabel.UNIT :
|
||||
RegexElementLabel.UNIT_SPAN));
|
||||
}
|
||||
|
||||
private static void RotateConcat(
|
||||
RegexAstNode root
|
||||
)
|
||||
{
|
||||
RegexAstNode left = root.Left;
|
||||
while (RegexAstNode.IsNullOrEmpty(left) &&
|
||||
left.EqualityTypeConvert == RegexElementLabel.CONCAT)
|
||||
{
|
||||
/*
|
||||
* root → root
|
||||
* ╱ ╲ ╱ ╲
|
||||
* left c a left
|
||||
* ╱ ╲ ╱ ╲
|
||||
* a b b c
|
||||
*/
|
||||
RegexAstNode a = left.Left;
|
||||
RegexAstNode b = left.Right;
|
||||
RegexAstNode c = root.Right;
|
||||
root.Left = a;
|
||||
if (RegexAstNode.IsNullOrEmpty(a)) {
|
||||
a.Parent = root;
|
||||
}
|
||||
|
||||
root.Right = left;
|
||||
left.Left = b;
|
||||
left.Right = c;
|
||||
if (RegexAstNode.IsNullOrEmpty(b)) {
|
||||
b.Parent = left;
|
||||
}
|
||||
|
||||
if (RegexAstNode.IsNullOrEmpty(c)) {
|
||||
c.Parent = left;
|
||||
}
|
||||
left = root.Left;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,24 +174,37 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
{
|
||||
// phase0: scan container and remove no parent nodes
|
||||
_tree.AllNodes.RemoveWhere(node => node.Parent == null && node != _tree.Root);
|
||||
HashSet<RegexAstNode>.Enumerator enumerator = _tree.AllNodes.GetEnumerator();
|
||||
TreeEnumerator<RegexAstNode, RegexElementLabel> enumerator = new TreeEnumerator<RegexAstNode, RegexElementLabel>(_tree.Root);
|
||||
RegexAstNode current;
|
||||
|
||||
// phase1: ALT flatten
|
||||
// phase2: CONCAT normalize
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
RegexAstNode node = enumerator.Current;
|
||||
if ((RegexElementLabel)node.NodeFlag == RegexElementLabel.ALT)
|
||||
current = enumerator.Current;
|
||||
if (current.EqualityTypeConvert == RegexElementLabel.ALT)
|
||||
{
|
||||
RegexAstNode list = FlattenAlt(node);
|
||||
node.Left = list;
|
||||
node.Right = RegexAstNode.Empty;
|
||||
RegexAstNode union = FlattenAlt(current);
|
||||
current.Right = union;
|
||||
current.Left = RegexAstNode.Empty;
|
||||
} else if (current.EqualityTypeConvert == RegexElementLabel.CONCAT) {
|
||||
RotateConcat(current);
|
||||
}
|
||||
}
|
||||
|
||||
// phase2: ALT merge
|
||||
|
||||
|
||||
// phase3: CONCAT merge
|
||||
enumerator.Reset();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
// suppose all concat is now in normalized form,
|
||||
// we can merge adjacent unit nodes into a single unit node with string value
|
||||
current = enumerator.Current;
|
||||
if (current.EqualityTypeConvert == RegexElementLabel.CONCAT) {
|
||||
MergeConcat(current);
|
||||
}
|
||||
}
|
||||
|
||||
// phase4: ALT union
|
||||
}
|
||||
|
||||
private void ProcessOperator(
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
|
||||
}
|
||||
position = pos + 2;
|
||||
return new Token<RegexElementLabel>(_source[(pos)..(pos + 2)], 0, pos,
|
||||
RegexElementLabel.UNIT);
|
||||
RegexElementLabel.UNIT_SET);
|
||||
case '[':
|
||||
for (int i = pos + 1; i < _source.Length; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user