Better support for method overloading
This commit is contained in:
parent
921abce011
commit
898cabb237
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue