Fixes enum store when SymbolTable is replaced while setting up config (#2729)

This PR fixes the issue where inbuilt enums are no longer part of the
config if the config's symbol table is replaced after initialization.

After initializing the config if SymbolTable was replaced, we were
loosing the enums attached to the previous default symbol table. This PR
will adds a default symbol table(seprate from existing table) to handle
that.
This commit is contained in:
Jas Valgotar 2024-11-05 18:02:14 -05:00 коммит произвёл GitHub
Родитель 7b70dd927c
Коммит c5a3aeadb5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 72 добавлений и 27 удалений

Просмотреть файл

@ -23,20 +23,41 @@ namespace Microsoft.PowerFx
internal static readonly int DefaultMaxCallDepth = 20;
internal static readonly int DefaultMaximumExpressionLength = 1000;
/// <summary>
/// Global symbols. Additional symbols beyond default function set and primitive types.
/// </summary>
public SymbolTable SymbolTable { get; set; } = new SymbolTable
private SymbolTable _symbolTable = new SymbolTable
{
DebugName = "DefaultConfig"
DebugName = "Host Symbols"
};
private readonly SymbolTable _internalConfigSymbols = new SymbolTable
{
DebugName = "InternalConfigSymbols"
};
internal SymbolTable InternalConfigSymbols => _internalConfigSymbols;
/// <summary>
/// We shouldn't cache it as SymbolTable is mutable.
/// </summary>
internal ReadOnlySymbolTable ComposedConfigSymbols => ReadOnlySymbolTable.Compose(InternalConfigSymbols, SymbolTable);
private static EnumStoreBuilder BuiltInEnumStoreBuilder => new EnumStoreBuilder().WithRequiredEnums(BuiltinFunctionsCore._library);
/// <summary>
/// Global symbols. Additional symbols beyond default function set and primitive types defined by host.
/// </summary>
///
public SymbolTable SymbolTable
{
get => _symbolTable;
set => _symbolTable = value;
}
internal readonly Dictionary<TexlFunction, IAsyncTexlFunction> AdditionalFunctions = new ();
[Obsolete("Use Config.EnumStore or symboltable directly")]
internal EnumStoreBuilder EnumStoreBuilder => SymbolTable.EnumStoreBuilder;
internal EnumStoreBuilder EnumStoreBuilder => InternalConfigSymbols.EnumStoreBuilder;
internal IEnumStore EnumStore => ReadOnlySymbolTable.Compose(SymbolTable);
internal IEnumStore EnumStore => ComposedConfigSymbols;
public Features Features { get; }
@ -47,7 +68,7 @@ namespace Microsoft.PowerFx
private PowerFxConfig(EnumStoreBuilder enumStoreBuilder, Features features = null)
{
Features = features ?? Features.None;
SymbolTable.EnumStoreBuilder = enumStoreBuilder;
InternalConfigSymbols.EnumStoreBuilder = enumStoreBuilder;
MaxCallDepth = DefaultMaxCallDepth;
MaximumExpressionLength = DefaultMaximumExpressionLength;
}
@ -66,7 +87,7 @@ namespace Microsoft.PowerFx
[Obsolete("Migrate to SymbolTables")]
public IEnumerable<FunctionInfo> FunctionInfos =>
new Engine(this).SupportedFunctions.Functions.Functions
.Concat(SymbolTable.Functions.Functions)
.Concat(ComposedConfigSymbols.Functions.Functions)
.Select(f => new FunctionInfo(f));
/// <summary>
@ -74,7 +95,7 @@ namespace Microsoft.PowerFx
/// </summary>
/// <param name="features">Features to use.</param>
public PowerFxConfig(Features features)
: this(new EnumStoreBuilder().WithRequiredEnums(BuiltinFunctionsCore._library), features)
: this(BuiltInEnumStoreBuilder, features)
{
}
@ -115,12 +136,12 @@ namespace Microsoft.PowerFx
}
}
internal bool GetSymbols(string name, out NameLookupInfo symbol) => SymbolTable._variables.TryGetValue(name, out symbol);
internal bool GetSymbols(string name, out NameLookupInfo symbol) => InternalConfigSymbols._variables.TryGetValue(name, out symbol) || SymbolTable._variables.TryGetValue(name, out symbol);
internal IEnumerable<string> GetSuggestableSymbolName() => SymbolTable._variables.Keys;
internal IEnumerable<string> GetSuggestableSymbolName() => InternalConfigSymbols._variables.Keys.Concat(SymbolTable._variables.Keys);
internal void AddEntity(IExternalEntity entity, DName displayName = default)
=> SymbolTable.AddEntity(entity, displayName);
=> SymbolTable.AddEntity(entity, displayName);
internal void AddFunction(TexlFunction function)
{
@ -137,7 +158,7 @@ namespace Microsoft.PowerFx
}
}
var overloads = SymbolTable.Functions.WithName(function.Name).Where(tf => tf.HasLambdas || tf.HasColumnIdentifiers);
var overloads = ComposedConfigSymbols.Functions.WithName(function.Name).Where(tf => tf.HasLambdas || tf.HasColumnIdentifiers);
if (overloads.Any())
{
@ -153,20 +174,25 @@ namespace Microsoft.PowerFx
}
}
SymbolTable.AddFunction(function);
InternalConfigSymbols.AddFunction(function);
}
internal void AddFunctions(TexlFunctionSet functionSet)
{
SymbolTable.AddFunctions(functionSet);
InternalConfigSymbols.AddFunctions(functionSet);
}
/// <summary>
/// Adds optionset to <see cref="SymbolTable"/>.
/// </summary>
/// <param name="optionSet"></param>
/// <param name="optionalDisplayName"></param>
public void AddOptionSet(OptionSet optionSet, DName optionalDisplayName = default)
{
SymbolTable.AddOptionSet(optionSet, optionalDisplayName);
}
internal bool TryGetVariable(DName name, out DName displayName)
=> SymbolTable.TryGetVariable(name, out _, out displayName);
=> ComposedConfigSymbols.TryGetVariable(name, out _, out displayName);
}
}

