diff --git a/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs b/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs index 270363e7..17d45905 100644 --- a/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs +++ b/StgSharp.RegularAnalysis/Abstraction/CompileStack.cs @@ -41,327 +41,129 @@ namespace StgSharp.RegularAnalysis.Abstraction where TLabel : unmanaged { - private Token? _lastToken; - - private TNode[] _nodeArray = new TNode[4]; - private Token[] _operatorArray = new Token[4]; - private Token[] _tokenArray = new Token[4]; - - private int _nodeCount,_tokenCount, _operatorCount; - private RandomAccessibleStack _depthStack = new RandomAccessibleStack( - ); - - private RandomAccessibleStack _isNode = new RandomAccessibleStack( - 8); - private Stack> _statementCache = new Stack>(); - private TNode _lastNode; + private readonly RandomAccessibleStack _depthMarks = []; + private readonly RandomAccessibleStack _operands = []; + private readonly RandomAccessibleStack _operators = []; public CompileStack() { - _depthStack.Push(new CompileDepthMark(0, 0, 0, null!)); - _statementCache.Push(new Stack()); + _depthMarks.Push(new DepthMark(0, 0)); } - public bool IsLastAddedOperator { get; private set; } - - public CompileDepthMark CurrentDepthMark + public int OperandInDepthCount { - get => _depthStack.Peek(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _operands.Count - _depthMarks.Peek().OperandPos; } - public int Depth => _depthStack.Count; - - public int OperandCount => _isNode.Count; - - public int OperatorCount => _operatorCount; - - public int OperatorAheadOfDepth + public int OperatorInDepthCount { - get => OperatorCount - _depthStack.Peek().OperatorBegin; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _operands.Count - _depthMarks.Peek().OperandPos; } - public int OperandAheadOfDepth + public int Depth { - get => OperandCount - _depthStack.Peek().OperandBegin; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _depthMarks.Count; } - public Stack StatementsInDepth - { - get => _statementCache.Peek(); - } - - public void DecreaseDepth() - { - CompileDepthMark mark = _depthStack.Pop(); - Stack statementsInDepth = _statementCache.Pop(); - } - - public bool GetOperandAt( - Index i, - out TNode operandNode, - out Token operandToken - ) - { - OperandIndex index = _isNode[i]; - if (index.IsNode) - { - operandNode = _nodeArray[index.Index]; - operandToken = Token.Empty; - return true; - } else - { - operandToken = _tokenArray[index.Index]; - operandNode = default!; - return false; - } - } - - public bool GetOperandAt( - CompileDepthMark depth, - Index i, - out Token operandToken, - out TNode operandNode - ) - { - OperandIndex index = _isNode[i]; - - _depthStack.Contains(depth); - if (index.IsNode) - { - operandNode = _nodeArray[index.Index]; - operandToken = Token.Empty; - return true; - } else - { - operandToken = _tokenArray[index.Index]; - operandNode = default!; - return false; - } - } - - public CompileDepthMark IncreaseDepth( - int usage - ) - { - CompileDepthMark mark = - new CompileDepthMark(OperatorCount, OperandCount, usage); - - _depthStack.Push(mark); - _statementCache.Push(new Stack()); - return mark; - } - - public TNode PackAllStatements() - { - if (StatementsInDepth.Count == 0) { - return TNode.Empty; - } - TNode end = StatementsInDepth.Peek(); - TNode first = end; - while (StatementsInDepth.Count > 0) - { - first = StatementsInDepth.Pop(); - end.PrependNode(first); - end = first; - } - return first; - } - - public bool PeekOperand( - out Token t, - out TNode n - ) - { - if (OperandAheadOfDepth <= 0) - { - t = Token.Empty; - n = TNode.Empty; - return false; - } - if (_isNode.Pop().IsNode) - { - t = Token.Empty; - n = _nodeArray[_nodeCount - 1]; - return true; - } else - { - t = _tokenArray[_tokenCount - 1]; - n = TNode.Empty; - return false; - } - } - - public Token PeekOperator() - { - if (OperatorAheadOfDepth > 0) { - return _operatorArray[_operatorCount - 1]; - } - return Token.Empty; - } - - public bool PopOperand( - out Token t, - out TNode n - ) - { - if (OperandAheadOfDepth <= 0) - { - t = Token.Empty; - n = TNode.Empty; - return false; - } - if (_isNode.Pop().IsNode) - { - t = Token.Empty; - n = _nodeArray[--_nodeCount]; - return true; - } else - { - t = _tokenArray[--_tokenCount]; - n = TNode.Empty; - return false; - } - } - - public Token PopOperator() - { - if (OperatorAheadOfDepth > 0) - { - _operatorCount--; - return _operatorArray[_operatorCount]; - } - return Token.Empty; - } - - public void PushOperand( - TNode node - ) - { - if (_nodeArray.Length == _nodeCount) { - Array.Resize(ref _nodeArray, _nodeArray.Length * 2); - } - _nodeArray[_nodeCount] = node; - _isNode.Push(new(true, _nodeCount)); - _nodeCount++; - IsLastAddedOperator = false; - } - - public void PushOperand( - Token token - ) - { - if (_tokenArray.Length == _tokenCount) { - Array.Resize(ref _tokenArray, _tokenArray.Length * 2); - } - _tokenArray[_tokenCount] = token; - _isNode.Push(new(false, _tokenCount)); - _tokenCount++; - IsLastAddedOperator = false; - } - - public void PushOperator( - Token token - ) - { - if (_operatorArray.Length == _operatorCount) { - Array.Resize(ref _operatorArray, _operatorArray.Length * 2); - } - _operatorArray[_operatorCount] = token; - _operatorCount++; - IsLastAddedOperator = true; - } - - public T StateOfCurrentDepth() where T : class, ICompileDepthState - { - return (CurrentDepthMark.StateCache as T)!; - } - - public bool TryPeekOperand( - out bool isNode, - out Token t, - out TNode n - ) - { - if (OperandAheadOfDepth <= 0) - { - t = Token.Empty; - n = TNode.Empty; - isNode = false; - return false; - } - if (_isNode.Pop().IsNode) - { - t = Token.Empty; - n = _nodeArray[_nodeCount - 1]; - isNode = true; - return true; - } else - { - t = _tokenArray[_tokenCount - 1]; - n = TNode.Empty; - isNode = false; - return false; - } - } - - public bool TryPeekOperator( - out Token op - ) - { - if (OperatorAheadOfDepth > 0) - { - op = _operatorArray[_operatorCount - 1]; - return true; - } - op = Token.Empty; - return false; - } - - public bool TryPopOperand( - out bool isNode, - out Token t, - out TNode n - ) - { - if (OperandAheadOfDepth <= 0) - { - t = Token.Empty; - n = TNode.Empty; - isNode = false; - return false; - } - if (_isNode.Pop().IsNode) - { - t = Token.Empty; - n = _nodeArray[--_nodeCount]; - isNode = true; - return true; - } else - { - t = _tokenArray[--_tokenCount]; - n = TNode.Empty; - isNode = false; - return true; - } - } - - public bool TryPopOperator( - out Token op - ) - { - if (OperatorAheadOfDepth > 0) - { - op = _operatorArray[--_operatorCount]; - return true; - } - op = Token.Empty; - return false; - } - - private record struct OperandIndex( - bool IsNode, - int Index + private record struct DepthMark( + int OperandPos, + int OperatorPos, + int Mark ); + #region operator + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TNode PopOperator( + ) + { + if (OperatorInDepthCount > 0) { + return _operators.Pop(); + } + return TNode.Empty; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PushOperator( + TNode operatorNode + ) + { + _operators.Push(operatorNode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPopOperator( + out TNode op + ) + { + if (OperatorInDepthCount > 0) + { + op = _operators.Pop(); + return true; + } + op = TNode.Empty; + return false; + } + + #endregion + + #region operand + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TNode PopOperand( + ) + { + if (OperatorInDepthCount > 0) { + return _operators.Pop(); + } + return TNode.Empty; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PushOperand( + TNode operatorNode + ) + { + _operators.Push(operatorNode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPopOperand( + out TNode op + ) + { + if (OperandInDepthCount > 0) + { + op = _operands.Pop(); + return true; + } + op = TNode.Empty; + return false; + } + + #endregion + + #region depth + + public void DecreaseDepth( + ) + { + if (Depth > 1) + { + DepthMark mark = _depthMarks.Pop(); + } + } + + public void IncreaseDepth( + int mark + ) + { + _depthMarks.Push(new DepthMark(_operands.Count, _operators.Count, mark)); + } + + #endregion } } diff --git a/StgSharp.RegularAnalysis/Abstraction/ISyntaxNode.cs b/StgSharp.RegularAnalysis/Abstraction/ISyntaxNode.cs index 020cbc65..6db22c62 100644 --- a/StgSharp.RegularAnalysis/Abstraction/ISyntaxNode.cs +++ b/StgSharp.RegularAnalysis/Abstraction/ISyntaxNode.cs @@ -49,9 +49,9 @@ namespace StgSharp.RegularAnalysis.Abstraction TNode Next { get; set; } - TNode Left { get; } + TNode Left { get; set; } - TNode Right { get; } + TNode Right { get; set; } static abstract TNode Empty { get; } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs index 7fc2dacb..b249be64 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexAstNode.cs @@ -1,4 +1,32 @@ -using System; +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="RegexAstNode" +// 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 System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,7 +34,82 @@ using System.Threading.Tasks; namespace StgSharp.RegularAnalysis.TextRegex { - internal class RegexAstNode + internal class RegexAstNode : ISyntaxNode { + + private RegexAstNode _left, _right; + + public RegexAstNode( + Token source + ) + { + Source = source; + } + + public long NodeFlag => (long)Source.Flag; + + public static RegexAstNode Empty + { + get => new EmptyRegexAstNode(); + } + + public RegexAstNode Previous { get; set; } + + public RegexAstNode Next { get; set; } + + public RegexAstNode Left { get; set; } + + public RegexAstNode Right { get; set; } + + public RegexElementLabel EqualityTypeConvert + { + get => Source.Flag; + } + + public string CodeConvertTemplate { get; } + + public Token Source { get; } + + public void AppendNode( + RegexAstNode nextToken + ) { } + + public void PrependNode( + RegexAstNode previousNode + ) { } + + public static bool TryMergeUnit( + RegexAstNode prev, + RegexAstNode next, + out RegexAstNode result + ) + { + Token pSource = prev.Source; + Token nSource = next.Source; + if (pSource.IsAccurateChar() && + nSource.IsAccurateChar() && + pSource.Column + pSource.Value.Length == nSource.Column) + { + // both are unit types and adjacent + // and are all accurate unit + Token newToken = new Token($"{pSource.Value}{nSource.Value}", + pSource.Line, + pSource.Column, + RegexElementLabel.UNIT_SPAN); + result = new RegexAstNode(newToken); + return true; + } + result = Empty; + return false; + } + + private class EmptyRegexAstNode : RegexAstNode + + { + + public EmptyRegexAstNode() : base(Token.Empty) { } + + } + } } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs b/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs index 47ff50b1..c27b8bf0 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexElementLabel.cs @@ -44,9 +44,11 @@ namespace StgSharp.RegularAnalysis.TextRegex COUNT = 8, ALT = 16, CONCAT = 32, + UNIT_SPAN = 64, + SEQUENCE = UNIT | UNIT_SPAN, OPERATOR = COUNT | ALT | CONCAT, - ATOM_BEGIN = UNIT | GROUP_BEGIN, - ATOM_END = UNIT | GROUP_END | COUNT, + ATOM_BEGIN = UNIT | GROUP_BEGIN | UNIT_SPAN, + ATOM_END = UNIT | GROUP_END | COUNT | UNIT_SPAN, } } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs index 71ec2670..56eb788d 100644 --- a/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs +++ b/StgSharp.RegularAnalysis/TextRegex/RegexInterpreter.cs @@ -38,17 +38,87 @@ namespace StgSharp.RegularAnalysis.TextRegex public class RegexInterpreter { - public static AbstractSyntaxTree Analyze( - string regex + private AbstractSyntaxTree _tree = new(); + + private CompileStack _stack = new(); + + public AbstractSyntaxTree Analyze( + string regex ) { RegexTokenReader reader = new RegexTokenReader(regex); TokenParser lexer = reader.Pipe(() => new RegexTokenParser()); - while (lexer.TryReadToken(out Token token)) { } + while (lexer.TryReadToken(out Token token)) + { + if ((token.Flag & RegexElementLabel.SEQUENCE) != 0) + { + // char or charset + _stack.PushOperand(new RegexAstNode(token)); + } else if ((token.Flag & RegexElementLabel.GROUP_BEGIN) != 0) + { + _stack.IncreaseDepth(0); + } else if ((token.Flag & RegexElementLabel.GROUP_END) != 0) + { + // TODO: close all nodes here + while (_stack.TryPopOperator(out RegexAstNode? op)) + { + ProcessOperator(op); + } + if (_stack.OperandInDepthCount != 1) { + throw new InvalidOperationException(); + } + _stack.DecreaseDepth(); + } else if ((token.Flag & RegexElementLabel.OPERATOR) != 0) { } + } } - private static void + private void ProcessOperator( + RegexAstNode op + ) + { + if (op.EqualityTypeConvert == RegexElementLabel.CONCAT) + { + if (_stack.TryPopOperand(out RegexAstNode? _1) && + _stack.TryPopOperand(out RegexAstNode? _2)) + { + if (RegexAstNode.TryMergeUnit(_2, _1, out RegexAstNode? _3)) + { + _stack.PushOperand(_3); + } else + { + op.Left = _2; + op.Right = _1; + _stack.PushOperand(op); + } + } else + { + throw new InvalidOperationException("Insufficient operands for CONCAT operator."); + } + } else if (op.EqualityTypeConvert == RegexElementLabel.COUNT) + { + if (_stack.TryPopOperand(out RegexAstNode? _1)) + { + op.Left = _1; + _stack.PushOperand(op); + } else + { + throw new InvalidOperationException("Insufficient operands for CONCAT operator."); + } + } else if (op.EqualityTypeConvert == RegexElementLabel.ALT) + { + if (_stack.TryPopOperand(out RegexAstNode? _1) && + _stack.TryPopOperand(out RegexAstNode? _2)) + { + op.Left = _1; + op.Right = _2; + _stack.PushOperand(op); + } else + { + throw new InvalidOperationException("Insufficient operands for CONCAT operator."); + } + } + } } } diff --git a/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs b/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs new file mode 100644 index 00000000..c4c4b0ce --- /dev/null +++ b/StgSharp.RegularAnalysis/TextRegex/RegexTokenUtil.cs @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------- +// ----------------------------------------------------------------------- +// file="RegexTokenUtil" +// 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StgSharp.RegularAnalysis.TextRegex +{ + internal static class RegexTokenUtil + { + + public static bool IsAccurateChar( + this Token token + ) + { + return ((token.Flag & RegexElementLabel.UNIT) != 0 && token.Value.Length == 0) || + (token.Flag & RegexElementLabel.UNIT_SPAN) != 0; + } + + } +}