public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); DType type0 = argTypes[0]; DType type1 = argTypes[1]; DType otherType = DType.Invalid; TexlNode otherArg = null; // At least one of the arguments has to be a table. if (type0.IsTable) { // Ensure we have a one-column table of colors. fValid &= CheckColorColumnType(type0, args[0], errors, ref nodeToCoercedTypeMap); // Borrow the return type from the 1st arg. returnType = type0; // Check arg1 below. otherArg = args[1]; otherType = type1; fValid &= CheckOtherType(otherType, otherArg, DType.Number, errors, ref nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); Contracts.Assert(!fValid || returnType.IsColumn); } else if (type1.IsTable) { // Ensure we have a one-column table of numerics. fValid &= CheckNumericColumnType(type1, args[1], errors, ref nodeToCoercedTypeMap); // Since the 1st arg is not a table, make a new table return type *[Result:c] returnType = DType.CreateTable(new TypedName(DType.Color, OneColumnTableResultName)); // Check arg0 below. otherArg = args[0]; otherType = type0; fValid &= CheckOtherType(otherType, otherArg, DType.Color, errors, ref nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); Contracts.Assert(!fValid || returnType.IsColumn); } else { Contracts.Assert(returnType.IsTable); errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTypeError_Ex1_Ex2_Found, TableKindString, DType.Color.GetKindString(), type0.GetKindString()); errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrTypeError_Ex1_Ex2_Found, TableKindString, DType.Number.GetKindString(), type1.GetKindString()); // Both args are invalid. No need to continue. return(false); } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); returnType = argTypes[0]; DType exprType = argTypes[1]; if (!exprType.IsPrimitive || exprType.IsOptionSet) { fValid = false; errors.EnsureError(args[1], TexlStrings.ErrSortWrongType); } if (args.Length == 3 && argTypes[2] != DType.String) { fValid = false; errors.EnsureError(args[2], TexlStrings.ErrSortIncorrectOrder); } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); // The argument should be a table of one column. DType argType = argTypes[0]; if (!argType.IsTable) { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNeedTable_Func, Name); } else if (argType.GetNames(DPath.Root).Count() != 1) { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNeedTableCol_Func, Name); } return(fValid); }
public TexlError EnsureError(TexlNode node, ErrorResourceKey errKey, params object[] args) { if (DefaultSeverity <= maximumSeverity) { return(errors.EnsureError(node, errKey, args)); } return(null); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == 2); Contracts.Assert(argTypes.Length == 2); Contracts.AssertValue(errors); if (!base.CheckInvocation(binding, args, argTypes, errors, out returnType, out nodeToCoercedTypeMap)) { return(false); } // Check if first argument is poly type or an activity pointer if (!argTypes[0].IsPolymorphic && !argTypes[0].IsActivityPointer) { errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrBadType_ExpectedType_ProvidedType, DKind.Polymorphic.ToString(), argTypes[0].GetKindString()); return(false); } // Check if table arg referrs to a connected data source. TexlNode tableArg = args[1]; FirstNameInfo tableInfo; IExternalDataSource tableDsInfo; if (!binding.TryGetFirstNameInfo(tableArg.Id, out tableInfo) || (tableDsInfo = (tableInfo.Data as IExternalDataSource)) == null || !(tableDsInfo is IExternalTabularDataSource)) { errors.EnsureError(tableArg, TexlStrings.ErrAsTypeAndIsTypeExpectConnectedDataSource); return(false); } if (binding.Document.Properties.EnabledFeatures.IsEnhancedDelegationEnabled && (tableDsInfo is IExternalCdsDataSource) && argTypes[0].HasPolymorphicInfo) { var expandInfo = argTypes[0].PolymorphicInfo.TryGetExpandInfo(tableDsInfo.TableMetadata.Name); if (expandInfo != null) { returnType = argTypes[0].ExpandPolymorphic(argTypes[1], expandInfo); return(true); } } returnType = argTypes[1].ToRecord(); return(true); }
private bool CheckOtherType(DType otherType, TexlNode otherArg, DType expectedType, IErrorContainer errors, ref Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.Assert(otherType.IsValid); Contracts.AssertValue(otherArg); Contracts.Assert(expectedType == DType.Color || expectedType == DType.Number); Contracts.AssertValue(errors); if (otherType.IsTable) { // Ensure we have a one-column table of numerics/color values based on expected type. return(expectedType == DType.Number ? CheckNumericColumnType(otherType, otherArg, errors, ref nodeToCoercedTypeMap) : CheckColorColumnType(otherType, otherArg, errors, ref nodeToCoercedTypeMap)); } if (expectedType.Accepts(otherType)) { return(true); } if (otherType.CoercesTo(expectedType)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, otherArg, expectedType); return(true); } errors.EnsureError(DocumentErrorSeverity.Severe, otherArg, TexlStrings.ErrTypeError_Ex1_Ex2_Found, TableKindString, expectedType.GetKindString(), otherType.GetKindString()); return(false); }
// Typecheck an input for this function, and get the cursor type for an invocation with that input. // arg0 and arg0Type correspond to the input and its type. // The cursor type for aggregate functions is generally the type of a row in the input schema (table), // for example Table in an invocation Average(Table, valueFunction). // Returns true on success, false if the input or its type are invalid with respect to this function's declaration // (and populate the error container accordingly). public virtual bool CheckInput(TexlNode inputNode, DType inputSchema, IErrorContainer errors, out DType typeScope) { Contracts.AssertValue(inputNode); Contracts.Assert(inputSchema.IsValid); Contracts.AssertValue(errors); CallNode callNode = inputNode.Parent.CastList().Parent.CastCall(); Contracts.AssertValue(callNode); typeScope = inputSchema; bool fArgsValid = true; if (_function.ParamTypes.Length == 0) { switch (typeScope.Kind) { case DKind.Record: break; case DKind.Error: fArgsValid = false; errors.EnsureError(inputNode, TexlStrings.ErrBadType); break; default: fArgsValid = false; errors.Error(callNode, TexlStrings.ErrBadType); break; } } else if (_function.ParamTypes[0].IsTable) { if (!typeScope.IsTable) { errors.Error(callNode, TexlStrings.ErrNeedTable_Func, _function.Name); fArgsValid = false; } // This assumes that the lambdas operate on the individual records // of the table, not the entire table. bool fError = false; typeScope = typeScope.ToRecord(ref fError); fArgsValid &= !fError; } else { Contracts.Assert(_function.ParamTypes[0].IsRecord); if (!typeScope.IsRecord) { errors.Error(callNode, TexlStrings.ErrNeedRecord_Func, _function.Name); bool fError = false; typeScope = typeScope.ToRecord(ref fError); fArgsValid = false; } } return(fArgsValid); }
// Typecheck an invocation of Table. public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool isValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); // Ensure that all args (if any) are records with compatible schemas. DType rowType = DType.EmptyRecord; for (int i = 0; i < argTypes.Length; i++) { DType argType = argTypes[i]; if (!argType.IsRecord) { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrNeedRecord); isValid = false; } else if (!rowType.CanUnionWith(argType)) { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrIncompatibleRecord); isValid = false; } else { bool isUnionError = false; rowType = DType.Union(ref isUnionError, rowType, argType); Contracts.Assert(!isUnionError); Contracts.Assert(rowType.IsRecord); } } Contracts.Assert(rowType.IsRecord); returnType = rowType.ToTable(); return(isValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.AssertAllValid(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); nodeToCoercedTypeMap = null; bool isValid = true; DType argType = argTypes[0]; if (!DType.Number.Accepts(argType) && !DType.String.Accepts(argType)) { if (argType.CoercesTo(DType.DateTime) && !argType.IsControl) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[0], DType.DateTime); } else { errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNumberOrStringExpected); isValid = false; } } if (args.Length > 1) { argType = argTypes[1]; if (!DType.String.Accepts(argType)) { errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrStringExpected); isValid = false; } } returnType = DType.Number; return(isValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); DType type0 = argTypes[0]; // Arg0 should not be a Time if (type0.Kind == DKind.Time) { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrDateExpected); } returnType = ReturnType; return(fValid); }
public void CheckLiteralPredicates(TexlNode[] args, IErrorContainer errors) { Contracts.AssertValue(args); Contracts.AssertValue(errors); if (!AcceptsLiteralPredicates) { for (int i = 0; i < args.Length; i++) { if (_function.IsLambdaParam(i)) { if (args[i].Kind == NodeKind.BoolLit || args[i].Kind == NodeKind.NumLit || args[i].Kind == NodeKind.StrLit) { errors.EnsureError(DocumentErrorSeverity.Warning, args[i], TexlStrings.WarnLiteralPredicate); } } } } }
public override bool CheckInvocation(TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType == DType.Number); bool matchedWithCoercion; // Ensure that all the arguments are numeric/coercible to numeric. for (int i = 0; i < argTypes.Length; i++) { if (CheckType(args[i], argTypes[i], DType.Number, DefaultErrorContainer, out matchedWithCoercion)) { if (matchedWithCoercion) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[i], DType.Number, allowDupes: true); } } else { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrNumberExpected); fValid = false; } } if (!fValid) { nodeToCoercedTypeMap = null; } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType == DType.DateTime); DType type0 = argTypes[0]; if (fValid) { // Arg0 should be either a DateTime or Date. if (type0.Kind == DKind.Date) { // Max resolution we support right now is "Days". If we start supporting sub-day resolutions // then we need to revisit this and return DateTime in those cases. returnType = DType.Date; } else if (type0.Kind == DKind.DateTime) { returnType = ReturnType; } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrDateExpected); returnType = ReturnType; } } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType == DType.Number); // Ensure that all the args starting at index 1 are booleans. for (int i = 1; i < args.Length; i++) { if (!DType.Boolean.Accepts(argTypes[i])) { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrBooleanExpected); fValid = false; } } return(fValid); }
public override bool CheckInvocation(TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.Assert(args.Length == 1); Contracts.AssertValue(errors); var reifiedError = ErrorType.ReifiedError(); var acceptedFields = reifiedError.GetNames(DPath.Root); var requiredKindField = acceptedFields.Where(tn => tn.Name == "Kind").First(); Contracts.Assert(requiredKindField.Type.IsEnum); var optionalFields = acceptedFields.Where(tn => tn.Name != "Kind"); returnType = DType.ObjNull; nodeToCoercedTypeMap = null; var argument = args[0]; var argumentType = argTypes[0]; if (argumentType.Kind != DKind.Record && argumentType.Kind != DKind.Table) { errors.EnsureError(argument, TexlStrings.ErrBadType); return(false); } // We cache the whole name list regardless of path. var names = argumentType.GetNames(DPath.Root).ToArray(); // First handle required fields (currently only 'Kind') if (!names.Any(field => field.Name == requiredKindField.Name)) { // Kind is required, point it out to the maker, and specify the enumeration type. errors.EnsureError( argument, TexlStrings.ErrBadSchema_ExpectedType, reifiedError.GetKindString()); errors.Error( argument, TexlStrings.ErrColumnMissing_ColName_ExpectedType, requiredKindField.Name.Value, "ErrorKind"); return(false); } var argumentKindType = names.First(tn => tn.Name == requiredKindField.Name).Type; if (!argumentKindType.CoercesTo(requiredKindField.Type)) { errors.EnsureError( argument, TexlStrings.ErrBadSchema_ExpectedType, reifiedError.GetKindString()); errors.Error( argument, TexlStrings.ErrBadRecordFieldType_FieldName_ExpectedType, requiredKindField.Name.Value, "ErrorKind"); return(false); } bool valid = true; var record = argument.AsRecord(); foreach (var name in names) { if (!acceptedFields.Any(field => field.Name == name.Name)) { // If they have a record literal, we can position the errors for rejected fields. if (record != null) { errors.EnsureError(record.Children.Where((_, i) => record.Ids[i].Name == name.Name).FirstOrDefault() ?? record, TexlStrings.ErrErrorIrrelevantField); } else { errors.EnsureError(argument, TexlStrings.ErrErrorIrrelevantField); } valid = false; } } bool matchedWithCoercion; bool typeValid; if (argumentType.Kind == DKind.Record) { // A record with the proper types for the fields that are specified. var expectedOptionalFieldsRecord = DType.CreateRecord( acceptedFields.Where(field => // Kind has already been handled before field.Name != "Kind" && names.Any(name => name.Name == field.Name))); typeValid = CheckType(argument, argumentType, expectedOptionalFieldsRecord, errors, true, out matchedWithCoercion); } else { // A table with the proper types for the fields that are specified. var expectedOptionalFieldsTable = DType.CreateTable( acceptedFields.Where(field => // Kind has already been handled before field.Name != "Kind" && names.Any(name => name.Name == field.Name))); typeValid = CheckType(argument, argumentType, expectedOptionalFieldsTable, errors, true, out matchedWithCoercion); } if (!typeValid) { errors.EnsureError(DocumentErrorSeverity.Severe, argument, TexlStrings.ErrTypeError); valid = false; } else if (matchedWithCoercion && valid) { var recordOrTableSchema = acceptedFields.Where(field => names.Any(name => name.Name == field.Name)); var expectedRecordOrTable = argumentType.Kind == DKind.Record ? DType.CreateRecord(recordOrTableSchema) : DType.CreateTable(recordOrTableSchema); CollectionUtils.Add(ref nodeToCoercedTypeMap, argument, expectedRecordOrTable); } return(valid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fArgsValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); // The first arg determines the scope type for the lambda params, and the return type. DType typeScope; fArgsValid &= ScopeInfo.CheckInput(args[0], argTypes[0], errors, out typeScope); Contracts.Assert(typeScope.IsRecord); // The result type has N additional columns, as specified by (args[1],args[2]), (args[3],args[4]), ... etc. returnType = typeScope.ToTable(); int count = args.Length; if ((count & 1) == 0) { errors.EnsureError(DocumentErrorSeverity.Severe, args[0].Parent.CastList().Parent.CastCall(), TexlStrings.ErrBadArityOdd, count); } for (var i = 1; i < count; i += 2) { TexlNode nameArg = args[i]; DType nameArgType = argTypes[i]; // Verify we have a string literal for the column name. Accd to spec, we don't support // arbitrary expressions that evaluate to string values, because these values contribute to // type analysis, so they need to be known upfront (before AddColumns executes). StrLitNode nameNode; if (nameArgType.Kind != DKind.String || (nameNode = nameArg.AsStrLit()) == null) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrExpectedStringLiteralArg_Name, nameArg.ToString()); continue; } // Verify that the name is valid. if (!DName.IsValidDName(nameNode.Value)) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrArgNotAValidIdentifier_Name, nameNode.Value); continue; } DName columnName = new DName(nameNode.Value); string colName; if (DType.TryGetDisplayNameForColumn(typeScope, columnName, out colName)) { columnName = new DName(colName); } // Verify that the name doesn't already exist as either a logical or display name DType columnType; string unused; if (typeScope.TryGetType(columnName, out columnType) || DType.TryGetLogicalNameForColumn(typeScope, columnName, out unused)) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Moderate, nameArg, TexlStrings.ErrColExists_Name, columnName); continue; } if (i + 1 >= count) { break; } columnType = argTypes[i + 1]; // Augment the result type to include the specified column, and verify that it // hasn't been specified already within the same invocation. bool fError = false; returnType = returnType.Add(ref fError, DPath.Root, columnName, columnType); if (fError) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Moderate, nameArg, TexlStrings.ErrColConflict_Name, columnName); continue; } } return(fArgsValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fArgsValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); if (!argTypes[0].IsTable) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNeedTable_Func, Name); } else { returnType = argTypes[0]; } // The result type has N fewer columns, as specified by (args[1],args[2],args[3],...) int count = args.Length; for (var i = 1; i < count; i++) { TexlNode nameArg = args[i]; DType nameArgType = argTypes[i]; // Verify we have a string literal for the column name. Accd to spec, we don't support // arbitrary expressions that evaluate to string values, because these values contribute to // type analysis, so they need to be known upfront (before DropColumns executes). StrLitNode nameNode; if (nameArgType.Kind != DKind.String || (nameNode = nameArg.AsStrLit()) == null) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrExpectedStringLiteralArg_Name, nameArg.ToString()); continue; } // Verify that the name is valid. if (!DName.IsValidDName(nameNode.Value)) { fArgsValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrArgNotAValidIdentifier_Name, nameNode.Value); continue; } DName columnName = new DName(nameNode.Value); // Verify that the name exists. DType columnType; if (!returnType.TryGetType(columnName, out columnType)) { fArgsValid = false; returnType.ReportNonExistingName(FieldNameKind.Logical, errors, columnName, nameArg); continue; } // Drop the specified column from the result type. bool fError = false; returnType = returnType.Drop(ref fError, DPath.Root, columnName); Contracts.Assert(!fError); } return(fArgsValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); DType type0 = argTypes[0]; DType type1 = argTypes[1]; DType type2 = argTypes[2]; // Arg0 should be either a string or a column of strings. // Its type dictates the function return type. if (type0.IsTable) { // Ensure we have a one-column table of strings fValid &= CheckStringColumnType(type0, args[0], errors, ref nodeToCoercedTypeMap); // Borrow the return type from the 1st arg returnType = type0; } else { returnType = DType.CreateTable(new TypedName(DType.String, OneColumnTableResultName)); if (!DType.String.Accepts(type0)) { if (type0.CoercesTo(DType.String)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[0], DType.String); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrStringExpected); } } } // Arg1 should be either a string or a column of strings. if (type1.IsTable) { fValid &= CheckStringColumnType(type1, args[1], errors, ref nodeToCoercedTypeMap); } else if (!DType.String.Accepts(type1)) { if (type1.CoercesTo(DType.String)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[1], DType.String); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrStringExpected); } } // Arg2 should be either a string or a column of strings. if (type2.IsTable) { fValid &= CheckStringColumnType(type2, args[2], errors, ref nodeToCoercedTypeMap); } else if (!DType.String.Accepts(type2)) { if (type2.CoercesTo(DType.String)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[2], DType.String); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrStringExpected); } } bool hasCount = args.Length == 4; if (hasCount) { DType type3 = argTypes[3]; // Arg3 should be either a number or a column of numbers. if (type3.IsTable) { fValid &= CheckNumericColumnType(type3, args[3], errors, ref nodeToCoercedTypeMap); } else if (!DType.Number.Accepts(type3)) { if (type3.CoercesTo(DType.Number)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[3], DType.Number); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[3], TexlStrings.ErrNumberExpected); } } } // At least one arg has to be a table. if (!(type0.IsTable || type1.IsTable || type2.IsTable) && (!hasCount || !argTypes[3].IsTable)) { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTypeError); errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrTypeError); errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrTypeError); if (hasCount) { errors.EnsureError(DocumentErrorSeverity.Severe, args[3], TexlStrings.ErrTypeError); } } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); nodeToCoercedTypeMap = null; int viewCount = 0; bool fArgsValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); var dataSourceVisitor = new ViewFilterDataSourceVisitor(binding); // Ensure that all the args starting at index 1 are booleans or view for (int i = 1; i < args.Length; i++) { if (argTypes[i].Kind == DKind.ViewValue) { if (++viewCount > 1) { // Only one view expected errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrOnlyOneViewExpected); fArgsValid = false; continue; } // Use the visitor to get the datasource info and if a view was already used anywhere in the node tree. args[0].Accept(dataSourceVisitor); var dataSourceInfo = dataSourceVisitor.cdsDataSourceInfo; if (dataSourceVisitor.ContainsViewFilter) { // Only one view expected errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrOnlyOneViewExpected); fArgsValid = false; continue; } if (dataSourceInfo != null) { // Verify the view belongs to the same datasource var viewInfo = argTypes[i].ViewInfo.VerifyValue(); if (viewInfo.RelatedEntityName != dataSourceInfo.Name) { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrViewFromCurrentTableExpected, dataSourceInfo.Name); fArgsValid = false; } } else { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrBooleanExpected); fArgsValid = false; } continue; } else if (DType.Boolean.Accepts(argTypes[i])) { continue; } else if (!argTypes[i].CoercesTo(DType.Boolean)) { errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrBooleanExpected); fArgsValid = false; continue; } } // The first Texl function arg determines the cursor type, the scope type for the lambda params, and the return type. DType typeScope; fArgsValid &= ScopeInfo.CheckInput(args[0], argTypes[0], errors, out typeScope); Contracts.Assert(typeScope.IsRecord); returnType = typeScope.ToTable(); return(fArgsValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(binding); Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); int count = args.Length; nodeToCoercedTypeMap = null; // Check the predicates. bool fArgsValid = true; DType type = ReturnType; bool isBehavior = binding.IsBehavior; Contracts.Assert(type == DType.Unknown); for (int i = 0; i < count;) { TexlNode nodeArg = args[i]; DType typeArg = argTypes[i]; if (typeArg.IsError) { errors.EnsureError(args[i], TexlStrings.ErrTypeError); } DType typeSuper = DType.Supertype(type, typeArg); if (!typeSuper.IsError) { type = typeSuper; } else if (type.Kind == DKind.Unknown) { // One of the args is also of unknown type, so we can't resolve the type of IfError type = typeSuper; fArgsValid = false; } else if (!type.IsError) { // Types don't resolve normally, coercion needed if (typeArg.CoercesTo(type)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, nodeArg, type); } else if (!isBehavior || !IsArgTypeInconsequential(nodeArg)) { errors.EnsureError(DocumentErrorSeverity.Severe, nodeArg, TexlStrings.ErrBadType_ExpectedType_ProvidedType, type.GetKindString(), typeArg.GetKindString()); fArgsValid = false; } } else if (typeArg.Kind != DKind.Unknown) { type = typeArg; fArgsValid = false; } // If there are an odd number of args, the last arg also participates. i += 2; if (i == count) { i--; } } returnType = type; return(fArgsValid); }
public override bool CheckInvocation(TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); DType type0 = argTypes[0]; DType type1 = argTypes[1]; TexlNode arg0 = args[0]; TexlNode arg1 = args[1]; // Arg0 should be either a number or a column of number (coercion is ok). bool matchedWithCoercion; if (type0.IsTable) { // Ensure we have a one-column table of numbers. fValid &= CheckNumericColumnType(type0, arg0, errors, ref nodeToCoercedTypeMap); } else if (CheckType(arg0, type0, DType.Number, DefaultErrorContainer, out matchedWithCoercion)) { if (matchedWithCoercion) { CollectionUtils.Add(ref nodeToCoercedTypeMap, arg0, DType.Number); } } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, arg0, TexlStrings.ErrNumberExpected); } // Arg1 should be either a number or a column of number (coercion is ok). if (type1.IsTable) { fValid &= CheckNumericColumnType(type1, arg1, errors, ref nodeToCoercedTypeMap); } else if (CheckType(arg1, type1, DType.Number, DefaultErrorContainer, out matchedWithCoercion)) { if (matchedWithCoercion) { CollectionUtils.Add(ref nodeToCoercedTypeMap, arg1, DType.Number); } } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, arg1, TexlStrings.ErrNumberExpected); } returnType = DType.CreateTable(new TypedName(DType.Number, OneColumnTableResultName)); // At least one arg has to be a table. if (!(type0.IsTable || type1.IsTable)) { fValid = false; } if (!fValid) { nodeToCoercedTypeMap = null; } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.Assert(args.Length >= 1); Contracts.AssertValue(errors); nodeToCoercedTypeMap = null; int count = args.Length; bool fArgsValid = true; bool fArgsNonNull = false; DType type = ReturnType; for (int i = 0; i < count; i++) { TexlNode nodeArg = args[i]; DType typeArg = argTypes[i]; if (typeArg.Kind == DKind.ObjNull) { continue; } fArgsNonNull = true; if (typeArg.IsError) { errors.EnsureError(args[i], TexlStrings.ErrTypeError); } DType typeSuper = DType.Supertype(type, typeArg); if (!typeSuper.IsError) { type = typeSuper; } else if (type.Kind == DKind.Unknown) { // One of the args is also of unknown type, so we can't resolve the type of IfError type = typeSuper; fArgsValid = false; } else if (!type.IsError) { // Types don't resolve normally, coercion needed if (typeArg.CoercesTo(type)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, nodeArg, type); } else { errors.EnsureError(DocumentErrorSeverity.Severe, nodeArg, TexlStrings.ErrBadType_ExpectedType_ProvidedType, type.GetKindString(), typeArg.GetKindString()); fArgsValid = false; } } else if (typeArg.Kind != DKind.Unknown) { type = typeArg; fArgsValid = false; } } if (!fArgsNonNull) { type = DType.ObjNull; } returnType = type; return(fArgsValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); DType type0 = argTypes[0]; DType type1 = argTypes[1]; returnType = DType.CreateTable(new TypedName(DType.Number, OneColumnTableResultName)); // Arg0 should be either a date or a column of dates. if (type0.IsTable) { // Ensure we have a one-column table of dates fValid &= CheckDateColumnType(type0, args[0], errors, ref nodeToCoercedTypeMap); } else if (!DType.DateTime.Accepts(type0)) { if (type0.CoercesTo(DType.DateTime)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[0], DType.DateTime); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrDateExpected); } } // Arg1 should be either a a date or a column of dates. if (type1.IsTable) { // Ensure we have a one-column table of dates fValid &= CheckDateColumnType(type1, args[1], errors, ref nodeToCoercedTypeMap); } else if (!DType.DateTime.Accepts(type1)) { if (type1.CoercesTo(DType.DateTime)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[1], DType.DateTime); } else { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrDateExpected); } } bool hasUnits = args.Length == 3; if (hasUnits && !DType.String.Accepts(argTypes[2])) { // Arg2 should be a string fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrStringExpected); } // At least one arg has to be a table. if (!(type0.IsTable || type1.IsTable)) { fValid = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTypeError); errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrTypeError); } return(fValid); }
public override bool CheckInvocation(TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool isValid = true; returnType = DType.String; nodeToCoercedTypeMap = null; TexlNode arg0 = args[0]; DType arg0Type = argTypes[0]; bool matchedWithCoercion; bool isValidString = true; bool isValidNumber = CheckType(arg0, arg0Type, DType.Number, DefaultErrorContainer, out matchedWithCoercion); DType arg0CoercedType = matchedWithCoercion ? DType.Number : DType.Invalid; if (!isValidNumber || matchedWithCoercion) { if (DType.DateTime.Accepts(arg0Type)) { // No coercion needed for datetimes here. arg0CoercedType = DType.Invalid; } else { isValidString = CheckType(arg0, arg0Type, DType.String, DefaultErrorContainer, out matchedWithCoercion); if (isValidString) { if (matchedWithCoercion) { // If both the matches were with coercion, we pick string over number. // For instance Text(true) returns true in the case of EXCEL. If we picked // number coercion, then we would return 1 and it will not match EXCEL behavior. arg0CoercedType = DType.String; } else { arg0CoercedType = DType.Invalid; } } } } if (!isValidNumber && !isValidString) { errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNumberOrStringExpected); isValid = false; } if (args.Length < 2) { if (isValid && arg0CoercedType.IsValid) { CollectionUtils.Add(ref nodeToCoercedTypeMap, arg0, arg0CoercedType); return(true); } return(isValid); } StrLitNode formatNode; if (!DType.String.Accepts(argTypes[1])) { errors.EnsureError(DocumentErrorSeverity.Severe, args[1], TexlStrings.ErrStringExpected); isValid = false; } else if ((formatNode = args[1].AsStrLit()) != null) { // Verify statically that the format string doesn't contain BOTH numeric and date/time // format specifiers. If it does, that's an error accd to Excel and our spec. string fmt = formatNode.Value; // But firstly skip any locale-prefix if (fmt.StartsWith("[$-")) { int end = fmt.IndexOf(']', 3); if (end > 0) { fmt = fmt.Substring(end + 1); } } bool hasDateTimeFmt = fmt.IndexOfAny(new char[] { 'm', 'd', 'y', 'h', 'H', 's', 'a', 'A', 'p', 'P' }) >= 0; bool hasNumericFmt = fmt.IndexOfAny(new char[] { '0', '#' }) >= 0; if (hasDateTimeFmt && hasNumericFmt) { errors.EnsureError(DocumentErrorSeverity.Moderate, formatNode, TexlStrings.ErrIncorrectFormat_Func, Name); isValid = false; } } if (args.Length > 2) { DType argType = argTypes[2]; if (!DType.String.Accepts(argType)) { errors.EnsureError(DocumentErrorSeverity.Severe, args[2], TexlStrings.ErrStringExpected); isValid = false; } } if (isValid) { if (arg0CoercedType.IsValid) { CollectionUtils.Add(ref nodeToCoercedTypeMap, arg0, arg0CoercedType); return(true); } } else { nodeToCoercedTypeMap = null; } return(isValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(binding); Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); int count = args.Length; nodeToCoercedTypeMap = null; // Check the predicates. bool fArgsValid = true; for (int i = 0; i < (count & ~1); i += 2) { bool withCoercion; fArgsValid &= CheckType(args[i], argTypes[i], DType.Boolean, errors, true, out withCoercion); if (withCoercion) { CollectionUtils.Add(ref nodeToCoercedTypeMap, args[i], DType.Boolean); } } DType type = ReturnType; // Are we on a behavior property? bool isBehavior = binding.IsBehavior; // Compute the result type by joining the types of all non-predicate args. Contracts.Assert(type == DType.Unknown); for (int i = 1; i < count;) { TexlNode nodeArg = args[i]; DType typeArg = argTypes[i]; if (typeArg.IsError) { errors.EnsureError(args[i], TexlStrings.ErrTypeError); } DType typeSuper = DType.Supertype(type, typeArg); if (!typeSuper.IsError) { type = typeSuper; } else if (type.Kind == DKind.Unknown) { type = typeSuper; fArgsValid = false; } else if (!type.IsError) { if (typeArg.CoercesTo(type)) { CollectionUtils.Add(ref nodeToCoercedTypeMap, nodeArg, type); } else if (!isBehavior || !IsArgTypeInconsequential(nodeArg)) { errors.EnsureError(DocumentErrorSeverity.Severe, nodeArg, TexlStrings.ErrBadType_ExpectedType_ProvidedType, type.GetKindString(), typeArg.GetKindString()); fArgsValid = false; } } else if (typeArg.Kind != DKind.Unknown) { type = typeArg; fArgsValid = false; } // If there are an odd number of args, the last arg also participates. i += 2; if (i == count) { i--; } } // Update the return type based on the specified invocation args. returnType = type; return(fArgsValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); returnType = argTypes[0]; DType sourceType = argTypes[0]; TexlNode nameArg = args[1]; DType nameArgType = argTypes[1]; StrLitNode nameNode = null; DType columnType = DType.Invalid; if (nameArgType.Kind != DKind.String) { errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrStringExpected); fValid = false; } else if ((nameNode = nameArg.AsStrLit()) != null) { // Verify that the name is valid. if (DName.IsValidDName(nameNode.Value)) { DName columnName = new DName(nameNode.Value); // Verify that the name exists. if (!sourceType.TryGetType(columnName, out columnType)) { sourceType.ReportNonExistingName(FieldNameKind.Logical, errors, columnName, nameNode); fValid = false; } else if (!columnType.IsPrimitive) { fValid = false; errors.EnsureError(nameArg, TexlStrings.ErrSortWrongType); } } else { errors.EnsureError(DocumentErrorSeverity.Severe, nameNode, TexlStrings.ErrArgNotAValidIdentifier_Name, nameNode.Value); fValid = false; } } TexlNode valuesArg = args[2]; IEnumerable <TypedName> columns; if ((columns = argTypes[2].GetNames(DPath.Root)).Count() != 1) { errors.EnsureError(DocumentErrorSeverity.Severe, valuesArg, TexlStrings.ErrInvalidSchemaNeedCol); return(false); } TypedName column = columns.Single(); if (nameNode != null && columnType.IsValid && !columnType.Accepts(column.Type)) { errors.EnsureError(DocumentErrorSeverity.Severe, valuesArg, TexlStrings.ErrTypeError_Arg_Expected_Found, nameNode.Value, columnType.GetKindString(), column.Type.GetKindString()); fValid = false; } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertAllValues(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool fValid = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); returnType = argTypes[0]; DType sourceType = argTypes[0]; for (int i = 1; i < args.Length; i += 2) { TexlNode colNameArg = args[i]; DType colNameArgType = argTypes[i]; StrLitNode nameNode; if (colNameArgType.Kind != DKind.String) { errors.EnsureError(DocumentErrorSeverity.Severe, colNameArg, TexlStrings.ErrStringExpected); fValid = false; } else if ((nameNode = colNameArg.AsStrLit()) != null) { // Verify that the name is valid. if (DName.IsValidDName(nameNode.Value)) { DName columnName = new DName(nameNode.Value); // Verify that the name exists. DType columnType; if (!sourceType.TryGetType(columnName, out columnType)) { sourceType.ReportNonExistingName(FieldNameKind.Logical, errors, columnName, args[i]); fValid = false; } else if (!columnType.IsPrimitive || columnType.IsOptionSet) { fValid = false; errors.EnsureError(colNameArg, TexlStrings.ErrSortWrongType); } } else { errors.EnsureError(DocumentErrorSeverity.Severe, nameNode, TexlStrings.ErrArgNotAValidIdentifier_Name, nameNode.Value); fValid = false; } } int nextArgIdx = i + 1; if (nextArgIdx < args.Length && argTypes[nextArgIdx] != DType.String) { fValid = false; errors.EnsureError(args[i + 1], TexlStrings.ErrSortIncorrectOrder); } } return(fValid); }
public override bool CheckInvocation(TexlBinding binding, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType returnType, out Dictionary <TexlNode, DType> nodeToCoercedTypeMap) { Contracts.AssertValue(args); Contracts.AssertValue(argTypes); Contracts.Assert(args.Length == argTypes.Length); Contracts.AssertValue(errors); Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity); bool isValidInvocation = base.CheckInvocation(args, argTypes, errors, out returnType, out nodeToCoercedTypeMap); Contracts.Assert(returnType.IsTable); if (!argTypes[0].IsTable) { isValidInvocation = false; errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrNeedTable_Func, Name); } else { returnType = argTypes[0]; } DType colsToKeep = DType.EmptyTable; // The result type has N columns, as specified by (args[1],args[2],args[3],...) int count = args.Length; for (var i = 1; i < count; i++) { TexlNode nameArg = args[i]; DType nameArgType = argTypes[i]; // Verify we have a string literal for the column name. Accd to spec, we don't support // arbitrary expressions that evaluate to string values, because these values contribute to // type analysis, so they need to be known upfront (before ShowColumns executes). StrLitNode nameNode; if (nameArgType.Kind != DKind.String || (nameNode = nameArg.AsStrLit()) == null) { isValidInvocation = false; errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrExpectedStringLiteralArg_Name, nameArg.ToString()); continue; } // Verify that the name is valid. if (!DName.IsValidDName(nameNode.Value)) { isValidInvocation = false; errors.EnsureError(DocumentErrorSeverity.Severe, nameArg, TexlStrings.ErrArgNotAValidIdentifier_Name, nameNode.Value); continue; } DName columnName = new DName(nameNode.Value); // Verify that the name exists. DType columnType; if (!returnType.TryGetType(columnName, out columnType)) { isValidInvocation = false; returnType.ReportNonExistingName(FieldNameKind.Logical, errors, columnName, args[i]); continue; } // Verify that the name was only specified once. DType existingColumnType; if (colsToKeep.TryGetType(columnName, out existingColumnType)) { isValidInvocation = false; errors.EnsureError(DocumentErrorSeverity.Warning, nameArg, TexlStrings.WarnColumnNameSpecifiedMultipleTimes_Name, columnName); continue; } // Make a note of which columns are being kept. Contracts.Assert(columnType.IsValid); colsToKeep = colsToKeep.Add(columnName, columnType); } // Drop everything but the columns that need to be kept. returnType = colsToKeep; return(isValidInvocation); }