// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Text; using EpicGames.Core; using EpicGames.UHT.Utils; namespace EpicGames.UHT.Tokenizer { /// /// Collection of general token reader extensions /// public static class UhtTokenReaderGeneralExtensions { /// /// Try to parse the given text /// /// Token reader /// Text to match /// True if the text matched public static bool TryOptional(this IUhtTokenReader tokenReader, string text) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier(text) || token.IsSymbol(text)) { tokenReader.ConsumeToken(); return true; } return false; } /// /// Try to parse the given text /// /// Token reader /// Text to match /// True if the text matched public static bool TryOptional(this IUhtTokenReader tokenReader, StringView text) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier(text) || token.IsSymbol(text)) { tokenReader.ConsumeToken(); return true; } return false; } /// /// Try to parse the given text. However, the matching token will not be consumed. /// /// Token reader /// Text to match /// True if the text matched public static bool TryPeekOptional(this IUhtTokenReader tokenReader, string text) { ref UhtToken token = ref tokenReader.PeekToken(); return token.IsIdentifier(text) || token.IsSymbol(text); } /// /// Try to parse the given text /// /// Token reader /// Text to match /// True if the text matched public static bool TryOptional(this IUhtTokenReader tokenReader, char text) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsSymbol(text)) { tokenReader.ConsumeToken(); return true; } return false; } /// /// Try to parse the given text /// /// Token reader /// Text to match /// Open that was matched /// True if the text matched public static bool TryOptional(this IUhtTokenReader tokenReader, char text, out UhtToken outToken) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsSymbol(text)) { outToken = token; tokenReader.ConsumeToken(); return true; } outToken = new UhtToken(); return false; } /// /// Try to parse the given text. However, the matching token will not be consumed. /// /// Token reader /// Text to match /// True if the text matched public static bool TryPeekOptional(this IUhtTokenReader tokenReader, char text) { ref UhtToken token = ref tokenReader.PeekToken(); return token.IsSymbol(text); } /// /// Parse optional text /// /// Token reader /// Text to match /// Token reader public static IUhtTokenReader Optional(this IUhtTokenReader tokenReader, string text) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier(text) || token.IsSymbol(text)) { tokenReader.ConsumeToken(); } return tokenReader; } /// /// Parse optional text /// /// Token reader /// Text to match /// Action to invoke if the text was found /// Token reader public static IUhtTokenReader Optional(this IUhtTokenReader tokenReader, string text, Action action) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier(text) || token.IsSymbol(text)) { tokenReader.ConsumeToken(); action(); } return tokenReader; } /// /// Parse optional text /// /// Token reader /// Text to match /// Token reader public static IUhtTokenReader Optional(this IUhtTokenReader tokenReader, char text) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsSymbol(text)) { tokenReader.ConsumeToken(); } return tokenReader; } /// /// Parse optional text /// /// Token reader /// Text to match /// Action to invoke if the text was found /// Token reader public static IUhtTokenReader Optional(this IUhtTokenReader tokenReader, char text, Action action) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsSymbol(text)) { tokenReader.ConsumeToken(); action(); } return tokenReader; } /// /// Parse optional token that starts with the given text /// /// Token reader /// Text to match /// Token reader public static IUhtTokenReader OptionalStartsWith(this IUhtTokenReader tokenReader, string text) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier() && token.ValueStartsWith(text)) { tokenReader.ConsumeToken(); } return tokenReader; } /// /// Parse optional token that starts with the given text /// /// Token reader /// Text to match /// Delegate to invoke on a match /// Token reader public static IUhtTokenReader OptionalStartsWith(this IUhtTokenReader tokenReader, string text, UhtTokenDelegate tokenDelegate) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier() && token.ValueStartsWith(text)) { UhtToken currentToken = token; tokenReader.ConsumeToken(); tokenDelegate(ref currentToken); } return tokenReader; } /// /// Parse optional text that ends with with the given text /// /// Token reader /// Text to match /// Action to invoke if the text was found /// Token reader public static IUhtTokenReader OptionalEndsWith(this IUhtTokenReader tokenReader, string text, Action action) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier() && token.Value.Span.EndsWith(text)) { tokenReader.ConsumeToken(); action(token.Value.Span.ToString()); } return tokenReader; } /// /// Parse attributes and optionally alignment specifier /// /// Token reader /// If true, also parse alignment specifiers /// If specified, action to be invoked for every attribute found /// Token reader public static IUhtTokenReader OptionalAttributes(this IUhtTokenReader tokenReader, bool enableAlignAs, Action? attributeAction = null) { for (; ; ) { UhtToken token = tokenReader.PeekToken(); if (token.IsIdentifier("DEPRECATED") || token.IsIdentifier("UE_DEPRECATED") || token.IsIdentifier("UE_DEPRECATED_FORGAME") || token.IsIdentifier("UE_DEPRECATED_FORENGINE")) { tokenReader.ConsumeToken(); tokenReader.Require('(', "deprecation macro"); token = tokenReader.GetToken(); if (!token.IsConstFloat() && !token.IsIdentifier("all", ignoreCase:true)) { throw new UhtTokenException(tokenReader, token, "'all' or constant float version in deprecation macro"); } tokenReader.Require(',', "deprecation macro") .RequireConstString("message in deprecation macro") .Require(')', "deprecation macro"); attributeAction?.Invoke("deprecated"); } else if (token.IsIdentifier("UE_EXPERIMENTAL")) { tokenReader.ConsumeToken(); tokenReader .Require('(', "experimental macro") .RequireConstFloat() .Require(',', "experimental macro") .RequireConstString("message in experimental macro") .Require(')', "experimental macro"); attributeAction?.Invoke("experimental"); } else if (token.IsIdentifier("UE_INTERNAL")) { tokenReader.ConsumeToken(); } else if (token.IsIdentifier("UE_NODISCARD") || token.IsIdentifier("UE_NODISCARD_CTOR")) { tokenReader.ConsumeToken(); attributeAction?.Invoke("nodiscard"); } else if (token.IsIdentifier("UE_NORETURN")) { tokenReader.ConsumeToken(); attributeAction?.Invoke("noreturn"); } else if (token.IsIdentifier("UE_NO_UNIQUE_ADDRESS")) { tokenReader.ConsumeToken(); attributeAction?.Invoke("no_unique_address"); } else if (token.IsSymbol("[[")) { tokenReader.ConsumeToken(); StringBuilder identifierBuilder = StringBuilderCache.Small.Borrow(); try { if (tokenReader.TryOptional("using")) { tokenReader .RequireCppIdentifier(UhtCppIdentifierOptions.None, x => x.Join(identifierBuilder, "::")) .Require(':'); } if (identifierBuilder.Length > 0) { identifierBuilder.Append("::"); } int identifierBuilderLength = identifierBuilder.Length; // Attributes do allow patterns such as [[,]]. for (; ; ) { token = tokenReader.GetToken(); if (token.IsIdentifier()) { tokenReader.RequireCppIdentifier(ref token, UhtCppIdentifierOptions.None, x => x.Join(identifierBuilder, "::")); if (tokenReader.TryOptional('(')) { int nestingCount = 1; while (nestingCount > 0) { if (tokenReader.IsEOF) { throw new UhtTokenException(tokenReader, tokenReader.GetToken(), ")"); } else if (tokenReader.TryOptional(')')) { nestingCount--; } else if (tokenReader.TryOptional('(')) { nestingCount++; } tokenReader.ConsumeToken(); } } attributeAction?.Invoke(identifierBuilder.ToString()); identifierBuilder.Length = identifierBuilderLength; } else if (token.IsSymbol(',')) { } else if (token.IsSymbol(']')) { // This isn't technically correct since it could be "] ]" tokenReader.Require(']'); break; } else { throw new UhtTokenException(tokenReader, token, "]], ',', or identifier"); } } } finally { StringBuilderCache.Small.Return(identifierBuilder); } } else if (enableAlignAs && token.IsIdentifier("alignas")) { tokenReader.ConsumeToken(); tokenReader .Require('(', "alignment specifier") .RequireConstInt("alignment specifier") .Require(')', "alignment specifier"); } else { break; } } return tokenReader; } /// /// Require the given text /// /// Token reader /// Required text /// Extra exception context /// Token reader /// Thrown if text is not found public static IUhtTokenReader Require(this IUhtTokenReader tokenReader, string text, object? exceptionContext = null) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier(text) || token.IsSymbol(text)) { tokenReader.ConsumeToken(); } else { throw new UhtTokenException(tokenReader, token, text, exceptionContext); } return tokenReader; } /// /// Require the given text /// /// Token reader /// Required text /// Delegate to invoke on a match /// Token reader /// Thrown if text is not found public static IUhtTokenReader Require(this IUhtTokenReader tokenReader, string text, UhtTokenDelegate tokenDelegate) { return tokenReader.Require(text, null, tokenDelegate); } /// /// Require the given text /// /// Token reader /// Required text /// Extra exception context /// Delegate to invoke on a match /// Token reader /// Thrown if text is not found public static IUhtTokenReader Require(this IUhtTokenReader tokenReader, string text, object? exceptionContext, UhtTokenDelegate tokenDelegate) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsIdentifier(text) || token.IsSymbol(text)) { UhtToken currentToken = token; tokenReader.ConsumeToken(); tokenDelegate(ref currentToken); } else { throw new UhtTokenException(tokenReader, token, text, exceptionContext); } return tokenReader; } /// /// Require the given text /// /// Token reader /// Required text /// Extra exception context /// Token reader /// Thrown if text is not found public static IUhtTokenReader Require(this IUhtTokenReader tokenReader, char text, object? exceptionContext = null) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsSymbol(text)) { tokenReader.ConsumeToken(); } else { throw new UhtTokenException(tokenReader, token, text, exceptionContext); } return tokenReader; } /// /// Require the given text /// /// Token reader /// Required text /// Delegate to invoke on a match /// Token reader /// Thrown if text is not found public static IUhtTokenReader Require(this IUhtTokenReader tokenReader, char text, UhtTokenDelegate tokenDelegate) { return tokenReader.Require(text, null, tokenDelegate); } /// /// Require the given text /// /// Token reader /// Required text /// Extra exception context /// Delegate to invoke on a match /// Token reader /// Thrown if text is not found public static IUhtTokenReader Require(this IUhtTokenReader tokenReader, char text, object? exceptionContext, UhtTokenDelegate tokenDelegate) { ref UhtToken token = ref tokenReader.PeekToken(); if (token.IsSymbol(text)) { UhtToken currentToken = token; tokenReader.ConsumeToken(); tokenDelegate(ref currentToken); } else { throw new UhtTokenException(tokenReader, token, text, exceptionContext); } return tokenReader; } } }