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(),
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)

View File

@ -7,10 +7,10 @@ namespace Upsilon.Binder.VariableSymbols
{
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)
: 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.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<FunctionVariableSymbolOption> options)
: base(name, local, resultType)
{
FunctionOption = options;
}
public override (bool IsValid, string Error,
BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
{

View File

@ -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<UserDataBoundMethod>();
var methods = new Dictionary<string, UserDataBoundMethod>();
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<UserDataBoundMethodOption>() {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 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 (bool IsValid, string Error,
BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
public bool ValidateParameters(ImmutableArray<BoundExpression> 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;
}
}