// all we need do is visit the function itself, it will handle everything public override int VisitValueFunc(YarnSpinnerParser.ValueFuncContext context) { Visit(context.function()); return(0); }
/// <summary> /// Visit a parse tree produced by the <c>valueFunc</c> /// labeled alternative in <see cref="YarnSpinnerParser.value"/>. /// <para> /// The default implementation returns the result of calling <see cref="AbstractParseTreeVisitor{Result}.VisitChildren(IRuleNode)"/> /// on <paramref name="context"/>. /// </para> /// </summary> /// <param name="context">The parse tree.</param> /// <return>The visitor result.</return> public virtual Result VisitValueFunc([NotNull] YarnSpinnerParser.ValueFuncContext context) { return(VisitChildren(context)); }
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); }
/// <summary> /// Exit a parse tree produced by the <c>valueFunc</c> /// labeled alternative in <see cref="YarnSpinnerParser.value"/>. /// <para>The default implementation does nothing.</para> /// </summary> /// <param name="context">The parse tree.</param> public virtual void ExitValueFunc([NotNull] YarnSpinnerParser.ValueFuncContext context) { }
public override Yarn.Type VisitValueFunc(YarnSpinnerParser.ValueFuncContext context) { string functionName = context.function().FUNC_ID().GetText(); Declaration functionDeclaration = Declarations .Where(d => d.DeclarationType == Declaration.Type.Function) .FirstOrDefault(d => d.Name == functionName); if (functionDeclaration == null) { // We don't have a declaration for this function. Create an // implicit one. functionDeclaration = new Declaration { Name = functionName, DeclarationType = Declaration.Type.Function, IsImplicit = true, ReturnType = Yarn.Type.Undefined, Description = $"Implicit declaration of function at {sourceFileName}:{context.Start.Line}:{context.Start.Column}", SourceFileName = sourceFileName, SourceFileLine = context.Start.Line, SourceNodeName = currentNodeName, SourceNodeLine = context.Start.Line - (this.currentNodeContext.BODY_START().Symbol.Line + 1), }; // 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. functionDeclaration.Parameters = context.function().expression() .Select(e => new Declaration.Parameter { Type = Yarn.Type.Undefined }) .ToArray(); NewDeclarations.Add(functionDeclaration); } // Check each parameter of the function var suppliedParameters = context.function().expression(); Declaration.Parameter[] expectedParameters = functionDeclaration.Parameters; if (suppliedParameters.Length != expectedParameters.Length) { // Wrong number of parameters supplied var parameters = expectedParameters.Length == 1 ? "parameter" : "parameters"; throw new TypeException(context, $"Function {functionName} expects {expectedParameters.Length} {parameters}, but received {suppliedParameters.Length}", sourceFileName); } for (int i = 0; i < expectedParameters.Length; i++) { var suppliedParameter = suppliedParameters[i]; var expectedType = expectedParameters[i].Type; var suppliedType = this.Visit(suppliedParameter); if (expectedType == Yarn.Type.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].Type = suppliedType; expectedType = suppliedType; } if (suppliedType != expectedType) { throw new TypeException(context, $"{functionName} parameter {i + 1} expects a {expectedType}, not a {suppliedType}", sourceFileName); } } // Cool, all the parameters check out! // Finally, return the return type of this function. return(functionDeclaration.ReturnType); }