/* * 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; /// /// An anime filename is tokenized into individual s. This class represents an individual token. /// public class Token { /// /// The category of the token. /// public enum TokenCategory { Unknown, Bracket, Delimiter, Identifier, Invalid } /// /// TokenFlag, used for searching specific token categories. This allows granular searching of TokenCategories. /// 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 } /// /// Set of token category flags /// private static readonly List s_flagMaskCategories = [ TokenFlag.FlagBracket, TokenFlag.FlagNotBracket, TokenFlag.FlagDelimiter, TokenFlag.FlagNotDelimiter, TokenFlag.FlagIdentifier, TokenFlag.FlagNotIdentifier, TokenFlag.FlagUnknown, TokenFlag.FlagNotUnknown, TokenFlag.FlagValid, TokenFlag.FlagNotValid ]; /// /// Set of token enclosed flags /// private static readonly List s_flagMaskEnclosed = [TokenFlag.FlagEnclosed, TokenFlag.FlagNotEnclosed]; public TokenCategory Category { get; set; } public string Content { get; set; } public bool Enclosed { get; } /// /// Constructs a new token /// /// the token category /// the token content /// whether or not the token is enclosed in braces public Token(TokenCategory category, string content, bool enclosed) { Category = category; Content = content; Enclosed = enclosed; } /// /// Validates a token against the flags. The flags is used as a search parameter. /// /// the token /// the flags the token must conform against /// true if the token conforms to the set of flags; false otherwise private static bool CheckTokenFlags(Token token, ICollection 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); } } /// /// Given a list of tokens, searches for any token token that matches the list of flags. /// /// the list of tokens /// the search starting position. /// the search ending position. /// the search flags /// the search result public static int FindToken(List tokens, int begin, int end, params TokenFlag[] flags) { return FindTokenBase(tokens, begin, end, i => i < tokens.Count, i => i + 1, flags); } /// /// Given a list of tokens, searches for the next token in tokens that matches the list of flags. /// /// the list of tokens /// the search starting position. /// the search flags /// the search result public static int FindNextToken(List tokens, int first, params TokenFlag[] flags) { return FindTokenBase(tokens, first + 1, tokens.Count, i => i < tokens.Count, i => i + 1, flags); } /// /// Given a list of tokens, searches for the previous token in tokens that matches the list of flags. /// /// the list of tokens /// the search starting position. Exclusive of position.Pos /// the search flags /// the search result public static int FindPrevToken(List tokens, int begin, params TokenFlag[] flags) { return FindTokenBase(tokens, begin - 1, -1, i => i >= 0, i => i - 1, flags); } /// /// Given a list of tokens finds the first token that passes . /// /// the list of the tokens to search /// the start index of the search. /// the end index of the search. /// a function that returns whether or not we should continue searching /// a function that returns the next search index /// the flags that each token should be validated against /// the found token private static int FindTokenBase( List tokens, int begin, int end, Func shouldContinue, Func next, params TokenFlag[] flags) { var find = new List(); 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 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.Default.GetHashCode(Content); hashCode = hashCode * -1521134295 + Enclosed.GetHashCode(); return hashCode; } public override string ToString() { return $"Token{{category={Category}, content='{Content}', enclosed={Enclosed}}}"; } }