public void Errors(TexlNode node, DType nodeType, KeyValuePair <string, DType> schemaDifference, DType schemaDifferenceType) { Contracts.AssertValue(node); Contracts.AssertValid(nodeType); Error(DocumentErrorSeverity.Severe, node, TexlStrings.ErrBadSchema_ExpectedType, nodeType.GetKindString()); // If there's no schema difference, this was just an invalid type. if (string.IsNullOrEmpty(schemaDifference.Key)) { return; } if (schemaDifferenceType.IsValid) { Error( DocumentErrorSeverity.Severe, node, TexlStrings.ErrColumnTypeMismatch_ColName_ExpectedType_ActualType, schemaDifference.Key, schemaDifference.Value.GetKindString(), schemaDifferenceType.GetKindString()); } else { Error( DocumentErrorSeverity.Severe, node, TexlStrings.ErrColumnMissing_ColName_ExpectedType, schemaDifference.Key, schemaDifference.Value.GetKindString()); } }
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(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(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); 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); }
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); }
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); }
private static CoercionKind FlattenCoercionMatrix(DType fromType, DType toType) { switch (toType.Kind) { case DKind.Number: case DKind.Currency: return(GetToNumberCoercion(fromType)); case DKind.Color: case DKind.PenImage: // It is not safe to coerce these. Contracts.Assert(false, "Unsupported type coercion"); break; case DKind.Hyperlink: if (DType.String.Accepts(fromType)) { switch (fromType.Kind) { case DKind.Blob: return(CoercionKind.BlobToHyperlink); case DKind.Image: return(CoercionKind.ImageToHyperlink); case DKind.Media: return(CoercionKind.MediaToHyperlink); default: return(CoercionKind.TextToHyperlink); } } Contracts.Assert(false, "Unsupported type coercion"); break; case DKind.Image: if (fromType.IsLargeImage) { return(CoercionKind.LargeImageToImage); } if (fromType.Kind != DKind.Media && fromType.Kind != DKind.Blob && DType.String.Accepts(fromType)) { return(CoercionKind.TextToImage); } Contracts.Assert(false, "Unsupported type coercion"); break; case DKind.Media: if (fromType.Kind != DKind.Image && fromType.Kind != DKind.Blob && DType.String.Accepts(fromType)) { return(CoercionKind.TextToMedia); } Contracts.Assert(false, "Unsupported type coercion"); break; case DKind.Blob: if (DType.String.Accepts(fromType)) { return(CoercionKind.TextToBlob); } Contracts.Assert(false, "Unsupported type coercion"); break; case DKind.String: return(GetToStringCoercion(fromType)); case DKind.Enum: return(GetToEnumCoercion(fromType, toType)); case DKind.Boolean: Contracts.Assert(DType.Number.Accepts(fromType) || DType.String.Accepts(fromType) || (DType.OptionSetValue.Accepts(fromType) && (fromType.OptionSetInfo?.IsBooleanValued ?? false)), "Unsupported type coercion"); if (DType.Number.Accepts(fromType)) { return(CoercionKind.NumberToBoolean); } if (DType.String.Accepts(fromType)) { return(CoercionKind.TextToBoolean); } if (DType.OptionSetValue.Accepts(fromType) && (fromType.OptionSetInfo?.IsBooleanValued ?? false)) { return(CoercionKind.BooleanOptionSetToBoolean); } return(CoercionKind.None); // Implicit coercion? case DKind.Record: Contracts.Assert(fromType.IsAggregate); Contracts.Assert(fromType.Kind == toType.Kind); return(CoercionKind.RecordToRecord); case DKind.Table: Contracts.Assert(fromType.IsAggregate); Contracts.Assert(fromType.Kind == DKind.Table || fromType.Kind == DKind.Record); if (fromType.Kind == DKind.Table) { return(CoercionKind.TableToTable); } if (fromType.Kind == DKind.Record) { return(CoercionKind.RecordToTable); } Contracts.Assert(false, "Unexpected type for coercion."); break; case DKind.DateTime: case DKind.DateTimeNoTimeZone: Contracts.Assert(DType.String.Accepts(fromType) || DType.Number.Accepts(fromType) || DType.Time.Accepts(fromType) || DType.Date.Accepts(fromType), "Unsupported type coercion"); if (DType.Number.Accepts(fromType)) { return(CoercionKind.NumberToDateTime); } else if (DType.Date.Accepts(fromType)) { return(CoercionKind.DateToDateTime); } else if (DType.Time.Accepts(fromType)) { return(CoercionKind.TimeToDateTime); } return(CoercionKind.TextToDateTime); case DKind.Time: Contracts.Assert(DType.String.Accepts(fromType) || DType.Number.Accepts(fromType) || DType.DateTime.Accepts(fromType) || DType.Date.Accepts(fromType), "Unsupported type coercion"); if (DType.Number.Accepts(fromType)) { return(CoercionKind.NumberToTime); } else if (DType.Date.Accepts(fromType)) { return(CoercionKind.DateToTime); } else if (DType.DateTime.Accepts(fromType)) { return(CoercionKind.DateTimeToTime); } return(CoercionKind.TextToTime); case DKind.Date: Contracts.Assert(DType.String.Accepts(fromType) || DType.Number.Accepts(fromType) || DType.DateTime.Accepts(fromType) || DType.Time.Accepts(fromType), "Unsupported type coercion"); if (DType.Number.Accepts(fromType)) { return(CoercionKind.NumberToDate); } else if (DType.Time.Accepts(fromType)) { return(CoercionKind.TimeToDate); } else if (DType.DateTime.Accepts(fromType)) { return(CoercionKind.DateTimeToDate); } return(CoercionKind.TextToDate); case DKind.OptionSetValue: Contracts.Assert(DType.OptionSetValue.Accepts(fromType) || (DType.Boolean.Accepts(fromType) && (toType.OptionSetInfo?.IsBooleanValued ?? false)), "Unsupported type coercion"); if (DType.Boolean.Accepts(fromType) && (toType.OptionSetInfo?.IsBooleanValued ?? false)) { return(CoercionKind.BooleanToOptionSet); } return(CoercionKind.None); // Implicit coercion? case DKind.ViewValue: Contracts.Assert(DType.ViewValue.Accepts(fromType), "Unsupported type coercion"); return(CoercionKind.None); // Implicit coercion? default: // Nothing else can be coerced. Contracts.Assert(false, "Unsupported type coercion"); break; } // This should be impossible, the caller can catch and treat it as CoercionKind.None but should investigate. throw new InvalidCoercionException($"Attempting to generate invalid coercion from {fromType.GetKindString()} to {toType.GetKindString()}"); }