diff --git a/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs b/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs index f7399ec4..13403696 100644 --- a/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs +++ b/StgSharp.RegularAnalysis/Abstraction/AbstractSyntaxTree.cs @@ -29,6 +29,7 @@ using StgSharp.Collections; using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Text; using System.Xml.Schema; @@ -38,6 +39,8 @@ namespace StgSharp.RegularAnalysis.Abstraction where TType : unmanaged { + private HashSet _allToken = new(); + private TNode _root; public int Count @@ -45,17 +48,31 @@ namespace StgSharp.RegularAnalysis.Abstraction get { return _allToken.Count; } } - public TNode Root => _root; + public TNode Root + { + get; + internal set + { + if (_allToken.Contains(value)) { + field = value; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AddNode( + TNode node + ) + { + return _allToken.Add(node); + } public bool Contains( TNode node ) { - return _allToken.Contains(node); + return _allToken.Contains(node); } - private List _allToken = -new(); - } } diff --git a/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs b/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs index 17d45905..dcb16116 100644 --- a/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs +++ b/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs @@ -47,7 +47,7 @@ namespace StgSharp.RegularAnalysis.Abstraction public CompileStack() { - _depthMarks.Push(new DepthMark(0, 0)); + _depthMarks.Push(new DepthMark(0, 0, 0)); } public int OperandInDepthCount @@ -59,7 +59,7 @@ namespace StgSharp.RegularAnalysis.Abstraction public int OperatorInDepthCount { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _operands.Count - _depthMarks.Peek().OperandPos; + get => _operators.Count - _depthMarks.Peek().OperandPos; } public int Depth @@ -108,6 +108,20 @@ namespace StgSharp.RegularAnalysis.Abstraction return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeekOperator( + out TNode op + ) + { + if (OperatorInDepthCount > 0) + { + op = _operators.Peek(); + return true; + } + op = TNode.Empty; + return false; + } + #endregion #region operand @@ -117,7 +131,7 @@ namespace StgSharp.RegularAnalysis.Abstraction ) { if (OperatorInDepthCount > 0) { - return _operators.Pop(); + return _operands.Pop(); } return TNode.Empty; } @@ -127,7 +141,7 @@ namespace StgSharp.RegularAnalysis.Abstraction TNode operatorNode ) { - _operators.Push(operatorNode); + _operands.Push(operatorNode); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -144,6 +158,20 @@ namespace StgSharp.RegularAnalysis.Abstraction return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeekOperand( + out TNode op + ) + { + if (OperandInDepthCount > 0) + { + op = _operands.Peek(); + return true; + } + op = TNode.Empty; + return false; + } + #endregion #region depth diff --git a/StgSharp.RegularAnalysis/Abstraction/Token.cs b/StgSharp.RegularAnalysis/Abstraction/Token.cs index 6fa9766c..557fa7c9 100644 --- a/StgSharp.RegularAnalysis/Abstraction/Token.cs +++ b/StgSharp.RegularAnalysis/Abstraction/Token.cs @@ -45,6 +45,14 @@ namespace StgSharp.RegularAnalysis.Abstraction public readonly string Value; public readonly TLabel Flag; + public Token() + { + Column = 0; + Line = 0; + Value = string.Empty; + Flag = default; + } + public Token( string chars, int lineNumber, diff --git a/StgSharp.RegularAnalysis/StgSharp.RegularAnalysis.csproj b/StgSharp.RegularAnalysis/StgSharp.RegularAnalysis.csproj index 0153ae4b..863f2ea1 100644 --- a/StgSharp.RegularAnalysis/StgSharp.RegularAnalysis.csproj +++ b/StgSharp.RegularAnalysis/StgSharp.RegularAnalysis.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + preview diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs index b249be64..5bc179d2 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs @@ -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 + public class RegexAstNode : ISyntaxNode { 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 ) { } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs b/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs index c27b8bf0..ccaac106 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs @@ -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, } } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs index 56eb788d..1ebc323b 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs @@ -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."); diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexTokenParser.cs b/StgSharp.RegularAnalysis/TextRegex/RegexTokenParser.cs index 46ab7326..f7888def 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexTokenParser.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexTokenParser.cs @@ -52,11 +52,12 @@ namespace StgSharp.RegularAnalysis.TextRegex } if (Source.TryReadToken(out Token orig)) { - if (((int)(orig.Flag | RegexElementLabel.ATOM_BEGIN) != 0) && ((int)_prev != 0)) + if (((int)(orig.Flag & RegexElementLabel.ATOM_BEGIN) != 0) && ((int)_prev != 0)) { - Token ret = new Token(string.Empty, orig.Line, - orig.Column, - RegexElementLabel.OPERATOR); + Token ret = new Token(string.Empty, + orig.Line, + orig.Column, + RegexElementLabel.CONCAT); _buffer.Enqueue(orig); _prev = orig.Flag & RegexElementLabel.ATOM_END; return ret; diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs b/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs index 9bda0712..dc83c19d 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexTokenReader.cs @@ -75,7 +75,7 @@ namespace StgSharp.RegularAnalysis.TextRegex position = i + 1; return new Token(_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(_source[(pos)..1], 0, pos, + return new Token(_source[(pos)..(pos + 1)], 0, pos, RegexElementLabel.GROUP_END); case '{': for (int i = pos + 1; i < _source.Length; i++) diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs b/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs index c4c4b0ce..3d51ab3c 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs @@ -41,7 +41,7 @@ namespace StgSharp.RegularAnalysis.TextRegex this Token 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; } diff --git a/StgSharpDebug/Program.cs b/StgSharpDebug/Program.cs index 21e9189a..6985b5eb 100644 --- a/StgSharpDebug/Program.cs +++ b/StgSharpDebug/Program.cs @@ -50,7 +50,9 @@ namespace StgSharp .UseModule() .InitializeModule(); - HlsfStressTest.Run(); + TestRegexAnalyse.Run(); + + // HlsfStressTest.Run(); // TestMatrix(); diff --git a/StgSharpDebug/StgSharpDebug.csproj b/StgSharpDebug/StgSharpDebug.csproj index 9709df1a..4fbb5667 100644 --- a/StgSharpDebug/StgSharpDebug.csproj +++ b/StgSharpDebug/StgSharpDebug.csproj @@ -77,6 +77,7 @@ + diff --git a/StgSharpDebug/TestRegexAnalyse.cs b/StgSharpDebug/TestRegexAnalyse.cs new file mode 100644 index 00000000..8c7df3d9 --- /dev/null +++ b/StgSharpDebug/TestRegexAnalyse.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="TestRegexAnalyse" +// Project: StgSharp +// AuthorGroup: Nitload +// Copyright (c) Nitload. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +using StgSharp.RegularAnalysis.Abstraction; +using StgSharp.RegularAnalysis.TextRegex; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StgSharp +{ + public class TestRegexAnalyse + { + + private const string regex = @"abc(?abc)"; + + public static void Run() + { + RegexInterpreter interpreter = new RegexInterpreter(); + AbstractSyntaxTree tree = interpreter.Analyze(regex); + } + + } +}