public MethodNode(DeltinScriptParser.MethodContext context, BuildAstVisitor visitor)
            : base(
                new Location(visitor.file, DocRange.GetRange(context)),
                DocRange.GetRange(context.PART().Symbol),
                DocRange.GetRange(context.LEFT_PAREN().Symbol, context.RIGHT_PAREN().Symbol)
                )
        {
            Name = context.PART().GetText();

            if (context.call_parameters() != null)
            {
                Parameters = new Node[context.call_parameters().expr().Length];
                for (int i = 0; i < Parameters.Length; i++)
                {
                    Parameters[i] = visitor.Visit(context.call_parameters().expr()[i]);
                }
            }
            else if (context.picky_parameters() != null)
            {
                PickyParameters = new PickyParameter[context.picky_parameters().picky_parameter().Length];
                for (int i = 0; i < PickyParameters.Length; i++)
                {
                    PickyParameters[i] = new PickyParameter(context.picky_parameters().picky_parameter(i), visitor);
                }
            }
            else
            {
                Parameters = new Node[0];
            }
        }
        /// <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 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);
        }
        public IWorkshopTree[] ParsePickyParameters(ScopeGroup getter, ScopeGroup scope, ParameterBase[] parameters, PickyParameter[] values, string methodName, LanguageServer.Location methodRange)
        {
            for (int f = 0; f < values.Length; f++)
            {
                // Syntax error if the parameter does not exist.
                if (!parameters.Any(param => param.Name.Replace(" ", "") == values[f].Name))
                {
                    throw new SyntaxErrorException(values[f].Name + " is not a parameter in the method " + methodName + ".", values[f].Location);
                }

                // Check if there are any duplicates.
                for (int n = 0; n < values.Length; n++)
                {
                    if (f != n && values[f].Name == values[n].Name)
                    {
                        int use = Math.Max(f, n);
                        throw new SyntaxErrorException(values[use].Name + " was already set.", values[use].Location);
                    }
                }
            }

            // Parse the parameters
            List <IWorkshopTree> parsedParameters = new List <IWorkshopTree>();

            for (int i = 0; i < parameters.Length; i++)
            {
                PickyParameter setter = values.FirstOrDefault(value => value.Name == parameters[i].Name.Replace(" ", ""));

                IWorkshopTree result;

                if (setter == null)
                {
                    result = GetDefaultValue(parameters[i], methodName, methodRange);
                }
                else
                {
                    result = parameters[i].Parse(this, getter, scope, setter.Expression);
                }

                parsedParameters.Add(result);
            }

            return(parsedParameters.ToArray());
        }
        private PickyParameter[] ParametersFromContext(List <ParameterValue> 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.Count];
            for (int i = 0; i < parameters.Length; i++)
            {
                PickyParameter parameter = new PickyParameter(false);
                parameters[i] = parameter;

                if (parameter.Picky = context[i].PickyParameter != null)
                {
                    // Get the picky name.
                    parameter.Name = context[i].PickyParameter.Text;
                    // Get the name range
                    parameter.NameRange = context[i].PickyParameter.Range;

                    // Check if there are any duplicate names.
                    if (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);
                    }
                }

                // Set expression and expressionRange.
                parameter.LambdaInfo      = new ExpectingLambdaInfo();
                parameter.Value           = parseInfo.SetLambdaInfo(parameter.LambdaInfo).GetExpression(getter, context[i].Expression);
                parameter.ExpressionRange = context[i].Expression.Range;
            }

            return(parameters);
        }
        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.Picky_parametersContext context)
        {
            if (_setContext)
            {
                throw new Exception("Already set the context for the overload chooser.");
            }
            PickyContext = context;
            _setContext  = true;

            PickyParameter[] parameters = new PickyParameter[context.picky_parameter().Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                string      name       = context.picky_parameter(i).PART().GetText();
                IExpression expression = null;

                DocRange expressionRange = null;

                // Get the expression. If it doesn't exist, add a syntax error.
                if (context.picky_parameter(i).expr() != null)
                {
                    expression      = DeltinScript.GetExpression(parseInfo, getter, context.picky_parameter(i).expr());
                    expressionRange = DocRange.GetRange(context.picky_parameter(i).expr());
                }
                else
                {
                    parseInfo.Script.Diagnostics.Error("Expected expression.", DocRange.GetRange(context.picky_parameter(i).TERNARY_ELSE()));
                }

                var nameRange = DocRange.GetRange(context.picky_parameter(i).PART());

                // Syntax error if the parameter was already set.
                if (parameters.Any(p => p != null && p.Name == name))
                {
                    parseInfo.Script.Diagnostics.Error($"The parameter {name} was already set.", nameRange);
                }
                else
                {
                    // Add the parameter.
                    parameters[i] = new PickyParameter(
                        name,
                        expression,
                        DocRange.GetRange(context.picky_parameter(i)),
                        nameRange,
                        expressionRange
                        );
                }
            }

            if (!SetParameterCount(parameters.Length))
            {
                return;
            }

            // Match by value types and parameter types.
            foreach (var parameter in parameters)
            {
                foreach (var option in CurrentOptions)
                {
                    int index = -1;
                    for (int i = 0; i < option.Parameters.Length; i++)
                    {
                        if (option.Parameters[i].Name == parameter.Name)
                        {
                            index = i;
                            break;
                        }
                    }

                    if (index == -1)
                    {
                        // Syntax error if the parameter does not exist.
                        optionDiagnostics[option].Add(new Diagnostic(
                                                          string.Format(ErrorMessages.ParameterDoesntExist, parameter.Name),
                                                          parameter.NameRange,
                                                          Diagnostic.Error
                                                          ));
                    }
                    // parameter.Value is null if there is no expression.
                    // A syntax error for this was already thrown earlier.
                    else if (parameter.Value != null)
                    {
                        // Check the types.
                        CompareParameterTypes(parameter.Value, option, index, parameter.ExpressionRange);
                    }
                }
            }
            GetBestOption();

            List <string> pickyParameterCompletion = Overload.Parameters.Select(p => p.Name).ToList();

            ParameterRanges = new DocRange[Overload.Parameters.Length];
            IExpression[] values = new IExpression[Overload.Parameters.Length];
            for (int i = 0; i < values.Length; i++)
            {
                int parameterIndex = -1;
                for (int p = 0; p < parameters.Length && parameterIndex == -1; p++)
                {
                    if (parameters[p].Name == Overload.Parameters[i].Name)
                    {
                        parameterIndex = p;
                        pickyParameterCompletion.Remove(parameters[p].Name);
                    }
                }

                if (parameterIndex != -1)
                {
                    values[i]          = parameters[parameterIndex].Value;
                    ParameterRanges[i] = parameters[parameterIndex].FullRange;
                }
                else
                {
                    values[i] = MissingParameter(Overload.Parameters[i]);
                }
            }
            Values = values;

            // Add the picky parameter completion.
            parseInfo.Script.AddCompletionRange(new CompletionRange(pickyParameterCompletion.Select(p => new CompletionItem()
            {
                Label = p + ":",
                Kind  = CompletionItemKind.Field
            }).ToArray(), CallRange, CompletionRangeKind.Additive));

            GetAdditionalData();
        }