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))); }
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); }
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); }); }
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()); }
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); } }); }
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); }); }
/// <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()); }); }
private static FormulaValue ReplaceBlankWithEmptyString(IRContext irContext, int index) { return(new StringValue(IRContext.NotInSource(FormulaType.String), "")); }
private static FormulaValue ReplaceBlankWithZero(IRContext irContext, int index) { return(new NumberValue(IRContext.NotInSource(FormulaType.Number), 0.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()))); }
internal StringValue ToLower() { return(new StringValue(IRContext.NotInSource(FormulaType.String), Value.ToLowerInvariant())); }
public void UpdateVariable(string name, double value) { UpdateVariable(name, new NumberValue(IRContext.NotInSource(FormulaType.Number), value)); }
internal virtual FormulaValue GetField(string name) { return(GetField(IRContext.NotInSource(FormulaType.Blank), name)); }
public static RecordValue Empty() { var type = new RecordType(); return(new InMemoryRecordValue(IRContext.NotInSource(type), new List <NamedValue>())); }