regex interpreter now can build partial correct AST (i forget groups), but in bad shape. building an optimizer.

This commit is contained in:
Youmu Liu
2026-02-06 18:02:00 +08:00
parent 3a441b270c
commit ed7972f1da
5 changed files with 66 additions and 25 deletions
@@ -43,6 +43,11 @@ namespace StgSharp.RegularAnalysis.Abstraction
private TNode _root; private TNode _root;
public HashSet<TNode> AllNodes
{
get { return _allToken; }
}
public int Count public int Count
{ {
get { return _allToken.Count; } get { return _allToken.Count; }
@@ -59,7 +59,7 @@ namespace StgSharp.RegularAnalysis.Abstraction
public int OperatorInDepthCount public int OperatorInDepthCount
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _operators.Count - _depthMarks.Peek().OperandPos; get => _operators.Count - _depthMarks.Peek().OperatorPos;
} }
public int Depth public int Depth
@@ -53,6 +53,8 @@ namespace StgSharp.RegularAnalysis.Abstraction
TNode Right { get; set; } TNode Right { get; set; }
TNode Parent { get; set; }
static abstract TNode Empty { get; } static abstract TNode Empty { get; }
Token<TLabel> Source { get; } Token<TLabel> Source { get; }
@@ -66,6 +66,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
get; get;
set set
{ {
field?.Parent = null;
field = value; field = value;
value.Parent = this; value.Parent = this;
} }
@@ -76,6 +77,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
get; get;
set set
{ {
field?.Parent = null;
field = value; field = value;
value.Parent = this; value.Parent = this;
} }
@@ -41,42 +41,59 @@ namespace StgSharp.RegularAnalysis.TextRegex
private AbstractSyntaxTree<RegexAstNode, RegexElementLabel> _tree = new(); private AbstractSyntaxTree<RegexAstNode, RegexElementLabel> _tree = new();
private CompileStack<RegexAstNode, RegexElementLabel> _stack = new(); private CompileStack<RegexAstNode, RegexElementLabel> _stack = new();
private string _source;
public AbstractSyntaxTree<RegexAstNode, RegexElementLabel> Analyze( public static AbstractSyntaxTree<RegexAstNode, RegexElementLabel> Interpret(
string regex string regex
) )
{ {
RegexTokenReader reader = new RegexTokenReader(regex); RegexInterpreter interpreter = new RegexInterpreter();
interpreter._source = regex;
interpreter.Analyze();
interpreter.OptimizeTree();
return interpreter._tree;
}
private void Analyze()
{
RegexTokenReader reader = new RegexTokenReader(_source);
TokenParser<RegexElementLabel, RegexElementLabel> lexer = reader.Pipe(() => new RegexTokenParser()); TokenParser<RegexElementLabel, RegexElementLabel> lexer = reader.Pipe(() => new RegexTokenParser());
while (lexer.TryReadToken(out Token<RegexElementLabel> token)) while (lexer.TryReadToken(out Token<RegexElementLabel> token))
{ {
if ((token.Flag & RegexElementLabel.SEQUENCE) != 0) if ((token.Flag & RegexElementLabel.SEQUENCE) != 0)
{ {
Console.WriteLine($"Unit {token.Value}");
// char or charset // char or charset
Console.WriteLine($"Unit {token.Value}");
_stack.PushOperand(new RegexAstNode(token)); _stack.PushOperand(new RegexAstNode(token));
} else if ((token.Flag & RegexElementLabel.GROUP_BEGIN) != 0) } else if ((token.Flag & RegexElementLabel.GROUP_BEGIN) != 0)
{ {
Console.WriteLine($"Group begin {token.Value}"); Console.WriteLine($"Group begin {token.Value}");
_stack.PushOperator(new RegexAstNode(token));
_stack.IncreaseDepth(0); _stack.IncreaseDepth(0);
Console.WriteLine(/**/_stack.OperatorInDepthCount/**/);
} else if ((token.Flag & RegexElementLabel.GROUP_END) != 0) } else if ((token.Flag & RegexElementLabel.GROUP_END) != 0)
{ {
// close all nodes here
Console.WriteLine($"Group end {token.Value}"); Console.WriteLine($"Group end {token.Value}");
RegexAstNode op;
// TODO: close all nodes here while (_stack.TryPopOperator(out op)) {
while (_stack.TryPopOperator(out RegexAstNode? op))
{
ProcessOperator(op); ProcessOperator(op);
} }
if (_stack.OperandInDepthCount != 1) { if (_stack.OperandInDepthCount != 1) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
_stack.DecreaseDepth(); _stack.DecreaseDepth();
op = _stack.PopOperator();
if (op.Source.Flag != RegexElementLabel.GROUP_BEGIN) {
throw new InvalidOperationException("Unmatched group begin and end symbol");
}
op.Right = _stack.PopOperand();
_stack.PushOperand(op);
} else if ((token.Flag & RegexElementLabel.OPERATOR) != 0) } else if ((token.Flag & RegexElementLabel.OPERATOR) != 0)
{ {
Console.WriteLine($"Operator {token.Value} with flag {token.Flag}"); Console.Write($"Operator {token.Value} with flag {token.Flag}, ");
Console.WriteLine(/**/_stack.OperatorInDepthCount/**/);
if (_stack.OperatorInDepthCount == 0) if (_stack.OperatorInDepthCount == 0)
{ {
// the first operator in stack // the first operator in stack
@@ -117,13 +134,35 @@ namespace StgSharp.RegularAnalysis.TextRegex
if (_stack.OperandInDepthCount == 1 && _stack.OperatorInDepthCount == 0) if (_stack.OperandInDepthCount == 1 && _stack.OperatorInDepthCount == 0)
{ {
_tree.Root = _stack.PopOperand(); _tree.Root = _stack.PopOperand();
return _tree; return;
} }
throw new InvalidOperationException("Invalid regular expression syntax."); throw new InvalidOperationException("Invalid regular expression syntax.");
} }
throw new InvalidOperationException("Invalid regular expression syntax."); throw new InvalidOperationException("Invalid regular expression syntax.");
} }
private void OptimizeTree(
)
{
// phase0P: ALT flatten
int opCount = 0;
do
{
opCount = 1;
} while (opCount != 0);
// phase1: ALT merge
opCount = 0;
do
{
opCount = 1;
} while (opCount != 0);
// phase2: CONCAT merge
// phase3: scan container and remove no parent nodes
_tree.AllNodes.RemoveWhere(node => node.Parent == null && node != _tree.Root);
}
private void ProcessOperator( private void ProcessOperator(
RegexAstNode op RegexAstNode op
) )
@@ -134,19 +173,12 @@ namespace StgSharp.RegularAnalysis.TextRegex
if (_stack.TryPopOperand(out RegexAstNode? _1) && if (_stack.TryPopOperand(out RegexAstNode? _1) &&
_stack.TryPopOperand(out RegexAstNode? _2)) _stack.TryPopOperand(out RegexAstNode? _2))
{ {
if (RegexAstNode.TryMergeUnit(_2, _1, out RegexAstNode? _3)) op.Left = _2;
{ op.Right = _1;
_stack.PushOperand(_3); _stack.PushOperand(op);
_tree.AddNode(_3); _tree.AddNode(op);
} else _tree.AddNode(_1);
{ _tree.AddNode(_2);
op.Left = _2;
op.Right = _1;
_stack.PushOperand(op);
_tree.AddNode(op);
_tree.AddNode(_1);
_tree.AddNode(_2);
}
} else } else
{ {
throw new InvalidOperationException("Insufficient operands for CONCAT operator."); throw new InvalidOperationException("Insufficient operands for CONCAT operator.");