From 898cabb2379c474c2b5bf55e0c4948b911588980 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 20 Jan 2019 21:00:01 +0100 Subject: [PATCH] Better support for method overloading --- Upsilon/Binder/Binder.cs | 23 +++--- .../VariableSymbols/FunctionVariableSymbol.cs | 4 +- .../InternalFunctionVariableSymbol.cs | 7 ++ .../BoundTypes/UserDataBoundTypeDefinition.cs | 73 +++++++++++-------- 4 files changed, 67 insertions(+), 40 deletions(-) diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 7c3f8e7..5468bf0 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -322,14 +322,16 @@ namespace Upsilon.Binder if (udSymbol.Parent.Properties.TryGetValue(resolved.Name.ToLowerInvariant(), out var ubProperty) && ubProperty is UserDataBoundMethod ubMethod) { - returnType = ubMethod.ResultType; - var (isValid, error, wrongParameter) = ubMethod.ValidateParameters(parameters.ToImmutable()); - if (!isValid) + var option = ubMethod.Validate(parameters.ToImmutable()); + if (option == null) { - var span = e.Span; - if (wrongParameter != null) - span = wrongParameter.Span; - _diagnostics.LogError(error, span); + _diagnostics.LogError( + $"No valid function with name '{ubMethod.Name}' and parameter types {string.Join(", ", parameters.Select(x => $"'x.Type'"))} found", + expression.Span); + } + else + { + returnType = option.ResultType; } } } @@ -906,10 +908,13 @@ namespace Upsilon.Binder BoundExpression indexableExpression; var value = BindExpression(e.Expression); + if (e.TableExpression.Kind == SyntaxKind.IndexExpression) { - var indexable = - (BoundIndexExpression) BindIndexExpression((IndexExpressionSyntax) e.TableExpression, true); + var idExp = BindIndexExpression((IndexExpressionSyntax) e.TableExpression, true); + if (idExp is BoundBadExpression) + return new BoundExpressionStatement(new BoundBadExpression(e.Span), e.Span); + var indexable = (BoundIndexExpression) idExp; if (indexable.Identifier.Kind == BoundKind.VariableExpression && indexable.Index.Kind == BoundKind.BoundLiteralExpression) diff --git a/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs index 8f46d7f..69dff50 100644 --- a/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs @@ -7,10 +7,10 @@ namespace Upsilon.Binder.VariableSymbols { public abstract class FunctionVariableSymbol : VariableSymbol { - public List FunctionOption { get; } = new List(); + public List FunctionOption { get; protected set; } = new List(); public FunctionVariableSymbol(string name, bool local, Type resultType) - : base(name, BaseTypes.Type.Function, local) + : base(name, Type.Function, local) { } diff --git a/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs index 65b70d4..87a232f 100644 --- a/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; @@ -16,6 +17,12 @@ namespace Upsilon.Binder.VariableSymbols FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType)); } + public InternalFunctionVariableSymbol(string name, bool local, Type resultType, List options) + : base(name, local, resultType) + { + FunctionOption = options; + } + public override (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(ImmutableArray callingParameters) { diff --git a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs index 102a66b..75038b1 100644 --- a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs +++ b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs @@ -29,8 +29,6 @@ namespace Upsilon.BoundTypes Properties = backingType; } - - public static UserDataBoundTypeDefinition Create(System.Type backingType, string name) { var obj = new UserDataBoundTypeDefinition(backingType, name) @@ -58,7 +56,7 @@ namespace Upsilon.BoundTypes { obj.Properties.Add(f.Name.ToLowerInvariant(), f); } - var methods = new List(); + var methods = new Dictionary(); var backingMethods = backingType.GetMethods(); foreach (var backingMethod in backingMethods) { @@ -78,21 +76,25 @@ namespace Upsilon.BoundTypes Type = StaticScope.DeriveValidTypes(parameter.ParameterType), IsOptional = parameter.IsOptional }).ToArray(); - methods.Add(new UserDataBoundMethod() + var option = new UserDataBoundMethodOption(backingMethod.ReturnType.GetScriptType(), parameters); + if (methods.TryGetValue(methodName, out var func)) { - Name = methodName, - Type = Type.Function, - ResultType = backingMethod.ReturnType.GetScriptType(), - Parameters = parameters - }); + func.Options.Add(option); + } + else + { + methods.Add(methodName, + new UserDataBoundMethod(methodName, new List() {option})); + } + } foreach (var f in methods) { - var cleanedName = f.Name.ToLowerInvariant(); + var cleanedName = f.Value.Name.ToLowerInvariant(); // TODO: handle this better, considering overloads if (obj.Properties.ContainsKey(cleanedName)) continue; - obj.Properties.Add(cleanedName, f); + obj.Properties.Add(cleanedName, f.Value); } return obj; @@ -159,55 +161,68 @@ namespace Upsilon.BoundTypes public class UserDataBoundMethod: UserDataBoundProperty { public override string ActualType => "Function"; - public TypeContainer ResultType { get; set; } + public List Options; + + public UserDataBoundMethod(string name, List options) + { + Name = name; + Options = options; + } + + public UserDataBoundMethodOption Validate(ImmutableArray callingParameters) + { + return Options.FirstOrDefault(x => x.ValidateParameters(callingParameters)); + } + } + + public class UserDataBoundMethodOption + { + public UserDataBoundMethodOption(TypeContainer resultType, UserDataBoundFunctionParameter[] parameters) + { + ResultType = resultType; + Parameters = parameters; + } + + public TypeContainer ResultType { get; set; } public UserDataBoundFunctionParameter[] Parameters { get; set; } - public (bool IsValid, string Error, - BoundExpression WrongParameter) ValidateParameters(ImmutableArray callingParameters) + public bool 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); + return false; } for (var i = 0; i < callingParameters.Length; i++) { var functionParameter = Parameters[i]; var callingParameter = callingParameters[i]; - if (callingParameter.Type == BaseTypes.Type.Unknown || callingParameter.Type == BaseTypes.Type.Nil) + if (callingParameter.Type == Type.Unknown || callingParameter.Type == Type.Nil) continue; if (!functionParameter.Type.Type.HasFlag(callingParameter.Type)) { - return (false, - $"Unexpected variable passed to internal function at variable {i + 1}. " + - $"Expected one of the following: {functionParameter.Type}, got: '{callingParameter.Type}'", - callingParameter); + return false; } - if (functionParameter.Type.Type.HasFlag(BaseTypes.Type.UserData)) + if (functionParameter.Type.Type.HasFlag(Type.UserData)) { var variable = Binder.Binder.ResolveVariable(callingParameter, null); - if (variable != null && variable.TypeContainer == BaseTypes.Type.UserData) + if (variable != null && variable.TypeContainer == Type.UserData) { var parent = (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition; if (functionParameter.ActualType != null && !string.Equals(functionParameter.ActualType, parent.Name, StringComparison.InvariantCultureIgnoreCase)) { - return (false, - $"Unexpected variable passed to internal function at variable {i + 1}. " + - $"Expected to be the following: {functionParameter.ActualType}, got: '{parent.Name}'", - callingParameter); + return false; } } } } - return (true, null, null); + return true; } }