Ejemplo n.º 1
0
        // all we need do is visit the function itself, it will handle
        // everything
        public override int VisitValueFunc(YarnSpinnerParser.ValueFuncContext context)
        {
            this.Visit(context.function_call());

            return(0);
        }
Ejemplo n.º 2
0
        public override Yarn.IType VisitValueFunc(YarnSpinnerParser.ValueFuncContext context)
        {
            string functionName = context.function_call().FUNC_ID().GetText();

            Declaration functionDeclaration = Declarations
                                              .Where(d => d.Type is FunctionType)
                                              .FirstOrDefault(d => d.Name == functionName);

            FunctionType functionType;

            if (functionDeclaration == null)
            {
                // We don't have a declaration for this function. Create an
                // implicit one.

                functionType = new FunctionType();
                // because it is an implicit declaration we will use the type hint to give us a return type
                functionType.ReturnType = context.Hint != BuiltinTypes.Undefined ? context.Hint : BuiltinTypes.Undefined;

                functionDeclaration = new Declaration
                {
                    Name           = functionName,
                    Type           = functionType,
                    IsImplicit     = true,
                    Description    = $"Implicit declaration of function at {sourceFileName}:{context.Start.Line}:{context.Start.Column}",
                    SourceFileName = sourceFileName,
                    SourceNodeName = currentNodeName,
                    Range          = new Range
                    {
                        Start =
                        {
                            Line      = context.Start.Line - 1,
                            Character = context.Start.Column,
                        },
                        End =
                        {
                            Line      = context.Stop.Line - 1,
                            Character = context.Stop.Column + context.Stop.Text.Length,
                        },
                    },
                };

                // Create the array of parameters for this function based
                // on how many we've seen in this call. Set them all to be
                // undefined; we'll bind their type shortly.
                var parameterTypes = context.function_call().expression()
                                     .Select(e => BuiltinTypes.Undefined)
                                     .ToList();

                foreach (var parameterType in parameterTypes)
                {
                    functionType.AddParameter(parameterType);
                }

                NewDeclarations.Add(functionDeclaration);
            }
            else
            {
                var a = (FunctionType)functionDeclaration.Type;
                functionType = functionDeclaration.Type as FunctionType;
                if (functionType == null)
                {
                    throw new InvalidOperationException($"Internal error: decl's type is not a {nameof(FunctionType)}");
                }

                // we have an existing function but its undefined
                // if we also have a type hint we can use that to update it
                if (functionType.ReturnType == BuiltinTypes.Undefined && context.Hint != BuiltinTypes.Undefined)
                {
                    NewDeclarations.Remove(functionDeclaration);
                    functionType.ReturnType  = context.Hint;
                    functionDeclaration.Type = functionType;
                    NewDeclarations.Add(functionDeclaration);
                }
            }

            // Check each parameter of the function
            var suppliedParameters = context.function_call().expression();

            var expectedParameters = functionType.Parameters;

            if (suppliedParameters.Length != expectedParameters.Count())
            {
                // Wrong number of parameters supplied
                var parameters = expectedParameters.Count() == 1 ? "parameter" : "parameters";

                this.diagnostics.Add(new Diagnostic(this.sourceFileName, context, $"Function {functionName} expects {expectedParameters.Count()} {parameters}, but received {suppliedParameters.Length}"));

                return(functionType.ReturnType);
            }

            for (int i = 0; i < expectedParameters.Count(); i++)
            {
                var suppliedParameter = suppliedParameters[i];

                var expectedType = expectedParameters[i];

                var suppliedType = this.Visit(suppliedParameter);

                if (expectedType == BuiltinTypes.Undefined)
                {
                    // The type of this parameter hasn't yet been bound.
                    // Bind this parameter type to what we've resolved the
                    // type to.
                    expectedParameters[i] = suppliedType;
                    expectedType          = suppliedType;
                }

                if (TypeUtil.IsSubType(expectedType, suppliedType) == false)
                {
                    this.diagnostics.Add(new Diagnostic(this.sourceFileName, context, $"{functionName} parameter {i + 1} expects a {expectedType?.Name ?? "undefined"}, not a {suppliedType?.Name ?? "undefined"}"));
                    return(functionType.ReturnType);
                }
            }

            // Cool, all the parameters check out!

            // Finally, return the return type of this function.
            return(functionType.ReturnType);
        }