Skip to content

Commit

Permalink
Value and Boolean function support for number and Boolean backed Opti…
Browse files Browse the repository at this point in the history
…on Sets (#2230)
  • Loading branch information
gregli-msft authored Feb 23, 2024
1 parent e26195c commit 1064cd8
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 17 deletions.
10 changes: 10 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Localization/Strings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,21 @@ internal static class TexlStrings
public static StringGetter AboutBooleanNT = (b) => StringResources.Get("AboutBooleanNT", b);
public static StringGetter BooleanNTArg1 = (b) => StringResources.Get("BooleanNTArg1", b);

public static StringGetter AboutBooleanW = (b) => StringResources.Get("AboutBooleanW", b);
public static StringGetter BooleanWArg1 = (b) => StringResources.Get("BooleanWArg1", b);
public static StringGetter AboutBooleanWT = (b) => StringResources.Get("AboutBooleanWT", b);
public static StringGetter BooleanWTArg1 = (b) => StringResources.Get("BooleanWTArg1", b);

public static StringGetter AboutBooleanB = (b) => StringResources.Get("AboutBooleanB", b);
public static StringGetter BooleanBArg1 = (b) => StringResources.Get("BooleanBArg1", b);
public static StringGetter AboutBooleanBT = (b) => StringResources.Get("AboutBooleanBT", b);
public static StringGetter BooleanBTArg1 = (b) => StringResources.Get("BooleanBTArg1", b);

public static StringGetter AboutBooleanL = (b) => StringResources.Get("AboutBooleanL", b);
public static StringGetter BooleanLArg1 = (b) => StringResources.Get("BooleanLArg1", b);
public static StringGetter AboutBooleanLT = (b) => StringResources.Get("AboutBooleanLT", b);
public static StringGetter BooleanLTArg1 = (b) => StringResources.Get("BooleanLTArg1", b);

public static StringGetter AboutConcatenate = (b) => StringResources.Get("AboutConcatenate", b);
public static StringGetter ConcatenateArg1 = (b) => StringResources.Get("ConcatenateArg1", b);
public static StringGetter AboutConcatenateT = (b) => StringResources.Get("AboutConcatenateT", b);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal class BuiltinFunctionsCore
public static readonly TexlFunction BooleanN = _library.Add(new BooleanNFunction());
public static readonly TexlFunction BooleanN_T = _library.Add(new BooleanNFunction_T());
public static readonly TexlFunction BooleanW = _library.Add(new BooleanWFunction());
public static readonly TexlFunction BooleanW_T = _library.Add(new BooleanWFunction_T());
public static readonly TexlFunction BooleanW_T = _library.Add(new BooleanWFunction_T());
public static readonly TexlFunction Char = _library.Add(new CharFunction());
public static readonly TexlFunction CharT = _library.Add(new CharTFunction());
public static readonly TexlFunction Clock24 = _library.Add(new IsClock24Function());
Expand Down Expand Up @@ -255,7 +255,9 @@ internal class BuiltinFunctionsCore
public static readonly TexlFunction IsUTCToday = _featureGateFunctions.Add(new IsUTCTodayFunction());
public static readonly TexlFunction OptionsSetInfo = _featureGateFunctions.Add(new OptionSetInfoFunction());
public static readonly TexlFunction UTCNow = _featureGateFunctions.Add(new UTCNowFunction());
public static readonly TexlFunction UTCToday = _featureGateFunctions.Add(new UTCTodayFunction());
public static readonly TexlFunction UTCToday = _featureGateFunctions.Add(new UTCTodayFunction());
public static readonly TexlFunction BooleanL = _featureGateFunctions.Add(new BooleanLFunction());
public static readonly TexlFunction BooleanL_T = _featureGateFunctions.Add(new BooleanLFunction_T());

// Slow API, only use for backward compatibility
#pragma warning disable CS0618 // Type or member is obsolete
Expand Down
113 changes: 104 additions & 9 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Boolean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.PowerFx.Core.Localization;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.Utils;
using Microsoft.PowerFx.Functions;
using Microsoft.PowerFx.Syntax;

#pragma warning disable SA1402 // File may only contain a single type
Expand Down Expand Up @@ -158,21 +159,21 @@ internal sealed class BooleanWFunction : BuiltinFunction

// Reusing BooleanN strings as they are generic for numbers
public BooleanWFunction()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanN, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.Decimal)
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanW, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.Decimal)
{
}

public override IEnumerable<TexlStrings.StringGetter[]> GetSignatures()
{
// Reusing BooleanN strings as they are generic for numbers
yield return new[] { TexlStrings.BooleanNArg1 };
yield return new[] { TexlStrings.BooleanWArg1 };
}

public override bool TryGetParamDescription(string paramName, out string paramDescription)
{
Contracts.AssertNonEmpty(paramName);

return StringResources.TryGet("AboutBooleanN_" + paramName, out paramDescription);
return StringResources.TryGet("AboutBooleanW_" + paramName, out paramDescription);
}
}

