Allow parameter overloading script functions

This commit is contained in:
Deukhoofd 2019-01-20 15:01:18 +01:00
parent 43da2b3d19
commit 3c0e5f5b13
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
8 changed files with 354 additions and 174 deletions

View File

@ -1,26 +1,105 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Evaluator; using Upsilon.Evaluator;
using Upsilon.Text; using Upsilon.Text;
namespace Upsilon.BaseTypes.ScriptFunction namespace Upsilon.BaseTypes.ScriptFunction
{ {
internal class ScriptRuntimeFunction : ScriptFunction, IScopeOwner internal class ScriptRuntimeFunction : ScriptFunction
{ {
public BoundBlockStatement Block { get; } public List<ScriptRuntimeFunctionOption> Options { get; }
public ImmutableArray<BoundVariableSymbol> Parameters { get; }
public EvaluationScope EvaluationScope { get; }
public ScriptRuntimeFunction(ImmutableArray<BoundVariableSymbol> parameters, BoundBlockStatement block, public ScriptRuntimeFunction(List<ScriptRuntimeFunctionOption> options)
EvaluationScope evaluationScope) {
Options = options;
}
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script,
EvaluationScope scope, TextSpan span)
{
var option = GetValidOption(variables);
if (option == null)
{
throw new EvaluationException(
$"No valid function found");
}
return option.Run(diagnostics, variables, script, scope, span);
}
public ScriptRuntimeFunctionOption GetValidOption(object[] variables)
{
foreach (var option in Options)
{
if (option.Parameters.Length != variables.Length)
continue;
bool isCompatible = true;
for (var index = 0; index < variables.Length; index++)
{
var parameter = option.Parameters[index];
var parameterSymbol = ((UserDataVariableSymbol)parameter.VariableSymbol);
var parameterType = variables[index].GetType();
var validSymbol =
parameterSymbol.BoundTypeDefinition.ValidInternalTypes.Any(validType =>
validType.IsAssignableFrom(parameterType));
if (!validSymbol)
{
isCompatible = false;
break;
}
}
if (!isCompatible)
continue;
return option;
}
return null;
}
public ScriptRuntimeFunctionOption GetValidOption(ScriptType[] variables)
{
foreach (var option in Options)
{
if (option.Parameters.Length != variables.Length)
continue;
bool isCompatible = true;
for (var index = 0; index < variables.Length; index++)
{
var callingVariable = variables[index];
var optionVariable = option.Parameters[index];
if (callingVariable.Type != optionVariable.Type)
{
isCompatible = false;
break;
}
}
if (!isCompatible)
continue;
return option;
}
return null;
}
public class ScriptRuntimeFunctionOption : IScopeOwner
{
public ScriptRuntimeFunctionOption(ImmutableArray<BoundVariableSymbol> parameters,
BoundBlockStatement block, EvaluationScope scope)
{ {
Parameters = parameters; Parameters = parameters;
Block = block; Block = block;
EvaluationScope = evaluationScope; EvaluationScope = scope;
} }
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span) public EvaluationScope EvaluationScope { get; }
public BoundBlockStatement Block { get; }
public ImmutableArray<BoundVariableSymbol> Parameters { get; }
public ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
{ {
var innerEvaluator = new Evaluator.Evaluator(diagnostics, EvaluationScope, script); var innerEvaluator = new Evaluator.Evaluator(diagnostics, EvaluationScope, script);
for (var i = 0; i < Parameters.Length; i++) for (var i = 0; i < Parameters.Length; i++)
@ -33,3 +112,4 @@ namespace Upsilon.BaseTypes.ScriptFunction
} }
} }
} }
}

View File

