示例#1
0
        public void Apply(DeltinScriptParser.Call_parametersContext context)
        {
            PickyParameter[] inputParameters = ParametersFromContext(context);

            // Compare parameter counts.
            if (!SetParameterCount(inputParameters.Length))
            {
                return;
            }

            // Match overloads.
            OverloadMatch[] matches = new OverloadMatch[CurrentOptions.Count];
            for (int i = 0; i < matches.Length; i++)
            {
                matches[i] = MatchOverload(CurrentOptions[i], inputParameters, context);
            }

            // Choose the best option.
            OverloadMatch bestOption = BestOption(matches);

            Values          = bestOption.OrderedParameters.Select(op => op?.Value).ToArray();
            ParameterRanges = bestOption.OrderedParameters.Select(op => op?.ExpressionRange).ToArray();

            GetAdditionalData();
        }
示例#2
0
 public override object VisitCall_parameters(DeltinScriptParser.Call_parametersContext context)
 {
     // Confirm there is an expression after the last ",".
     if (context.children?.Last().GetText() == ",")
     {
         _diagnostics.Error("Expected parameter.", new Location(_file, Range.GetRange(context).end.ToRange()));
     }
     return(base.VisitCall_parameters(context));
 }
        private PickyParameter[] ParametersFromContext(DeltinScriptParser.Call_parametersContext context)
        {
            // Return empty if context is null.
            if (context == null)
            {
                return(new PickyParameter[0]);
            }

            // Create the parameters array with the same length as the number of input parameters.
            PickyParameter[] parameters = new PickyParameter[context.call_parameter().Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                PickyParameter parameter = new PickyParameter(false);
                parameters[i] = parameter;

                // Get the picky name.
                // A is not picky, B is picky.
                // 'name' will be null depending on how the parameter is written. A is null, B is not null.
                // A: '5'
                // B: 'parameter: 5'
                parameter.Name  = context.call_parameter(i).PART()?.GetText();
                parameter.Picky = parameter.Name != null;

                if (parameter.Picky)
                {
                    parameter.NameRange = DocRange.GetRange(context.call_parameter(i).PART());                  // Get the name range if picky.
                }
                // If the expression context exists, set expression and expressionRange.
                if (context.call_parameter(i).expr() != null)
                {
                    parameter.Value           = parseInfo.GetExpression(getter, context.call_parameter(i).expr());
                    parameter.ExpressionRange = DocRange.GetRange(context.call_parameter(i).expr());
                }
                else if (parameter.Picky) // TODO: remove condition so only 'else' remains if parameter-quick-skip is not implemented.
                // Throw a syntax error if picky.
                {
                    parseInfo.Script.Diagnostics.Error("Expected expression.", DocRange.GetRange(context.call_parameter(i).TERNARY_ELSE()));
                }

                // Check if there are any duplicate names.
                if (parameter.Picky && parameters.Any(p => p != null && p.Picky && p != parameter && p.Name == parameter.Name))
                {
                    // If there are, syntax error
                    parseInfo.Script.Diagnostics.Error($"The parameter {parameter.Name} was already set.", parameter.NameRange);
                }
            }

            return(parameters);
        }
        private void AddContextualParameter(DeltinScriptParser.Call_parametersContext context, DocRange functionCallRange, int parameter)
        {
            // No parameters set, set range for first parameter to callRange.
            if (parameter == 0 && OrderedParameters.All(p => p?.Value == null))
            {
                OrderedParameters[0].ExpressionRange = functionCallRange;
            }
            // If this is the last contextual parameter and the context contains comma, set the expression range so signature help works with the last comma when there is no set expression.
            else if (LastContextualParameterIndex == parameter && context.COMMA().Length > 0)
            {
                // Get the last comma in the context.
                var lastComma = context.COMMA().Last();

                // Set the expression range if the last child in the context is a comma.
                if (lastComma == context.children.Last())
                {
                    // Set the range to be the end of the comma to the start of the call range.
                    OrderedParameters[parameter].ExpressionRange = new DocRange(
                        DocRange.GetRange(lastComma).end,
                        functionCallRange.end
                        );
                }
            }
        }
        /// <summary>Determines if there are any missing parameters.</summary>
        public void GetMissingParameters(DocRange genericErrorRange, OverloadError messageHandler, DeltinScriptParser.Call_parametersContext context, DocRange functionCallRange)
        {
            for (int i = 0; i < OrderedParameters.Length; i++)
            {
                if (OrderedParameters[i]?.Value == null)
                {
                    if (OrderedParameters[i] == null)
                    {
                        OrderedParameters[i] = new PickyParameter(true);
                    }
                    AddContextualParameter(context, functionCallRange, i);

                    // Default value
                    if (Option.Parameters[i].DefaultValue != null)
                    {
                        // Set the default value.
                        OrderedParameters[i].Value = Option.Parameters[i].DefaultValue;
                    }
                    else
                    {
                        // Parameter is missing.
                        Error(string.Format(messageHandler.MissingParameter, Option.Parameters[i].Name), genericErrorRange);
                    }
                }
            }
        }
        private OverloadMatch MatchOverload(IParameterCallable option, PickyParameter[] inputParameters, DeltinScriptParser.Call_parametersContext context)
        {
            PickyParameter lastPicky = null;

            OverloadMatch match = new OverloadMatch(option);

            match.OrderedParameters = new PickyParameter[option.Parameters.Length];

            // Iterate through the option's parameters.
            for (int i = 0; i < inputParameters.Length; i++)
            {
                if (inputParameters[i].ParameterOrdered(option.Parameters[i]))
                {
                    // If the picky parameters end but there is an additional picky parameter, throw a syntax error.
                    if (lastPicky != null && inputParameters[i].Name == null)
                    {
                        match.Error($"Named argument '{lastPicky.Name}' is used out-of-position but is followed by an unnamed argument", lastPicky.NameRange);
                    }
                    else
                    {
                        match.OrderedParameters[i] = inputParameters[i];
                        // Next contextual parameter
                        if (i == inputParameters.Length - 1 && i < option.Parameters.Length - 1)
                        {
                            match.LastContextualParameterIndex = i + 1;
                        }
                    }
                }
                else
                {
                    // Picky parameter ends.
                    lastPicky = inputParameters[i];

                    // Set relevant picky parameter.
                    bool nameFound = false;
                    for (int p = 0; p < option.Parameters.Length && !nameFound; p++)
                    {
                        if (inputParameters[i].Name == option.Parameters[p].Name)
                        {
                            match.OrderedParameters[p] = inputParameters[i];
                            nameFound = true;
                        }
                    }

                    // If the named argument's name is not found, throw an error.
                    if (!nameFound)
                    {
                        match.Error($"Named argument '{lastPicky.Name}' does not exist in the function '{option.GetLabel(false)}'.", inputParameters[i].NameRange);
                    }
                }
            }

            // Compare parameter types.
            for (int i = 0; i < match.OrderedParameters.Length; i++)
            {
                match.CompareParameterTypes(i);
            }

            // Get the missing parameters.
            match.GetMissingParameters(genericErrorRange, ErrorMessages, context, CallRange);

            return(match);
        }
        public void SetContext(DeltinScriptParser.Call_parametersContext context)
        {
            if (_setContext)
            {
                throw new Exception("Already set the context for the overload chooser.");
            }
            CallContext = context;
            _setContext = true;

            IExpression[] values          = new IExpression[context.expr().Length];
            DocRange[]    errorRanges     = new DocRange[values.Length];
            var           parameterRanges = new List <DocRange>();

            for (int i = 0; i < values.Length; i++)
            {
                values[i]      = DeltinScript.GetExpression(parseInfo, getter, context.expr(i));
                errorRanges[i] = DocRange.GetRange(context.expr(i));
                parameterRanges.Add(errorRanges[i]);
            }

            if (!SetParameterCount(values.Length))
            {
                return;
            }
            if (values.Any(v => v == null))
            {
                return;
            }

            // Match by value types and parameter types.
            for (int i = 0; i < values.Length; i++)
            {
                foreach (var option in CurrentOptions)
                {
                    CompareParameterTypes(values[i], option, i, errorRanges[i]);
                }
            }
            GetBestOption();

            Values = new IExpression[Overload.Parameters.Length];
            for (int i = 0; i < values.Length; i++)
            {
                Values[i] = values[i];
            }

            if (values.Length < Overload.Parameters.Length)
            {
                parameterRanges.Add(new DocRange(
                                        DocRange.GetRange(context).end,
                                        CallRange.end
                                        ));
            }

            ParameterRanges = parameterRanges.ToArray();

            // Get the missing parameters.
            for (int i = values.Length; i < Values.Length; i++)
            {
                Values[i] = MissingParameter(Overload.Parameters[i]);
            }
            GetAdditionalData();
        }