diff --git a/Upsilon/BaseTypes/LuaBoolean.cs b/Upsilon/BaseTypes/LuaBoolean.cs index d654904..6dee64b 100644 --- a/Upsilon/BaseTypes/LuaBoolean.cs +++ b/Upsilon/BaseTypes/LuaBoolean.cs @@ -13,6 +13,11 @@ namespace Upsilon.BaseTypes return Value; } + public override System.Type GetCSharpType() + { + return typeof(bool); + } + public bool Value { get; } public static implicit operator bool(LuaBoolean b) diff --git a/Upsilon/BaseTypes/LuaFunction.cs b/Upsilon/BaseTypes/LuaFunction.cs index 1ba0dd1..ec37d55 100644 --- a/Upsilon/BaseTypes/LuaFunction.cs +++ b/Upsilon/BaseTypes/LuaFunction.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using System.Linq; using System.Reflection; +using Upsilon.BaseTypes.UserData; using Upsilon.Binder; using Upsilon.Evaluator; @@ -14,6 +15,11 @@ namespace Upsilon.BaseTypes return this; } + public override System.Type GetCSharpType() + { + return null; + } + public abstract LuaType Run(Diagnostics diagnostics, LuaType[] variables); } @@ -46,19 +52,21 @@ namespace Upsilon.BaseTypes internal class LuaMethodInfoFunction : LuaFunction { - public LuaMethodInfoFunction(MethodInfo method, object o) + public LuaMethodInfoFunction(UserDataMethod method, object o) { _method = method; _object = o; } - private readonly MethodInfo _method; + private readonly UserDataMethod _method; private readonly object _object; public override LuaType Run(Diagnostics diagnostics, LuaType[] variables) { + var types = variables.Select(x => x.GetCSharpType()); + var method = _method.GetMethod(types.ToArray()); var objects = variables.Select(x => x.ToCSharpObject()); - return _method.Invoke(_object, objects.ToArray()).ToLuaType(); + return method.Invoke(_object, objects.ToArray()).ToLuaType(); } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/LuaNull.cs b/Upsilon/BaseTypes/LuaNull.cs index 4ce1ec6..32c026d 100644 --- a/Upsilon/BaseTypes/LuaNull.cs +++ b/Upsilon/BaseTypes/LuaNull.cs @@ -8,6 +8,11 @@ namespace Upsilon.BaseTypes return null; } + public override System.Type GetCSharpType() + { + return null; + } + public override string ToString() { return "null"; diff --git a/Upsilon/BaseTypes/LuaString.cs b/Upsilon/BaseTypes/LuaString.cs index cf794da..83a9dc4 100644 --- a/Upsilon/BaseTypes/LuaString.cs +++ b/Upsilon/BaseTypes/LuaString.cs @@ -19,6 +19,11 @@ namespace Upsilon.BaseTypes return Value; } + public override System.Type GetCSharpType() + { + return typeof(string); + } + public override bool Equals(object obj) { if (obj == null) diff --git a/Upsilon/BaseTypes/LuaTable.cs b/Upsilon/BaseTypes/LuaTable.cs index 34c9192..bebfcdc 100644 --- a/Upsilon/BaseTypes/LuaTable.cs +++ b/Upsilon/BaseTypes/LuaTable.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using Upsilon.Binder; using Upsilon.Evaluator; @@ -21,6 +22,11 @@ namespace Upsilon.BaseTypes return EvaluationScope.Variables.ToDictionary(x => x.Key, x => x.Value.ToCSharpObject()); } + public override System.Type GetCSharpType() + { + return typeof(Dictionary); + } + public LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope) { if (EvaluationScope.TryGet(s, out var o)) diff --git a/Upsilon/BaseTypes/LuaType.cs b/Upsilon/BaseTypes/LuaType.cs index 698c88d..41e34e5 100644 --- a/Upsilon/BaseTypes/LuaType.cs +++ b/Upsilon/BaseTypes/LuaType.cs @@ -4,5 +4,6 @@ namespace Upsilon.BaseTypes { public abstract Type Type { get; } public abstract object ToCSharpObject(); + public abstract System.Type GetCSharpType(); } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/Number/NumberDouble.cs b/Upsilon/BaseTypes/Number/NumberDouble.cs index a9d29f9..a39069d 100644 --- a/Upsilon/BaseTypes/Number/NumberDouble.cs +++ b/Upsilon/BaseTypes/Number/NumberDouble.cs @@ -31,5 +31,10 @@ namespace Upsilon.BaseTypes.Number { return Value; } + + public override System.Type GetCSharpType() + { + return typeof(double); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/Number/NumberLong.cs b/Upsilon/BaseTypes/Number/NumberLong.cs index b68cd9a..1da827a 100644 --- a/Upsilon/BaseTypes/Number/NumberLong.cs +++ b/Upsilon/BaseTypes/Number/NumberLong.cs @@ -34,5 +34,10 @@ namespace Upsilon.BaseTypes.Number { return Value; } + + public override System.Type GetCSharpType() + { + return typeof(long); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserData.cs b/Upsilon/BaseTypes/UserData/UserData.cs index 22fc877..112b379 100644 --- a/Upsilon/BaseTypes/UserData/UserData.cs +++ b/Upsilon/BaseTypes/UserData/UserData.cs @@ -20,6 +20,11 @@ namespace Upsilon.BaseTypes.UserData return Value; } + public override System.Type GetCSharpType() + { + return Value.GetType(); + } + public LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope) { var (type, failed) = _typeInfo.Get(Value, s); diff --git a/Upsilon/BaseTypes/UserData/UserDataMethod.cs b/Upsilon/BaseTypes/UserData/UserDataMethod.cs new file mode 100644 index 0000000..d7f7993 --- /dev/null +++ b/Upsilon/BaseTypes/UserData/UserDataMethod.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Upsilon.BaseTypes.UserData +{ + public class UserDataMethod + { + private class UserDataMethodPart + { + public UserDataMethodPart(MethodInfo method) + { + Method = method; + Parameters = method.GetParameters().Select(x => new UserDataMethodParameter(x)).ToArray(); + } + + public MethodInfo Method { get; } + public UserDataMethodParameter[] Parameters { get; } + } + + private struct UserDataMethodParameter + { + public UserDataMethodParameter(ParameterInfo info) + { + TypeHash = info.ParameterType.GetHashCode(); + IsOptional = info.IsOptional; + } + + public int TypeHash { get; } + public bool IsOptional { get; } + } + + private List MethodParts { get; } + + public UserDataMethod(MethodInfo method) + { + var part = new UserDataMethodPart(method); + MethodParts = new List() + { + part + }; + } + + public void LoadMethodPart(MethodInfo method) + { + var part = new UserDataMethodPart(method); + MethodParts.Add(part); + } + + public MethodInfo GetMethod(System.Type[] parameterTypes) + { + foreach (var userDataMethodPart in MethodParts) + { + bool valid = true; + for (var index = 0; index < userDataMethodPart.Parameters.Length; index++) + { + var userDataMethodParameter = userDataMethodPart.Parameters[index]; + if (index >= parameterTypes.Length) + { + if (userDataMethodParameter.IsOptional) + return userDataMethodPart.Method; + valid = false; + break; + } + var parameterHash = parameterTypes[index].GetHashCode(); + if (userDataMethodParameter.TypeHash != parameterHash) + { + valid = false; + break; + } + } + + if (valid) return userDataMethodPart.Method; + } + + return null; + } + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataMethodKey.cs b/Upsilon/BaseTypes/UserData/UserDataMethodKey.cs new file mode 100644 index 0000000..aa14ed3 --- /dev/null +++ b/Upsilon/BaseTypes/UserData/UserDataMethodKey.cs @@ -0,0 +1,22 @@ +using System.Linq; + +namespace Upsilon.BaseTypes.UserData +{ + public struct UserDataMethodKey + { + public UserDataMethodKey(string name, System.Type[] parameterTypes) + { + Name = name; + ParameterTypeHashes = parameterTypes.Select(x => x.GetHashCode()).ToArray(); + } + + public UserDataMethodKey(string name, int[] parameterTypeHashes) + { + Name = name; + ParameterTypeHashes = parameterTypeHashes; + } + + public string Name { get; } + public int[] ParameterTypeHashes { get; } + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataType.cs b/Upsilon/BaseTypes/UserData/UserDataType.cs index 5a4b267..ea5fd2e 100644 --- a/Upsilon/BaseTypes/UserData/UserDataType.cs +++ b/Upsilon/BaseTypes/UserData/UserDataType.cs @@ -11,13 +11,27 @@ namespace Upsilon.BaseTypes.UserData Type = type; Variables = type.GetFields().ToDictionary(x => x.Name.ToLowerInvariant(), x => x); Properties = type.GetProperties().ToDictionary(x => x.Name.ToLowerInvariant(), x => x); - Methods = type.GetMethods().ToDictionary(x => x.Name.ToLowerInvariant(), x => x); + Methods = new Dictionary(); + foreach (var methodInfo in type.GetMethods()) + { + var commonName = methodInfo.Name.ToLowerInvariant(); + if (Methods.TryGetValue(commonName, out var methodData)) + { + methodData.LoadMethodPart(methodInfo); + } + else + { + Methods.Add(commonName, new UserDataMethod(methodInfo)); + } + } + _operatorHandler = new UserDataTypeOperators(type); } private System.Type Type { get; } private Dictionary Variables { get; } private Dictionary Properties { get; } - private Dictionary Methods { get; } + private Dictionary Methods { get; } + private UserDataTypeOperators _operatorHandler { get; } public (LuaType Type, bool Failed) Get(object value, string member) { @@ -57,5 +71,10 @@ namespace Upsilon.BaseTypes.UserData return true; } + + public (LuaType Type, bool Failed) BinaryOperator(object value, OperatorType op, object value2) + { + return (null, true); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs b/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs new file mode 100644 index 0000000..f37aace --- /dev/null +++ b/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs @@ -0,0 +1,24 @@ +using System; +using System.Reflection; + +namespace Upsilon.BaseTypes.UserData +{ + public enum OperatorType + { + Addition, + } + + public class UserDataTypeOperators + { + private class OperatorKeyData + { + + } + + public UserDataTypeOperators(System.Type t) + { + var additionMethod = t.GetMethod("op_Addition", BindingFlags.Static | BindingFlags.Public); + Console.WriteLine(additionMethod); + } + } +} \ No newline at end of file diff --git a/UpsilonTests/TableTests.cs b/UpsilonTests/TableTests.cs index a75d507..23cca6a 100644 --- a/UpsilonTests/TableTests.cs +++ b/UpsilonTests/TableTests.cs @@ -1,4 +1,3 @@ -using System; using Upsilon.Evaluator; using Xunit; diff --git a/UpsilonTests/UserDataOperatorTests.cs b/UpsilonTests/UserDataOperatorTests.cs new file mode 100644 index 0000000..8afa63a --- /dev/null +++ b/UpsilonTests/UserDataOperatorTests.cs @@ -0,0 +1,63 @@ +using System; +using Upsilon.BaseTypes.UserData; +using Upsilon.Evaluator; +using Xunit; +// ReSharper disable UnusedMember.Local +// ReSharper disable ClassNeverInstantiated.Global + +namespace UpsilonTests +{ + public class UserDataOperatorTests : IClassFixture + { + public class UserDataOperatorTestsFixture : IDisposable + { + public UserDataOperatorTestsFixture() + { + UserDataTypeHandler.LoadType(); + } + + public void Dispose() + { + } + } + +#pragma warning disable 414, 649 + private class UserDataHelper + { + public UserDataHelper(double value) + { + Value = value; + } + + public double Value { get; } + + public static UserDataHelper operator +(UserDataHelper a, UserDataHelper b) + { + return new UserDataHelper(a.Value + b.Value); + } + + public static UserDataHelper operator +(UserDataHelper a, double b) + { + return new UserDataHelper(a.Value + b); + } + + } +#pragma warning restore 414, 649 + + + [Fact] + public void TestAddition() + { + const string input = @" +function add(o1, o2) + return o1 + o2 +end +"; + var script = new Script(input); + Assert.Empty(script.Diagnostics.Messages); + var o1 = new UserDataHelper(100); + var o2 = new UserDataHelper(215); + var result = script.EvaluateFunction("add", new[] {o1, o2}); + } + } +} \ No newline at end of file