227 lines
8.2 KiB
C#
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}}}";
|
|
}
|
|
}
|