@ -19,8 +19,8 @@ namespace Upsilon.Binder
public BoundScope Scope { get; private set; } public BoundScope Scope { get; private set; }
private readonly Script _script; private readonly Script _script;
private Dictionary<string, UnboundFunctionExpression> _unboundFunctions = private List<UnboundFunctionExpression> _unboundFunctions =
new Dictionary<string, UnboundFunctionExpression>(); new List<UnboundFunctionExpression>();
public Binder(Diagnostics diagnostics, Dictionary<string, VariableSymbol> variables, Script script) public Binder(Diagnostics diagnostics, Dictionary<string, VariableSymbol> variables, Script script)
{ {
@ -52,23 +52,22 @@ namespace Upsilon.Binder
foreach (var unboundFunctionStatement in _unboundFunctions) foreach (var unboundFunctionStatement in _unboundFunctions)
{ {
Scope = new BoundScope(Scope); Scope = new BoundScope(Scope);
foreach (var valueParameter in unboundFunctionStatement.Value.Parameters) foreach (var valueParameter in unboundFunctionStatement.Parameters)
{ {
Scope.AssignToNearest(valueParameter.VariableSymbol); Scope.AssignToNearest(valueParameter.VariableSymbol);
if (valueParameter.VariableSymbol.TypeContainer == Type.Unknown) if (valueParameter.VariableSymbol.TypeContainer == Type.Unknown)
_diagnostics.LogUnknownVariableType(valueParameter.VariableSymbol.Name, valueParameter.Span); _diagnostics.LogUnknownVariableType(valueParameter.VariableSymbol.Name, valueParameter.Span);
} }
unboundFunctionStatement.Value.Block = unboundFunctionStatement.Block =
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.Value.UnboundBlock); (BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock);
var resultType = Scope.ReturnType; var resultType = Scope.ReturnType;
Scope = Scope.ParentScope; Scope = Scope.ParentScope;
var variable = //var variable =
(ScriptFunctionVariableSymbol) unboundFunctionStatement.Value.Scope.ParentScope.Variables[ // (ScriptFunctionVariableSymbol) unboundFunctionStatement.Scope.ParentScope.Variables[
unboundFunctionStatement.Key]; // unboundFunctionStatement];
variable.IsBound = true; //variable.IsBound = true;
variable.ResultType = resultType;
} }
_unboundFunctions = new Dictionary<string, UnboundFunctionExpression>(); _unboundFunctions.Clear();
return new BoundScript((BoundBlockStatement) bound, e.Span, Scope, fileName, _script); return new BoundScript((BoundBlockStatement) bound, e.Span, Scope, fileName, _script);
} }
@ -231,25 +230,41 @@ namespace Upsilon.Binder
{ {
if (function is ScriptFunctionVariableSymbol scriptFunction) if (function is ScriptFunctionVariableSymbol scriptFunction)
{ {
if (!scriptFunction.IsBound) if (!(scriptFunction.GetFirstValid(parameters.Select(x => x.Type).ToArray()) is ScriptFunctionVariableOption functionOption))
throw new Exception();
if (!functionOption.IsBound)
{ {
Scope = new BoundScope(Scope); Scope = new BoundScope(Scope);
for (var index = 0; index < scriptFunction.Parameters.Length; index++) for (var index = 0; index < functionOption.Parameters.Length; index++)
{ {
var functionVariable = scriptFunction.Parameters[index]; var functionVariable = functionOption.Parameters[index];
var callingVariable = parameters[index]; var callingVariable = parameters[index];
functionVariable.TypeContainer = callingVariable.Type; functionVariable.TypeContainer = callingVariable.Type;
Scope.DefineLocalVariable(functionVariable); Scope.DefineLocalVariable(functionVariable);
} }
var unboundFunctionStatement = _unboundFunctions[scriptFunction.Name]; UnboundFunctionExpression unbound = null;
unboundFunctionStatement.Block = foreach (var functionExpression in this._unboundFunctions.Where(x =>
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock); {
if (x.Name == function.Name)
{
return parameters.Count == functionOption.Parameters.Length;
}
return false;
}))
{
unbound = functionExpression;
break;
}
unbound.Block = (BoundBlockStatement) BindBlockStatement(unbound.UnboundBlock);
returnType = Scope.ReturnType; returnType = Scope.ReturnType;
Scope = Scope.ParentScope; Scope = Scope.ParentScope;
scriptFunction.IsBound = true; functionOption.IsBound = true;
scriptFunction.ResultType = returnType; functionOption.ResultType = returnType;
_unboundFunctions.Remove(scriptFunction.Name);
} }
} }
else else
@ -287,7 +302,7 @@ namespace Upsilon.Binder
} }
else else
{ {
returnType = function.ResultType; //returnType = function.ResultType;
} }
} }
@ -636,15 +651,8 @@ namespace Upsilon.Binder
} }
else else
{ {
var unbound = new UnboundFunctionExpression(parameters.ToImmutable(), e.Block, e.Span, innerScope); var unbound = new UnboundFunctionExpression(parameters.ToImmutable(), e.Block, e.Span, innerScope, functionVariableSymbol);
if (functionVariableSymbol == null) _unboundFunctions.Add(unbound);
{
_unboundFunctions.Add( Guid.NewGuid().ToString(), unbound);
}
else
{
_unboundFunctions.Add(functionVariableSymbol, unbound);
}
return unbound; return unbound;
} }
} }
@ -677,8 +685,8 @@ namespace Upsilon.Binder
CommentValue = commentData.ToArray() CommentValue = commentData.ToArray()
}; };
variable = functionVariable; variable = functionVariable;
functionVariable.IsBound = !(func is UnboundFunctionExpression); ((ScriptFunctionVariableOption)functionVariable.FunctionOption[0]).IsBound = !(func is UnboundFunctionExpression);
functionVariable.ResultType = func.ReturnType; functionVariable.FunctionOption[0].ResultType = func.ReturnType;
if (isLocal) if (isLocal)
Scope.DefineLocalVariable(variable); Scope.DefineLocalVariable(variable);
else else
@ -699,6 +707,18 @@ namespace Upsilon.Binder
return new BoundExpressionStatement(new BoundBadExpression(e.Span), e.Span); return new BoundExpressionStatement(new BoundBadExpression(e.Span), e.Span);
} }
} }
else
{
if (variable is ScriptFunctionVariableSymbol functionVariable)
{
var functionOption =
new ScriptFunctionVariableOption(func.ReturnType, parameters.ToImmutable())
{
IsBound = !(func is UnboundFunctionExpression)
};
functionVariable.FunctionOption.Add(functionOption);
}
}
} }

