// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Reflection;
using EpicGames.Core;
using EpicGames.UHT.Parsers;
using EpicGames.UHT.Tokenizer;
using EpicGames.UHT.Utils;
namespace EpicGames.UHT.Tables
{
///
/// Invoke the given method when the keyword is parsed.
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class UhtKeywordAttribute : Attribute
{
///
/// Keyword table/scope being extended
///
public string? Extends { get; set; }
///
/// Name of the keyword
///
public string? Keyword { get; set; } = null;
///
/// Text to be displayed to the user when referencing this keyword
///
public string? AllowText { get; set; } = null;
///
/// If true, this applies to all scopes
///
public bool AllScopes { get; set; } = false;
///
/// If true, do not include in usage errors
///
public bool DisableUsageError { get; set; } = false;
///
/// List of the allowed compiler directives.
///
public UhtCompilerDirective AllowedCompilerDirectives { get; set; } = UhtCompilerDirective.DefaultAllowedCheck;
}
///
/// Invoked as a last chance processor for a keyword
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class UhtKeywordCatchAllAttribute : Attribute
{
///
/// Table/scope to be extended
///
public string? Extends { get; set; }
}
///
/// Delegate to notify a keyword was parsed
///
/// Current scope being parsed
/// The scope who's table was matched
/// Matching token
/// Results of the parsing
public delegate UhtParseResult UhtKeywordDelegate(UhtParsingScope topScope, UhtParsingScope actionScope, ref UhtToken token);
///
/// Delegate to invoke as a last chance processor for a keyword
///
/// Current scope being parsed
/// Matching token
/// Results of the parsing
public delegate UhtParseResult UhtKeywordCatchAllDelegate(UhtParsingScope topScope, ref UhtToken token);
///
/// Defines a keyword
///
public readonly struct UhtKeyword
{
///
/// Name of the keyword
///
public string Name { get; }
///
/// Delegate to invoke
///
public UhtKeywordDelegate Delegate { get; }
///
/// Text to be displayed to the user when referencing this keyword
///
public string? AllowText { get; }
///
/// If true, this applies to all scopes
///
public bool AllScopes { get; }
///
/// If true, do not include in usage errors
///
public bool DisableUsageError { get; }
///
/// List of the allowed compiler directives.
///
public UhtCompilerDirective AllowedCompilerDirectives { get; }
///
/// Construct a new keyword
///
/// Name of the keyword
/// Delegate to invoke
/// Defining attribute
public UhtKeyword(string name, UhtKeywordDelegate keywordDelegate, UhtKeywordAttribute? attribute)
{
Name = name;
Delegate = keywordDelegate;
if (attribute != null)
{
AllowText = attribute.AllowText;
AllScopes = attribute.AllScopes;
DisableUsageError = attribute.DisableUsageError;
AllowedCompilerDirectives = attribute.AllowedCompilerDirectives;
}
else
{
AllowText = null;
AllScopes = false;
DisableUsageError = false;
AllowedCompilerDirectives = UhtCompilerDirective.DefaultAllowedCheck;
}
}
}
///
/// Keyword table for a specific scope
///
public class UhtKeywordTable : UhtLookupTable
{
///
/// List of catch-alls associated with this table
///
public List CatchAlls { get; } = new List();
///
/// Construct a new keyword table
///
public UhtKeywordTable() : base(StringViewComparer.Ordinal)
{
}
///
/// Add the given value to the lookup table. It will throw an exception if it is a duplicate.
///
/// Value to be added
public UhtKeywordTable Add(UhtKeyword value)
{
base.Add(value.Name, value);
return this;
}
///
/// Add the given catch-all to the table.
///
/// The catch-all to be added
public UhtKeywordTable AddCatchAll(UhtKeywordCatchAllDelegate catchAll)
{
CatchAlls.Add(catchAll);
return this;
}
///
/// Merge the given keyword table. Duplicates in the BaseTypeTable will be ignored.
///
/// Base table being merged
public override void Merge(UhtLookupTableBase baseTable)
{
base.Merge(baseTable);
CatchAlls.AddRange(((UhtKeywordTable)baseTable).CatchAlls);
}
}
///
/// Table of all keyword tables
///
public class UhtKeywordTables : UhtLookupTables
{
///
/// Construct the keyword tables
///
public UhtKeywordTables() : base("keywords")
{
}
///
/// Handle a keyword attribute
///
/// Containing type
/// Method information
/// Defining attribute
/// Thrown if the attribute isn't well defined
public void OnKeywordCatchAllAttribute(Type type, MethodInfo methodInfo, UhtKeywordCatchAllAttribute keywordCatchAllAttribute)
{
if (String.IsNullOrEmpty(keywordCatchAllAttribute.Extends))
{
throw new UhtIceException($"The 'KeywordCatchAlll' attribute on the {type.Name}.{methodInfo.Name} method doesn't have a table specified.");
}
UhtKeywordTable table = Get(keywordCatchAllAttribute.Extends);
table.AddCatchAll((UhtKeywordCatchAllDelegate)Delegate.CreateDelegate(typeof(UhtKeywordCatchAllDelegate), methodInfo));
}
///
/// Handle a keyword attribute
///
/// Containing type
/// Method information
/// Defining attribute
/// Thrown if the attribute isn't well defined
public void OnKeywordAttribute(Type type, MethodInfo methodInfo, UhtKeywordAttribute keywordAttribute)
{
string name = UhtLookupTableBase.GetSuffixedName(type, methodInfo, keywordAttribute.Keyword, "Keyword");
if (String.IsNullOrEmpty(keywordAttribute.Extends))
{
throw new UhtIceException($"The 'Keyword' attribute on the {type.Name}.{methodInfo.Name} method doesn't have a table specified.");
}
UhtKeywordTable table = Get(keywordAttribute.Extends);
table.Add(new UhtKeyword(name, (UhtKeywordDelegate)Delegate.CreateDelegate(typeof(UhtKeywordDelegate), methodInfo), keywordAttribute));
}
///
/// Log an unhandled error
///
/// Destination message site
/// Keyword
public void LogUnhandledError(IUhtMessageSite messageSite, UhtToken token)
{
List? tables = null;
foreach (KeyValuePair kvp in Tables)
{
UhtKeywordTable keywordTable = kvp.Value;
if (keywordTable.Internal)
{
continue;
}
if (keywordTable.TryGetValue(token.Value, out UhtKeyword info))
{
if (info.DisableUsageError)
{
// Do not log anything for this keyword in this table
}
else
{
tables ??= new List();
if (info.AllowText != null)
{
tables.Add($"{keywordTable.UserName} {info.AllowText}");
}
else
{
tables.Add(keywordTable.UserName);
}
}
}
}
if (tables != null)
{
string text = UhtUtilities.MergeTypeNames(tables, "and");
messageSite.LogError(token.InputLine, $"Invalid use of keyword '{token.Value}'. It may only appear in {text} scopes");
}
}
}
}