// 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;
}
}
}