MobileInternetTechnolody/Chiara/AnitomySharp/Token.cs
2024-07-04 15:27:19 +08:00

227 lines
8.2 KiB
C#

/*
* Copyright (c) 2014-2017, Eren Okka
* Copyright (c) 2016-2017, Paul Miller
* Copyright (c) 2017-2018, Tyler Bratton
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
namespace AnitomySharp;
/// <summary>
/// An anime filename is tokenized into individual <see cref="Token"/>s. This class represents an individual token.
/// </summary>
public class Token
{
/// <summary>
/// The category of the token.
/// </summary>
public enum TokenCategory
{
Unknown,
Bracket,
Delimiter,
Identifier,
Invalid
}
/// <summary>
/// TokenFlag, used for searching specific token categories. This allows granular searching of TokenCategories.
/// </summary>
public enum TokenFlag
{
// None
FlagNone,
// Categories
FlagBracket,
FlagNotBracket,
FlagDelimiter,
FlagNotDelimiter,
FlagIdentifier,
FlagNotIdentifier,
FlagUnknown,
FlagNotUnknown,
FlagValid,
FlagNotValid,
// Enclosed (Meaning that it is enclosed in some bracket (e.g. [ ] ))
FlagEnclosed,
FlagNotEnclosed
}
/// <summary>
/// Set of token category flags
/// </summary>
private static readonly List<TokenFlag> s_flagMaskCategories =
[
TokenFlag.FlagBracket, TokenFlag.FlagNotBracket,
TokenFlag.FlagDelimiter, TokenFlag.FlagNotDelimiter,
TokenFlag.FlagIdentifier, TokenFlag.FlagNotIdentifier,
TokenFlag.FlagUnknown, TokenFlag.FlagNotUnknown,
TokenFlag.FlagValid, TokenFlag.FlagNotValid
];
/// <summary>
/// Set of token enclosed flags
/// </summary>
private static readonly List<TokenFlag> s_flagMaskEnclosed = [TokenFlag.FlagEnclosed, TokenFlag.FlagNotEnclosed];
public TokenCategory Category { get; set; }
public string Content { get; set; }
public bool Enclosed { get; }
/// <summary>
/// Constructs a new token
/// </summary>
/// <param name="category">the token category</param>
/// <param name="content">the token content</param>
/// <param name="enclosed">whether or not the token is enclosed in braces</param>
public Token(TokenCategory category, string content, bool enclosed)
{
Category = category;
Content = content;
Enclosed = enclosed;
}
/// <summary>
/// Validates a token against the <code>flags</code>. The <code>flags</code> is used as a search parameter.
/// </summary>
/// <param name="token">the token</param>
/// <param name="flags">the flags the token must conform against</param>
/// <returns>true if the token conforms to the set of <code>flags</code>; false otherwise</returns>
private static bool CheckTokenFlags(Token token, ICollection<TokenFlag> flags)
{
// Make sure token is the correct closure
if (flags.Any(f => s_flagMaskEnclosed.Contains(f)))
{
bool success = CheckFlag(TokenFlag.FlagEnclosed) == token.Enclosed;
if (!success) return false; // Not enclosed correctly (e.g. enclosed when we're looking for non-enclosed).
}
// Make sure token is the correct category
if (!flags.Any(f => s_flagMaskCategories.Contains(f))) return true;
bool secondarySuccess = false;
CheckCategory(TokenFlag.FlagBracket, TokenFlag.FlagNotBracket, TokenCategory.Bracket);
CheckCategory(TokenFlag.FlagDelimiter, TokenFlag.FlagNotDelimiter, TokenCategory.Delimiter);
CheckCategory(TokenFlag.FlagIdentifier, TokenFlag.FlagNotIdentifier, TokenCategory.Identifier);
CheckCategory(TokenFlag.FlagUnknown, TokenFlag.FlagNotUnknown, TokenCategory.Unknown);
CheckCategory(TokenFlag.FlagNotValid, TokenFlag.FlagValid, TokenCategory.Invalid);
return secondarySuccess;
void CheckCategory(TokenFlag fe, TokenFlag fn, TokenCategory c)
{
if (secondarySuccess) return;
bool result = CheckFlag(fe) ? token.Category == c : CheckFlag(fn) && token.Category != c;
secondarySuccess = result;
}
// Simple alias to check if flag is a part of the set
bool CheckFlag(TokenFlag flag)
{
return flags.Contains(flag);
}
}
/// <summary>
/// Given a list of <code>tokens</code>, searches for any token token that matches the list of <code>flags</code>.
/// </summary>
/// <param name="tokens">the list of tokens</param>
/// <param name="begin">the search starting position.</param>
/// <param name="end">the search ending position.</param>
/// <param name="flags">the search flags</param>
/// <returns>the search result</returns>
public static int FindToken(List<Token> tokens, int begin, int end, params TokenFlag[] flags)
{
return FindTokenBase(tokens, begin, end, i => i < tokens.Count, i => i + 1, flags);
}
/// <summary>
/// Given a list of <code>tokens</code>, searches for the next token in <code>tokens</code> that matches the list of <code>flags</code>.
/// </summary>
/// <param name="tokens">the list of tokens</param>
/// <param name="first">the search starting position.</param>
/// <param name="flags">the search flags</param>
/// <returns>the search result</returns>
public static int FindNextToken(List<Token> tokens, int first, params TokenFlag[] flags)
{
return FindTokenBase(tokens, first + 1, tokens.Count, i => i < tokens.Count, i => i + 1, flags);
}
/// <summary>
/// Given a list of <code>tokens</code>, searches for the previous token in <code>tokens</code> that matches the list of <code>flags</code>.
/// </summary>
/// <param name="tokens">the list of tokens</param>
/// <param name="begin">the search starting position. Exclusive of position.Pos</param>
/// <param name="flags">the search flags</param>
/// <returns>the search result</returns>
public static int FindPrevToken(List<Token> tokens, int begin, params TokenFlag[] flags)
{
return FindTokenBase(tokens, begin - 1, -1, i => i >= 0, i => i - 1, flags);
}
/// <summary>
/// Given a list of tokens finds the first token that passes <see cref="CheckTokenFlags"/>.
/// </summary>
/// <param name="tokens">the list of the tokens to search</param>
/// <param name="begin">the start index of the search.</param>
/// <param name="end">the end index of the search.</param>
/// <param name="shouldContinue">a function that returns whether or not we should continue searching</param>
/// <param name="next">a function that returns the next search index</param>
/// <param name="flags">the flags that each token should be validated against</param>
/// <returns>the found token</returns>
private static int FindTokenBase(
List<Token> tokens,
int begin,
int end,
Func<int, bool> shouldContinue,
Func<int, int> next,
params TokenFlag[] flags)
{
var find = new List<TokenFlag>();
find.AddRange(flags);
for (int i = begin; shouldContinue(i); i = next(i))
{
var token = tokens[i];
if (CheckTokenFlags(token, find))
{
return i;
}
}
return end;
}
public static bool InListRange(int pos, List<Token> list)
{
return -1 < pos && pos < list.Count;
}
public override bool Equals(object? o)
{
if (this == o)
return true;
if (o is not Token token)
return false;
return Enclosed == token.Enclosed && Category == token.Category && Equals(Content, token.Content);
}
public override int GetHashCode()
{
int hashCode = -1776802967;
hashCode = hashCode * -1521134295 + Category.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Content);
hashCode = hashCode * -1521134295 + Enclosed.GetHashCode();
return hashCode;
}
public override string ToString()
{
return $"Token{{category={Category}, content='{Content}', enclosed={Enclosed}}}";
}
}