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;
public HashSet<TNode> AllNodes
{
get { return _allToken; }
}
public int Count
{
get { return _allToken.Count; }
@@ -59,7 +59,7 @@ namespace StgSharp.RegularAnalysis.Abstraction
public int OperatorInDepthCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _operators.Count - _depthMarks.Peek().OperandPos;
get => _operators.Count - _depthMarks.Peek().OperatorPos;
}
public int Depth
@@ -53,6 +53,8 @@ namespace StgSharp.RegularAnalysis.Abstraction
TNode Right { get; set; }
TNode Parent { get; set; }
static abstract TNode Empty { get; }
Token<TLabel> Source { get; }
@@ -66,6 +66,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
get;
set
{
field?.Parent = null;
field = value;
value.Parent = this;
}
@@ -76,6 +77,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
get;
set
{
field?.Parent = null;
field = value;
value.Parent = this;
}
@@ -41,42 +41,59 @@ namespace StgSharp.RegularAnalysis.TextRegex
private AbstractSyntaxTree<RegexAstNode, RegexElementLabel> _tree = new();
private CompileStack<RegexAstNode, RegexElementLabel> _stack = new();
private string _source;
public AbstractSyntaxTree<RegexAstNode, RegexElementLabel> Analyze(
string regex
public static AbstractSyntaxTree<RegexAstNode, RegexElementLabel> Interpret(
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());
while (lexer.TryReadToken(out Token<RegexElementLabel> token))
{
if ((token.Flag & RegexElementLabel.SEQUENCE) != 0)
{
Console.WriteLine($"Unit {token.Value}");
// char or charset
Console.WriteLine($"Unit {token.Value}");
_stack.PushOperand(new RegexAstNode(token));
} else if ((token.Flag & RegexElementLabel.GROUP_BEGIN) != 0)
{
Console.WriteLine($"Group begin {token.Value}");
_stack.PushOperator(new RegexAstNode(token));
_stack.IncreaseDepth(0);
Console.WriteLine(/**/_stack.OperatorInDepthCount/**/);
} else if ((token.Flag & RegexElementLabel.GROUP_END) != 0)
{
// close all nodes here
Console.WriteLine($"Group end {token.Value}");
// TODO: close all nodes here
while (_stack.TryPopOperator(out RegexAstNode? op))
{
RegexAstNode op;
while (_stack.TryPopOperator(out op)) {
ProcessOperator(op);
}
if (_stack.OperandInDepthCount != 1) {
throw new InvalidOperationException();
}
_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)
{
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)
{
// the first operator in stack
@@ -117,13 +134,35 @@ namespace StgSharp.RegularAnalysis.TextRegex
if (_stack.OperandInDepthCount == 1 && _stack.OperatorInDepthCount == 0)
{
_tree.Root = _stack.PopOperand();
return _tree;
return;
}
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(
RegexAstNode op
)
@@ -134,19 +173,12 @@ namespace StgSharp.RegularAnalysis.TextRegex
if (_stack.TryPopOperand(out RegexAstNode? _1) &&
_stack.TryPopOperand(out RegexAstNode? _2))
{
if (RegexAstNode.TryMergeUnit(_2, _1, out RegexAstNode? _3))
{
_stack.PushOperand(_3);
_tree.AddNode(_3);
} else
{
op.Left = _2;
op.Right = _1;
_stack.PushOperand(op);
_tree.AddNode(op);
_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
{
throw new InvalidOperationException("Insufficient operands for CONCAT operator.");