From babbf2875f85e6c4bce4510e3e2e6d5cc9727cc1 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Wed, 28 Nov 2018 20:53:45 +0100 Subject: [PATCH] Type check when calling script function from CSharp --- Upsilon/Binder/Binder.cs | 19 ++++++++++------- Upsilon/Binder/VariableSymbol.cs | 17 +++++++++++++++ Upsilon/BoundTypes/BoundTypeHandler.cs | 9 +++++--- Upsilon/Evaluator/EvaluationException.cs | 12 +++++++++++ Upsilon/Evaluator/Evaluator.cs | 27 +++++++++++++++++++++--- Upsilon/Evaluator/Script.cs | 20 ++---------------- 6 files changed, 72 insertions(+), 32 deletions(-) create mode 100644 Upsilon/Evaluator/EvaluationException.cs diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index d5cffc0..d26f9c0 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -439,22 +439,25 @@ namespace Upsilon.Binder } } - private BoundExpression BindFunctionExpression(FunctionExpressionSyntax e, string variableSymbol = null) + private BoundExpression BindFunctionExpression(FunctionExpressionSyntax e, string functionVariableSymbol = null) { var innerScope = new BoundScope(Scope); var parameters = ImmutableArray.CreateBuilder(); foreach (var identifierToken in e.Parameters) { var variable = identifierToken; - var scriptType = Type.Unknown; + VariableSymbol variableSymbol; if (variable.TypeName != null) { var type = BoundTypeHandler.GetTypeDefinition(variable.TypeName.Name); - scriptType = type.ScriptType; + variableSymbol = new FunctionParameterSymbol(variable.IdentifierName.Name, type); } - var vari = new VariableSymbol(variable.IdentifierName.Name, scriptType, true); - parameters.Add(new BoundVariableSymbol(vari, identifierToken.Span)); - innerScope.DefineLocalVariable(vari); + else + { + variableSymbol = new FunctionParameterSymbol(variable.IdentifierName.Name, Type.Unknown); + } + parameters.Add(new BoundVariableSymbol(variableSymbol, identifierToken.Span)); + innerScope.DefineLocalVariable(variableSymbol); } if (parameters.All(x => x.Type != Type.Unknown)) @@ -470,13 +473,13 @@ namespace Upsilon.Binder else { var unbound = new UnboundFunctionExpression(parameters.ToImmutable(), e.Block, e.Span, innerScope); - if (variableSymbol == null) + if (functionVariableSymbol == null) { _unboundFunctions.Add( Guid.NewGuid().ToString(), unbound); } else { - _unboundFunctions.Add(variableSymbol, unbound); + _unboundFunctions.Add(functionVariableSymbol, unbound); } return unbound; } diff --git a/Upsilon/Binder/VariableSymbol.cs b/Upsilon/Binder/VariableSymbol.cs index 276813b..d160a4a 100644 --- a/Upsilon/Binder/VariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbol.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; +using Upsilon.BoundTypes; namespace Upsilon.Binder { @@ -34,6 +35,22 @@ namespace Upsilon.Binder } } + public class FunctionParameterSymbol : VariableSymbol + { + public BoundTypeDefinition BoundTypeDefinition { get; } + + public FunctionParameterSymbol(string name, Type type) : base(name, type, true) + { + + } + + public FunctionParameterSymbol(string name, BoundTypeDefinition type) : base(name, type.ScriptType, true) + { + BoundTypeDefinition = type; + } + + } + public class TableVariableSymbol : VariableSymbol { public Dictionary Variables { get; } diff --git a/Upsilon/BoundTypes/BoundTypeHandler.cs b/Upsilon/BoundTypes/BoundTypeHandler.cs index b16e5fe..8a234c1 100644 --- a/Upsilon/BoundTypes/BoundTypeHandler.cs +++ b/Upsilon/BoundTypes/BoundTypeHandler.cs @@ -1,3 +1,4 @@ +using System.Collections; using System.Collections.Generic; using Upsilon.BaseTypes; @@ -5,7 +6,7 @@ namespace Upsilon.BoundTypes { public static class BoundTypeHandler { - private static Dictionary _typeDefinitions = new Dictionary + private static readonly Dictionary TypeDefinitions = new Dictionary { {"string", new BoundTypeDefinition(Type.String, typeof(string))}, { @@ -13,12 +14,14 @@ namespace Upsilon.BoundTypes new BoundTypeDefinition(Type.Number, new[] {typeof(int), typeof(long), typeof(float), typeof(double)}) }, - {"bool", new BoundTypeDefinition(Type.Boolean, typeof(bool))} + {"bool", new BoundTypeDefinition(Type.Boolean, typeof(bool))}, + {"table", new BoundTypeDefinition(Type.Table, typeof(IEnumerator))}, + {"function", new BoundTypeDefinition(Type.Function, new System.Type[0])}, }; public static BoundTypeDefinition GetTypeDefinition(string key) { - return _typeDefinitions[key.ToLowerInvariant()]; + return TypeDefinitions[key.ToLowerInvariant()]; } } diff --git a/Upsilon/Evaluator/EvaluationException.cs b/Upsilon/Evaluator/EvaluationException.cs new file mode 100644 index 0000000..d7623fa --- /dev/null +++ b/Upsilon/Evaluator/EvaluationException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Upsilon.Evaluator +{ + public class EvaluationException : Exception + { + public EvaluationException(string message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 1da322d..40a7d7f 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Net.Http.Headers; using Upsilon.BaseTypes; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; @@ -53,7 +54,7 @@ namespace Upsilon.Evaluator return _returnValue; } - public ScriptType Evaluate(BoundScript e, string functionName, ImmutableArray parameters) + public ScriptType Evaluate(BoundScript e, string functionName, object[] parameters) { EvaluateNode(e.Statement); if (!Scope.TryGet(functionName, out var statement) || statement.Type != Type.Function) @@ -65,8 +66,28 @@ namespace Upsilon.Evaluator for (var index = 0; index < parameters.Length; index++) { var parameter = parameters[index]; - var parameterName = function.Parameters[index]; - innerEvaluator.Scope.CreateLocal(parameterName.VariableSymbol, parameter); + var parameterSymbol = (FunctionParameterSymbol)function.Parameters[index].VariableSymbol; + if (parameterSymbol.BoundTypeDefinition != 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( + $"Parameter '{parameterSymbol.Name}' of function '{functionName}' can't handle the given object with type '{parameterType}'"); + } + } + + innerEvaluator.Scope.CreateLocal(parameterSymbol, parameter.ToScriptType()); } var result = innerEvaluator.EvaluateNode(function.Block); diff --git a/Upsilon/Evaluator/Script.cs b/Upsilon/Evaluator/Script.cs index 75264d7..7efc4a9 100644 --- a/Upsilon/Evaluator/Script.cs +++ b/Upsilon/Evaluator/Script.cs @@ -74,15 +74,7 @@ namespace Upsilon.Evaluator public object EvaluateFunction(string functionName, object[] parameters = null) { - var luaParameters = ImmutableArray.CreateBuilder(); - if (parameters != null) - { - foreach (var parameter in parameters) - { - luaParameters.Add(parameter.ToScriptType()); - } - } - return Convert(Evaluator.Evaluate(Bind(), functionName, luaParameters.ToImmutable())); + return Convert(Evaluator.Evaluate(Bind(), functionName, parameters)); } private static T Convert(ScriptType t) @@ -108,15 +100,7 @@ namespace Upsilon.Evaluator public T EvaluateFunction(string functionName, object[] parameters = null) { - var luaParameters = ImmutableArray.CreateBuilder(); - if (parameters != null) - { - foreach (var parameter in parameters) - { - luaParameters.Add(parameter.ToScriptType()); - } - } - return Convert(Evaluator.Evaluate(Bind(), functionName, luaParameters.ToImmutable())); + return Convert(Evaluator.Evaluate(Bind(), functionName, parameters)); } public T GetVariable(string variableName)