diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 5b29008..3f80d73 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -292,6 +292,14 @@ namespace Upsilon.Binder if (udSymbol.Parent.Properties.TryGetValue(resolved.Name, out var ubProperty) && ubProperty is UserDataBoundMethod ubMethod) { returnType = ubMethod.ResultType; + var (isValid, error, wrongParameter) = ubMethod.ValidateParameters(parameters.ToImmutable()); + if (!isValid) + { + var span = e.Span; + if (wrongParameter != null) + span = wrongParameter.Span; + _diagnostics.LogError(error, span); + } } } } diff --git a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs index 1af8849..59f1770 100644 --- a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs +++ b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Reflection; using Upsilon.BaseTypes; +using Upsilon.Binder; using Upsilon.StandardLibraries; using Type = Upsilon.BaseTypes.Type; @@ -67,11 +69,20 @@ namespace Upsilon.BoundTypes { methodName = sfa.Name; } + + var parameters = backingMethod.GetParameters().Select(parameter => new UserDataBoundFunctionParameter() + { + Name = parameter.Name, + ActualType = parameter.ParameterType.Name, + Type = StaticScope.DeriveValidTypes(parameter.ParameterType), + IsOptional = parameter.IsOptional + }).ToArray(); methods.Add(new UserDataBoundMethod() { Name = methodName, Type = Type.Function, - ResultType = backingMethod.ReturnType.GetScriptType() + ResultType = backingMethod.ReturnType.GetScriptType(), + Parameters = parameters }); } foreach (var f in methods) @@ -123,10 +134,47 @@ namespace Upsilon.BoundTypes public string Comment { get; set; } } + public class UserDataBoundFunctionParameter : UserDataBoundProperty + { + public bool IsOptional { get; set; } + } + public class UserDataBoundMethod: UserDataBoundProperty { public override string ActualType => "Function"; public Type ResultType { get; set; } + public UserDataBoundFunctionParameter[] Parameters { get; set; } + + public (bool IsValid, string Error, + BoundExpression WrongParameter) ValidateParameters(ImmutableArray callingParameters) + { + if (callingParameters.Length < Parameters.Count(x => !x.IsOptional) + || callingParameters.Length > Parameters.Length) + { + return (false, + $"Invalid number of parameters for function '{Name}'. Expected {Parameters.Length}, got {callingParameters.Length}", + null); + } + + for (var i = 0; i < callingParameters.Length; i++) + { + var functionParameter = Parameters[i]; + var callingParameter = callingParameters[i]; + if (callingParameter.Type == Type.Unknown || callingParameter.Type == Type.Nil) + continue; + + if (!functionParameter.Type.HasFlag(callingParameter.Type)) + { + return (false, + $"Unexpected variable passed to internal function at variable {i + 1}. " + + $"Expected one of the following: {functionParameter.Type.ToString()}, got: '{callingParameter.Type}'", + callingParameter); + } + } + + return (true, null, null); + } + } } \ No newline at end of file diff --git a/Upsilon/StandardLibraries/StaticScope.cs b/Upsilon/StandardLibraries/StaticScope.cs index 0b1af37..2b0ebdb 100644 --- a/Upsilon/StandardLibraries/StaticScope.cs +++ b/Upsilon/StandardLibraries/StaticScope.cs @@ -138,7 +138,7 @@ namespace Upsilon.StandardLibraries new InternalFunctionVariableSymbol.InternalFunctionParameter(t, false)).ToArray()); } - private static Type DeriveValidTypes(System.Type type) + public static Type DeriveValidTypes(System.Type type) { var typeCode = System.Type.GetTypeCode(type); switch (typeCode)