//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // file="RegexInterpreter.Optimize" // 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.IO; using System.Linq; using System.Net.Security; using System.Runtime.CompilerServices; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; namespace StgSharp.RegularAnalysis.TextRegex { public partial class RegexInterpreter { private static List FlattenAlt( RegexAstNode root ) { TreeEnumerator enumerator = new(root); List cases = []; RegexAstNode current; _ = enumerator.MoveNext(); // skip root while (enumerator.MoveNext()) { current = enumerator.Current; if (current.EqualityTypeConvert != RegexElementLabel.ALT) { cases.Add(current); enumerator.SkipNode(current); } current.Parent.RemoveChild(current); } return cases; } private static bool MergeConcat( RegexAstNode root ) { if (root.EqualityTypeConvert != RegexElementLabel.CONCAT) { return false; } /* * concat → concat * ╱ ╲ / * 'a' concat "abc" * / \ * 'b' 'c' */ List buffer = []; int length = 0; RegexAstNode right = root.Right; RegexAstNode left = root.Left; if (left.EqualityTypeConvert != RegexElementLabel.UNIT_SPAN && left.EqualityTypeConvert != RegexElementLabel.UNIT) { return false; } string str = left.Value; buffer.Add(str); length += str.Length; while (!RegexAstNode.IsNullOrEmpty(right) && right.EqualityTypeConvert == RegexElementLabel.CONCAT) { left = right.Left; if (left.EqualityTypeConvert != RegexElementLabel.UNIT_SPAN && left.EqualityTypeConvert != RegexElementLabel.UNIT) { break; } str = left.Value; buffer.Add(str); length += str.Length; root.Right = right.Right; right.Parent = RegexAstNode.Empty; right = root.Right; right.Parent = root; } if (right.EqualityTypeConvert == RegexElementLabel.UNIT_SPAN || right.EqualityTypeConvert == RegexElementLabel.UNIT) { root.Right = RegexAstNode.Empty; buffer.Add(right.Value); length += right.Value.Length; } str = string.Create(length, buffer, static( span, buffer ) => { int offset = 0; for (int i = 0; i < buffer.Count; i++) { buffer[i].AsSpan().CopyTo(span[offset..]); offset += buffer[i].Length; } }); root.Left = new RegexAstNode(new Token(str, left.Source.Line, left.Source.Column, str.Length == 1 ? RegexElementLabel.UNIT : RegexElementLabel.UNIT_SPAN)); return true; } private static void RotateConcat( RegexAstNode root ) { RegexAstNode left = root.Left; while (!RegexAstNode.IsNullOrEmpty(left) && left.EqualityTypeConvert == RegexElementLabel.CONCAT) { /* * root → root * ╱ ╲ ╱ ╲ * left c a left * ╱ ╲ ╱ ╲ * a b b c */ RegexAstNode a = left.Left; RegexAstNode b = left.Right; RegexAstNode c = root.Right; root.Left = a; if (RegexAstNode.IsNullOrEmpty(a)) { a.Parent = root; } root.Right = left; left.Left = b; left.Right = c; if (RegexAstNode.IsNullOrEmpty(b)) { b.Parent = left; } if (RegexAstNode.IsNullOrEmpty(c)) { c.Parent = left; } left = root.Left; } } #region merge alt /// /// Get union beginning part of cases /// /// /// /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private static RegexAstNode MergeAlt( List cases ) { UnionAlt union = new(0, cases); return union.AsNormal(); } private class UnionAlt : RegexAstNode { private const int MaxLevel = 8; private UnionAlt( int level, RegexAstNode node ) : base(new Token(string.Empty, 0, 0, 0)) { Level = level; Cases = new List(); Cases.Add(node.Right ?? Empty); UnionBegin = node; UnionEnd = node; } public UnionAlt( int level, List c ) : base(new Token(string.Empty, 0, 0, 0)) { Cases = c; Level = level; } public List Cases { get; set; } public RegexAstNode UnionBegin { get; set; } public RegexAstNode UnionEnd { get; set; } private int Level { get; set; } public RegexAstNode AsNormal() { if (Level >= MaxLevel) { goto build; } merge: List buffer = []; for (int i = 0; i < Cases.Count; i++) { RegexAstNode current = Cases[i]; int j = 0; for (; j < buffer.Count; j++) { if (CanMerge(current, buffer[j])) { // find a case in buffer that can merge with current case, // then merge them and break RegexAstNode b = buffer[j]; if (b is not UnionAlt alt) { alt = new UnionAlt(Level + 1, b); alt.AddCase(current); buffer[j] = alt; } else { alt.AddCase(current); } break; } } if (j >= buffer.Count) { buffer.Add(current); } } Cases = buffer; // TODO problem1 没处理case链表 // TODO problem2 没有处理立刻返回策略 if (Cases.Count == 1) { // if there is only one case, // then we suppose that its contained cases has a same beginning, // so we attach it to UnionEnd and stay level unchanged UnionAlt alt = (Cases[0] as UnionAlt)!; if (IsNullOrEmpty(UnionBegin)) { UnionBegin = alt.UnionBegin; UnionEnd = alt.UnionEnd; } else { alt.UnionBegin.Parent = UnionEnd; UnionEnd.Right = alt.UnionBegin; } Cases = alt.Cases; goto merge; } build: // suppose that from UnionBegin to UnionEnd is a normal form without union for (int i = 0; i < Cases.Count; i++) { if (Cases[i] is UnionAlt union) { Cases[i] = union.AsNormal(); } } RegexAstNode branch = new RegexAstNode(new Token(string.Empty, 0, UnionEnd.Source.Column, RegexElementLabel.ALT)); UnionEnd.Right = branch; branch.Parent = UnionEnd; UnionEnd = branch; for (int i = 0; i < Cases.Count - 1; i++) { Cases[i].AppendNode(Cases[i + 1]); Cases[i].Parent = UnionEnd; } UnionEnd.Right = Cases[0]; return UnionBegin; } private void AddCase( RegexAstNode _case ) { RegexAstNode right = _case.Right; right ??= Empty; Cases.Add(right); } private static bool CanMerge( RegexAstNode origin, RegexAstNode target ) { // suppose that origin is not a union, if (target is UnionAlt alt) { // then we suppose that alt has only one are same return origin .EqualityTypeConvert == RegexElementLabel.CONCAT && (origin.Left.EqualityTypeConvert & RegexElementLabel.SINGLE) != 0 && origin.Left.EqualityTypeConvert == alt.UnionEnd.Left.EqualityTypeConvert && origin.Left.Value == alt.UnionEnd.Left.Value; } else { return origin.EqualityTypeConvert == RegexElementLabel.CONCAT && target.EqualityTypeConvert == RegexElementLabel.CONCAT && (origin.Left.EqualityTypeConvert & RegexElementLabel.SINGLE) != 0 && origin.Left.EqualityTypeConvert == target.Left.EqualityTypeConvert && origin.Left.Value == target.Left.Value; } } } #endregion /**/ } }