Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions src/libraries/Microsoft.PowerFx.Core/Binding/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ private TexlBinding(
HasParentItemReference = false;

ContextScope = ruleScope;
BinderNodeMetadataArgTypeVisitor = new BinderNodesMetadataArgTypeVisitor(this, resolver, ruleScope, BindingConfig.UseThisRecordForRuleScope, features);
BinderNodeMetadataArgTypeVisitor = new BinderNodesMetadataArgTypeVisitor(this, resolver, ruleScope, BindingConfig.UseThisRecordForRuleScope, features, BindingConfig.NumberIsFloat);
HasReferenceToAttachment = false;
NodesToReplace = new List<KeyValuePair<Token, string>>();
UpdateDisplayNames = updateDisplayNames;
Expand Down Expand Up @@ -430,7 +430,7 @@ public static TexlBinding Run(
features ??= Features.None;

var txb = new TexlBinding(glue, scopeResolver, queryOptionsMap, node, resolver, bindingConfig, ruleScope, updateDisplayNames, forceUpdateDisplayNames, rule: rule, features: features, delegationHintProvider: delegationHintProvider);
var vis = new Visitor(txb, resolver, ruleScope, bindingConfig.UseThisRecordForRuleScope, features);
var vis = new Visitor(txb, resolver, ruleScope, bindingConfig.UseThisRecordForRuleScope, features, bindingConfig.NumberIsFloat);
vis.Run();

// If the expression is dataflow only Ie non-side effecting, void doesn't have any practical use and should be converted to errors.
Expand Down Expand Up @@ -1061,8 +1061,7 @@ internal bool TryGetDataSourceInfo(TexlNode node, out IExternalDataSource dataSo
return dataSourceInfo != null;

case NodeKind.DottedName:
IExpandInfo info;
if (TryGetEntityInfo(node.AsDottedName(), out info))
if (TryGetEntityInfo(node.AsDottedName(), out IExpandInfo info))
{
dataSourceInfo = info.ParentDataSource;
return dataSourceInfo != null;
Expand Down Expand Up @@ -2507,16 +2506,18 @@ public Scope Up(int upCount)
private readonly TexlBinding _txb;
private Scope _currentScope;
private int _currentScopeDsNodeId;
private readonly Features _features;
private readonly Features _features;
private readonly bool _numberIsFloat;

public Visitor(TexlBinding txb, INameResolver resolver, DType topScope, bool useThisRecordForRuleScope, Features features)
public Visitor(TexlBinding txb, INameResolver resolver, DType topScope, bool useThisRecordForRuleScope, Features features, bool numberIsFloat)
{
Contracts.AssertValue(txb);
Contracts.AssertValueOrNull(resolver);

_txb = txb;
_nameResolver = resolver;
_features = features;
_features = features;
_numberIsFloat = numberIsFloat;

_topScope = new Scope(null, null, topScope ?? DType.Error, useThisRecordForRuleScope ? new[] { ThisRecordDefaultName } : default);
_currentScope = _topScope;
Expand Down Expand Up @@ -2609,7 +2610,8 @@ private void VisitType(TypeLiteralNode node)
return;
}

var type = DTypeVisitor.Run(node.TypeRoot, _nameResolver);
var visitor = new DTypeVisitor(_numberIsFloat);
var type = visitor.Run(node.TypeRoot, _nameResolver);

if (type.IsValid)
{
Expand Down Expand Up @@ -5178,7 +5180,8 @@ private void PreVisitTypeArgAndProccesCallNode(CallNode node, TexlFunction func)

if (args[1] is FirstNameNode typeName)
{
if (_nameResolver.LookupType(typeName.Ident.Name, out var typeArgType))
var name = DType.MapNumber(typeName.Ident.Name, _numberIsFloat);
if (_nameResolver.LookupType(name, out var typeArgType))
{
_txb.SetType(typeName, typeArgType._type);
_txb.SetInfo(typeName, FirstNameInfo.Create(typeName, new NameLookupInfo(BindKind.NamedType, typeArgType._type, DPath.Root, 0)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ private sealed class BinderNodesMetadataArgTypeVisitor : Visitor
{
private readonly TexlBinding _txb;

public BinderNodesMetadataArgTypeVisitor(TexlBinding binding, INameResolver resolver, DType topScope, bool useThisRecordForRuleScope, Features features)
: base(binding, resolver, topScope, useThisRecordForRuleScope, features)
public BinderNodesMetadataArgTypeVisitor(TexlBinding binding, INameResolver resolver, DType topScope, bool useThisRecordForRuleScope, Features features, bool numberIsFloat)
: base(binding, resolver, topScope, useThisRecordForRuleScope, features, numberIsFloat)
{
Contracts.AssertValue(binding);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ public static IEnumerable<UserDefinedFunction> CreateFunctions(IEnumerable<UDF>
continue;
}

var parametersOk = CheckParameters(udf.Args, errors, nameResolver, out var parameterTypes);
var returnTypeOk = CheckReturnType(udf.ReturnType, errors, nameResolver, udf.IsImperative, out var returnType);
var parametersOk = CheckParameters(udf.Args, errors, nameResolver, udf.NumberIsFloat, out var parameterTypes);
var returnTypeOk = CheckReturnType(udf.ReturnType, errors, nameResolver, udf.IsImperative, udf.NumberIsFloat, out var returnType);
if (!parametersOk || !returnTypeOk)
{
continue;
Expand Down Expand Up @@ -401,8 +401,8 @@ internal static UserDefinedFunction CreatePartialFunction(UDF udf, INameResolver
errors.Add(new TexlError(udf.Ident, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_TooManyParameters, udfName, MaxParameterCount));
}

var parametersOk = CheckParameters(udf.Args, errors, nameResolver, out var parameterTypes);
var returnTypeOk = CheckReturnType(udf.ReturnType, errors, nameResolver, udf.IsImperative, out var returnType);
var parametersOk = CheckParameters(udf.Args, errors, nameResolver, udf.NumberIsFloat, out var parameterTypes);
var returnTypeOk = CheckReturnType(udf.ReturnType, errors, nameResolver, udf.IsImperative, udf.NumberIsFloat, out var returnType);

if (!returnTypeOk)
{
Expand All @@ -421,7 +421,7 @@ internal static UserDefinedFunction CreatePartialFunction(UDF udf, INameResolver
return func;
}

private static bool CheckParameters(ISet<UDFArg> args, List<TexlError> errors, INameResolver nameResolver, out DType[] parameterTypes)
private static bool CheckParameters(ISet<UDFArg> args, List<TexlError> errors, INameResolver nameResolver, bool numberIsFloat, out DType[] parameterTypes)
{
if (args.Count == 0)
{
Expand All @@ -447,7 +447,9 @@ private static bool CheckParameters(ISet<UDFArg> args, List<TexlError> errors, I

if (arg.TypeIdent != null)
{
if (!nameResolver.LookupType(arg.TypeIdent.Name, out var parameterType))
var name = DType.MapNumber(arg.TypeIdent.Name, numberIsFloat: numberIsFloat);

if (!nameResolver.LookupType(name, out var parameterType))
{
errors.Add(new TexlError(arg.TypeIdent, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_UnknownType, arg.TypeIdent.Name));
isParamCheckSuccessful = false;
Expand All @@ -474,15 +476,17 @@ private static bool CheckParameters(ISet<UDFArg> args, List<TexlError> errors, I
return isParamCheckSuccessful;
}

private static bool CheckReturnType(IdentToken returnTypeToken, List<TexlError> errors, INameResolver nameResolver, bool isImperative, out DType returnType)
private static bool CheckReturnType(IdentToken returnTypeToken, List<TexlError> errors, INameResolver nameResolver, bool isImperative, bool numberIsFloat, out DType returnType)
{
if (returnTypeToken == null)
{
returnType = DType.Unknown;
return false;
}

var name = DType.MapNumber(returnTypeToken.Name, numberIsFloat);

if (!nameResolver.LookupType(returnTypeToken.Name, out var returnTypeFormulaType))
if (!nameResolver.LookupType(name, out var returnTypeFormulaType))
{
errors.Add(new TexlError(returnTypeToken, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_UnknownType, returnTypeToken.Name));
returnType = DType.Invalid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class ParserOptions
/// This primarily determines numeric decimal separator character
/// as well as chaining operator.
/// </summary>
public CultureInfo Culture { get; set; }
public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture;

/// <summary>
/// If greater than 0, enforces a maximum length on a single expression.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,27 +212,29 @@ public void AddConstant(string name, FormulaValue data)
_variables.Add(name, info); // can't exist
}

internal static ParserOptions GetUDFParserOptions(CultureInfo culture, bool allowSideEffects)
internal static ParserOptions GetUDFParserOptions(CultureInfo culture, bool numberIsFloat, bool allowSideEffects)
{
return new ParserOptions()
{
AllowsSideEffects = allowSideEffects,
Culture = culture ?? CultureInfo.InvariantCulture,
NumberIsFloat = numberIsFloat
};
}

/// <summary>
/// Adds user defined functions in the script.
/// </summary>
/// <param name="script">String representation of the user defined function.</param>
/// <param name="parseCulture">CultureInfo to parse the script againts. Default is invariant.</param>
/// <param name="parserOptions">CultureInfo to parse the script againts. Default is invariant.</param>
/// <param name="symbolTable">Extra symbols to bind UDF. Commonly coming from Engine.</param>
/// <param name="extraSymbolTable">Additional symbols to bind UDF.</param>
/// <param name="allowSideEffects">Allow for curly brace parsing.</param>
internal DefinitionsCheckResult AddUserDefinedFunction(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, ReadOnlySymbolTable extraSymbolTable = null, bool allowSideEffects = false)
internal DefinitionsCheckResult AddUserDefinedFunction(string script, ParserOptions parserOptions = null, ReadOnlySymbolTable symbolTable = null, ReadOnlySymbolTable extraSymbolTable = null, bool allowSideEffects = false)
{
var composedSymbolTable = ReadOnlySymbolTable.Compose(this, symbolTable, extraSymbolTable);
var checkResult = GetDefinitionsCheckResult(script, parseCulture, composedSymbolTable, allowSideEffects);
var options = parserOptions ?? new ParserOptions(CultureInfo.InvariantCulture);
var checkResult = GetDefinitionsCheckResult(script, options, composedSymbolTable, allowSideEffects);

var udfs = checkResult.ApplyCreateUserDefinedFunctions();

Expand All @@ -246,9 +248,9 @@ internal DefinitionsCheckResult AddUserDefinedFunction(string script, CultureInf
return checkResult;
}

internal static DefinitionsCheckResult GetDefinitionsCheckResult(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
internal static DefinitionsCheckResult GetDefinitionsCheckResult(string script, ParserOptions parserOptions, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
{
var options = GetUDFParserOptions(parseCulture, allowSideEffects);
var options = GetUDFParserOptions(parserOptions.Culture, parserOptions.NumberIsFloat, allowSideEffects);
var checkResult = new DefinitionsCheckResult();
return checkResult.SetText(script, options)
.SetBindingInfo(symbolTable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ internal IReadOnlyDictionary<DName, FormulaType> ApplyResolveTypes()
{
if (_parse.DefinedTypes.Any())
{
this._resolvedTypes = DefinedTypeResolver.ResolveTypes(_parse.DefinedTypes.Where(dt => dt.IsParseValid), _symbols, out var errors);
this._resolvedTypes = DefinedTypeResolver.ResolveTypes(_parse.DefinedTypes.Where(dt => dt.IsParseValid), _symbols, _parserOptions.NumberIsFloat, out var errors);
this._localSymbolTable.AddTypes(this._resolvedTypes);
_errors.AddRange(ExpressionError.New(errors, _defaultErrorCulture));
}
Expand Down
6 changes: 4 additions & 2 deletions src/libraries/Microsoft.PowerFx.Core/Public/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -564,16 +564,18 @@ public string GetDisplayExpression(string expressionText, ReadOnlySymbolTable sy
return ExpressionLocalizationHelper.ConvertExpression(expressionText, ruleScope, GetDefaultBindingConfig(), CreateResolverInternal(symbolTable), CreateBinderGlue(), culture, Config.Features, toDisplay: true);
}

public DefinitionsCheckResult AddUserDefinedFunction(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
public DefinitionsCheckResult AddUserDefinedFunction(string script, ParserOptions parserOptions = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
{
return Config.SymbolTable.AddUserDefinedFunction(script, parseCulture, UDFDefaultBindingSymbols, symbolTable, allowSideEffects);
return Config.SymbolTable.AddUserDefinedFunction(script, parserOptions, UDFDefaultBindingSymbols, symbolTable, allowSideEffects);
}

#if false
public ReadOnlySymbolTable GetUserDefinedFunctionSymbol(string script, CultureInfo parseCulture = null, ReadOnlySymbolTable symbolTable = null, bool allowSideEffects = false)
{
var resultSymbols = new SymbolTable();
resultSymbols.AddUserDefinedFunction(script, parseCulture, UDFDefaultBindingSymbols, symbolTable, allowSideEffects);
return resultSymbols;
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ internal FormulaType(DType type)
{ new DName("DateTime"), DateTime },
{ new DName("DateTimeTZInd"), DateTimeNoTimeZone },
{ new DName("GUID"), Guid },
{ new DName("Number"), Number },
{ new DName("Float"), Number },
{ new DName("Decimal"), Decimal },
{ new DName("Text"), String },
{ new DName("Hyperlink"), Hyperlink },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,28 @@ namespace Microsoft.PowerFx.Core.Syntax.Visitors
// Visitor to resolve TypeLiteralNode.TypeRoot into DType.
internal class DTypeVisitor : DefaultVisitor<DType, INameResolver>
{
private DTypeVisitor()
private readonly bool _numberIsFloat;

public DTypeVisitor(bool numberIsFloat)
: base(DType.Invalid)
{
_numberIsFloat = numberIsFloat;
}

public static DType Run(TexlNode node, INameResolver context)
public DType Run(TexlNode node, INameResolver context)
{
Contracts.AssertValue(node);
Contracts.AssertValue(context);

return node.Accept(new DTypeVisitor(), context);
return node.Accept(this, context);
}

public override DType Visit(FirstNameNode node, INameResolver context)
{
Contracts.AssertValue(node);
Contracts.AssertValue(context);

var name = node.Ident.Name;
var name = DType.MapNumber(node.Ident.Name, _numberIsFloat);
if (context.LookupType(name, out FormulaType ft))
{
return ft._type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public override bool HasSuggestionsForParam(int argIndex)
return argIndex == 1;
}

// This list needs to be used with the intersection of Engine.PrimitiveTypes. The parser will effectively do this.
// Notably this list excludes ObjNull and Void, which make no sense to use in this context.
// It also excludes Color, as we have not implemented the coversion of "Red" and other color names.
internal static readonly ISet<DType> SupportedJSONTypes = new HashSet<DType> { DType.Boolean, DType.Number, DType.Decimal, DType.Date, DType.DateTime, DType.DateTimeNoTimeZone, DType.Time, DType.String, DType.Guid, DType.Hyperlink, DType.UntypedObject };

public UntypedOrJSONConversionFunction(string name, TexlStrings.StringGetter description, DType returnType, int arityMax, params DType[] paramTypes)
Expand Down
12 changes: 12 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Types/DType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4081,5 +4081,17 @@ internal static bool IsSupportedType(DType type, ISet<DType> supportedTypes, out

return true;
}

internal static DName MapNumber(DName name, bool numberIsFloat)
{
if (name.Value == "Number")
{
return numberIsFloat ? new DName("Float") : new DName("Decimal");
}
else
{
return name;
}
}
}
}
Loading
Loading