diff --git a/Upsilon/BaseTypes/UserData/UserData.cs b/Upsilon/BaseTypes/UserData/UserData.cs index e2300ec..2230647 100644 --- a/Upsilon/BaseTypes/UserData/UserData.cs +++ b/Upsilon/BaseTypes/UserData/UserData.cs @@ -48,5 +48,10 @@ namespace Upsilon.BaseTypes.UserData { return _typeInfo.BinaryOperator(Value, par1, op, par2); } + + public (LuaType Type, bool Failed) UnaryOperator(LuaType par1, OperatorType op) + { + return _typeInfo.UnaryOperator(Value, par1, op); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataType.cs b/Upsilon/BaseTypes/UserData/UserDataType.cs index 02e25cf..d91ea5f 100644 --- a/Upsilon/BaseTypes/UserData/UserDataType.cs +++ b/Upsilon/BaseTypes/UserData/UserDataType.cs @@ -82,5 +82,15 @@ namespace Upsilon.BaseTypes.UserData return (method.Invoke(value, new[] {par1.ToCSharpObject(), par2.ToCSharpObject()}).ToLuaType(), false); } + public (LuaType Type, bool Failed) UnaryOperator(object value, LuaType par1, OperatorType op) + { + var method = OperatorHandler.GetUnaryOperator(op, par1.GetCSharpType()); + if (method == null) + { + return (new LuaNull(), true); + } + + return (method.Invoke(value, new[] {par1.ToCSharpObject()}).ToLuaType(), false); + } } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundUnaryOperator.cs b/Upsilon/Binder/BoundUnaryOperator.cs index 2caa6e0..55cb7d2 100644 --- a/Upsilon/Binder/BoundUnaryOperator.cs +++ b/Upsilon/Binder/BoundUnaryOperator.cs @@ -50,6 +50,8 @@ namespace Upsilon.Binder throw new Exception("Unknown unary operator token: " + operatorToken); } + if (inType == Type.Unknown) + return _operators.FirstOrDefault(op => op.Kind == kind); return _operators.FirstOrDefault(op => op.Kind == kind && op.InType == inType); } } diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index f67c26d..43e2063 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -165,9 +165,28 @@ namespace Upsilon.Evaluator case BoundUnaryOperator.OperatorKind.Identity: return operand; case BoundUnaryOperator.OperatorKind.Negation: - return -((Number)operand); + if (operand.Type == Type.Number) + return -((Number)operand); + else if (operand.Type == Type.UserData) + { + var ud = (UserData) operand; + var (type, failed) = ud.UnaryOperator(operand, OperatorType.UnaryNegation); + if (failed) goto default; + return type; + } + goto default; case BoundUnaryOperator.OperatorKind.LogicalNegation: - return !(LuaBoolean) operand; + if (operand.Type == Type.Boolean) + return !(LuaBoolean) operand; + else if (operand.Type == Type.UserData) + { + var ud = (UserData) operand; + var (type, failed) = ud.UnaryOperator(operand, OperatorType.LogicalNot); + if (failed) goto default; + return type; + } + goto default; + default: throw new Exception("Invalid Unary Operator: " + e.Operator.Kind); } diff --git a/UpsilonTests/UserDataOperatorTests.cs b/UpsilonTests/UserDataOperatorTests.cs index 04f0e2a..17a8925 100644 --- a/UpsilonTests/UserDataOperatorTests.cs +++ b/UpsilonTests/UserDataOperatorTests.cs @@ -46,6 +46,11 @@ namespace UpsilonTests return new UserDataHelper(a.Value - b.Value); } + public static UserDataHelper operator -(UserDataHelper a) + { + return new UserDataHelper(-a.Value); + } + public static UserDataHelper operator *(UserDataHelper a, UserDataHelper b) { return new UserDataHelper(a.Value * b.Value); @@ -145,5 +150,21 @@ end Assert.Equal(10, result.Value); } + [Fact] + public void TestNegation() + { + const string input = @" +function negate(o1) + return -o1 +end +"; + var script = new Script(input); + Assert.Empty(script.Diagnostics.Messages); + var o1 = new UserDataHelper(100); + var result = script.EvaluateFunction("negate", new object[] {o1}); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(-100, result.Value); + } + } } \ No newline at end of file