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 17:07:03 +08:00
parent 1bfb8b67c4
commit 3a441b270c
13 changed files with 236 additions and 31 deletions
@@ -28,13 +28,15 @@
using StgSharp.RegularAnalysis.Abstraction;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.RegularAnalysis.TextRegex
{
internal class RegexAstNode : ISyntaxNode<RegexAstNode, RegexElementLabel>
public class RegexAstNode : ISyntaxNode<RegexAstNode, RegexElementLabel>
{
private RegexAstNode _left, _right;
@@ -57,9 +59,27 @@ namespace StgSharp.RegularAnalysis.TextRegex
public RegexAstNode Next { get; set; }
public RegexAstNode Left { get; set; }
public RegexAstNode Parent { get; set; }
public RegexAstNode Right { get; set; }
public RegexAstNode Left
{
get;
set
{
field = value;
value.Parent = this;
}
}
public RegexAstNode Right
{
get;
set
{
field = value;
value.Parent = this;
}
}
public RegexElementLabel EqualityTypeConvert
{
@@ -74,6 +94,21 @@ namespace StgSharp.RegularAnalysis.TextRegex
RegexAstNode nextToken
) { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (bool Valid,int Value) ComparePrecedence(
RegexElementLabel left,
RegexElementLabel right
)
{
left |= RegexElementLabel.OPERATOR;
right |= RegexElementLabel.OPERATOR;
int v_left = (int)left, v_right = (int)right;
if (v_left == 0 || v_right == 0) {
return (false,0);
}
return (true,v_left - v_right);
}
public void PrependNode(
RegexAstNode previousNode
) { }
@@ -34,21 +34,22 @@ using System.Threading.Tasks;
namespace StgSharp.RegularAnalysis.TextRegex
{
[Flags]
internal enum RegexElementLabel
public enum RegexElementLabel
{
NONE = 0,
UNIT = 1,
GROUP_BEGIN = 2,
GROUP_END = 4,
COUNT = 8,
ALT = 16,
CONCAT = 32,
UNIT_SPAN = 64,
SEQUENCE = UNIT | UNIT_SPAN,
UNIT_SET = 2,
UNIT_SPAN = 4,
GROUP_BEGIN = 8,
GROUP_END = 16,
COUNT = 32,
CONCAT = 64,
ALT = 128,
SEQUENCE = UNIT | UNIT_SPAN | UNIT_SET,
OPERATOR = COUNT | ALT | CONCAT,
ATOM_BEGIN = UNIT | GROUP_BEGIN | UNIT_SPAN,
ATOM_END = UNIT | GROUP_END | COUNT | UNIT_SPAN,
ATOM_BEGIN = SEQUENCE | GROUP_BEGIN ,
ATOM_END = SEQUENCE | GROUP_END,
}
}
@@ -53,13 +53,18 @@ namespace StgSharp.RegularAnalysis.TextRegex
{
if ((token.Flag & RegexElementLabel.SEQUENCE) != 0)
{
Console.WriteLine($"Unit {token.Value}");
// char or charset
_stack.PushOperand(new RegexAstNode(token));
} else if ((token.Flag & RegexElementLabel.GROUP_BEGIN) != 0)
{
Console.WriteLine($"Group begin {token.Value}");
_stack.IncreaseDepth(0);
} else if ((token.Flag & RegexElementLabel.GROUP_END) != 0)
{
Console.WriteLine($"Group end {token.Value}");
// TODO: close all nodes here
while (_stack.TryPopOperator(out RegexAstNode? op))
{
@@ -69,14 +74,61 @@ namespace StgSharp.RegularAnalysis.TextRegex
throw new InvalidOperationException();
}
_stack.DecreaseDepth();
} else if ((token.Flag & RegexElementLabel.OPERATOR) != 0) { }
} else if ((token.Flag & RegexElementLabel.OPERATOR) != 0)
{
Console.WriteLine($"Operator {token.Value} with flag {token.Flag}");
if (_stack.OperatorInDepthCount == 0)
{
// the first operator in stack
_stack.PushOperator(new RegexAstNode(token));
} else
{
// process operator by precedence
while (_stack.TryPeekOperator(out RegexAstNode topOp))
{
(bool Valid, int Value) cmp = RegexAstNode.ComparePrecedence(token.Flag, topOp.Source.Flag);
if (!cmp.Valid) {
throw new InvalidOperationException("Operators with same precedence are not supported.");
}
if (cmp.Value < 0)
{
_stack.PopOperator();
ProcessOperator(topOp);
continue;
} else
{
break;
}
}
_stack.PushOperator(new RegexAstNode(token));
}
}
}
Console.WriteLine(1);
if (_stack.Depth == 1)
{
if (_stack.OperandInDepthCount != 1 || _stack.OperatorInDepthCount != 0)
{
while (_stack.TryPopOperator(out RegexAstNode? op)) {
ProcessOperator(op);
}
}
if (_stack.OperandInDepthCount == 1 && _stack.OperatorInDepthCount == 0)
{
_tree.Root = _stack.PopOperand();
return _tree;
}
throw new InvalidOperationException("Invalid regular expression syntax.");
}
throw new InvalidOperationException("Invalid regular expression syntax.");
}
private void ProcessOperator(
RegexAstNode op
)
{
Console.WriteLine($"Processing a {op.Source.Flag} operator");
if (op.EqualityTypeConvert == RegexElementLabel.CONCAT)
{
if (_stack.TryPopOperand(out RegexAstNode? _1) &&
@@ -85,11 +137,15 @@ namespace StgSharp.RegularAnalysis.TextRegex
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);
}
} else
{
@@ -101,6 +157,8 @@ namespace StgSharp.RegularAnalysis.TextRegex
{
op.Left = _1;
_stack.PushOperand(op);
_tree.AddNode(_1);
_tree.AddNode(op);
} else
{
throw new InvalidOperationException("Insufficient operands for CONCAT operator.");
@@ -113,6 +171,9 @@ namespace StgSharp.RegularAnalysis.TextRegex
op.Left = _1;
op.Right = _2;
_stack.PushOperand(op);
_tree.AddNode(op);
_tree.AddNode(_1);
_tree.AddNode(_2);
} else
{
throw new InvalidOperationException("Insufficient operands for CONCAT operator.");
@@ -52,11 +52,12 @@ namespace StgSharp.RegularAnalysis.TextRegex
}
if (Source.TryReadToken(out Token<RegexElementLabel> orig))
{
if (((int)(orig.Flag | RegexElementLabel.ATOM_BEGIN) != 0) && ((int)_prev != 0))
if (((int)(orig.Flag & RegexElementLabel.ATOM_BEGIN) != 0) && ((int)_prev != 0))
{
Token<RegexElementLabel> ret = new Token<RegexElementLabel>(string.Empty, orig.Line,
orig.Column,
RegexElementLabel.OPERATOR);
Token<RegexElementLabel> ret = new Token<RegexElementLabel>(string.Empty,
orig.Line,
orig.Column,
RegexElementLabel.CONCAT);
_buffer.Enqueue(orig);
_prev = orig.Flag & RegexElementLabel.ATOM_END;
return ret;
@@ -75,7 +75,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
position = i + 1;
return new Token<RegexElementLabel>(_source[(pos - 1)..(i - 1)], 0,
position,
RegexElementLabel.CHAR_SET);
RegexElementLabel.UNIT_SET);
}
}
throw new InvalidOperationException("Unclosed character set");
@@ -85,7 +85,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
return ReadGroupBeginToken();
case ')':
position = pos + 1;
return new Token<RegexElementLabel>(_source[(pos)..1], 0, pos,
return new Token<RegexElementLabel>(_source[(pos)..(pos + 1)], 0, pos,
RegexElementLabel.GROUP_END);
case '{':
for (int i = pos + 1; i < _source.Length; i++)
@@ -41,7 +41,7 @@ namespace StgSharp.RegularAnalysis.TextRegex
this Token<RegexElementLabel> token
)
{
return ((token.Flag & RegexElementLabel.UNIT) != 0 && token.Value.Length == 0) ||
return ((token.Flag & RegexElementLabel.UNIT) != 0 && token.Value.Length == 1) ||
(token.Flag & RegexElementLabel.UNIT_SPAN) != 0;
}