зеркало из https://github.com/microsoft/Power-Fx.git
UO set property (#2547)
Issue https://github.com/microsoft/Power-Fx/issues/2501.
This commit is contained in:
Родитель
07a17a998f
Коммит
d193162359
|
@ -122,6 +122,13 @@ namespace Microsoft.PowerFx.Types
|
||||||
public static ErrorValue NewError(IEnumerable<ExpressionError> error, FormulaType type)
|
public static ErrorValue NewError(IEnumerable<ExpressionError> error, FormulaType type)
|
||||||
{
|
{
|
||||||
return new ErrorValue(IRContext.NotInSource(type), error.ToList());
|
return new ErrorValue(IRContext.NotInSource(type), error.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UntypedObjectValue New(UntypedObjectBase untypedObject)
|
||||||
|
{
|
||||||
|
return new UntypedObjectValue(
|
||||||
|
IRContext.NotInSource(new UntypedObjectType()),
|
||||||
|
untypedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UntypedObjectValue New(IUntypedObject untypedObject)
|
public static UntypedObjectValue New(IUntypedObject untypedObject)
|
||||||
|
|
|
@ -79,5 +79,39 @@ namespace Microsoft.PowerFx.Types
|
||||||
// Not supported for the time being.
|
// Not supported for the time being.
|
||||||
throw new NotImplementedException("UntypedObjectValue cannot be serialized.");
|
throw new NotImplementedException("UntypedObjectValue cannot be serialized.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class UntypedObjectBase : IUntypedObject
|
||||||
|
{
|
||||||
|
public abstract IUntypedObject this[int index] { get; }
|
||||||
|
|
||||||
|
public abstract FormulaType Type { get; }
|
||||||
|
|
||||||
|
public abstract int GetArrayLength();
|
||||||
|
|
||||||
|
public abstract bool GetBoolean();
|
||||||
|
|
||||||
|
public abstract decimal GetDecimal();
|
||||||
|
|
||||||
|
public abstract double GetDouble();
|
||||||
|
|
||||||
|
public abstract string GetString();
|
||||||
|
|
||||||
|
public abstract string GetUntypedNumber();
|
||||||
|
|
||||||
|
public abstract bool TryGetProperty(string value, out IUntypedObject result);
|
||||||
|
|
||||||
|
public abstract bool TryGetPropertyNames(out IEnumerable<string> propertyNames);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a property on the object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyName">Property name.</param>
|
||||||
|
/// <param name="value">FormulaValue to be set.</param>
|
||||||
|
public virtual void SetProperty(string propertyName, FormulaValue value)
|
||||||
|
{
|
||||||
|
// In case of unwanted behavior, throw an CustomFunctionErrorException exception.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using Microsoft.PowerFx.Core.Functions;
|
using Microsoft.PowerFx.Core.Functions;
|
||||||
using Microsoft.PowerFx.Core.Localization;
|
using Microsoft.PowerFx.Core.Localization;
|
||||||
using Microsoft.PowerFx.Core.Types;
|
using Microsoft.PowerFx.Core.Types;
|
||||||
|
@ -35,6 +33,8 @@ namespace Microsoft.PowerFx.Core.Texl.Builtins
|
||||||
|
|
||||||
public override bool IsSelfContained => true;
|
public override bool IsSelfContained => true;
|
||||||
|
|
||||||
|
public override bool PropagatesMutability => true;
|
||||||
|
|
||||||
public IndexFunction_UO()
|
public IndexFunction_UO()
|
||||||
: base(IndexInvariantFunctionName, TexlStrings.AboutIndex, FunctionCategories.Table, DType.UntypedObject, 0, 2, 2, DType.UntypedObject, DType.Number)
|
: base(IndexInvariantFunctionName, TexlStrings.AboutIndex, FunctionCategories.Table, DType.UntypedObject, 0, 2, 2, DType.UntypedObject, DType.Number)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,7 @@ using Microsoft.PowerFx.Interpreter;
|
||||||
using Microsoft.PowerFx.Interpreter.Exceptions;
|
using Microsoft.PowerFx.Interpreter.Exceptions;
|
||||||
using Microsoft.PowerFx.Types;
|
using Microsoft.PowerFx.Types;
|
||||||
using static Microsoft.PowerFx.Functions.Library;
|
using static Microsoft.PowerFx.Functions.Library;
|
||||||
|
using static Microsoft.PowerFx.Syntax.PrettyPrintVisitor;
|
||||||
|
|
||||||
namespace Microsoft.PowerFx
|
namespace Microsoft.PowerFx
|
||||||
{
|
{
|
||||||
|
@ -154,28 +155,58 @@ namespace Microsoft.PowerFx
|
||||||
// Set is unique because it has an l-value for the first arg.
|
// Set is unique because it has an l-value for the first arg.
|
||||||
// Async params can't have out-params.
|
// Async params can't have out-params.
|
||||||
// Return null if not handled. Else non-null if handled.
|
// Return null if not handled. Else non-null if handled.
|
||||||
private async Task<FormulaValue> TryHandleSet(CallNode node, EvalVisitorContext context)
|
private async Task<FormulaValue> TryHandleSet(CallNode node, EvalVisitorContext context)
|
||||||
{
|
{
|
||||||
// Special case Set() calls because they take an LValue.
|
// Special case Set() calls because they take an LValue.
|
||||||
if (node.Function.GetType() != typeof(RecalcEngineSetFunction))
|
if (node.Function.GetType() != typeof(RecalcEngineSetFunction))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var arg0 = node.Args[0];
|
var arg0 = node.Args[0];
|
||||||
var arg1 = node.Args[1];
|
var arg1 = node.Args[1];
|
||||||
|
|
||||||
var newValue = await arg1.Accept(this, context).ConfigureAwait(false);
|
var newValue = await arg1.Accept(this, context).ConfigureAwait(false);
|
||||||
|
|
||||||
if (arg0.IRContext.IsMutation && arg0 is RecordFieldAccessNode rfan)
|
if (arg0.IRContext.IsMutation)
|
||||||
{
|
{
|
||||||
var arg0value = await rfan.From.Accept(this, context).ConfigureAwait(false);
|
if (arg0 is RecordFieldAccessNode rfan)
|
||||||
|
|
||||||
if (arg0value is RecordValue rv)
|
|
||||||
{
|
{
|
||||||
rv.ShallowCopyFieldInPlace(rfan.Field);
|
var arg0value = await rfan.From.Accept(this, context).ConfigureAwait(false);
|
||||||
rv.UpdateField(rfan.Field, newValue);
|
|
||||||
return node.IRContext.ResultType._type.Kind == DKind.Boolean ? FormulaValue.New(true) : FormulaValue.NewVoid();
|
if (arg0value is RecordValue rv)
|
||||||
|
{
|
||||||
|
rv.ShallowCopyFieldInPlace(rfan.Field);
|
||||||
|
rv.UpdateField(rfan.Field, newValue);
|
||||||
|
return node.IRContext.ResultType._type.Kind == DKind.Boolean ? FormulaValue.New(true) : FormulaValue.NewVoid();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return CommonErrors.UnreachableCodeError(node.IRContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (arg0 is BinaryOpNode bon && bon.Op == BinaryOpKind.DynamicGetField)
|
||||||
|
{
|
||||||
|
var arg0value = await bon.Left.Accept(this, context).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (arg0value is UntypedObjectValue uov && uov.Impl is UntypedObjectBase impl)
|
||||||
|
{
|
||||||
|
TextLiteralNode textLiteralNode = (TextLiteralNode)bon.Right;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
impl.SetProperty(textLiteralNode.LiteralValue, newValue);
|
||||||
|
return node.IRContext.ResultType._type.Kind == DKind.Boolean ? FormulaValue.New(true) : FormulaValue.NewVoid();
|
||||||
|
}
|
||||||
|
catch (CustomFunctionErrorException ex)
|
||||||
|
{
|
||||||
|
return new ErrorValue(node.IRContext, new ExpressionError() { Message = ex.Message, Span = node.IRContext.SourceContext, Kind = ex.ErrorKind });
|
||||||
|
}
|
||||||
|
catch (NotImplementedException)
|
||||||
|
{
|
||||||
|
return CommonErrors.NotYetImplementedError(node.IRContext, $"Class {impl.GetType()} does not implement 'SetProperty'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -184,9 +215,9 @@ namespace Microsoft.PowerFx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binder has already ensured this is a first name node as well as mutable symbol.
|
// Binder has already ensured this is a first name node as well as mutable symbol.
|
||||||
if (arg0 is ResolvedObjectNode obj)
|
if (arg0 is ResolvedObjectNode obj)
|
||||||
{
|
{
|
||||||
if (obj.Value is ISymbolSlot sym)
|
if (obj.Value is ISymbolSlot sym)
|
||||||
{
|
{
|
||||||
if (_symbolValues != null)
|
if (_symbolValues != null)
|
||||||
{
|
{
|
||||||
|
@ -197,9 +228,9 @@ namespace Microsoft.PowerFx
|
||||||
// This may happen if the runtime symbols are missing a value and we failed to update.
|
// This may happen if the runtime symbols are missing a value and we failed to update.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail?
|
// Fail?
|
||||||
return CommonErrors.UnreachableCodeError(node.IRContext);
|
return CommonErrors.UnreachableCodeError(node.IRContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle invoke SetProperty(source.Prop, newValue)
|
// Handle invoke SetProperty(source.Prop, newValue)
|
||||||
|
|
|
@ -55,6 +55,12 @@ namespace Microsoft.PowerFx.Interpreter
|
||||||
nodeToCoercedTypeMap = null;
|
nodeToCoercedTypeMap = null;
|
||||||
returnType = context.Features.PowerFxV1CompatibilityRules ? DType.Void : DType.Boolean;
|
returnType = context.Features.PowerFxV1CompatibilityRules ? DType.Void : DType.Boolean;
|
||||||
|
|
||||||
|
if (argTypes[0].IsUntypedObject)
|
||||||
|
{
|
||||||
|
// if arg0 is untyped object, the host implementation will handle arg1.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var isValid = CheckType(context, args[1], argTypes[1], argTypes[0], errors, ref nodeToCoercedTypeMap);
|
var isValid = CheckType(context, args[1], argTypes[1], argTypes[0], errors, ref nodeToCoercedTypeMap);
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
|
@ -64,43 +70,63 @@ namespace Microsoft.PowerFx.Interpreter
|
||||||
public override void CheckSemantics(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors)
|
public override void CheckSemantics(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors)
|
||||||
{
|
{
|
||||||
base.CheckSemantics(binding, args, argTypes, errors);
|
base.CheckSemantics(binding, args, argTypes, errors);
|
||||||
|
|
||||||
Contracts.AssertValue(args);
|
Contracts.AssertValue(args);
|
||||||
Contracts.AssertAllValues(args);
|
Contracts.AssertAllValues(args);
|
||||||
Contracts.AssertValue(argTypes);
|
Contracts.AssertValue(argTypes);
|
||||||
Contracts.AssertAllValid(argTypes);
|
Contracts.AssertAllValid(argTypes);
|
||||||
Contracts.Assert(args.Length == argTypes.Length);
|
Contracts.Assert(args.Length == argTypes.Length);
|
||||||
Contracts.AssertValue(errors);
|
Contracts.AssertValue(errors);
|
||||||
Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity);
|
Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity);
|
||||||
|
|
||||||
var arg0 = argTypes[0];
|
var arg0 = argTypes[0];
|
||||||
var arg1 = argTypes[1];
|
var arg1 = argTypes[1];
|
||||||
|
|
||||||
// Type check
|
// Type check
|
||||||
if (!(arg0.Accepts(arg1, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: binding.Features.PowerFxV1CompatibilityRules) ||
|
if (arg0.IsUntypedObject)
|
||||||
(arg0.IsNumeric && arg1.IsNumeric)))
|
|
||||||
{
|
{
|
||||||
errors.EnsureError(DocumentErrorSeverity.Critical, args[1], ErrBadType_ExpectedType_ProvidedType, arg0.GetKindString(), arg1.GetKindString());
|
if (CheckMutability(binding, args, argTypes, errors))
|
||||||
return;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(arg0.Accepts(arg1, exact: true, useLegacyDateTimeAccepts: false, usePowerFxV1CompatibilityRules: binding.Features.PowerFxV1CompatibilityRules) ||
|
||||||
|
(arg0.IsNumeric && arg1.IsNumeric)))
|
||||||
|
{
|
||||||
|
errors.EnsureError(DocumentErrorSeverity.Critical, args[1], ErrBadType_ExpectedType_ProvidedType, arg0.GetKindString(), arg1.GetKindString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg1.AggregateHasExpandedType())
|
||||||
|
{
|
||||||
|
if (arg1.IsTable)
|
||||||
|
{
|
||||||
|
errors.EnsureError(DocumentErrorSeverity.Critical, args[1], ErrSetVariableWithRelationshipNotAllowTable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg1.IsRecord)
|
||||||
|
{
|
||||||
|
errors.EnsureError(DocumentErrorSeverity.Critical, args[1], ErrSetVariableWithRelationshipNotAllowRecord);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckMutability(binding, args, argTypes, errors))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg1.AggregateHasExpandedType())
|
errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNeedValidVariableName_Arg, Name, args[0]);
|
||||||
{
|
return;
|
||||||
if (arg1.IsTable)
|
}
|
||||||
{
|
|
||||||
errors.EnsureError(DocumentErrorSeverity.Critical, args[1], ErrSetVariableWithRelationshipNotAllowTable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg1.IsRecord)
|
|
||||||
{
|
|
||||||
errors.EnsureError(DocumentErrorSeverity.Critical, args[1], ErrSetVariableWithRelationshipNotAllowRecord);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstName = args[0].AsFirstName();
|
|
||||||
|
|
||||||
|
private bool CheckMutability(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors)
|
||||||
|
{
|
||||||
|
var firstName = args[0].AsFirstName();
|
||||||
if (firstName != null)
|
if (firstName != null)
|
||||||
{
|
{
|
||||||
// Variable reference assignment, for example Set( x, 3 )
|
// Variable reference assignment, for example Set( x, 3 )
|
||||||
|
@ -108,18 +134,17 @@ namespace Microsoft.PowerFx.Interpreter
|
||||||
if (info.Data is NameSymbol nameSymbol && nameSymbol.Props.CanSet)
|
if (info.Data is NameSymbol nameSymbol && nameSymbol.Props.CanSet)
|
||||||
{
|
{
|
||||||
// We have a variable, success
|
// We have a variable, success
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (binding.Features.PowerFxV1CompatibilityRules)
|
else if (binding.Features.PowerFxV1CompatibilityRules)
|
||||||
{
|
{
|
||||||
// Deep mutation, for example Set( x.a, 4 )
|
// Deep mutation, for example Set( x.a, 4 )
|
||||||
base.ValidateArgumentIsSetMutable(binding, args[0], errors);
|
base.ValidateArgumentIsSetMutable(binding, args[0], errors);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNeedValidVariableName_Arg, Name, args[0]);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,27 +9,27 @@ using Microsoft.PowerFx.Types;
|
||||||
|
|
||||||
namespace Microsoft.PowerFx.Functions
|
namespace Microsoft.PowerFx.Functions
|
||||||
{
|
{
|
||||||
internal class JsonUntypedObject : IUntypedObject
|
internal class JsonUntypedObject : UntypedObjectBase
|
||||||
{
|
{
|
||||||
internal readonly JsonElement _element;
|
internal readonly JsonElement _element;
|
||||||
|
|
||||||
public JsonUntypedObject(JsonElement element)
|
public JsonUntypedObject(JsonElement element)
|
||||||
{
|
{
|
||||||
_element = element;
|
_element = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormulaType Type
|
public override FormulaType Type
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
switch (_element.ValueKind)
|
switch (_element.ValueKind)
|
||||||
{
|
{
|
||||||
case JsonValueKind.Object:
|
case JsonValueKind.Object:
|
||||||
return ExternalType.ObjectType;
|
return ExternalType.ObjectType;
|
||||||
case JsonValueKind.Array:
|
case JsonValueKind.Array:
|
||||||
return ExternalType.ArrayType;
|
return ExternalType.ArrayType;
|
||||||
case JsonValueKind.String:
|
case JsonValueKind.String:
|
||||||
return FormulaType.String;
|
return FormulaType.String;
|
||||||
case JsonValueKind.Number:
|
case JsonValueKind.Number:
|
||||||
// Do not be tempted to use FormulaType.Number here. JSON numbers can be interpreted as either
|
// Do not be tempted to use FormulaType.Number here. JSON numbers can be interpreted as either
|
||||||
// a float or a decimal and connectors take advantage of this to interop with decimals in databases.
|
// a float or a decimal and connectors take advantage of this to interop with decimals in databases.
|
||||||
|
@ -41,45 +41,45 @@ namespace Microsoft.PowerFx.Functions
|
||||||
// number types of various capacities and complements, fixed or floating, binary or decimal.That can make
|
// number types of various capacities and complements, fixed or floating, binary or decimal.That can make
|
||||||
// interchange between different programming languages difficult. JSON instead offers only the representation of
|
// interchange between different programming languages difficult. JSON instead offers only the representation of
|
||||||
// numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
|
// numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit
|
||||||
// sequences even if they disagree on internal representations. That is enough to allow interchange
|
// sequences even if they disagree on internal representations. That is enough to allow interchange
|
||||||
return ExternalType.UntypedNumber;
|
return ExternalType.UntypedNumber;
|
||||||
case JsonValueKind.True:
|
case JsonValueKind.True:
|
||||||
case JsonValueKind.False:
|
case JsonValueKind.False:
|
||||||
return FormulaType.Boolean;
|
return FormulaType.Boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FormulaType.Blank;
|
return FormulaType.Blank;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public IUntypedObject this[int index] => new JsonUntypedObject(_element[index]);
|
|
||||||
|
|
||||||
public int GetArrayLength()
|
|
||||||
{
|
|
||||||
return _element.GetArrayLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public double GetDouble()
|
|
||||||
{
|
|
||||||
return _element.GetDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetString()
|
|
||||||
{
|
|
||||||
return _element.GetString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetBoolean()
|
|
||||||
{
|
|
||||||
return _element.GetBoolean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public decimal GetDecimal()
|
public override IUntypedObject this[int index] => new JsonUntypedObject(_element[index]);
|
||||||
|
|
||||||
|
public override int GetArrayLength()
|
||||||
|
{
|
||||||
|
return _element.GetArrayLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double GetDouble()
|
||||||
|
{
|
||||||
|
return _element.GetDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetString()
|
||||||
|
{
|
||||||
|
return _element.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetBoolean()
|
||||||
|
{
|
||||||
|
return _element.GetBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override decimal GetDecimal()
|
||||||
{
|
{
|
||||||
return _element.GetDecimal();
|
return _element.GetDecimal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUntypedNumber()
|
public override string GetUntypedNumber()
|
||||||
{
|
{
|
||||||
if (Type == ExternalType.UntypedNumber)
|
if (Type == ExternalType.UntypedNumber)
|
||||||
{
|
{
|
||||||
|
@ -89,25 +89,25 @@ namespace Microsoft.PowerFx.Functions
|
||||||
{
|
{
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetProperty(string value, out IUntypedObject result)
|
|
||||||
{
|
|
||||||
var res = _element.TryGetProperty(value, out var je);
|
|
||||||
result = new JsonUntypedObject(je);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetPropertyNames(out IEnumerable<string> result)
|
public override bool TryGetProperty(string value, out IUntypedObject result)
|
||||||
|
{
|
||||||
|
var res = _element.TryGetProperty(value, out var je);
|
||||||
|
result = new JsonUntypedObject(je);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryGetPropertyNames(out IEnumerable<string> result)
|
||||||
{
|
{
|
||||||
if (_element.ValueKind != JsonValueKind.Object)
|
if (_element.ValueKind != JsonValueKind.Object)
|
||||||
{
|
{
|
||||||
result = null;
|
result = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = _element.EnumerateObject().Select(x => x.Name);
|
result = _element.EnumerateObject().Select(x => x.Name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,7 @@ namespace Microsoft.PowerFx.Core.Tests
|
||||||
"Microsoft.PowerFx.Types.IDelegatableTableValue",
|
"Microsoft.PowerFx.Types.IDelegatableTableValue",
|
||||||
"Microsoft.PowerFx.Types.ITypeVisitor",
|
"Microsoft.PowerFx.Types.ITypeVisitor",
|
||||||
"Microsoft.PowerFx.Types.IUntypedObject",
|
"Microsoft.PowerFx.Types.IUntypedObject",
|
||||||
|
"Microsoft.PowerFx.Types.UntypedObjectBase",
|
||||||
"Microsoft.PowerFx.Types.IValueVisitor",
|
"Microsoft.PowerFx.Types.IValueVisitor",
|
||||||
"Microsoft.PowerFx.Types.NamedFormulaType",
|
"Microsoft.PowerFx.Types.NamedFormulaType",
|
||||||
"Microsoft.PowerFx.Types.NamedValue",
|
"Microsoft.PowerFx.Types.NamedValue",
|
||||||
|
|
|
@ -294,7 +294,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SimpleObject : IUntypedObject
|
private class SimpleObject : UntypedObjectBase
|
||||||
{
|
{
|
||||||
private readonly FormulaValue _value;
|
private readonly FormulaValue _value;
|
||||||
|
|
||||||
|
@ -303,46 +303,46 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
_value = value;
|
_value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUntypedObject this[int index] => throw new NotImplementedException();
|
public override IUntypedObject this[int index] => throw new NotImplementedException();
|
||||||
|
|
||||||
public FormulaType Type => _value.Type;
|
public override FormulaType Type => _value.Type;
|
||||||
|
|
||||||
public int GetArrayLength()
|
public override int GetArrayLength()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetBoolean()
|
public override bool GetBoolean()
|
||||||
{
|
{
|
||||||
return ((BooleanValue)_value).Value;
|
return ((BooleanValue)_value).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetDouble()
|
public override double GetDouble()
|
||||||
{
|
{
|
||||||
return ((NumberValue)_value).Value;
|
return ((NumberValue)_value).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public decimal GetDecimal()
|
public override decimal GetDecimal()
|
||||||
{
|
{
|
||||||
return ((DecimalValue)_value).Value;
|
return ((DecimalValue)_value).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUntypedNumber()
|
public override string GetUntypedNumber()
|
||||||
{
|
{
|
||||||
return ((StringValue)_value).Value;
|
return ((StringValue)_value).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetString()
|
public override string GetString()
|
||||||
{
|
{
|
||||||
return ((StringValue)_value).Value;
|
return ((StringValue)_value).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetProperty(string value, out IUntypedObject result)
|
public override bool TryGetProperty(string value, out IUntypedObject result)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetPropertyNames(out IEnumerable<string> result)
|
public override bool TryGetPropertyNames(out IEnumerable<string> result)
|
||||||
{
|
{
|
||||||
result = null;
|
result = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -339,4 +339,8 @@ Errors: Error 45-52: The value passed to the 'Set' function cannot be changed.
|
||||||
>> rwt1_copy3
|
>> rwt1_copy3
|
||||||
{Field1:3,Field2:{Field2_1:321,Field2_2:"2_2",Field2_4:Table({Field1:1,Field2:"earth",Field3:DateTime(2022,1,1,0,0,0,0),Field4:true})}}
|
{Field1:3,Field2:{Field2_1:321,Field2_2:"2_2",Field2_4:Table({Field1:1,Field2:"earth",Field3:DateTime(2022,1,1,0,0,0,0),Field4:true})}}
|
||||||
|
|
||||||
|
// Untyped object
|
||||||
|
>> IsError(Set(ParseJSON("{""x"":5}").x, 99))
|
||||||
|
Errors: Error 34-36: The value passed to the 'Set' function cannot be changed.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,9 +45,59 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
Assert.Equal(FormulaType.UntypedObject, fv3.Type);
|
Assert.Equal(FormulaType.UntypedObject, fv3.Type);
|
||||||
Assert.True(fv3 is BlankValue);
|
Assert.True(fv3 is BlankValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PadUntypedObjectMutationTest()
|
||||||
|
{
|
||||||
|
DataTable dt = new DataTable("someTable");
|
||||||
|
dt.Columns.Add("Id", typeof(int));
|
||||||
|
dt.Columns.Add("Column1", typeof(string));
|
||||||
|
dt.Columns.Add("Column2", typeof(string));
|
||||||
|
dt.Rows.Add(1, "data1", "data2");
|
||||||
|
dt.Rows.Add(2, "data3", "data4");
|
||||||
|
|
||||||
|
PadUntypedObject uo = new PadUntypedObject(dt);
|
||||||
|
PadUntypedObject uoCell = new PadUntypedObject(99);
|
||||||
|
NotImplementedUntypedObject notImplementedUO = new NotImplementedUntypedObject(dt);
|
||||||
|
|
||||||
|
UntypedObjectValue uov = new UntypedObjectValue(IRContext.NotInSource(FormulaType.UntypedObject), uo);
|
||||||
|
UntypedObjectValue uovCell = new UntypedObjectValue(IRContext.NotInSource(FormulaType.UntypedObject), uoCell);
|
||||||
|
UntypedObjectValue notImplementedValue = new UntypedObjectValue(IRContext.NotInSource(FormulaType.UntypedObject), notImplementedUO);
|
||||||
|
|
||||||
|
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);
|
||||||
|
RecalcEngine engine = new RecalcEngine(config);
|
||||||
|
|
||||||
|
engine.Config.SymbolTable.EnableMutationFunctions();
|
||||||
|
engine.UpdateVariable("padTable", uov, new SymbolProperties() { CanMutate = true, CanSetMutate = true });
|
||||||
|
engine.UpdateVariable("notImplementedUO", notImplementedValue, new SymbolProperties() { CanMutate = true, CanSetMutate = true });
|
||||||
|
engine.UpdateVariable("padCell", uovCell);
|
||||||
|
|
||||||
|
// Setting an untyped object (padCell).
|
||||||
|
DecimalValue result = (DecimalValue)engine.Eval(@"Set(Index(padTable, 1).Id, padCell);Index(padTable, 1).Id+1", options: new ParserOptions() { AllowsSideEffects = true });
|
||||||
|
Assert.Equal(100m, result.ToObject());
|
||||||
|
|
||||||
|
// Setting a strongly typed object (99).
|
||||||
|
result = (DecimalValue)engine.Eval(@"Set(Index(padTable, 1).Id, 99);Index(padTable, 1).Id+1", options: new ParserOptions() { AllowsSideEffects = true });
|
||||||
|
Assert.Equal(100m, result.ToObject());
|
||||||
|
|
||||||
|
// Property does not exist.
|
||||||
|
ErrorValue errorValue = (ErrorValue)engine.Eval(@"Set(Index(padTable, 1).DoesNotExist, 99)", options: new ParserOptions() { AllowsSideEffects = true });
|
||||||
|
Assert.IsType<ErrorValue>(errorValue);
|
||||||
|
Assert.Equal(ErrorKind.InvalidArgument, errorValue.Errors.First().Kind);
|
||||||
|
|
||||||
|
// Type not supported.
|
||||||
|
errorValue = (ErrorValue)engine.Eval(@"Set(Index(padTable, 1).Column2, GUID())", options: new ParserOptions() { AllowsSideEffects = true });
|
||||||
|
Assert.IsType<ErrorValue>(errorValue);
|
||||||
|
Assert.Equal(ErrorKind.InvalidArgument, errorValue.Errors.First().Kind);
|
||||||
|
|
||||||
|
// 'SetProperty' not implemented.
|
||||||
|
errorValue = (ErrorValue)engine.Eval(@"Set(Index(notImplementedUO, 1).Id, 1)", options: new ParserOptions() { AllowsSideEffects = true });
|
||||||
|
Assert.IsType<ErrorValue>(errorValue);
|
||||||
|
Assert.Equal(ErrorKind.NotSupported, errorValue.Errors.First().Kind);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PadUntypedObject : IUntypedObject
|
public class PadUntypedObject : UntypedObjectBase
|
||||||
{
|
{
|
||||||
public DataTable DataTable;
|
public DataTable DataTable;
|
||||||
public DataRow DataRow;
|
public DataRow DataRow;
|
||||||
|
@ -74,8 +124,6 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
Cell = cell;
|
Cell = cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUntypedObject this[int index] => Index(index);
|
|
||||||
|
|
||||||
private IUntypedObject Index(int index)
|
private IUntypedObject Index(int index)
|
||||||
{
|
{
|
||||||
return (DataTable != null)
|
return (DataTable != null)
|
||||||
|
@ -85,7 +133,9 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
: throw new NotImplementedException();
|
: throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormulaType Type => GetFormulaType();
|
public override FormulaType Type => GetFormulaType();
|
||||||
|
|
||||||
|
public override IUntypedObject this[int index] => Index(index);
|
||||||
|
|
||||||
private FormulaType GetFormulaType()
|
private FormulaType GetFormulaType()
|
||||||
{
|
{
|
||||||
|
@ -102,7 +152,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
: ExternalType.ArrayAndObject;
|
: ExternalType.ArrayAndObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetArrayLength()
|
public override int GetArrayLength()
|
||||||
{
|
{
|
||||||
return (DataTable != null)
|
return (DataTable != null)
|
||||||
? DataTable.Rows.Count
|
? DataTable.Rows.Count
|
||||||
|
@ -111,12 +161,12 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
: throw new NotImplementedException();
|
: throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetBoolean()
|
public override bool GetBoolean()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetDouble()
|
public override double GetDouble()
|
||||||
{
|
{
|
||||||
return Cell switch
|
return Cell switch
|
||||||
{
|
{
|
||||||
|
@ -127,12 +177,12 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public decimal GetDecimal()
|
public override decimal GetDecimal()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUntypedNumber()
|
public override string GetUntypedNumber()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -142,12 +192,12 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetString()
|
public override string GetString()
|
||||||
{
|
{
|
||||||
return Cell.ToString();
|
return Cell.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetProperty(string propertyName, out IUntypedObject result)
|
public override bool TryGetProperty(string propertyName, out IUntypedObject result)
|
||||||
{
|
{
|
||||||
if (DataTable != null)
|
if (DataTable != null)
|
||||||
{
|
{
|
||||||
|
@ -165,7 +215,154 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetPropertyNames(out IEnumerable<string> result)
|
public override bool TryGetPropertyNames(out IEnumerable<string> result)
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetProperty(string propertyName, FormulaValue value)
|
||||||
|
{
|
||||||
|
if (DataTable != null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DataRow.Table.Columns.Contains(propertyName))
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
throw new CustomFunctionErrorException($"Property '{propertyName}' does not exist.", ErrorKind.InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is DecimalValue dv)
|
||||||
|
{
|
||||||
|
DataRow[propertyName] = dv.Value;
|
||||||
|
}
|
||||||
|
else if (value is GuidValue)
|
||||||
|
{
|
||||||
|
throw new CustomFunctionErrorException($"Type '{value.Type.ToString()}' is not supported.", ErrorKind.InvalidArgument);
|
||||||
|
}
|
||||||
|
else if (value is UntypedObjectValue uov)
|
||||||
|
{
|
||||||
|
if (DataRow[propertyName].GetType() == typeof(string))
|
||||||
|
{
|
||||||
|
DataRow[propertyName] = uov.Impl.GetString();
|
||||||
|
}
|
||||||
|
else if (DataRow[propertyName].GetType() == typeof(int))
|
||||||
|
{
|
||||||
|
DataRow[propertyName] = uov.Impl.GetDouble();
|
||||||
|
}
|
||||||
|
else if (DataRow[propertyName].GetType() == typeof(bool))
|
||||||
|
{
|
||||||
|
DataRow[propertyName] = uov.Impl.GetBoolean();
|
||||||
|
}
|
||||||
|
else if (DataRow[propertyName].GetType() == typeof(decimal))
|
||||||
|
{
|
||||||
|
DataRow[propertyName] = uov.Impl.GetDecimal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new CustomFunctionErrorException($"Type '{DataRow[propertyName].GetType()}' is not supported.", ErrorKind.InvalidArgument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NotImplementedUntypedObject : UntypedObjectBase
|
||||||
|
{
|
||||||
|
public DataTable DataTable;
|
||||||
|
public DataRow DataRow;
|
||||||
|
public object Cell;
|
||||||
|
|
||||||
|
public NotImplementedUntypedObject(DataTable dt)
|
||||||
|
{
|
||||||
|
DataTable = dt;
|
||||||
|
DataRow = null;
|
||||||
|
Cell = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotImplementedUntypedObject(DataRow dr)
|
||||||
|
{
|
||||||
|
DataTable = null;
|
||||||
|
DataRow = dr;
|
||||||
|
Cell = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotImplementedUntypedObject(object cell)
|
||||||
|
{
|
||||||
|
DataTable = null;
|
||||||
|
DataRow = null;
|
||||||
|
Cell = cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IUntypedObject Index(int index)
|
||||||
|
{
|
||||||
|
return new NotImplementedUntypedObject(DataTable.Rows[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override FormulaType Type => GetFormulaType();
|
||||||
|
|
||||||
|
public override IUntypedObject this[int index] => Index(index);
|
||||||
|
|
||||||
|
private FormulaType GetFormulaType()
|
||||||
|
{
|
||||||
|
return ExternalType.ArrayAndObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetArrayLength()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetBoolean()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double GetDouble()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override decimal GetDecimal()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetUntypedNumber()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetPropertyNames()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetString()
|
||||||
|
{
|
||||||
|
return Cell.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryGetProperty(string propertyName, out IUntypedObject result)
|
||||||
|
{
|
||||||
|
if (DataTable != null)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DataRow.Table.Columns.Contains(propertyName))
|
||||||
|
{
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cell = DataRow[propertyName];
|
||||||
|
result = new NotImplementedUntypedObject(cell);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryGetPropertyNames(out IEnumerable<string> result)
|
||||||
{
|
{
|
||||||
result = null;
|
result = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -799,6 +799,8 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
var t_bsType = TableType.Empty().Add("b", FormulaType.String);
|
var t_bsType = TableType.Empty().Add("b", FormulaType.String);
|
||||||
engine.UpdateVariable("t_bs1", FormulaValue.NewTable(t_bsType.ToRecord()));
|
engine.UpdateVariable("t_bs1", FormulaValue.NewTable(t_bsType.ToRecord()));
|
||||||
engine.UpdateVariable("t_bs2", FormulaValue.NewTable(t_bsType.ToRecord()));
|
engine.UpdateVariable("t_bs2", FormulaValue.NewTable(t_bsType.ToRecord()));
|
||||||
|
|
||||||
|
engine.Config.EnableJsonFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TraceSetup(RecalcEngine engine, bool numberIsFloat)
|
private static void TraceSetup(RecalcEngine engine, bool numberIsFloat)
|
||||||
|
|
|
@ -14,7 +14,8 @@ namespace Microsoft.PowerFx.Tests
|
||||||
// Wrap a .net object as an UntypedObject.
|
// Wrap a .net object as an UntypedObject.
|
||||||
// This will lazily marshal through the object as it's accessed.
|
// This will lazily marshal through the object as it's accessed.
|
||||||
[DebuggerDisplay("{_source}")]
|
[DebuggerDisplay("{_source}")]
|
||||||
public class PrimitiveWrapperAsUnknownObject : IUntypedObject
|
[DebuggerDisplay("{_source}")]
|
||||||
|
public class PrimitiveWrapperAsUnknownObject : UntypedObjectBase
|
||||||
{
|
{
|
||||||
public readonly object _source;
|
public readonly object _source;
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ namespace Microsoft.PowerFx.Tests
|
||||||
return FormulaValue.New(new PrimitiveWrapperAsUnknownObject(source));
|
return FormulaValue.New(new PrimitiveWrapperAsUnknownObject(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormulaType Type
|
public override FormulaType Type
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -61,7 +62,7 @@ namespace Microsoft.PowerFx.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUntypedObject this[int index]
|
public override IUntypedObject this[int index]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -81,13 +82,13 @@ namespace Microsoft.PowerFx.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetArrayLength()
|
public override int GetArrayLength()
|
||||||
{
|
{
|
||||||
var a = (Array)_source;
|
var a = (Array)_source;
|
||||||
return a.Length;
|
return a.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetBoolean()
|
public override bool GetBoolean()
|
||||||
{
|
{
|
||||||
Assert.True(Type == FormulaType.Boolean);
|
Assert.True(Type == FormulaType.Boolean);
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ namespace Microsoft.PowerFx.Tests
|
||||||
throw new InvalidOperationException($"Not a boolean type");
|
throw new InvalidOperationException($"Not a boolean type");
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetDouble()
|
public override double GetDouble()
|
||||||
{
|
{
|
||||||
// Fx will only call this helper for numbers.
|
// Fx will only call this helper for numbers.
|
||||||
Assert.True(Type == FormulaType.Number);
|
Assert.True(Type == FormulaType.Number);
|
||||||
|
@ -122,7 +123,7 @@ namespace Microsoft.PowerFx.Tests
|
||||||
throw new InvalidOperationException($"Not a number type");
|
throw new InvalidOperationException($"Not a number type");
|
||||||
}
|
}
|
||||||
|
|
||||||
public decimal GetDecimal()
|
public override decimal GetDecimal()
|
||||||
{
|
{
|
||||||
// Fx will only call this helper for decimals.
|
// Fx will only call this helper for decimals.
|
||||||
Assert.True(Type == FormulaType.Decimal);
|
Assert.True(Type == FormulaType.Decimal);
|
||||||
|
@ -145,12 +146,12 @@ namespace Microsoft.PowerFx.Tests
|
||||||
throw new InvalidOperationException($"Not a decimal type");
|
throw new InvalidOperationException($"Not a decimal type");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUntypedNumber()
|
public override string GetUntypedNumber()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetString()
|
public override string GetString()
|
||||||
{
|
{
|
||||||
Assert.True(Type == FormulaType.String);
|
Assert.True(Type == FormulaType.String);
|
||||||
|
|
||||||
|
@ -162,7 +163,7 @@ namespace Microsoft.PowerFx.Tests
|
||||||
throw new InvalidOperationException($"Not a string type");
|
throw new InvalidOperationException($"Not a string type");
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetProperty(string value, out IUntypedObject result)
|
public override bool TryGetProperty(string value, out IUntypedObject result)
|
||||||
{
|
{
|
||||||
Assert.True(Type == ExternalType.ObjectType);
|
Assert.True(Type == ExternalType.ObjectType);
|
||||||
|
|
||||||
|
@ -191,7 +192,7 @@ namespace Microsoft.PowerFx.Tests
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetPropertyNames(out IEnumerable<string> result)
|
public override bool TryGetPropertyNames(out IEnumerable<string> result)
|
||||||
{
|
{
|
||||||
result = null;
|
result = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
{
|
{
|
||||||
// Demonstrate mutation example using IUntypedObject
|
// Demonstrate mutation example using IUntypedObject
|
||||||
public class ScenarioMutation : PowerFxTest
|
public class ScenarioMutation : PowerFxTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MutabilityTest()
|
public void MutabilityTest()
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
{
|
{
|
||||||
["prop"] = FormulaValue.New(123)
|
["prop"] = FormulaValue.New(123)
|
||||||
};
|
};
|
||||||
|
|
||||||
var obj = MutableObject.New(d);
|
var obj = MutableObject.New(d);
|
||||||
engine.UpdateVariable("obj", obj);
|
engine.UpdateVariable("obj", obj);
|
||||||
|
|
||||||
|
@ -45,15 +45,15 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("Assert2(obj.prop, 123); Set2(obj, \"prop\", 456); Assert2(obj.prop, 456)")]
|
[InlineData("Assert2(obj.prop, 123); Set2(obj, \"prop\", 456); Assert2(obj.prop, 456)")]
|
||||||
[InlineData("Assert2(obj.prop, 123); Set3(obj, \"prop\", \"prop2\"); Assert2(obj.prop, 456)")]
|
[InlineData("Assert2(obj.prop, 123); Set3(obj, \"prop\", \"prop2\"); Assert2(obj.prop, 456)")]
|
||||||
public void MutabilityTest_Chain(string expr)
|
public void MutabilityTest_Chain(string expr)
|
||||||
{
|
{
|
||||||
var config = new PowerFxConfig();
|
var config = new PowerFxConfig();
|
||||||
config.AddFunction(new Assert2Function());
|
config.AddFunction(new Assert2Function());
|
||||||
config.AddFunction(new Set2Function());
|
config.AddFunction(new Set2Function());
|
||||||
config.AddFunction(new Set3Function());
|
config.AddFunction(new Set3Function());
|
||||||
var engine = new RecalcEngine(config);
|
var engine = new RecalcEngine(config);
|
||||||
|
|
||||||
var d = new Dictionary<string, FormulaValue>
|
var d = new Dictionary<string, FormulaValue>
|
||||||
{
|
{
|
||||||
["prop"] = FormulaValue.New(123),
|
["prop"] = FormulaValue.New(123),
|
||||||
|
@ -72,7 +72,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
|
|
||||||
Assert.IsType<DecimalValue>(x);
|
Assert.IsType<DecimalValue>(x);
|
||||||
Assert.Equal(456, ((DecimalValue)x).Value);
|
Assert.Equal(456, ((DecimalValue)x).Value);
|
||||||
Assert.Equal(456, ((DecimalValue)d["prop"]).Value);
|
Assert.Equal(456, ((DecimalValue)d["prop"]).Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -133,7 +133,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
{
|
{
|
||||||
var impl = (MutableObject)obj.Impl;
|
var impl = (MutableObject)obj.Impl;
|
||||||
impl.TryGetProperty(propName2.Value, out var propValue);
|
impl.TryGetProperty(propName2.Value, out var propValue);
|
||||||
var val = propValue.GetDecimal();
|
var val = propValue.GetDecimal();
|
||||||
impl.Set(propName.Value, new DecimalValue(IRContext.NotInSource(FormulaType.Decimal), val));
|
impl.Set(propName.Value, new DecimalValue(IRContext.NotInSource(FormulaType.Decimal), val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MutableObject : IUntypedObject
|
private class MutableObject : UntypedObjectBase
|
||||||
{
|
{
|
||||||
private Dictionary<string, FormulaValue> _values = new Dictionary<string, FormulaValue>();
|
private Dictionary<string, FormulaValue> _values = new Dictionary<string, FormulaValue>();
|
||||||
|
|
||||||
|
@ -212,41 +212,41 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
return FormulaValue.New(x);
|
return FormulaValue.New(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IUntypedObject this[int index] => throw new NotImplementedException();
|
public override IUntypedObject this[int index] => throw new NotImplementedException();
|
||||||
|
|
||||||
public FormulaType Type => ExternalType.ObjectType;
|
public override FormulaType Type => ExternalType.ObjectType;
|
||||||
|
|
||||||
public int GetArrayLength()
|
public override int GetArrayLength()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetBoolean()
|
public override bool GetBoolean()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetDouble()
|
public override double GetDouble()
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public decimal GetDecimal()
|
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUntypedNumber()
|
public override decimal GetDecimal()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetString()
|
public override string GetUntypedNumber()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetProperty(string value, out IUntypedObject result)
|
public override string GetString()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryGetProperty(string value, out IUntypedObject result)
|
||||||
{
|
{
|
||||||
if (_values.TryGetValue(value, out var x))
|
if (_values.TryGetValue(value, out var x))
|
||||||
{
|
{
|
||||||
|
@ -258,7 +258,7 @@ namespace Microsoft.PowerFx.Interpreter.Tests
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetPropertyNames(out IEnumerable<string> result)
|
public override bool TryGetPropertyNames(out IEnumerable<string> result)
|
||||||
{
|
{
|
||||||
result = null;
|
result = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче