| File: System\Text\RegularExpressions\Symbolic\SymbolicRegexInfo.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.RegularExpressions\src\System.Text.RegularExpressions.csproj (System.Text.RegularExpressions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; namespace System.Text.RegularExpressions.Symbolic { /// <summary>Misc information of structural properties of a <see cref="SymbolicRegexNode{S}"/> that is computed bottom up.</summary> internal readonly struct SymbolicRegexInfo : IEquatable<SymbolicRegexInfo> { private const uint IsAlwaysNullableMask = 1; private const uint StartsWithLineAnchorMask = 2; private const uint IsLazyLoopMask = 4; private const uint CanBeNullableMask = 8; private const uint ContainsSomeAnchorMask = 16; private const uint StartsWithSomeAnchorMask = 32; private const uint IsHighPriorityNullableMask = 64; private const uint ContainsEffectMask = 128; private const uint ContainsLineAnchorMask = 256; private const uint ContainsEndZAnchorMask = 512; private readonly uint _info; private SymbolicRegexInfo(uint i) => _info = i; private static SymbolicRegexInfo Create( bool isAlwaysNullable = false, bool canBeNullable = false, bool startsWithLineAnchor = false, bool containsLineAnchor = false, bool startsWithSomeAnchor = false, bool containsSomeAnchor = false, bool isHighPriorityNullable = false, bool containsEffect = false, bool containsEndZAnchor = false) { // Assert that the expected implications hold. For example, every node that contains a line anchor // must also be marked as containing some anchor. Debug.Assert(!isAlwaysNullable || canBeNullable); Debug.Assert(!startsWithLineAnchor || containsLineAnchor); Debug.Assert(!startsWithLineAnchor || startsWithSomeAnchor); Debug.Assert(!containsLineAnchor || containsSomeAnchor); Debug.Assert(!startsWithSomeAnchor || containsSomeAnchor); return new SymbolicRegexInfo( (isAlwaysNullable ? IsAlwaysNullableMask : 0) | (canBeNullable ? CanBeNullableMask : 0) | (startsWithLineAnchor ? StartsWithLineAnchorMask : 0) | (containsLineAnchor ? ContainsLineAnchorMask : 0) | (startsWithSomeAnchor ? StartsWithSomeAnchorMask : 0) | (containsSomeAnchor ? ContainsSomeAnchorMask : 0) | (isHighPriorityNullable ? IsHighPriorityNullableMask : 0) | (containsEffect ? ContainsEffectMask : 0) | (containsEndZAnchor ? ContainsEndZAnchorMask : 0)); } public bool IsNullable => (_info & IsAlwaysNullableMask) != 0; public bool CanBeNullable => (_info & CanBeNullableMask) != 0; public bool StartsWithLineAnchor => (_info & StartsWithLineAnchorMask) != 0; public bool ContainsLineAnchor => (_info & ContainsLineAnchorMask) != 0; public bool StartsWithSomeAnchor => (_info & StartsWithSomeAnchorMask) != 0; public bool ContainsSomeAnchor => (_info & ContainsSomeAnchorMask) != 0; public bool IsLazyLoop => (_info & IsLazyLoopMask) != 0; public bool IsHighPriorityNullable => (_info & IsHighPriorityNullableMask) != 0; public bool ContainsEffect => (_info & ContainsEffectMask) != 0; public bool ContainsEndZAnchor => (_info & ContainsEndZAnchorMask) != 0; /// <summary> /// Used for any node that acts as an epsilon, i.e., something that always matches the empty string. /// </summary> public static SymbolicRegexInfo Epsilon() => Create( isAlwaysNullable: true, canBeNullable: true, isHighPriorityNullable: true); /// <summary> /// Used for all anchors. /// </summary> /// <param name="isLineAnchor">whether this anchor is a line anchor</param> /// <param name="isEndZAnchor">whether this anchor is an end Z anchor</param> public static SymbolicRegexInfo Anchor(bool isLineAnchor, bool isEndZAnchor) => Create( canBeNullable: true, startsWithLineAnchor: isLineAnchor, containsLineAnchor: isLineAnchor, startsWithSomeAnchor: true, containsSomeAnchor: true, containsEndZAnchor: isEndZAnchor); /// <summary> /// The alternation remains high priority nullable if the left alternative is so. /// All other info properties are the logical disjunction of the respective info properties /// except that IsLazyLoop is false. /// </summary> public static SymbolicRegexInfo Alternate(SymbolicRegexInfo left_info, SymbolicRegexInfo right_info) => Create( isAlwaysNullable: left_info.IsNullable || right_info.IsNullable, canBeNullable: left_info.CanBeNullable || right_info.CanBeNullable, startsWithLineAnchor: left_info.StartsWithLineAnchor || right_info.StartsWithLineAnchor, containsLineAnchor: left_info.ContainsLineAnchor || right_info.ContainsLineAnchor, startsWithSomeAnchor: left_info.StartsWithSomeAnchor || right_info.StartsWithSomeAnchor, containsSomeAnchor: left_info.ContainsSomeAnchor || right_info.ContainsSomeAnchor, isHighPriorityNullable: left_info.IsHighPriorityNullable, containsEffect: left_info.ContainsEffect || right_info.ContainsEffect, containsEndZAnchor: left_info.ContainsEndZAnchor || right_info.ContainsEndZAnchor); /// <summary> /// Concatenation remains high priority nullable if both left and right are so. /// Nullability is conjunctive and other properies are essentially disjunctive, /// except that IsLazyLoop is false. /// </summary> public static SymbolicRegexInfo Concat(SymbolicRegexInfo left_info, SymbolicRegexInfo right_info) => Create( isAlwaysNullable: left_info.IsNullable && right_info.IsNullable, canBeNullable: left_info.CanBeNullable && right_info.CanBeNullable, startsWithLineAnchor: left_info.StartsWithLineAnchor || (left_info.CanBeNullable && right_info.StartsWithLineAnchor), containsLineAnchor: left_info.ContainsLineAnchor || right_info.ContainsLineAnchor, startsWithSomeAnchor: left_info.StartsWithSomeAnchor || (left_info.CanBeNullable && right_info.StartsWithSomeAnchor), containsSomeAnchor: left_info.ContainsSomeAnchor || right_info.ContainsSomeAnchor, isHighPriorityNullable: left_info.IsHighPriorityNullable && right_info.IsHighPriorityNullable, containsEffect: left_info.ContainsEffect || right_info.ContainsEffect, containsEndZAnchor: left_info.ContainsEndZAnchor || right_info.ContainsEndZAnchor ); /// <summary> /// Inherits anchor visibility from the loop body. /// Is nullable if either the body is nullable or if the lower bound is 0. /// Is high priority nullable when lazy and the lower bound is 0. /// </summary> public static SymbolicRegexInfo Loop(SymbolicRegexInfo body_info, int lowerBound, bool isLazy) { // Inherit anchor visibility from the loop body uint i = body_info._info; // The loop is nullable if either the body is nullable or if the lower bound is 0 if (lowerBound == 0) { i |= IsAlwaysNullableMask | CanBeNullableMask; if (isLazy) { i |= IsHighPriorityNullableMask; } } // The loop is lazy iff it is marked lazy if (isLazy) { i |= IsLazyLoopMask; } else { i &= ~IsLazyLoopMask; } return new SymbolicRegexInfo(i); } public static SymbolicRegexInfo Effect(SymbolicRegexInfo childInfo) => new SymbolicRegexInfo(childInfo._info | ContainsEffectMask); public override bool Equals(object? obj) => obj is SymbolicRegexInfo i && Equals(i); public bool Equals(SymbolicRegexInfo other) => _info == other._info; public override int GetHashCode() => _info.GetHashCode(); #if DEBUG public override string ToString() => _info.ToString("X"); #endif } }