зеркало из https://github.com/microsoft/Power-Fx.git
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:
Родитель
4a9f9e996d
Коммит
f366046f70
|
@ -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())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче