// Copyright Epic Games, Inc. All Rights Reserved. using System; using EpicGames.UHT.Utils; namespace EpicGames.UHT.Tokenizer { /// /// Options when parsing identifier /// [Flags] public enum UhtCppIdentifierOptions { /// /// No options /// None, /// /// Include template arguments when parsing identifier /// AllowTemplates = 1 << 0, } /// /// Helper methods for testing flags. These methods perform better than the generic HasFlag which hits /// the GC and stalls. /// public static class UhtGetCppIdentifierOptionsExtensions { /// /// Test to see if any of the specified flags are set /// /// Current flags /// Flags to test for /// True if any of the flags are set public static bool HasAnyFlags(this UhtCppIdentifierOptions inFlags, UhtCppIdentifierOptions testFlags) { return (inFlags & testFlags) != 0; } /// /// Test to see if all of the specified flags are set /// /// Current flags /// Flags to test for /// True if all the flags are set public static bool HasAllFlags(this UhtCppIdentifierOptions inFlags, UhtCppIdentifierOptions testFlags) { return (inFlags & testFlags) == testFlags; } /// /// Test to see if a specific set of flags have a specific value. /// /// Current flags /// Flags to test for /// Expected value of the tested flags /// True if the given flags have a specific value. public static bool HasExactFlags(this UhtCppIdentifierOptions inFlags, UhtCppIdentifierOptions testFlags, UhtCppIdentifierOptions matchFlags) { return (inFlags & testFlags) == matchFlags; } } /// /// Collection of token reader extensions for working with identifiers /// public static class UhtTokenReaderIdentifierExtensions { /// /// Get the next token and verify that it is an identifier /// /// True if it is an identifier, false if not. public static bool TryOptionalIdentifier(this IUhtTokenReader tokenReader) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier()) { tokenReader.ConsumeToken(); return true; } return false; } /// /// Get the next token and verify that it is an identifier /// /// Token reader /// The fetched value of the identifier /// True if it is an identifier, false if not. public static bool TryOptionalIdentifier(this IUhtTokenReader tokenReader, out UhtToken identifier) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier()) { identifier = token; tokenReader.ConsumeToken(); return true; } identifier = new UhtToken(); return false; } /// /// Parse an optional identifier /// /// Token reader /// Invoked of an identifier is parsed /// Token reader public static IUhtTokenReader OptionalIdentifier(this IUhtTokenReader tokenReader, UhtTokenDelegate tokenDelegate) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier()) { UhtToken tokenCopy = token; tokenReader.ConsumeToken(); tokenDelegate(ref tokenCopy); } return tokenReader; } /// /// Parse an optional namespace /// /// Token reader /// The text of the namespace /// Token reader public static IUhtTokenReader OptionalNamespace(this IUhtTokenReader tokenReader, string namespaceText) { if (tokenReader.TryOptional(namespaceText)) { tokenReader.Require("::"); } return tokenReader; } /// /// Parse a required identifier /// /// Token reader /// Extra exception context /// Token reader /// Thrown if an identifier isn't found public static IUhtTokenReader RequireIdentifier(this IUhtTokenReader tokenReader, object? exceptionContext = null) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier()) { tokenReader.ConsumeToken(); return tokenReader; } throw new UhtTokenException(tokenReader, token, "an identifier", exceptionContext); } /// /// Parse a required identifier /// /// Token reader /// Invoked if an identifier is parsed /// Token reader public static IUhtTokenReader RequireIdentifier(this IUhtTokenReader tokenReader, UhtTokenDelegate tokenDelegate) { return tokenReader.RequireIdentifier(null, tokenDelegate); } /// /// Parse a required identifier /// /// Token reader /// Extra exception context /// Invoked if an identifier is parsed /// Token reader /// Thrown if an identifier isn't found public static IUhtTokenReader RequireIdentifier(this IUhtTokenReader tokenReader, object? exceptionContext, UhtTokenDelegate tokenDelegate) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier()) { UhtToken currentToken = token; tokenReader.ConsumeToken(); tokenDelegate(ref currentToken); return tokenReader; } throw new UhtTokenException(tokenReader, token, "an identifier", exceptionContext); } /// /// Get a required identifier /// /// Token reader /// Extra exception context /// Identifier token /// Thrown if an identifier isn't found public static UhtToken GetIdentifier(this IUhtTokenReader tokenReader, object? exceptionContext = null) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier()) { UhtToken currentToken = token; tokenReader.ConsumeToken(); return currentToken; } throw new UhtTokenException(tokenReader, token, exceptionContext ?? "an identifier"); } /// /// Parse a required cpp identifier /// /// Token reader /// Parsing options /// Invoked when identifier is parsed /// Token reader public static IUhtTokenReader RequireCppIdentifier(this IUhtTokenReader tokenReader, UhtCppIdentifierOptions options, UhtTokenListDelegate tokenListDelegate) { UhtTokenList tokenList = tokenReader.GetCppIdentifier(options); tokenListDelegate(tokenList); UhtTokenListCache.Return(tokenList); return tokenReader; } /// /// Parse a required cpp identifier /// /// Token reader /// Initial token of the identifier /// Parsing options /// Invoked when identifier is parsed /// Token reader public static IUhtTokenReader RequireCppIdentifier(this IUhtTokenReader tokenReader, ref UhtToken initialIdentifier, UhtCppIdentifierOptions options, UhtTokenListDelegate tokenListDelegate) { UhtTokenList tokenList = tokenReader.GetCppIdentifier(ref initialIdentifier, options); tokenListDelegate(tokenList); UhtTokenListCache.Return(tokenList); return tokenReader; } /// /// Get a required cpp identifier /// /// Token reader /// Parsing options /// Token list public static UhtTokenList GetCppIdentifier(this IUhtTokenReader tokenReader, UhtCppIdentifierOptions options = UhtCppIdentifierOptions.None) { UhtToken token = tokenReader.GetIdentifier(); return tokenReader.GetCppIdentifier(ref token, options); } /// /// Get a required cpp identifier /// /// Token reader /// Initial token of the identifier /// Parsing options /// Token list public static UhtTokenList GetCppIdentifier(this IUhtTokenReader tokenReader, ref UhtToken initialIdentifier, UhtCppIdentifierOptions options = UhtCppIdentifierOptions.None) { UhtTokenList listHead = UhtTokenListCache.Borrow(initialIdentifier); UhtTokenList listTail = listHead; if (options.HasAnyFlags(UhtCppIdentifierOptions.AllowTemplates)) { while (true) { if (tokenReader.TryPeekOptional('<')) { listTail.Next = UhtTokenListCache.Borrow(tokenReader.GetToken()); listTail = listTail.Next; int nestedScopes = 1; while (nestedScopes > 0) { UhtToken token = tokenReader.GetToken(); if (token.TokenType.IsEndType()) { throw new UhtTokenException(tokenReader, token, new string[] { "<", ">" }, "template"); } listTail.Next = UhtTokenListCache.Borrow(token); listTail = listTail.Next; if (token.IsSymbol('<')) { ++nestedScopes; } else if (token.IsSymbol('>')) { --nestedScopes; } } } if (!tokenReader.TryPeekOptional("::")) { break; } listTail.Next = UhtTokenListCache.Borrow(tokenReader.GetToken()); listTail = listTail.Next; listTail.Next = UhtTokenListCache.Borrow(tokenReader.GetIdentifier()); listTail = listTail.Next; } } else if (tokenReader.PeekToken().IsSymbol("::")) { tokenReader.While("::", () => { listTail.Next = UhtTokenListCache.Borrow(tokenReader.GetIdentifier()); listTail = listTail.Next; }); } return listHead; } } }