Esempio n. 1
0
        public static FormulaValue Split(EvalVisitor runner, SymbolContext symbolContext, IRContext irContext, StringValue[] args)
        {
            var text      = args[0].Value;
            var separator = args[1].Value;

            // The separator can be zero, one, or more characters that are matched as a whole in the text string. Using a zero length or blank
            // string results in each character being broken out individually.
            var substrings = string.IsNullOrEmpty(separator) ? text.Select(c => new string(c, 1)) : text.Split(new string[] { separator }, StringSplitOptions.None);
            var rows       = substrings.Select(s => new StringValue(IRContext.NotInSource(FormulaType.String), s));

            return(new InMemoryTableValue(irContext, StandardSingleColumnTableFromValues(irContext, rows.ToArray(), BuiltinFunction.OneColumnTableResultName)));
        }
Esempio n. 2
0
 private FormulaValue PropagateFieldType(FormulaValue fieldValue, FormulaType fieldType)
 {
     if (fieldValue is RecordValue recordValue)
     {
         return(new InMemoryRecordValue(IRContext.NotInSource(fieldType), recordValue.Fields));
     }
     if (fieldValue is TableValue tableValue)
     {
         return(new InMemoryTableValue(IRContext.NotInSource(fieldType), tableValue.Rows));
     }
     return(fieldValue);
 }
Esempio n. 3
0
        private static Func <IRContext, int, FormulaValue> ReplaceBlankWithZeroForSpecificIndices(params int[] indices)
        {
            var indicesToReplace = new HashSet <int>(indices);

            return((irContext, index) =>
            {
                if (indicesToReplace.Contains(index))
                {
                    return new NumberValue(IRContext.NotInSource(FormulaType.Number), 0.0);
                }
                return new BlankValue(irContext);
            });
        }
Esempio n. 4
0
        private static IEnumerable <FormulaValue> MidFunctionExpandArgs(IRContext irContext, IEnumerable <FormulaValue> args)
        {
            List <FormulaValue> res = new List <FormulaValue>(args);

            while (res.Count < 3)
            {
                // The third argument to Mid can only ever be used if the first argument is a string
                if (args.First() is StringValue stringValue)
                {
                    var count = new NumberValue(IRContext.NotInSource(FormulaType.Number), stringValue.Value.Length);
                    res.Add(count);
                }
            }
            return(res.ToArray());
        }
Esempio n. 5
0
        internal InMemoryTableValue(IRContext irContext, IEnumerable <DValue <RecordValue> > records) : base(irContext)
        {
            Contract.Assert(IRContext.ResultType is TableType);
            var tableType  = (TableType)IRContext.ResultType;
            var recordType = tableType.ToRecord();

            _records = records.Select(r =>
            {
                if (r.IsValue)
                {
                    return(DValue <RecordValue> .Of(new InMemoryRecordValue(IRContext.NotInSource(recordType), r.Value.Fields)));
                }
                else
                {
                    return(r);
                }
            });
        }
Esempio n. 6
0
        public static Func <EvalVisitor, SymbolContext, IRContext, TableValue[], FormulaValue> StandardSingleColumnTable <T>(Func <EvalVisitor, SymbolContext, IRContext, T[], FormulaValue> targetFunction) where T : FormulaValue
        {
            return((runner, symbolContext, irContext, args) =>
            {
                var tableType = (TableType)irContext.ResultType;
                var resultType = tableType.ToRecord();
                var itemType = resultType.GetFieldType(BuiltinFunction.OneColumnTableResultNameStr);

                var arg0 = args[0];
                var resultRows = new List <DValue <RecordValue> >();
                foreach (var row in arg0.Rows)
                {
                    if (row.IsValue)
                    {
                        var value = row.Value.GetField(BuiltinFunction.ColumnName_ValueStr);
                        NamedValue namedValue;
                        namedValue = value switch
                        {
                            T t => new NamedValue(BuiltinFunction.OneColumnTableResultNameStr, targetFunction(runner, symbolContext, IRContext.NotInSource(itemType), new T[] { t })),
                            BlankValue bv => new NamedValue(BuiltinFunction.OneColumnTableResultNameStr, bv),
                            ErrorValue ev => new NamedValue(BuiltinFunction.OneColumnTableResultNameStr, ev),
                            _ => new NamedValue(BuiltinFunction.OneColumnTableResultNameStr, CommonErrors.RuntimeTypeMismatch(IRContext.NotInSource(itemType)))
                        };
                        var record = new InMemoryRecordValue(IRContext.NotInSource(resultType), new List <NamedValue>()
                        {
                            namedValue
                        });
                        resultRows.Add(DValue <RecordValue> .Of(record));
                    }
                    else if (row.IsBlank)
                    {
                        resultRows.Add(DValue <RecordValue> .Of(row.Blank));
                    }
                    else
                    {
                        resultRows.Add(DValue <RecordValue> .Of(row.Error));
                    }
                }
                return new InMemoryTableValue(irContext, resultRows);
            });
        }
