Introduce NamedTypes to INameResolver (#2350)

Step 3 of allowing user defined types effort .

* Introduces NamedTypes, LookupType in INameResolver. 
* Remove PrimitiveTypeSymbolTable usage, and use NamedTypes from Engine
Symbols
* Require INameResolver to create UDFs.
* Removes `TypeSymbolTable`, `PrimitiveTypesSymbolTable` and
`DefinedTypeSymbolTable`

This will be followed with

1. Introduce parsing and adding user defined NamedTypes.
2. Optimize type graph to update incremental.
This commit is contained in:
Adithya Selvaprithiviraj 2024-04-29 16:12:22 -07:00 коммит произвёл GitHub
Родитель 4a9f9e996d
Коммит f366046f70
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
22 изменённых файлов: 456 добавлений и 453 удалений

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

@ -10,6 +10,7 @@ using Microsoft.PowerFx.Core.Entities;
using Microsoft.PowerFx.Core.Functions;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core.Binding
{
@ -41,6 +42,10 @@ namespace Microsoft.PowerFx.Core.Binding
TexlFunctionSet Functions { get; }
// List of all valid named types in a given namespace
// Intellisense can use this when suggesting type options.
IEnumerable<KeyValuePair<DName, FormulaType>> NamedTypes { get; }
// This advertises whether the INameResolver instance will suggest unqualified enums ("Hours")
// or only qualified enums ("TimeUnit.Hours").
// This must be consistent with how the other Lookup functions behave.
@ -82,6 +87,9 @@ namespace Microsoft.PowerFx.Core.Binding
bool LookupGlobalEntity(DName name, out NameLookupInfo lookupInfo);
bool TryLookupEnum(DName name, out NameLookupInfo lookupInfo);
// Look up a type by name.
bool LookupType(DName name, out FormulaType fType);
}
internal static class NameResolverExtensions

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

@ -21,6 +21,7 @@ using Microsoft.PowerFx.Core.Texl;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Syntax;
using Microsoft.PowerFx.Types;
using static Microsoft.PowerFx.Core.Localization.TexlStrings;
namespace Microsoft.PowerFx.Core.Functions
@ -52,8 +53,9 @@ namespace Microsoft.PowerFx.Core.Functions
/// <param name="body">TexlNode for user defined function body.</param>
/// <param name="isImperative"></param>
/// <param name="args"></param>
public UserDefinedFunction(string functionName, DType returnType, TexlNode body, bool isImperative, ISet<UDFArg> args)
: base(DPath.Root, functionName, functionName, SG(functionName), FunctionCategories.UserDefined, returnType, 0, args.Count, args.Count, args.Select(a => a.TypeIdent.GetFormulaType()._type).ToArray())
/// <param name="argTypes">Array of argTypes.</param>
public UserDefinedFunction(string functionName, DType returnType, TexlNode body, bool isImperative, ISet<UDFArg> args, DType[] argTypes)
: base(DPath.Root, functionName, functionName, SG(functionName), FunctionCategories.UserDefined, returnType, 0, args.Count, args.Count, argTypes)
{
this._args = args;
this._isImperative = isImperative;
@ -116,7 +118,7 @@ namespace Microsoft.PowerFx.Core.Functions
}
bindingConfig = bindingConfig ?? new BindingConfig(this._isImperative);
_binding = TexlBinding.Run(documentBinderGlue, UdfBody, UserDefinitionsNameResolver.Create(nameResolver, _args), bindingConfig, features: features, rule: rule);
_binding = TexlBinding.Run(documentBinderGlue, UdfBody, UserDefinitionsNameResolver.Create(nameResolver, _args, ParamTypes), bindingConfig, features: features, rule: rule);
CheckTypesOnDeclaration(_binding.CheckTypesContext, _binding.ResultType, _binding);
@ -176,7 +178,7 @@ namespace Microsoft.PowerFx.Core.Functions
throw new ArgumentNullException(nameof(binderGlue));
}
var func = new UserDefinedFunction(Name, ReturnType, UdfBody, _isImperative, new HashSet<UDFArg>(_args));
var func = new UserDefinedFunction(Name, ReturnType, UdfBody, _isImperative, new HashSet<UDFArg>(_args), ParamTypes);
binding = func.BindBody(nameResolver, binderGlue, bindingConfig, features, rule);
return func;
@ -188,9 +190,10 @@ namespace Microsoft.PowerFx.Core.Functions
/// Helper to create IR UserDefinedFunctions.
/// </summary>
/// <param name="uDFs">Valid Parsed UDFs to be converted into UserDefinedFunction.</param>
/// <param name="nameResolver">NameResolver to resolve type names.</param>
/// <param name="errors">Errors when creating functions.</param>
/// <returns>IEnumerable of UserDefinedFunction.</returns>
public static IEnumerable<UserDefinedFunction> CreateFunctions(IEnumerable<UDF> uDFs, out List<TexlError> errors)
public static IEnumerable<UserDefinedFunction> CreateFunctions(IEnumerable<UDF> uDFs, INameResolver nameResolver, out List<TexlError> errors)
{
Contracts.AssertValue(uDFs);
Contracts.AssertAllValues(uDFs);
@ -210,14 +213,14 @@ namespace Microsoft.PowerFx.Core.Functions
continue;
}
var parametersOk = CheckParameters(udf.Args, errors);
var returnTypeOk = CheckReturnType(udf.ReturnType, errors);
var parametersOk = CheckParameters(udf.Args, errors, nameResolver, out var parameterTypes);
var returnTypeOk = CheckReturnType(udf.ReturnType, errors, nameResolver, out var returnType);
if (!parametersOk || !returnTypeOk)
{
continue;
}
var func = new UserDefinedFunction(udfName.Value, udf.ReturnType.GetFormulaType()._type, udf.Body, udf.IsImperative, udf.Args);
var func = new UserDefinedFunction(udfName.Value, returnType, udf.Body, udf.IsImperative, udf.Args, parameterTypes);
texlFunctionSet.Add(func);
userDefinedFunctions.Add(func);
@ -226,10 +229,17 @@ namespace Microsoft.PowerFx.Core.Functions
return userDefinedFunctions;
}
private static bool CheckParameters(ISet<UDFArg> args, List<TexlError> errors)
private static bool CheckParameters(ISet<UDFArg> args, List<TexlError> errors, INameResolver nameResolver, out DType[] parameterTypes)
{
if (args.Count == 0)
{
parameterTypes = Array.Empty<DType>();
return true;
}
var isParamCheckSuccessful = true;
var argsAlreadySeen = new HashSet<string>();
parameterTypes = new DType[args.Count];
foreach (var arg in args)
{
@ -242,30 +252,36 @@ namespace Microsoft.PowerFx.Core.Functions
{
argsAlreadySeen.Add(arg.NameIdent.Name);
var parameterType = arg.TypeIdent.GetFormulaType()._type;
if (parameterType.Kind.Equals(DType.Unknown.Kind) || UserDefinitions.RestrictedTypes.Contains(parameterType))
if (!nameResolver.LookupType(arg.TypeIdent.Name, out var parameterType) || UserDefinitions.RestrictedTypes.Contains(parameterType._type))
{
errors.Add(new TexlError(arg.TypeIdent, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_UnknownType, arg.TypeIdent.Name));
isParamCheckSuccessful = false;
}
else
{
Contracts.Assert(arg.ArgIndex >= 0);
Contracts.Assert(arg.ArgIndex < args.Count);
parameterTypes[arg.ArgIndex] = parameterType._type;
}
}
}
return isParamCheckSuccessful;
}
private static bool CheckReturnType(IdentToken returnType, List<TexlError> errors)
private static bool CheckReturnType(IdentToken returnTypeToken, List<TexlError> errors, INameResolver nameResolver, out DType returnType)
{
var returnTypeFormulaType = returnType.GetFormulaType()._type;
var isReturnTypeCheckSuccessful = true;
if (returnTypeFormulaType.Kind.Equals(DType.Unknown.Kind) || UserDefinitions.RestrictedTypes.Contains(returnTypeFormulaType))
if (!nameResolver.LookupType(returnTypeToken.Name, out var returnTypeFormulaType) || UserDefinitions.RestrictedTypes.Contains(returnTypeFormulaType._type))
{
errors.Add(new TexlError(returnType, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_UnknownType, returnType.Name));
isReturnTypeCheckSuccessful = false;
errors.Add(new TexlError(returnTypeToken, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_UnknownType, returnTypeToken.Name));
returnType = DType.Invalid;
return false;
}
else
{
returnType = returnTypeFormulaType._type;
return true;
}
return isReturnTypeCheckSuccessful;
}
/// <summary>
@ -275,16 +291,23 @@ namespace Microsoft.PowerFx.Core.Functions
{
private readonly INameResolver _globalNameResolver;
private readonly IReadOnlyDictionary<string, UDFArg> _args;
private readonly DType[] _argTypes;
public static INameResolver Create(INameResolver globalNameResolver, IEnumerable<UDFArg> args)
public static INameResolver Create(INameResolver globalNameResolver, IEnumerable<UDFArg> args, DType[] argTypes)
{
return new UserDefinitionsNameResolver(globalNameResolver, args);
return new UserDefinitionsNameResolver(globalNameResolver, args, argTypes);
}
private UserDefinitionsNameResolver(INameResolver globalNameResolver, IEnumerable<UDFArg> args)
private UserDefinitionsNameResolver(INameResolver globalNameResolver, IEnumerable<UDFArg> args, DType[] argTypes)
{
Contracts.AssertValue(args);
Contracts.AssertValue(argTypes);
Contracts.AssertValue(globalNameResolver);
Contracts.Assert(args.Count() == argTypes.Length);
this._globalNameResolver = globalNameResolver;
this._args = args.ToDictionary(arg => arg.NameIdent.Name.Value, arg => arg);
this._argTypes = argTypes;
}
public IExternalDocument Document => _globalNameResolver.Document;
@ -299,6 +322,8 @@ namespace Microsoft.PowerFx.Core.Functions
public TexlFunctionSet Functions => _globalNameResolver.Functions;
public IEnumerable<KeyValuePair<DName, FormulaType>> NamedTypes => _globalNameResolver.NamedTypes;
public bool SuggestUnqualifiedEnums => _globalNameResolver.SuggestUnqualifiedEnums;
public bool Lookup(DName name, out NameLookupInfo nameInfo, NameLookupPreferences preferences = NameLookupPreferences.None)
@ -306,7 +331,7 @@ namespace Microsoft.PowerFx.Core.Functions
// lookup in the local scope i.e., function params & body and then look in global scope.
if (_args.TryGetValue(name, out var value))
{
var type = value.TypeIdent.GetFormulaType()._type;
var type = _argTypes[value.ArgIndex];
nameInfo = new NameLookupInfo(BindKind.PowerFxResolvedObject, type, DPath.Root, 0, new UDFParameterInfo(type, value.ArgIndex, value.NameIdent.Name));
return true;
@ -331,6 +356,11 @@ namespace Microsoft.PowerFx.Core.Functions
return _globalNameResolver.LookupFunctionsInNamespace(nameSpace);
}
public bool LookupType(DName name, out FormulaType fType)
{
return _globalNameResolver.LookupType(name, out fType);
}
public bool LookupGlobalEntity(DName name, out NameLookupInfo lookupInfo)
{
return _globalNameResolver.LookupGlobalEntity(name, out lookupInfo);

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

@ -82,6 +82,25 @@ namespace Microsoft.PowerFx
}
}
// Expose the list to aide in intellisense suggestions.
IEnumerable<KeyValuePair<DName, FormulaType>> INameResolver.NamedTypes
{
get
{
var names = new HashSet<string>();
foreach (INameResolver table in _symbolTables)
{
foreach (var type in table.NamedTypes)
{
if (names.Add(type.Key))
{
yield return type;
}
}
}
}
}
public IEnumerable<KeyValuePair<string, NameLookupInfo>> GlobalSymbols
{
get
@ -188,6 +207,20 @@ namespace Microsoft.PowerFx
return false;
}
public virtual bool LookupType(DName name, out FormulaType fType)
{
foreach (INameResolver table in _symbolTables)
{
if (table.LookupType(name, out fType))
{
return true;
}
}
fType = default;
return false;
}
internal override IExternalEntityScope InternalEntityScope
{
get

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

@ -291,7 +291,40 @@ namespace Microsoft.PowerFx
}
return NewDefault(tfs);
}
}
// Helper to create a ReadOnly symbol table around a set of core types.
internal static ReadOnlySymbolTable NewDefaultTypes(IEnumerable<KeyValuePair<DName, FormulaType>> types)
{
Contracts.AssertValue(types);
var s = new SymbolTable
{
DebugName = $"BuiltinTypes ({types?.Count()})"
};
s.AddTypes(types);
return s;
}
// Overload Helper to create a ReadOnly symbol table around a set of core functions and types.
internal static ReadOnlySymbolTable NewDefault(TexlFunctionSet coreFunctions, IEnumerable<KeyValuePair<DName, FormulaType>> types)
{
Contracts.AssertValue(types);
Contracts.AssertValue(coreFunctions);
var s = new SymbolTable
{
EnumStoreBuilder = new EnumStoreBuilder(),
DebugName = $"BuiltinFunctions ({coreFunctions.Count()}), BuiltinTypes ({types?.Count()})"
};
s.AddFunctions(coreFunctions);
s.AddTypes(types);
return s;
}
internal static readonly ReadOnlySymbolTable PrimitiveTypesTableInstance = NewDefaultTypes(FormulaType.PrimitiveTypes);
/// <summary>
/// Helper to create a symbol table around a set of core functions.
@ -463,8 +496,10 @@ namespace Microsoft.PowerFx
DPath INameResolver.CurrentEntityPath => default;
bool INameResolver.SuggestUnqualifiedEnums => false;
bool INameResolver.SuggestUnqualifiedEnums => false;
IEnumerable<KeyValuePair<DName, FormulaType>> INameResolver.NamedTypes => Enumerable.Empty<KeyValuePair<DName, FormulaType>>();
bool INameResolver.LookupParent(out NameLookupInfo lookupInfo)
{
lookupInfo = default;
@ -505,7 +540,14 @@ namespace Microsoft.PowerFx
bool INameResolver.LookupExpandedControlType(IExternalControl control, out DType controlType)
{
throw new NotImplementedException();
}
}
bool INameResolver.LookupType(DName name, out FormulaType fType)
{
fType = default;
return false;
}
#endregion
}
}

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