Expand All @@ -186,14 +187,14 @@ internal sealed class BooleanWFunction_T : BuiltinFunction

// Reusing BooleanN strings as they are generic for numbers
public BooleanWFunction_T()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanNT, FunctionCategories.Table, DType.EmptyTable, 0, 1, 1, DType.EmptyTable)
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanWT, FunctionCategories.Table, DType.EmptyTable, 0, 1, 1, DType.EmptyTable)
{
}

public override IEnumerable<TexlStrings.StringGetter[]> GetSignatures()
{
// Reusing BooleanN strings as they are generic for numbers
yield return new[] { TexlStrings.BooleanNTArg1 };
yield return new[] { TexlStrings.BooleanWTArg1 };
}

public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
Expand All @@ -216,7 +217,7 @@ public override bool TryGetParamDescription(string paramName, out string paramDe
Contracts.AssertNonEmpty(paramName);

// Reusing BooleanN strings as they are generic for numbers
return StringResources.TryGet("AboutBooleanNT_" + paramName, out paramDescription);
return StringResources.TryGet("AboutBooleanWT_" + paramName, out paramDescription);
}
}

Expand All @@ -229,13 +230,21 @@ internal sealed class BooleanBFunction : BuiltinFunction
public override bool SupportsParamCoercion => false;

public BooleanBFunction()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanN, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.Boolean)
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanB, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.Boolean)
{
}

public override IEnumerable<TexlStrings.StringGetter[]> GetSignatures()
{
yield return new[] { TexlStrings.BooleanArg1 };
yield return new[] { TexlStrings.BooleanBArg1 };
}

public override bool TryGetParamDescription(string paramName, out string paramDescription)
{
Contracts.AssertNonEmpty(paramName);

// Reusing BooleanN strings as they are generic for numbers
return StringResources.TryGet("AboutBooleanB_" + paramName, out paramDescription);
}

/// <summary>
Expand Down Expand Up @@ -269,7 +278,7 @@ public BooleanBFunction_T()

public override IEnumerable<TexlStrings.StringGetter[]> GetSignatures()
{
yield return new[] { TexlStrings.BooleanNTArg1 };
yield return new[] { TexlStrings.BooleanBTArg1 };
}

public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
Expand Down Expand Up @@ -312,6 +321,92 @@ internal override IR.Nodes.IntermediateNode CreateIRCallNode(PowerFx.Syntax.Call
return base.CreateIRCallNode(node, context, args, scope);
}
}
}

// Boolean(arg:L)
// Corresponding Excel and DAX function: Boolean
internal sealed class BooleanLFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;

public BooleanLFunction()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanL, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.OptionSetValue)
{
}

public override IEnumerable<TexlStrings.StringGetter[]> GetSignatures()
{
yield return new[] { TexlStrings.BooleanLArg1 };
}

public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
var argType = argTypes[0];

var fValid = base.CheckTypes(context, args, argTypes, errors, out returnType, out nodeToCoercedTypeMap) &&
argType.IsOptionSetBackedByBoolean;

returnType = DType.Boolean;

return fValid;
}

public override bool TryGetParamDescription(string paramName, out string paramDescription)
{
Contracts.AssertNonEmpty(paramName);

return StringResources.TryGet("AboutBooleanL_" + paramName, out paramDescription);
}
}

// Boolean(E:*[L])
// Corresponding Excel and DAX function: Boolean
internal sealed class BooleanLFunction_T : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;

public BooleanLFunction_T()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanLT, FunctionCategories.Table, DType.EmptyTable, 0, 1, 1, DType.EmptyTable)
{
}

public override IEnumerable<TexlStrings.StringGetter[]> GetSignatures()
{
yield return new[] { TexlStrings.BooleanLTArg1 };
}

public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
var fValid = base.CheckTypes(context, args, argTypes, errors, out returnType, out nodeToCoercedTypeMap);
Contracts.Assert(returnType.IsTable);

var arg = args[0];
var argType = argTypes[0];

// Check first that it is a single column table of option set values.
// Next check that the column is not only option sets, but also backed by Booleans.
if (CheckColumnType(context, arg, argType, DType.OptionSetValue, errors, ref nodeToCoercedTypeMap) &&
argType.TypeTree.GetPairs().First().Value.IsOptionSetBackedByBoolean)
{
var rowType = DType.EmptyRecord.Add(new TypedName(DType.Boolean, ColumnName_Value));
returnType = rowType.ToTable();

return true;
}

return false;
}

public override bool TryGetParamDescription(string paramName, out string paramDescription)
{
Contracts.AssertNonEmpty(paramName);

return StringResources.TryGet("AboutBooleanLT_" + paramName, out paramDescription);
}
}

