Better support for method overloading

This commit is contained in:
Deukhoofd 2019-01-20 21:00:01 +01:00
parent 921abce011
commit 898cabb237
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
4 changed files with 67 additions and 40 deletions

View File

@ -322,14 +322,16 @@ namespace Upsilon.Binder
if (udSymbol.Parent.Properties.TryGetValue(resolved.Name.ToLowerInvariant(), if (udSymbol.Parent.Properties.TryGetValue(resolved.Name.ToLowerInvariant(),
out var ubProperty) && ubProperty is UserDataBoundMethod ubMethod) out var ubProperty) && ubProperty is UserDataBoundMethod ubMethod)
{ {
returnType = ubMethod.ResultType; var option = ubMethod.Validate(parameters.ToImmutable());
var (isValid, error, wrongParameter) = ubMethod.ValidateParameters(parameters.ToImmutable()); if (option == null)
if (!isValid)
{ {
var span = e.Span; _diagnostics.LogError(
if (wrongParameter != null) $"No valid function with name '{ubMethod.Name}' and parameter types {string.Join(", ", parameters.Select(x => $"'x.Type'"))} found",
span = wrongParameter.Span; expression.Span);
_diagnostics.LogError(error, span); }
else
{
returnType = option.ResultType;
} }
} }
} }
@ -906,10 +908,13 @@ namespace Upsilon.Binder
BoundExpression indexableExpression; BoundExpression indexableExpression;
var value = BindExpression(e.Expression); var value = BindExpression(e.Expression);
if (e.TableExpression.Kind == SyntaxKind.IndexExpression) if (e.TableExpression.Kind == SyntaxKind.IndexExpression)
{ {
var indexable = var idExp = BindIndexExpression((IndexExpressionSyntax) e.TableExpression, true);
(BoundIndexExpression) 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 && if (indexable.Identifier.Kind == BoundKind.VariableExpression &&
indexable.Index.Kind == BoundKind.BoundLiteralExpression) indexable.Index.Kind == BoundKind.BoundLiteralExpression)

View File

@ -7,10 +7,10 @@ namespace Upsilon.Binder.VariableSymbols
{ {
public abstract class FunctionVariableSymbol : VariableSymbol public abstract class FunctionVariableSymbol : VariableSymbol
{ {
public List<FunctionVariableSymbolOption> FunctionOption { get; } = new List<FunctionVariableSymbolOption>(); public List<FunctionVariableSymbolOption> FunctionOption { get; protected set; } = new List<FunctionVariableSymbolOption>();
public FunctionVariableSymbol(string name, bool local, Type resultType) public FunctionVariableSymbol(string name, bool local, Type resultType)
: base(name, BaseTypes.Type.Function, local) : base(name, Type.Function, local)
{ {
} }

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -16,6 +17,12 @@ namespace Upsilon.Binder.VariableSymbols
FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType)); FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType));
} }
public InternalFunctionVariableSymbol(string name, bool local, Type resultType, List<FunctionVariableSymbolOption> options)
: base(name, local, resultType)
{
FunctionOption = options;
}
public override (bool IsValid, string Error, public override (bool IsValid, string Error,
BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters) BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
{ {

View File

@ -29,8 +29,6 @@ namespace Upsilon.BoundTypes
Properties = backingType; Properties = backingType;
} }
public static UserDataBoundTypeDefinition Create(System.Type backingType, string name) public static UserDataBoundTypeDefinition Create(System.Type backingType, string name)
{ {
var obj = new UserDataBoundTypeDefinition(backingType, name) var obj = new UserDataBoundTypeDefinition(backingType, name)
@ -58,7 +56,7 @@ namespace Upsilon.BoundTypes
{ {
obj.Properties.Add(f.Name.ToLowerInvariant(), f); obj.Properties.Add(f.Name.ToLowerInvariant(), f);
} }
var methods = new List<UserDataBoundMethod>(); var methods = new Dictionary<string, UserDataBoundMethod>();
var backingMethods = backingType.GetMethods(); var backingMethods = backingType.GetMethods();
foreach (var backingMethod in backingMethods) foreach (var backingMethod in backingMethods)
{ {
@ -78,21 +76,25 @@ namespace Upsilon.BoundTypes
Type = StaticScope.DeriveValidTypes(parameter.ParameterType), Type = StaticScope.DeriveValidTypes(parameter.ParameterType),
IsOptional = parameter.IsOptional IsOptional = parameter.IsOptional
}).ToArray(); }).ToArray();
methods.Add(new UserDataBoundMethod() var option = new UserDataBoundMethodOption(backingMethod.ReturnType.GetScriptType(), parameters);
if (methods.TryGetValue(methodName, out var func))
{ {
Name = methodName, func.Options.Add(option);
Type = Type.Function, }
ResultType = backingMethod.ReturnType.GetScriptType(), else
Parameters = parameters {
}); methods.Add(methodName,
new UserDataBoundMethod(methodName, new List<UserDataBoundMethodOption>() {option}));
}
} }
foreach (var f in methods) foreach (var f in methods)
{ {
var cleanedName = f.Name.ToLowerInvariant(); var cleanedName = f.Value.Name.ToLowerInvariant();
// TODO: handle this better, considering overloads // TODO: handle this better, considering overloads
if (obj.Properties.ContainsKey(cleanedName)) if (obj.Properties.ContainsKey(cleanedName))
continue; continue;
obj.Properties.Add(cleanedName, f); obj.Properties.Add(cleanedName, f.Value);
} }
return obj; return obj;
@ -159,55 +161,68 @@ namespace Upsilon.BoundTypes
public class UserDataBoundMethod: UserDataBoundProperty public class UserDataBoundMethod: UserDataBoundProperty
{ {
public override string ActualType => "Function"; public override string ActualType => "Function";
public TypeContainer ResultType { get; set; } public List<UserDataBoundMethodOption> Options;
public UserDataBoundMethod(string name, List<UserDataBoundMethodOption> options)
{
Name = name;
Options = options;
}
public UserDataBoundMethodOption Validate(ImmutableArray<BoundExpression> 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 UserDataBoundFunctionParameter[] Parameters { get; set; }
public (bool IsValid, string Error, public bool ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
{ {
if (callingParameters.Length < Parameters.Count(x => !x.IsOptional) if (callingParameters.Length < Parameters.Count(x => !x.IsOptional)
|| callingParameters.Length > Parameters.Length) || callingParameters.Length > Parameters.Length)
{ {
return (false, return false;
$"Invalid number of parameters for function '{Name}'. Expected {Parameters.Length}, got {callingParameters.Length}",
null);
} }
for (var i = 0; i < callingParameters.Length; i++) for (var i = 0; i < callingParameters.Length; i++)
{ {
var functionParameter = Parameters[i]; var functionParameter = Parameters[i];
var callingParameter = callingParameters[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; continue;
if (!functionParameter.Type.Type.HasFlag(callingParameter.Type)) if (!functionParameter.Type.Type.HasFlag(callingParameter.Type))
{ {
return (false, return false;
$"Unexpected variable passed to internal function at variable {i + 1}. " +
$"Expected one of the following: {functionParameter.Type}, got: '{callingParameter.Type}'",
callingParameter);
} }
if (functionParameter.Type.Type.HasFlag(BaseTypes.Type.UserData)) if (functionParameter.Type.Type.HasFlag(Type.UserData))
{ {
var variable = Binder.Binder.ResolveVariable(callingParameter, null); var variable = Binder.Binder.ResolveVariable(callingParameter, null);
if (variable != null && variable.TypeContainer == BaseTypes.Type.UserData) if (variable != null && variable.TypeContainer == Type.UserData)
{ {
var parent = var parent =
(UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition; (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition;
if (functionParameter.ActualType != null && if (functionParameter.ActualType != null &&
!string.Equals(functionParameter.ActualType, parent.Name, StringComparison.InvariantCultureIgnoreCase)) !string.Equals(functionParameter.ActualType, parent.Name, StringComparison.InvariantCultureIgnoreCase))
{ {
return (false, return false;
$"Unexpected variable passed to internal function at variable {i + 1}. " +
$"Expected to be the following: {functionParameter.ActualType}, got: '{parent.Name}'",
callingParameter);
} }
} }
} }
} }
return (true, null, null); return true;
} }
} }