示例#1
0
        // 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);
        }
示例#2
0
 public TexlError Error(TexlNode node, ErrorResourceKey errKey, params object[] args)
 {
     if (DefaultSeverity <= maximumSeverity)
     {
         return(errors.Error(node, errKey, args));
     }
     return(null);
 }
示例#3
0
        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);
        }