Esempio n. 7
0
        /// <summary>
        /// A pipeline that maps blanks to a value, checks
        /// runtime types, and possibly map values to errors
        /// before filtering errors and possibly returning
        /// an ErrorValue instead of executing
        /// </summary>
        /// <typeparam name="T">The specific FormulaValue type that the implementation of the builtin expects, for exmaple NumberValue for math functions</typeparam>
        /// <param name="expandArguments">This stage of the pipeline can be used to expand an argument list if some of the arguments are optional and missing</param>
        /// <param name="replaceBlankValues">This stage can be used to transform Blank() into something else, for example the number 0</param>
        /// <param name="checkRuntimeTypes">This stage can be used to check to that all the arguments have type T, or check that all arguments have type T | Blank(), etc.</param>
        /// <param name="checkRuntimeValues">This stage can be used to generate errors if specific values occur in the arguments, for example infinity, NaN, etc.</param>
        /// <param name="returnBehavior">A flag that can be used to activate pre-defined early return behavior, such as returning Blank() if any argument is Blank()</param>
        /// <param name="targetFunction">The implementation of the builtin function</param>
        /// <returns></returns>
        private static FunctionPtr StandardErrorHandling <T>(
            Func <IRContext, IEnumerable <FormulaValue>, IEnumerable <FormulaValue> > expandArguments,
            Func <IRContext, int, FormulaValue> replaceBlankValues,
            Func <IRContext, int, FormulaValue, FormulaValue> checkRuntimeTypes,
            Func <IRContext, int, FormulaValue, FormulaValue> checkRuntimeValues,
            ReturnBehavior returnBehavior,
            Func <EvalVisitor, SymbolContext, IRContext, T[], FormulaValue> targetFunction
            ) where T : FormulaValue
        {
            return((runner, symbolContext, irContext, args) =>
            {
                var argumentsExpanded = expandArguments(irContext, args);

                var blankValuesReplaced = argumentsExpanded.Select((arg, i) =>
                {
                    if (arg is BlankValue)
                    {
                        return replaceBlankValues(arg.IRContext, i);
                    }
                    else
                    {
                        return arg;
                    }
                });

                var runtimeTypesChecked = blankValuesReplaced.Select((arg, i) => checkRuntimeTypes(irContext, i, arg));

                var runtimeValuesChecked = runtimeTypesChecked.Select((arg, i) =>
                {
                    if (arg is T t)
                    {
                        return checkRuntimeValues(arg.IRContext, i, t);
                    }
                    else
                    {
                        return arg;
                    }
                });

                var errors = runtimeValuesChecked.OfType <ErrorValue>();
                if (errors.Count() != 0)
                {
                    return ErrorValue.Combine(irContext, errors);
                }

                switch (returnBehavior)
                {
                case ReturnBehavior.ReturnBlankIfAnyArgIsBlank:
                    if (runtimeValuesChecked.Any(arg => arg is BlankValue))
                    {
                        return new BlankValue(IRContext.NotInSource(FormulaType.Blank));
                    }
                    break;

                case ReturnBehavior.ReturnEmptyStringIfAnyArgIsBlank:
                    if (runtimeValuesChecked.Any(arg => arg is BlankValue))
                    {
                        return new StringValue(IRContext.NotInSource(FormulaType.String), "");
                    }
                    break;

                case ReturnBehavior.ReturnFalseIfAnyArgIsBlank:
                    if (runtimeValuesChecked.Any(arg => arg is BlankValue))
                    {
                        return new BooleanValue(IRContext.NotInSource(FormulaType.Boolean), false);
                    }
                    break;

                case ReturnBehavior.AlwaysEvaluateAndReturnResult:
                    break;
                }

                return targetFunction(runner, symbolContext, irContext, runtimeValuesChecked.Select(arg => arg as T).ToArray());
            });
        }
Esempio n. 8
0
 private static FormulaValue ReplaceBlankWithEmptyString(IRContext irContext, int index)
 {
     return(new StringValue(IRContext.NotInSource(FormulaType.String), ""));
 }
Esempio n. 9
0
 private static FormulaValue ReplaceBlankWithZero(IRContext irContext, int index)
 {
     return(new NumberValue(IRContext.NotInSource(FormulaType.Number), 0.0));
 }
Esempio n. 10
0
        // https://docs.microsoft.com/en-us/powerapps/maker/canvas-apps/functions/function-sequence
        public static FormulaValue Sequence(IRContext irContext, NumberValue[] args)
        {
            double records = args[0].Value;
            double start   = args[1].Value;
            double step    = args[2].Value;

            var rows = LazySequence(records, start, step).Select(n => new NumberValue(IRContext.NotInSource(FormulaType.Number), n));

            return(new InMemoryTableValue(irContext, StandardTableNodeRecords(irContext, rows.ToArray())));
        }
Esempio n. 11
0
 internal StringValue ToLower()
 {
     return(new StringValue(IRContext.NotInSource(FormulaType.String), Value.ToLowerInvariant()));
 }
Esempio n. 12
0
 public void UpdateVariable(string name, double value)
 {
     UpdateVariable(name, new NumberValue(IRContext.NotInSource(FormulaType.Number), value));
 }
Esempio n. 13
0
 internal virtual FormulaValue GetField(string name)
 {
     return(GetField(IRContext.NotInSource(FormulaType.Blank), name));
 }
Esempio n. 14
0
        public static RecordValue Empty()
        {
            var type = new RecordType();

            return(new InMemoryRecordValue(IRContext.NotInSource(type), new List <NamedValue>()));
        }