Просмотреть файл

@ -353,7 +353,8 @@ namespace Microsoft.PowerFx
// Which enums are available.
// These do not compose - only bottom one wins.
// ComposedReadOnlySymbolTable will handle composition by looking up in each symbol table.
private protected EnumStoreBuilder _enumStoreBuilder;
private protected EnumStoreBuilder _enumStoreBuilder;
private EnumSymbol[] _enumSymbolCache;
private EnumSymbol[] GetEnumSymbolSnapshot

Просмотреть файл

@ -175,7 +175,7 @@ namespace Microsoft.PowerFx
// Compose all the symbol tables most likely to have functions into a single
// symbol table and then cache that.
// That will cache unifying into a single TexlFunctionSet - which is the most expensive part.
var functionList = _functionListCache.GetComposedCached(SupportedFunctions, Config.SymbolTable);
var functionList = _functionListCache.GetComposedCached(SupportedFunctions, Config.ComposedConfigSymbols);
var symbols = ReadOnlySymbolTable.Compose(EngineSymbols, functionList, PrimitiveTypes);
@ -548,7 +548,7 @@ namespace Microsoft.PowerFx
public DefinitionsCheckResult AddUserDefinedFunction(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
{
var engineTypesAndFunctions = ReadOnlySymbolTable.Compose(PrimitiveTypes, SupportedFunctions);
var engineTypesAndFunctions = ReadOnlySymbolTable.Compose(PrimitiveTypes, SupportedFunctions, Config.InternalConfigSymbols);
return Config.SymbolTable.AddUserDefinedFunction(script, parseCulture, engineTypesAndFunctions, symbolTable, allowSideEffects);
}
}

Просмотреть файл

@ -70,12 +70,12 @@ namespace Microsoft.PowerFx
foreach (KeyValuePair<TexlFunction, IAsyncTexlFunction> func in Library.RegexFunctions(regExTimeout, regexTypeCache))
{
if (config.SymbolTable.Functions.AnyWithName(func.Key.Name))
if (config.ComposedConfigSymbols.Functions.AnyWithName(func.Key.Name))
{
throw new InvalidOperationException("Cannot add RegEx functions more than once.");
}
config.SymbolTable.AddFunction(func.Key);
config.InternalConfigSymbols.AddFunction(func.Key);
config.AdditionalFunctions.Add(func.Key, func.Value);
}
}

Просмотреть файл

@ -420,7 +420,7 @@ namespace Microsoft.PowerFx
}
// Compose will handle null symbols
var composedSymbols = SymbolTable.Compose(Config.SymbolTable, SupportedFunctions, PrimitiveTypes, _symbolTable);
var composedSymbols = SymbolTable.Compose(Config.ComposedConfigSymbols, SupportedFunctions, PrimitiveTypes, _symbolTable);
if (parseResult.DefinedTypes.Any())
{

Просмотреть файл

@ -487,12 +487,12 @@ namespace Microsoft.PowerFx.Core.Tests
PowerFxConfig config = new PowerFxConfig();
bool fOk = config.SymbolTable.TryGetSymbolType("os1", out var type);
bool fOk = config.ComposedConfigSymbols.TryGetSymbolType("os1", out var type);
Assert.False(fOk);
config.AddOptionSet(os);
fOk = config.SymbolTable.TryGetSymbolType("os1", out type);
fOk = config.ComposedConfigSymbols.TryGetSymbolType("os1", out type);
Assert.True(fOk);
AssertOptionSetType(type, os);

Просмотреть файл

@ -397,8 +397,8 @@ namespace Microsoft.PowerFx.Tests
// After (A,B) = 3,6
var result = engine.Eval("With({x:A, y:B}, Set(A,3); x & y & A & B)", options: _opts);
Assert.Equal("102036", result.ToObject());
}
}
[Fact]
public void BasicEval()
{
@ -409,6 +409,24 @@ namespace Microsoft.PowerFx.Tests
Assert.Equal(14.0, ((NumberValue)result).Value);
}
[Fact]
public void BuiltInEnumConfigCheck()
{
var config = new PowerFxConfig()
{
SymbolTable = null
};
#pragma warning disable CS0618 // Type or member is obsolete
config.EnableRegExFunctions();
#pragma warning restore CS0618 // Type or member is obsolete
var expression = "Match(\"test\", \"t\", MatchOptions.Contains)";
var engine = new RecalcEngine(config);
var check = engine.Check(expression);
Assert.True(check.IsSuccess);
}
[Fact]
public void FormulaErrorUndefined()
{