View File

@ -9,12 +9,14 @@ namespace Upsilon.Binder
public class UnboundFunctionExpression : BoundFunctionExpression public class UnboundFunctionExpression : BoundFunctionExpression
{ {
public UnboundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters, public UnboundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters,
BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope) BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope, string name)
: base(parameters, null, span, scope, BaseTypes.Type.Unknown) : base(parameters, null, span, scope, BaseTypes.Type.Unknown)
{ {
UnboundBlock = unboundBlock; UnboundBlock = unboundBlock;
Name = name;
} }
public string Name { get; }
public override BoundKind Kind => BoundKind.BoundPromise; public override BoundKind Kind => BoundKind.BoundPromise;
public BlockStatementSyntax UnboundBlock { get; } public BlockStatementSyntax UnboundBlock { get; }

View File

@ -1,19 +1,49 @@
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
namespace Upsilon.Binder.VariableSymbols namespace Upsilon.Binder.VariableSymbols
{ {
public abstract class FunctionVariableSymbol : VariableSymbol public abstract class FunctionVariableSymbol : VariableSymbol
{ {
public Type ResultType { get; internal set; } public List<FunctionVariableSymbolOption> FunctionOption { get; } = 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, BaseTypes.Type.Function, local)
{ {
ResultType = resultType;
} }
public abstract (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters( public abstract (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(
ImmutableArray<BoundExpression> callingParameters); ImmutableArray<BoundExpression> callingParameters);
public FunctionVariableSymbolOption GetFirstValid(TypeContainer[] types)
{
return FunctionOption.FirstOrDefault(x =>
{
var parTypes = x.GetParameterTypes();
for (var i = 0; i < parTypes.Length; i++)
{
var parType = parTypes[i];
var givenType = types[i];
if (parType == Type.Unknown || givenType == Type.Unknown)
continue;
if (!parType.Type.HasFlag(givenType))
return false;
}
return true;
});
}
}
public abstract class FunctionVariableSymbolOption
{
public FunctionVariableSymbolOption(TypeContainer resultType)
{
ResultType = resultType;
}
public abstract TypeContainer[] GetParameterTypes();
public TypeContainer ResultType { get; internal set; }
} }
} }

View File

@ -9,47 +9,40 @@ namespace Upsilon.Binder.VariableSymbols
{ {
public class InternalFunctionVariableSymbol : FunctionVariableSymbol public class InternalFunctionVariableSymbol : FunctionVariableSymbol
{ {
// ReSharper disable once MemberCanBePrivate.Global public InternalFunctionVariableSymbol(string name, bool local, TypeContainer resultType,
public InternalFunctionParameter[] FunctionParameters { get; }
private int MinimalParametersRequired { get; }
private MethodInfo OverrideResultType { get; }
public InternalFunctionVariableSymbol(string name, bool local, Type resultType,
InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType) InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType)
: base(name, local, resultType) : base(name, local, resultType)
{ {
FunctionParameters = functionParameters; FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType));
MinimalParametersRequired = functionParameters.Count(x => !x.IsOptional);
OverrideResultType = overrideResultType;
} }
public override (bool IsValid, string Error, public override (bool IsValid, string Error,
BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters) BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
{ {
if (callingParameters.Length < MinimalParametersRequired foreach (var functionVariableSymbolOption in FunctionOption)
|| callingParameters.Length > FunctionParameters.Length)
{ {
return (false, if (!(functionVariableSymbolOption is InternalFunctionVariableOption option))
$"Invalid number of parameters for function '{Name}'. Expected {FunctionParameters.Length}, got {callingParameters.Length}", continue;
null); if (option.FunctionParameters.Length != callingParameters.Length)
} continue;
var isValid = true;
for (var i = 0; i < callingParameters.Length; i++) for (var i = 0; i < callingParameters.Length; i++)
{ {
var functionParameter = FunctionParameters[i]; var functionParameter = option.FunctionParameters[i];
var callingParameter = callingParameters[i]; var callingParameter = callingParameters[i];
if (callingParameter.Type == Type.Unknown || callingParameter.Type == Type.Nil) if (callingParameter.Type == Type.Unknown || callingParameter.Type == Type.Nil)
continue;
if (!functionParameter.ValidTypes.HasFlag(callingParameter.Type))
{ {
return (false, isValid = false;
$"Unexpected variable passed to internal function at variable {i + 1}. " + break;
$"Expected one of the following: {functionParameter.ValidTypes.ToString()}, got: '{callingParameter.Type}'",
callingParameter);
} }
if (functionParameter.ValidTypes.HasFlag(Type.UserData)) if (!functionParameter.ValidTypes.Type.HasFlag(callingParameter.Type))
{
isValid = false;
break;
}
if (functionParameter.ValidTypes.Type.HasFlag(Type.UserData))
{ {
var variable = Binder.ResolveVariable(callingParameter, null); var variable = Binder.ResolveVariable(callingParameter, null);
if (variable != null && variable.TypeContainer == Type.UserData) if (variable != null && variable.TypeContainer == Type.UserData)
@ -58,23 +51,48 @@ namespace Upsilon.Binder.VariableSymbols
(UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition; (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition;
if (functionParameter.ExpectedUserData != null && !functionParameter.ExpectedUserData.Contains(parent.Name)) if (functionParameter.ExpectedUserData != null && !functionParameter.ExpectedUserData.Contains(parent.Name))
{ {
return (false, isValid = false;
$"Unexpected variable passed to internal function at variable {i + 1}. " + break;
$"Expected to be the following: {string.Join(", ", functionParameter.ExpectedUserData)}, got: '{parent.Name}'",
callingParameter);
} }
} }
} }
} }
if (!isValid)
continue;
return (true, null, null);
}
return (true, null, null); return (false,
$"No valid function with name '{Name}' and variables of type {string.Join(", ", callingParameters.Select(x => $"'{x.Type}'"))} found",
null);
} }
public TypeContainer GetResultType(BoundExpression[] parameters) public TypeContainer GetResultType(BoundExpression[] parameters)
{ {
if (OverrideResultType == null) foreach (var functionVariableSymbolOption in FunctionOption)
return ResultType; {
return (TypeContainer) OverrideResultType.Invoke(null, new object[] {parameters}); if (!(functionVariableSymbolOption is InternalFunctionVariableOption option))
continue;
if (option.FunctionParameters.Length != parameters.Length)
continue;
var isValid = true;
for (var i = 0; i < parameters.Length; i++)
{
var functionParameter = option.FunctionParameters[i];
var callingParameter = parameters[i];
if (!functionParameter.ValidTypes.Type.HasFlag(callingParameter.Type))
{
isValid = false;
break;
}
}
if (!isValid)
continue;
if (option.OverrideResultType == null)
return option.ResultType;
return (TypeContainer) option.OverrideResultType.Invoke(null, new object[] {parameters});
}
return Type.Unknown;
} }
public class InternalFunctionParameter public class InternalFunctionParameter
@ -95,9 +113,28 @@ namespace Upsilon.Binder.VariableSymbols
} }
public string Name { get; } public string Name { get; }
public Type ValidTypes { get; } public TypeContainer ValidTypes { get; }
public string[] ExpectedUserData { get; } public string[] ExpectedUserData { get; }
public bool IsOptional { get; } public bool IsOptional { get; }
} }
} }
public class InternalFunctionVariableOption : FunctionVariableSymbolOption
{
public InternalFunctionVariableSymbol.InternalFunctionParameter[] FunctionParameters { get; }
public MethodInfo OverrideResultType { get; }
public InternalFunctionVariableOption(TypeContainer resultType,
InternalFunctionVariableSymbol.InternalFunctionParameter[] functionParameters,
MethodInfo overrideResultType) : base(resultType)
{
FunctionParameters = functionParameters;
OverrideResultType = overrideResultType;
}
public override TypeContainer[] GetParameterTypes()
{
return FunctionParameters.Select(x => x.ValidTypes).ToArray();
}
}
} }

