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); 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); }