// Boolean(arg:O)
Expand Down
3 changes: 2 additions & 1 deletion src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Value.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp
if (!DType.Number.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) &&
!DType.Decimal.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) &&
!DType.String.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) &&
!DType.Boolean.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules))
!DType.Boolean.Accepts(argType, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: context.Features.PowerFxV1CompatibilityRules) &&
!argType.IsOptionSetBackedByNumber)
{
if (argType.CoercesTo(DType.DateTime, aggregateCoercion: true, isTopLevelCoercion: false, context.Features) && !argType.IsControl)
{
Expand Down
6 changes: 6 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Types/DType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,12 @@ internal void AssertValid()

public bool IsOptionSet => Kind == DKind.OptionSet || Kind == DKind.OptionSetValue;

public bool IsOptionSetBackedByNumber => IsOptionSet && OptionSetInfo.BackingKind == DKind.Number;

public bool IsOptionSetBackedByBoolean => IsOptionSet && OptionSetInfo.BackingKind == DKind.Boolean;

public bool IsOptionSetBackedByColor => IsOptionSet && OptionSetInfo.BackingKind == DKind.Color;

public bool IsView => Kind == DKind.View || Kind == DKind.ViewValue;

public bool IsAggregate => IsRecord || IsTable;
Expand Down
15 changes: 15 additions & 0 deletions src/libraries/Microsoft.PowerFx.Interpreter/Functions/Library.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ static Library()
returnBehavior: ReturnBehavior.ReturnBlankIfAnyArgIsBlank,
targetFunction: DecimalToBoolean)
},
{
BuiltinFunctionsCore.BooleanL,
StandardErrorHandling<OptionSetValue>(
BuiltinFunctionsCore.BooleanL.Name,
expandArguments: NoArgExpansion,
replaceBlankValues: DoNotReplaceBlank,
checkRuntimeTypes: ExactValueTypeOrBlank<OptionSetValue>,
checkRuntimeValues: DeferRuntimeValueChecking,
returnBehavior: ReturnBehavior.ReturnBlankIfAnyArgIsBlank,
targetFunction: BooleanOptionSetToBoolean)
},

// This implementation is not actually used for this as this is handled at IR level.
// This is a placeholder, so that RecalcEngine._interpreterSupportedFunctions can add it for txt tests.
Expand Down Expand Up @@ -1909,6 +1920,10 @@ static Library()
BuiltinFunctionsCore.BooleanW_T,
StandardErrorHandlingTabularOverload<DecimalValue>(BuiltinFunctionsCore.BooleanW_T.Name, SimpleFunctionImplementations[BuiltinFunctionsCore.BooleanW], DoNotReplaceBlank)
},
{
BuiltinFunctionsCore.BooleanL_T,
StandardErrorHandlingTabularOverload<OptionSetValue>(BuiltinFunctionsCore.BooleanL_T.Name, SimpleFunctionImplementations[BuiltinFunctionsCore.BooleanL], DoNotReplaceBlank)
},

// This implementation is not actually used for this as this is handled at IR level.
// This is a placeholder, so that RecalcEngine._interpreterSupportedFunctions can add it for txt tests.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ public static bool TryFloat(FormattingInfo formatInfo, IRContext irContext, Form
case BooleanValue b:
result = BooleanToNumber(irContext, b);
break;
case OptionSetValue osv:
result = new NumberValue(irContext, (double)osv.ExecutionValue);
break;
case DateValue dv:
result = DateToNumber(formatInfo, irContext, dv);
break;
Expand Down Expand Up @@ -261,7 +264,7 @@ public static bool TryDecimal(FormattingInfo formatInfo, IRContext irContext, Fo
{
result = null;

Contract.Assert(DecimalValue.AllowedListConvertToDecimal.Contains(value.Type));
Contract.Assert(DecimalValue.AllowedListConvertToDecimal.Contains(value.Type) || value.Type._type.IsOptionSetBackedByNumber);

switch (value)
{
Expand All @@ -284,6 +287,21 @@ public static bool TryDecimal(FormattingInfo formatInfo, IRContext irContext, Fo
break;
case DateTimeValue dtv:
result = DateTimeToDecimal(formatInfo, irContext, dtv);
break;
case OptionSetValue osv:
if (value.Type._type.IsOptionSetBackedByNumber)
{
var (os, osErr) = ConvertNumberToDecimal((double)osv.ExecutionValue);
if (osErr == ConvertionStatus.Ok)
{
result = new DecimalValue(irContext, os);
}
}
else
{
result = new DecimalValue(irContext, (decimal)osv.ExecutionValue);
}

break;
case StringValue sv:
var (str, strErr) = ConvertToDecimal(sv.Value, formatInfo.CultureInfo);
Expand Down Expand Up @@ -624,6 +642,14 @@ private static string ResolveDateTimeFormatAmbiguities(string format, DateTime d
return resultString;
}

public static BooleanValue BooleanOptionSetToBoolean(IRContext irContext, OptionSetValue[] args)
{
Contract.Assert(args[0].Type._type.IsOptionSetBackedByBoolean);

var n = args[0].ExecutionValue;
return new BooleanValue(irContext, (bool)n);
}

private static string RestoreDoubleQuotedStrings(string format, List<string> replaceList, CancellationToken cancellationToken)
{
var stringReplaceRegex = new Regex("\u0011");
Expand Down
Loading

0 comments on commit 1064cd8

Please sign in to comment.