Working on tree optimizer

This commit is contained in:
Youmu Liu
2026-02-11 18:02:45 +08:00
parent 4735b987f4
commit 39dd32992d
9 changed files with 193 additions and 63 deletions
@@ -181,9 +181,20 @@ namespace StgSharp.RegularAnalysis.Abstraction
_state = 0;
}
public void SkipNode()
/// <summary>
/// Skips the node and its subtree if the current node is the given node. Otherwise, do
/// nothing.
/// </summary>
/// <param name="node">
/// The node to skip.
/// </param>
public void SkipNode(
TNode node
)
{
if (_stack.TryPop(out Record? r)) {
if (_stack.TryPeek(out Record? r) && ReferenceEquals(r.Node, node))
{
_ = _stack.Pop();
_recycle.Push(r);
}
}
@@ -176,6 +176,7 @@ namespace StgSharp.RegularAnalysis.Abstraction
#region depth
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void DecreaseDepth(
)
{
@@ -185,6 +186,7 @@ namespace StgSharp.RegularAnalysis.Abstraction
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void IncreaseDepth(
int mark
)
@@ -66,14 +66,6 @@ namespace StgSharp.RegularAnalysis.Abstraction
TNode nextToken
);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static virtual bool IsNullOrEmpty(
TNode node
)
{
return node is null || node.Equals(TNode.Empty);
}
void PrependNode(
TNode previousNode
);
@@ -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++)
{