@ -36,10 +36,14 @@ namespace Microsoft.PowerFx
private readonly SlotMap<NameLookupInfo?> _slots = new SlotMap<NameLookupInfo?>();
private readonly IDictionary<DName, FormulaType> _namedTypes = new Dictionary<DName, FormulaType>();
private DisplayNameProvider _environmentSymbolDisplayNameProvider = new SingleSourceDisplayNameProvider();
IEnumerable<KeyValuePair<string, NameLookupInfo>> IGlobalSymbolNameResolver.GlobalSymbols => _variables;
IEnumerable<KeyValuePair<DName, FormulaType>> INameResolver.NamedTypes => _namedTypes;
internal const string UserInfoSymbolName = "User";
/// <summary>
@ -216,7 +220,11 @@ namespace Microsoft.PowerFx
var sb = new StringBuilder();
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
// Compose will handle null symbols
var composedSymbols = Compose(this, symbolTable, extraSymbolTable);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), composedSymbols, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
@ -232,9 +240,6 @@ namespace Microsoft.PowerFx
throw new InvalidOperationException(sb.ToString());
}
// Compose will handle null symbols
var composedSymbols = Compose(this, symbolTable, extraSymbolTable);
foreach (var udf in udfs)
{
AddFunction(udf);
@ -417,6 +422,62 @@ namespace Microsoft.PowerFx
displayName: default);
_variables.Add(hostDName, info);
}
}
/// <summary>
/// Adds a named type that can be referenced in expression.
/// </summary>
/// <param name="typeName">Name of the type to be added into Symbol table.</param>
/// <param name="type">Type associated with the name.</param>
public void AddType(DName typeName, FormulaType type)
{
Contracts.AssertValue(typeName.Value);
Contracts.AssertValue(type);
Contracts.Assert(typeName.Value.Length > 0);
Contracts.AssertValid(typeName);
using var guard = _guard.Enter(); // Region is single threaded.
Inc();
_namedTypes.Add(typeName, type);
}
internal void AddTypes(IEnumerable<KeyValuePair<DName, FormulaType>> types)
{
Contracts.AssertValue(types);
using var guard = _guard.Enter(); // Region is single threaded.
Inc();
foreach (var type in types)
{
_namedTypes.Add(type.Key, type.Value);
}
}
/// <summary>
/// Helper to create a symbol table with primitive types.
/// </summary>
/// <returns>SymbolTable with primitive types.</returns>
public static SymbolTable WithPrimitiveTypes()
{
var s = new SymbolTable
{
DebugName = $"SymbolTable with PrimitiveTypes"
};
s.AddTypes(FormulaType.PrimitiveTypes);
return s;
}
bool INameResolver.LookupType(DName name, out FormulaType fType)
{
if (_namedTypes.TryGetValue(name, out fType))
{
return true;
}
return false;
}
}
}

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

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection;
using Microsoft.PowerFx.Core;
using Microsoft.PowerFx.Core.App.Controls;
using Microsoft.PowerFx.Core.Binding;
@ -30,30 +30,30 @@ namespace Microsoft.PowerFx
/// Configuration symbols for this Power Fx engine.
/// </summary>
public PowerFxConfig Config { get; }
// Volatile means it can be set by multiple threads.
// But all threads will produce the same value, so it's ok.
private static volatile string _assemblyVersion;
/// <summary>
/// For diagnostics, get the assembly version of Power Fx Engine.
/// </summary>
public static string AssemblyVersion
{
get
{
if (_assemblyVersion == null)
{
var fxAttr = typeof(Engine).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
var fullVerStr = fxAttr.InformationalVersion;
var parts = fullVerStr.Split('+');
_assemblyVersion = parts[0];
}
return _assemblyVersion;
}
}
// Volatile means it can be set by multiple threads.
// But all threads will produce the same value, so it's ok.
private static volatile string _assemblyVersion;
/// <summary>
/// For diagnostics, get the assembly version of Power Fx Engine.
/// </summary>
public static string AssemblyVersion
{
get
{
if (_assemblyVersion == null)
{
var fxAttr = typeof(Engine).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
var fullVerStr = fxAttr.InformationalVersion;
var parts = fullVerStr.Split('+');
_assemblyVersion = parts[0];
}
return _assemblyVersion;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Engine"/> class.
@ -82,6 +82,11 @@ namespace Microsoft.PowerFx
/// </summary>
public ReadOnlySymbolTable SupportedFunctions { get; protected internal set; } = _allBuiltinCoreFunctions;
/// <summary>
/// Builtin Types supported by this engine.
/// </summary>
public ReadOnlySymbolTable PrimitiveTypes { get; protected internal set; } = ReadOnlySymbolTable.PrimitiveTypesTableInstance;
// By default, we pull the core functions.
// These can be overridden.
internal TexlFunctionSet Functions => CreateResolverInternal().Functions;
@ -89,13 +94,13 @@ namespace Microsoft.PowerFx
/// <summary>
/// List of transforms to apply to an IR.
/// </summary>
internal readonly List<Core.IR.IRTransform> IRTransformList = new List<Core.IR.IRTransform>();
/// <summary>
/// List of error processor that will add host specific custom errors at the end of the check.
/// </summary>
private readonly IList<IPostCheckErrorHandler> _postCheckErrorHandlers = new List<IPostCheckErrorHandler>();
internal readonly List<Core.IR.IRTransform> IRTransformList = new List<Core.IR.IRTransform>();
/// <summary>
/// List of error processor that will add host specific custom errors at the end of the check.
/// </summary>
private readonly IList<IPostCheckErrorHandler> _postCheckErrorHandlers = new List<IPostCheckErrorHandler>();
public IList<IPostCheckErrorHandler> PostCheckErrorHandlers => _postCheckErrorHandlers;
/// <summary>
@ -156,27 +161,27 @@ namespace Microsoft.PowerFx
symbols = ReadOnlySymbolTable.Compose(localSymbols, GetCombinedEngineSymbols());
return symbols;
}
/// <summary>
/// Get a combined engine symbol table, including builtins and config.
/// </summary>
/// <returns></returns>
public ReadOnlySymbolTable GetCombinedEngineSymbols()
{
var symbols = ReadOnlySymbolTable.Compose(EngineSymbols, SupportedFunctions, Config.SymbolTable);
return symbols;
}
/// <summary>
/// Overriable method to be able to supply custom implementation of IExternalRuleScopeResolver.
/// Defaults to Null.
/// </summary>
/// <returns>Implementation of IExternalRuleScopeResolver.</returns>
// <para>IExternalRuleScopeResolver is used in Canvas App backend when calling TexlBinding.Run().
// There was no option to supply a custom implementation of this in Engine,
// so following the existing pattern in Engine,
}
/// <summary>
/// Get a combined engine symbol table, including builtins and config.
/// </summary>
/// <returns></returns>
public ReadOnlySymbolTable GetCombinedEngineSymbols()
{
var symbols = ReadOnlySymbolTable.Compose(EngineSymbols, SupportedFunctions, Config.SymbolTable, PrimitiveTypes);
return symbols;
}
/// <summary>
/// Overriable method to be able to supply custom implementation of IExternalRuleScopeResolver.
/// Defaults to Null.
/// </summary>
/// <returns>Implementation of IExternalRuleScopeResolver.</returns>
// <para>IExternalRuleScopeResolver is used in Canvas App backend when calling TexlBinding.Run().
// There was no option to supply a custom implementation of this in Engine,
// so following the existing pattern in Engine,
// added this virtual function for the derived classes of Engine to override and supply custom implementation.</para>
private protected virtual IExternalRuleScopeResolver CreateExternalRuleScopeResolver()
{
@ -191,9 +196,9 @@ namespace Microsoft.PowerFx
public virtual ParserOptions GetDefaultParserOptionsCopy()
{
return new ParserOptions
{
Culture = null,
AllowsSideEffects = false,
{
Culture = null,
AllowsSideEffects = false,
MaxExpressionLength = Config.MaximumExpressionLength,
};
}
@ -226,9 +231,9 @@ namespace Microsoft.PowerFx
if (expressionText == null)
{
throw new ArgumentNullException(nameof(expressionText));
}
options ??= new ParserOptions();
}
options ??= new ParserOptions();
var result = options.Parse(expressionText, features ?? Features.None);
return result;
}
@ -284,12 +289,12 @@ namespace Microsoft.PowerFx
// Called after check result, can inject additional errors or constraints.
protected virtual IEnumerable<ExpressionError> PostCheck(CheckResult check)
{
var hostErrors = new List<ExpressionError>();
foreach (var postCheckErrorHandler in _postCheckErrorHandlers)
{
hostErrors.AddRange(postCheckErrorHandler.Process(check));
}
{
var hostErrors = new List<ExpressionError>();
foreach (var postCheckErrorHandler in _postCheckErrorHandlers)
{
hostErrors.AddRange(postCheckErrorHandler.Process(check));
}
return hostErrors;
}
@ -304,12 +309,12 @@ namespace Microsoft.PowerFx
private protected virtual RecordType GetRuleScope()
{
return null;
}
internal bool TryGetRuleScope(out RecordType record)
{
record = this.GetRuleScope();
return record != null;
}
internal bool TryGetRuleScope(out RecordType record)
{
record = this.GetRuleScope();
return record != null;
}
private BindingConfig GetDefaultBindingConfig()
@ -325,29 +330,29 @@ namespace Microsoft.PowerFx
}
return bindingConfig;
}
/// <summary>
/// Creates and returns binding config from the given parser options.
/// </summary>
/// <param name="options">Parser Options.</param>
/// <param name="ruleScope">Optional: Rule Scope. If not supplied, then rule scope from <see cref="GetRuleScope"/> would be used.</param>
/// <returns>Binding Config.</returns>
// Power Apps never set BindingConfig.UseThisRecordForRuleScope to true
// This virtual overload of GetDefaultBindingConfig allows us to supply custom binding config from PowerApps
// Default implementation is similar to how binding config is created in ComputeBinding
// Optional ruleScope is passed from ComputeBinding() so we don't have to call GetRuleScope twice
}
/// <summary>
/// Creates and returns binding config from the given parser options.
/// </summary>
/// <param name="options">Parser Options.</param>
/// <param name="ruleScope">Optional: Rule Scope. If not supplied, then rule scope from <see cref="GetRuleScope"/> would be used.</param>
/// <returns>Binding Config.</returns>
// Power Apps never set BindingConfig.UseThisRecordForRuleScope to true
// This virtual overload of GetDefaultBindingConfig allows us to supply custom binding config from PowerApps
// Default implementation is similar to how binding config is created in ComputeBinding
// Optional ruleScope is passed from ComputeBinding() so we don't have to call GetRuleScope twice
private protected virtual BindingConfig GetDefaultBindingConfig(ParserOptions options, RecordType ruleScope = null)
{
ruleScope ??= this.GetRuleScope();
// Canvas apps uses rule scope for lots of cases.
// But in general, we should only use rule scope for 'ThisRecord' binding.
// Anything else should be accomplished with SymbolTables.
bool useThisRecordForRuleScope = ruleScope != null;
ruleScope ??= this.GetRuleScope();
// Canvas apps uses rule scope for lots of cases.
// But in general, we should only use rule scope for 'ThisRecord' binding.
// Anything else should be accomplished with SymbolTables.
bool useThisRecordForRuleScope = ruleScope != null;
return new BindingConfig(options.AllowsSideEffects, useThisRecordForRuleScope, options.NumberIsFloat);
}
}
// Called by CheckResult.ApplyBinding to compute the binding.
internal (TexlBinding, ReadOnlySymbolTable) ComputeBinding(CheckResult result)
@ -365,19 +370,19 @@ namespace Microsoft.PowerFx
var glue = CreateBinderGlue();
var ruleScope = this.GetRuleScope();
var bindingConfig = GetDefaultBindingConfig(result.Parse.Options, ruleScope);
var bindingConfig = GetDefaultBindingConfig(result.Parse.Options, ruleScope);
var binding = TexlBinding.Run(
glue,
externalRuleScopeResolver,
new DataSourceToQueryOptionsMap(),
parse.Root,
resolver,
bindingConfig,
false,
ruleScope?._type,
false,
null,
var binding = TexlBinding.Run(
glue,
externalRuleScopeResolver,
new DataSourceToQueryOptionsMap(),
parse.Root,
resolver,
bindingConfig,
false,
ruleScope?._type,
false,
null,
Config.Features);
return (binding, combinedSymbols);
@ -402,14 +407,14 @@ namespace Microsoft.PowerFx
/// Get intellisense from the formula, with parser options.
/// </summary>
public IIntellisenseResult Suggest(CheckResult checkResult, int cursorPosition)
{
return this.Suggest(checkResult, cursorPosition, null);
}
public IIntellisenseResult Suggest(CheckResult checkResult, int cursorPosition, IServiceProvider services)
{
return this.Suggest(checkResult, cursorPosition, null);
}
public IIntellisenseResult Suggest(CheckResult checkResult, int cursorPosition, IServiceProvider services)
{
// Note that for completions, we just need binding,
// but we don't need errors or dependency info.
// Note that for completions, we just need binding,
// but we don't need errors or dependency info.
var binding = checkResult.ApplyBindingInternal();
var formula = checkResult.GetParseFormula();
@ -417,10 +422,10 @@ namespace Microsoft.PowerFx
// CheckResult has the binding, which has already captured both the INameResolver and any row scope parameters.
// So these both become available to intellisense.
var context = new IntellisenseContext(expression, cursorPosition, checkResult.ExpectedReturnType)
{
Services = services
};
var context = new IntellisenseContext(expression, cursorPosition, checkResult.ExpectedReturnType)
{
Services = services
};
var intellisense = this.CreateIntellisense();
var suggestions = intellisense.Suggest(context, binding, formula);
@ -440,8 +445,8 @@ namespace Microsoft.PowerFx
public RenameDriver CreateFieldRenamer(RecordType parameters, DPath pathToRename, DName updatedName, CultureInfo culture)
{
return CreateFieldRenamer(parameters, pathToRename, updatedName, new ParserOptions() { Culture = culture });
}
}
/// <summary>
/// Creates a renamer instance for updating a field reference from <paramref name="parameters"/> in expressions.
/// </summary>
@ -449,7 +454,7 @@ namespace Microsoft.PowerFx
/// be acecssed as top-level identifiers in the formula. Must be the names from before any rename operation is applied.</param>
/// <param name="pathToRename">Path to the field to rename.</param>
/// <param name="updatedName">New name. Replaces the last segment of <paramref name="pathToRename"/>.</param>
/// <param name="options">Parser option to support TextFirst (if necessary) and culture.</param>
/// <param name="options">Parser option to support TextFirst (if necessary) and culture.</param>
public RenameDriver CreateFieldRenamer(RecordType parameters, DPath pathToRename, DName updatedName, ParserOptions options)
{
Contracts.CheckValue(parameters, nameof(parameters));
@ -466,11 +471,11 @@ namespace Microsoft.PowerFx
** display name support and construct a resolver from that instead, which we use for the rewrite binding.
*/
return new RenameDriver(parameters, pathToRename, updatedName, this, CreateResolverInternal() as ReadOnlySymbolTable, CreateBinderGlue(), options, false);
}
public RenameDriver CreateOptionSetRenamer(RecordType parameters, DPath pathToRename, DName updatedName, CultureInfo culture)
{
return new RenameDriver(parameters, pathToRename, updatedName, this, CreateResolverInternal() as ReadOnlySymbolTable, CreateBinderGlue(), culture, true);
}
public RenameDriver CreateOptionSetRenamer(RecordType parameters, DPath pathToRename, DName updatedName, CultureInfo culture)
{
return new RenameDriver(parameters, pathToRename, updatedName, this, CreateResolverInternal() as ReadOnlySymbolTable, CreateBinderGlue(), culture, true);
}
/// <summary>
@ -488,10 +493,10 @@ namespace Microsoft.PowerFx
var symbolTable = (parameters == null) ? null : SymbolTable.NewFromRecord(parameters);
return GetInvariantExpressionWorker(expressionText, symbolTable, parseCulture);
}
}
public string GetInvariantExpressionParserOption(string expressionText, RecordType parameters, ParserOptions options)
{
{
var ruleScope = this.GetRuleScope();
var symbolTable = (parameters == null) ? null : SymbolTable.NewFromRecord(parameters);
@ -503,8 +508,8 @@ namespace Microsoft.PowerFx
var ruleScope = this.GetRuleScope();
return ExpressionLocalizationHelper.ConvertExpression(expressionText, ruleScope, GetDefaultBindingConfig(), CreateResolverInternal(symbolTable), CreateBinderGlue(), parseCulture, Config.Features, toDisplay: false);
}
}
internal string GetInvariantExpressionWorker(string expressionText, ReadOnlySymbolTable symbolTable, ParserOptions options)
{
var ruleScope = this.GetRuleScope();
@ -531,11 +536,12 @@ namespace Microsoft.PowerFx
{
var ruleScope = this.GetRuleScope();
return ExpressionLocalizationHelper.ConvertExpression(expressionText, ruleScope, GetDefaultBindingConfig(), CreateResolverInternal(symbolTable), CreateBinderGlue(), culture, Config.Features, toDisplay: true);
}
internal void AddUserDefinedFunction(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
{
Config.SymbolTable.AddUserDefinedFunction(script, parseCulture, SupportedFunctions, symbolTable, allowSideEffects);
}
internal void AddUserDefinedFunction(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
{
var engineTypesAndFunctions = ReadOnlySymbolTable.Compose(PrimitiveTypes, SupportedFunctions);
Config.SymbolTable.AddUserDefinedFunction(script, parseCulture, engineTypesAndFunctions, symbolTable, allowSideEffects);
}
}
}

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

@ -2,6 +2,8 @@
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.PowerFx.Core.Entities;
@ -70,7 +72,25 @@ namespace Microsoft.PowerFx.Types
internal FormulaType(DType type)
{
_type = type;
}
}
// Primitive types - Keeping it same as PrimitiveTypeSymbolTable
internal static readonly IReadOnlyDictionary<DName, FormulaType> PrimitiveTypes = ImmutableDictionary.CreateRange(new Dictionary<DName, FormulaType>()
{
{ new DName("Boolean"), Boolean },
{ new DName("Color"), Color },
{ new DName("Date"), Date },
{ new DName("Time"), Time },
{ new DName("DateTime"), DateTime },
{ new DName("DateTimeTZInd"), DateTimeNoTimeZone },
{ new DName("GUID"), Guid },
{ new DName("Number"), Number },
{ new DName("Decimal"), Decimal },
{ new DName("Text"), String },
{ new DName("Hyperlink"), Hyperlink },
{ new DName("None"), Blank },
{ new DName("UntypedObject"), UntypedObject },
});
/// <summary>
/// Initializes a new instance of the <see cref="FormulaType"/> class.
@ -110,25 +130,6 @@ namespace Microsoft.PowerFx.Types
return type;
}
internal static FormulaType[] GetValidUDFPrimitiveTypes()
{
FormulaType[] validTypes = { Blank, Boolean, Number, Decimal, String, Time, Date, DateTime, DateTimeNoTimeZone, Hyperlink, Color, Guid };
return validTypes;
}
internal static FormulaType GetFromStringOrNull(string formula)
{
foreach (FormulaType formulaType in GetValidUDFPrimitiveTypes())
{
if (string.Equals(formulaType.ToString(), formula, StringComparison.Ordinal))
{
return formulaType;
}
}
return null;
}
// Get the correct derived type
internal static FormulaType Build(DType type)
{

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

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Binding.BindInfo;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.Utils;
@ -13,104 +14,104 @@ using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core.Syntax.Visitors
{
internal class DTypeVisitor : TexlFunctionalVisitor<DType, DefinedTypeSymbolTable>
internal class DTypeVisitor : TexlFunctionalVisitor<DType, INameResolver>
{
private DTypeVisitor()
{
}
public static DType Run(TexlNode node, DefinedTypeSymbolTable context)
public static DType Run(TexlNode node, INameResolver context)
{
return node.Accept(new DTypeVisitor(), context);
}
public override DType Visit(ErrorNode node, DefinedTypeSymbolTable context)
public override DType Visit(ErrorNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(BlankNode node, DefinedTypeSymbolTable context)
public override DType Visit(BlankNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(BoolLitNode node, DefinedTypeSymbolTable context)
public override DType Visit(BoolLitNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(StrLitNode node, DefinedTypeSymbolTable context)
public override DType Visit(StrLitNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(NumLitNode node, DefinedTypeSymbolTable context)
public override DType Visit(NumLitNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(DecLitNode node, DefinedTypeSymbolTable context)
public override DType Visit(DecLitNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(FirstNameNode node, DefinedTypeSymbolTable context)
public override DType Visit(FirstNameNode node, INameResolver context)
{
var name = node.Ident.Name.Value;
if (context.TryLookup(new DName(name), out NameLookupInfo nameInfo))
if (context.LookupType(new DName(name), out FormulaType ft))
{
return nameInfo.Type;
return ft._type;
}
return FormulaType.GetFromStringOrNull(name)._type;
return DType.Invalid;
}
public override DType Visit(ParentNode node, DefinedTypeSymbolTable context)
public override DType Visit(ParentNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(SelfNode node, DefinedTypeSymbolTable context)
public override DType Visit(SelfNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(StrInterpNode node, DefinedTypeSymbolTable context)
public override DType Visit(StrInterpNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(DottedNameNode node, DefinedTypeSymbolTable context)
public override DType Visit(DottedNameNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(UnaryOpNode node, DefinedTypeSymbolTable context)
public override DType Visit(UnaryOpNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(BinaryOpNode node, DefinedTypeSymbolTable context)
public override DType Visit(BinaryOpNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(VariadicOpNode node, DefinedTypeSymbolTable context)
public override DType Visit(VariadicOpNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(CallNode node, DefinedTypeSymbolTable context)
public override DType Visit(CallNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(ListNode node, DefinedTypeSymbolTable context)
public override DType Visit(ListNode node, INameResolver context)
{
throw new NotImplementedException();
}
public override DType Visit(RecordNode node, DefinedTypeSymbolTable context)
public override DType Visit(RecordNode node, INameResolver context)
{
var list = new List<TypedName>();
foreach (var (cNode, ident) in node.ChildNodes.Zip(node.Ids, (a, b) => (a, b)))
@ -127,7 +128,7 @@ namespace Microsoft.PowerFx.Core.Syntax.Visitors
return DType.CreateRecord(list);
}
public override DType Visit(TableNode node, DefinedTypeSymbolTable context)
public override DType Visit(TableNode node, INameResolver context)
{
var childNode = node.ChildNodes.First();
var ty = childNode.Accept(this, context);
@ -139,7 +140,7 @@ namespace Microsoft.PowerFx.Core.Syntax.Visitors
return ty.ToTable();
}
public override DType Visit(AsNode node, DefinedTypeSymbolTable context)
public override DType Visit(AsNode node, INameResolver context)
{
throw new NotImplementedException();
}

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

@ -1,56 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Binding.BindInfo;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.UtilityDataStructures;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core
{
[NotThreadSafe]
internal class DefinedTypeSymbolTable : TypeSymbolTable, IGlobalSymbolNameResolver
{
private readonly BidirectionalDictionary<string, FormulaType> _definedTypes = new ();
IEnumerable<KeyValuePair<string, NameLookupInfo>> IGlobalSymbolNameResolver.GlobalSymbols => _definedTypes.ToDictionary(kvp => kvp.Key, kvp => ToLookupInfo(kvp.Value));
internal void RegisterType(string typeName, FormulaType type)
{
Inc();
_definedTypes.Add(typeName, type);
}
protected void ValidateName(string name)
{
if (!DName.IsValidDName(name))
{
throw new ArgumentException("Invalid name: ${name}");
}
}
internal override bool TryLookup(DName name, out NameLookupInfo nameInfo)
{
if (!_definedTypes.TryGetFromFirst(name.Value, out var type))
{
nameInfo = default;
return false;
}
nameInfo = ToLookupInfo(type);
return true;
}
internal override bool TryGetTypeName(FormulaType type, out string typeName)
{
return _definedTypes.TryGetFromSecond(type, out typeName);
}
}
}

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

@ -1,66 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Binding.BindInfo;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.UtilityDataStructures;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core.Public.Types
{
[ThreadSafeImmutable]
internal sealed class PrimitiveTypesSymbolTable : TypeSymbolTable, IGlobalSymbolNameResolver
{
private static readonly IReadOnlyDictionary<string, FormulaType> _knownTypes = new Dictionary<string, FormulaType>()
{
{ "Boolean", FormulaType.Boolean },
{ "Color", FormulaType.Color },
{ "Date", FormulaType.Date },
{ "Time", FormulaType.Time },
{ "DateTime", FormulaType.DateTime },
{ "DateTimeTZInd", FormulaType.DateTimeNoTimeZone },
{ "GUID", FormulaType.Guid },
{ "Number", FormulaType.Number },
{ "Decimal", FormulaType.Decimal },
{ "Text", FormulaType.String },
{ "Hyperlink", FormulaType.Hyperlink },
{ "None", FormulaType.Blank },
{ "UntypedObject", FormulaType.UntypedObject },
};
IEnumerable<KeyValuePair<string, NameLookupInfo>> IGlobalSymbolNameResolver.GlobalSymbols => _knownTypes.ToDictionary(kvp => kvp.Key, kvp => ToLookupInfo(kvp.Value));
internal override VersionHash VersionHash => base.VersionHash;
private PrimitiveTypesSymbolTable()
{
}
public static readonly PrimitiveTypesSymbolTable Instance = new PrimitiveTypesSymbolTable();
internal override bool TryLookup(DName name, out NameLookupInfo nameInfo)
{
if (!_knownTypes.TryGetValue(name.Value, out var type))
{
nameInfo = default;
return false;
}
nameInfo = ToLookupInfo(type);
return true;
}
internal override bool TryGetTypeName(FormulaType type, out string typeName)
{
typeName = _knownTypes.Where(kvp => kvp.Value.Equals(type)).FirstOrDefault().Key;
return typeName != null;
}
}
}

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

@ -1,25 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Binding.BindInfo;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.UtilityDataStructures;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core
{
internal abstract class TypeSymbolTable : ReadOnlySymbolTable
{
internal abstract bool TryGetTypeName(FormulaType type, out string typeName);
protected NameLookupInfo ToLookupInfo(FormulaType type)
{
return new NameLookupInfo(BindKind.TypeName, type._type, DPath.Root, 0, data: type);
}
}
}

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

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.PowerFx.Core.Public.Types;
using Microsoft.PowerFx.Syntax;
using Microsoft.PowerFx.Types;
namespace Microsoft.PowerFx.Core.Utils
{
internal static class TokenUtils
{
internal static FormulaType GetFormulaType(this IdentToken token)
{
var formulaType = FormulaType.Unknown;
if (PrimitiveTypesSymbolTable.Instance.TryLookup(token.Name, out var info) && info.Data is FormulaType ft)
{
formulaType = ft;
}
return formulaType;
}
}
}

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

@ -33,7 +33,6 @@ namespace Microsoft.PowerFx
internal readonly SymbolTable _symbolTable;
internal readonly SymbolValues _symbolValues;
internal readonly DefinedTypeSymbolTable _definedTypeSymbolTable;
/// <summary>
/// Initializes a new instance of the <see cref="RecalcEngine"/> class.
@ -49,7 +48,6 @@ namespace Microsoft.PowerFx
{
_symbolTable = new SymbolTable { DebugName = "Globals" };
_symbolValues = new SymbolValues(_symbolTable);
_definedTypeSymbolTable = new DefinedTypeSymbolTable();
_symbolValues.OnUpdate += OnSymbolValuesOnUpdate;
base.EngineSymbols = _symbolTable;
@ -215,21 +213,6 @@ namespace Microsoft.PowerFx
return result;
}
internal FormulaType GetFormulaTypeFromName(string name)
{
return FormulaType.Build(GetTypeFromName(name));
}
internal DType GetTypeFromName(string name)
{
if (_definedTypeSymbolTable.TryLookup(new DName(name), out NameLookupInfo nameInfo))
{
return nameInfo.Type;
}
return FormulaType.GetFromStringOrNull(name)._type;
}
// Invoke onUpdate() each time this formula is changed, passing in the new value.
public void SetFormula(string name, string expr, Action<string, FormulaValue> onUpdate)
{
@ -404,7 +387,11 @@ namespace Microsoft.PowerFx
var sb = new StringBuilder();
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
// Compose will handle null symbols
var composedSymbols = SymbolTable.Compose(Config.SymbolTable, SupportedFunctions, PrimitiveTypes);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), composedSymbols, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
if (errors.Any())
@ -419,9 +406,6 @@ namespace Microsoft.PowerFx
throw new InvalidOperationException(sb.ToString());
}
// Compose will handle null symbols
var composedSymbols = SymbolTable.Compose(Config.SymbolTable, SupportedFunctions);
foreach (var udf in udfs)
{
Config.SymbolTable.AddFunction(udf);

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

@ -14,11 +14,11 @@ namespace Microsoft.PowerFx.Core
{
public class FormulaTypeJsonConverter : JsonConverter<FormulaType>
{
private readonly DefinedTypeSymbolTable _definedTypes;
private readonly SymbolTable _definedTypes;
private readonly FormulaTypeSerializerSettings _settings;
internal FormulaTypeJsonConverter(DefinedTypeSymbolTable definedTypes)
internal FormulaTypeJsonConverter(SymbolTable definedTypes)
{
_definedTypes = definedTypes;
_settings = new FormulaTypeSerializerSettings(null);
@ -37,7 +37,7 @@ namespace Microsoft.PowerFx.Core
/// </summary>
/// <param name="settings"></param>
public FormulaTypeJsonConverter(FormulaTypeSerializerSettings settings)
: this(new DefinedTypeSymbolTable())
: this(new SymbolTable())
{
_settings = settings ?? _settings;
}

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

@ -41,7 +41,7 @@ namespace Microsoft.PowerFx.Core
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, FormulaTypeSchema> Fields { get; set; }
public FormulaType ToFormulaType(DefinedTypeSymbolTable definedTypeSymbols, FormulaTypeSerializerSettings settings)
public FormulaType ToFormulaType(INameResolver definedTypeSymbols, FormulaTypeSerializerSettings settings)
{
var typeName = Type.Name;
@ -86,19 +86,14 @@ namespace Microsoft.PowerFx.Core
return FormulaType.BindingError;
}
private static bool TryLookupType(string typeName, DefinedTypeSymbolTable definedTypeSymbols, out FormulaType type)
private static bool TryLookupType(string typeName, INameResolver definedTypeSymbols, out FormulaType type)
{
var lookupOrder = new List<TypeSymbolTable>() { definedTypeSymbols, PrimitiveTypesSymbolTable.Instance };
var lookupOrder = new List<INameResolver>() { definedTypeSymbols, ReadOnlySymbolTable.PrimitiveTypesTableInstance };
foreach (var table in lookupOrder)
{
if (table.TryLookup(new DName(typeName), out var lookupInfo))
if (table.LookupType(new DName(typeName), out var ft))
{
if (lookupInfo.Kind != BindKind.TypeName || lookupInfo.Data is not FormulaType castType)
{
throw new InvalidOperationException("Resolved non-type name when constructing FormulaType definition");
}
type = castType;
type = ft;
return true;
}
}

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

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Public.Types;
using Microsoft.PowerFx.Types;
@ -10,7 +12,7 @@ namespace Microsoft.PowerFx.Core
{
internal static class FormulaTypeToSchemaHelper
{
public static FormulaTypeSchema ToSchema(this FormulaType type, DefinedTypeSymbolTable definedTypeSymbols, FormulaTypeSerializerSettings settings)
public static FormulaTypeSchema ToSchema(this FormulaType type, INameResolver definedTypeSymbols, FormulaTypeSerializerSettings settings)
{
// Converting a formulaType to a FormulaTypeSchema requires cutting off at a max depth
// FormulaType may contain recurisve definitions that are not supported by FormulaTypeSchema
@ -18,7 +20,7 @@ namespace Microsoft.PowerFx.Core
return ToSchema(type, definedTypeSymbols, settings, maxDepth: 5);
}
private static FormulaTypeSchema ToSchema(FormulaType type, DefinedTypeSymbolTable definedTypeSymbols, FormulaTypeSerializerSettings settings, int maxDepth)
private static FormulaTypeSchema ToSchema(FormulaType type, INameResolver definedTypeSymbols, FormulaTypeSerializerSettings settings, int maxDepth)
{
if (TryLookupTypeName(type, definedTypeSymbols, out var typeName))
{
@ -84,13 +86,16 @@ namespace Microsoft.PowerFx.Core
};
}
private static bool TryLookupTypeName(FormulaType type, DefinedTypeSymbolTable definedTypeSymbols, out string typeName)
private static bool TryLookupTypeName(FormulaType type, INameResolver definedTypeSymbols, out string typeName)
{
var lookupOrder = new List<TypeSymbolTable>() { definedTypeSymbols, PrimitiveTypesSymbolTable.Instance };
var lookupOrder = new List<INameResolver>() { definedTypeSymbols, ReadOnlySymbolTable.PrimitiveTypesTableInstance };
foreach (var table in lookupOrder)
{
if (table.TryGetTypeName(type, out typeName))
var typeNames = table.NamedTypes.Where(kvp => kvp.Value.Equals(type));
if (typeNames.Any())
{
typeName = typeNames.FirstOrDefault().Key.Value;
return true;
}
}
@ -99,7 +104,7 @@ namespace Microsoft.PowerFx.Core
return false;
}
private static Dictionary<string, FormulaTypeSchema> GetChildren(AggregateType type, DefinedTypeSymbolTable definedTypeSymbols, FormulaTypeSerializerSettings settings, int maxDepth)
private static Dictionary<string, FormulaTypeSchema> GetChildren(AggregateType type, INameResolver definedTypeSymbols, FormulaTypeSerializerSettings settings, int maxDepth)
{
var fields = new Dictionary<string, FormulaTypeSchema>(StringComparer.Ordinal);
foreach (var child in type.GetFieldTypes())

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

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.PowerFx.Core.Binding;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Types;
@ -14,23 +15,23 @@ namespace Microsoft.PowerFx.Core
/// Aggregate Type Definition derived from a .fx.yaml file
/// This may recursively refer to itself or other types, and so we resolve
/// the field types lazily using the DefinedTypeSymbolTable
/// passed to <see cref="FormulaTypeSchema.ToFormulaType(DefinedTypeSymbolTable, FormulaTypeSerializerSettings)"/>"/>.
/// passed to <see cref="FormulaTypeSchema.ToFormulaType(INameResolver, FormulaTypeSerializerSettings)"/>"/>.
/// </summary>
internal class UserDefinedRecordType : RecordType
{
private readonly FormulaTypeSchema _backingSchema;
private readonly DefinedTypeSymbolTable _symbolTable;
private readonly INameResolver _symbolTable;
private readonly FormulaTypeSerializerSettings _settings;
public override IEnumerable<string> FieldNames => _backingSchema.Fields?.Keys ?? Enumerable.Empty<string>();
public UserDefinedRecordType(FormulaTypeSchema backingSchema, DefinedTypeSymbolTable definedTypes)
public UserDefinedRecordType(FormulaTypeSchema backingSchema, INameResolver definedTypes)
{
_backingSchema = backingSchema;
_symbolTable = definedTypes;
}
public UserDefinedRecordType(FormulaTypeSchema backingSchema, DefinedTypeSymbolTable definedTypes, FormulaTypeSerializerSettings settings)
public UserDefinedRecordType(FormulaTypeSchema backingSchema, INameResolver definedTypes, FormulaTypeSerializerSettings settings)
{
_backingSchema = backingSchema;
_symbolTable = definedTypes;

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

@ -18,7 +18,9 @@ using Xunit;
namespace Microsoft.PowerFx.Core.Tests
{
public class ParseTests : PowerFxTest
{
{
private static readonly ReadOnlySymbolTable _primitiveTypes = ReadOnlySymbolTable.PrimitiveTypesTableInstance;
[Theory]
[InlineData("0")]
[InlineData("-0")]
@ -901,7 +903,7 @@ namespace Microsoft.PowerFx.Core.Tests
};
var parseResult = UserDefinitions.Parse(script, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.Equal(udfCount, udfs.Count());

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

@ -21,6 +21,8 @@ namespace Microsoft.PowerFx.Core.Tests
{
public class UserDefinedFunctionTests : PowerFxTest
{
private static readonly ReadOnlySymbolTable _primitiveTypes = ReadOnlySymbolTable.PrimitiveTypesTableInstance;
[Theory]
[InlineData("Foo(x: Number): Number = Abs(x);", 1, 0, false)]
[InlineData("IsType(x: Number): Number = Abs(x);", 0, 0, true)]
@ -56,10 +58,12 @@ namespace Microsoft.PowerFx.Core.Tests
};
var parseResult = UserDefinitions.Parse(script, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library, FormulaType.PrimitiveTypes);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), nameResolver, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library);
var glue = new Glue2DocumentBinderGlue();
var hasBinderErrors = false;
@ -87,7 +91,7 @@ namespace Microsoft.PowerFx.Core.Tests
var glue = new Glue2DocumentBinderGlue();
var parseResult = UserDefinitions.Parse(udfScript, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
var texlFunctionSet = new TexlFunctionSet(udfs);
@ -135,11 +139,11 @@ namespace Microsoft.PowerFx.Core.Tests
AllowsSideEffects = false
};
var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library);
var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library, FormulaType.PrimitiveTypes);
var glue = new Glue2DocumentBinderGlue();
var parseResult = UserDefinitions.Parse(udfScript, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), nameResolver, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
var texlFunctionSet = new TexlFunctionSet(udfs);
@ -398,7 +402,7 @@ namespace Microsoft.PowerFx.Core.Tests
[Fact]
public void Basic()
{
var st1 = new SymbolTable();
var st1 = SymbolTable.WithPrimitiveTypes();
st1.AddUserDefinedFunction("Foo1(x: Number): Number = x*2;");
st1.AddUserDefinedFunction("Foo2(x: Number): Number = Foo1(x)+1;");
@ -408,7 +412,7 @@ namespace Microsoft.PowerFx.Core.Tests
Assert.Equal(FormulaType.Number, check.ReturnType);
// A different symbol table can have same function name with different type.
var st2 = new SymbolTable();
var st2 = SymbolTable.WithPrimitiveTypes();
st2.AddUserDefinedFunction("Foo2(x: Number): Text = x;");
check = engine.Check("Foo2(3)", symbolTable: st2);
Assert.True(check.IsSuccess);
@ -419,8 +423,7 @@ namespace Microsoft.PowerFx.Core.Tests
public void DefineEmpty()
{
// Empty symbol table doesn't get builtins.
var st = new SymbolTable();
var st = SymbolTable.WithPrimitiveTypes();
st.AddUserDefinedFunction("Foo1(x: Number): Number = x;"); // ok
Assert.Throws<InvalidOperationException>(() => st.AddUserDefinedFunction("Foo2(x: Number): Number = Abs(x);"));
}
@ -451,13 +454,15 @@ namespace Microsoft.PowerFx.Core.Tests
var script = "Add(a: Number, b: Number):Number = a + b;";
var parseResult = UserDefinitions.Parse(script, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library, FormulaType.PrimitiveTypes);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), nameResolver, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
var func = udfs.FirstOrDefault();
Assert.NotNull(func);
var nameResolver = ReadOnlySymbolTable.NewDefault(BuiltinFunctionsCore._library);
var glue = new Glue2DocumentBinderGlue();
var texlFunctionSet = new TexlFunctionSet(udfs);
@ -486,7 +491,7 @@ namespace Microsoft.PowerFx.Core.Tests
};
var parseResult = UserDefinitions.Parse(formula, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.Equal(nfCount, parseResult.NamedFormulas.Count());
@ -509,7 +514,7 @@ namespace Microsoft.PowerFx.Core.Tests
};
var parseResult = UserDefinitions.Parse(script, parserOptions);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.Contains(errors, x => x.MessageKey == "ErrUDF_UnknownType");

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

@ -553,7 +553,7 @@ namespace Microsoft.PowerFx.Tests
engine.UpdateVariable("myArg", FormulaValue.New(10));
symbolTable.AddUserDefinedFunction(script, CultureInfo.InvariantCulture, engine.SupportedFunctions);
symbolTable.AddUserDefinedFunction(script, CultureInfo.InvariantCulture, engine.SupportedFunctions, engine.PrimitiveTypes);
var check = engine.Check(expression, symbolTable: symbolTable);
var result = check.GetEvaluator().Eval();
@ -640,10 +640,10 @@ namespace Microsoft.PowerFx.Tests
public void FunctionInner()
{
// Inner table
SymbolTable stInner = new SymbolTable { DebugName = "Extras" };
SymbolTable stInner = SymbolTable.WithPrimitiveTypes();
stInner.AddUserDefinedFunction("Func1() : Text = \"inner\";");
SymbolTable st = new SymbolTable { DebugName = "Extras" };
SymbolTable st = SymbolTable.WithPrimitiveTypes();
st.AddUserDefinedFunction("Func2() : Text = Func1() & \"2\";", symbolTable: stInner);
var engine = new RecalcEngine();

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

@ -12,7 +12,9 @@ using Xunit;
namespace Microsoft.PowerFx.Interpreter.Tests
{
public class UserDefinedTests
{
{
private static readonly ReadOnlySymbolTable _primitiveTypes = ReadOnlySymbolTable.PrimitiveTypesTableInstance;
[Theory]
[InlineData("x=1;y=2;z=x+y;", "Float(Abs(-(x+y+z)))", 6d)]
[InlineData("x=1;y=2;Foo(x: Number): Number = Abs(x);", "Foo(-(y*y)+x)", 3d)]
@ -42,7 +44,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
Culture = CultureInfo.InvariantCulture
};
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.False(errors.Any());
@ -59,7 +61,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
Culture = CultureInfo.InvariantCulture
};
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.False(errors.Any());
@ -75,7 +77,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
Culture = CultureInfo.InvariantCulture
};
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.True(errors.Any());
@ -92,7 +94,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
Culture = CultureInfo.InvariantCulture
};
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.True(errors.Any());
@ -111,7 +113,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
};
var parseResult = UserDefinitions.Parse(script, options);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out var errors);
var udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out var errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.True(errors.Any());
@ -122,7 +124,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
Culture = CultureInfo.InvariantCulture
};
parseResult = UserDefinitions.Parse(script, options);
udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), out errors);
udfs = UserDefinedFunction.CreateFunctions(parseResult.UDFs.Where(udf => udf.IsParseValid), _primitiveTypes, out errors);
errors.AddRange(parseResult.Errors ?? Enumerable.Empty<TexlError>());
Assert.False(errors.Any());

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

@ -125,7 +125,7 @@ namespace Microsoft.PowerFx.Json.Tests
Converters =
{
// Serialize types without accounting for any defined type names
new FormulaTypeJsonConverter(new DefinedTypeSymbolTable())
new FormulaTypeJsonConverter(new SymbolTable())
}
});
}
@ -139,7 +139,7 @@ namespace Microsoft.PowerFx.Json.Tests
Converters =
{
// Serialize types without accounting for any defined type names
new FormulaTypeJsonConverter(new DefinedTypeSymbolTable())
new FormulaTypeJsonConverter(new SymbolTable())
}
});
}