View File

@ -1,47 +1,63 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
namespace Upsilon.Binder.VariableSymbols namespace Upsilon.Binder.VariableSymbols
{ {
public class ScriptFunctionVariableSymbol : FunctionVariableSymbol public class ScriptFunctionVariableSymbol : FunctionVariableSymbol
{ {
public ImmutableArray<VariableSymbol> Parameters { get; }
public bool IsBound { get; set; }
public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType) public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType)
: base(name, local, resultType) : base(name, local, resultType)
{ {
Parameters = parameters; FunctionOption.Add(new ScriptFunctionVariableOption(resultType, parameters));
} }
public override (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters) public override (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> callingParameters)
{ {
if (Parameters.Length != callingParameters.Length) foreach (var functionVariableSymbolOption in FunctionOption)
{ {
return (false, if (!(functionVariableSymbolOption is ScriptFunctionVariableOption option))
$"Invalid number of parameters for function '{Name}'. Expected {Parameters.Length}, got {callingParameters.Length}", continue;
null); if (option.Parameters.Length != callingParameters.Length)
} continue;
bool isValid = true;
for (var i = 0; i < Parameters.Length; i++) for (var i = 0; i < option.Parameters.Length; i++)
{ {
var functionParameter = Parameters[i]; var functionParameter = option.Parameters[i];
var callingParameter = callingParameters[i]; var callingParameter = callingParameters[i];
if (functionParameter.TypeContainer != BaseTypes.Type.Unknown && if (functionParameter.TypeContainer != BaseTypes.Type.Unknown &&
callingParameter.Type != BaseTypes.Type.Unknown && callingParameter.Type != BaseTypes.Type.Nil) callingParameter.Type != BaseTypes.Type.Unknown &&
callingParameter.Type != BaseTypes.Type.Nil)
{ {
if (callingParameter.Type != functionParameter.TypeContainer) if (callingParameter.Type != functionParameter.TypeContainer)
{ {
return (false, $"Invalid type for function '{Name}' at parameter '{functionParameter.Name}'. " + isValid = false;
$"Expected type '{functionParameter.TypeContainer}', got '{callingParameter.Type}'", break;
callingParameter);
} }
} }
} }
if (!isValid)
continue;
return (true, null, null); return (true, null, null);
} }
return (false, null, null);
}
}
public class ScriptFunctionVariableOption : FunctionVariableSymbolOption
{
public ImmutableArray<VariableSymbol> Parameters { get; }
public bool IsBound { get; set; }
public ScriptFunctionVariableOption(Type resultType, ImmutableArray<VariableSymbol> parameters) : base(resultType)
{
Parameters = parameters;
}
public override TypeContainer[] GetParameterTypes()
{
return Parameters.Select(x => x.TypeContainer).ToArray();
}
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.Number;
@ -86,55 +87,11 @@ namespace Upsilon.Evaluator
} }
var function = (ScriptRuntimeFunction) statement; var function = (ScriptRuntimeFunction) statement;
var innerEvaluator = new Evaluator(_diagnostics, Scope, _script); var innerEvaluator = new Evaluator(_diagnostics, Scope, _script);
if (parameters != null) var option = function.GetValidOption(parameters);
{ if (option == null)
for (var index = 0; index < parameters.Length; index++)
{
object parameter;
if (index < parameters.Length)
{
parameter = parameters[index];
}
else
{
parameter = null;
}
UserDataVariableSymbol parameterSymbol;
if (index < function.Parameters.Length)
{
parameterSymbol = (UserDataVariableSymbol)function.Parameters[index].VariableSymbol;
}
else
{
continue;
}
if (parameterSymbol.BoundTypeDefinition != null && parameter != null)
{
bool isCompatible = false;
var parameterType = parameter.GetType();
foreach (var validType in parameterSymbol.BoundTypeDefinition.ValidInternalTypes)
{
if (validType.IsAssignableFrom(parameterType))
{
isCompatible = true;
break;
}
}
if (!isCompatible)
{
throw new EvaluationException( throw new EvaluationException(
$"Parameter '{parameterSymbol.Name}' of function '{functionName}' can't handle the given object with type '{parameterType}'"); $"No function found with name '{functionName}' and available parameters {string.Join(", ", parameters.Select(x => $"{x.GetType().Name}"))}");
} var result = option.Run(_diagnostics, parameters.Select(x => x.ToScriptType()).ToArray(), _script, Scope, new TextSpan());
}
var parameterConverted = parameter == null ? new ScriptNull() : parameter.ToScriptType();
innerEvaluator.Scope.CreateLocal(parameterSymbol, parameterConverted);
}
}
var result = innerEvaluator.EvaluateNode(function.Block);
return result; return result;
} }
@ -539,22 +496,42 @@ namespace Upsilon.Evaluator
private void EvaluateBoundFunctionAssigmentStatement(BoundFunctionAssignmentStatement e) private void EvaluateBoundFunctionAssigmentStatement(BoundFunctionAssignmentStatement e)
{ {
var func = EvaluateBoundFunctionStatement(e.Func); var func = (ScriptRuntimeFunction)EvaluateBoundFunctionStatement(e.Func);
if (e.Variable.Local) if (e.Variable.Local)
Scope.CreateLocal(e.Variable, func); {
if (Scope.Variables.TryGetValue(e.Variable.Name, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction)
{
scriptRuntimeFunction.Options.AddRange(func.Options);
}
else else
{
Scope.CreateLocal(e.Variable, func);
}
}
else
{
if (Scope.TryGet(e.Variable, out var f) && f is ScriptRuntimeFunction scriptRuntimeFunction)
{
scriptRuntimeFunction.Options.AddRange(func.Options);
}
else
{
Scope.AssignToNearest(e.Variable, func); Scope.AssignToNearest(e.Variable, func);
} }
}
}
private ScriptType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression) private ScriptType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression)
{ {
var func = new ScriptRuntimeFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope); var option = new ScriptRuntimeFunction.ScriptRuntimeFunctionOption(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope);
var func = new ScriptRuntimeFunction(new List<ScriptRuntimeFunction.ScriptRuntimeFunctionOption>(){option});
return func; return func;
} }
private ScriptType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression) private ScriptType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression)
{ {
var func = new ScriptRuntimeFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope); var option = new ScriptRuntimeFunction.ScriptRuntimeFunctionOption(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope);
var func = new ScriptRuntimeFunction(new List<ScriptRuntimeFunction.ScriptRuntimeFunctionOption>(){option});
return func; return func;
} }

View File

@ -182,5 +182,23 @@ return value
var result = Executor.EvaluateScript<long>(input, Options); var result = Executor.EvaluateScript<long>(input, Options);
Assert.Equal(6, result); Assert.Equal(6, result);
} }
[Fact]
public void HandleMultipleFunctionOptions()
{
const string input = @"
function a(number v)
return v + 10
end
function a(string s)
return s == ""test""
end
return a(50) == 60 and a(""test"")
";
Assert.Equal(60, Executor.EvaluateFunction<long>(input, "a", new object[] {50}));
Assert.True(Executor.EvaluateFunction<bool>(input, "a", new object[] {"test"}));
Assert.True(Executor.EvaluateScript<bool>(input, Options));
}
} }
} }