diff --git a/Upsilon/BaseTypes/LuaFunction.cs b/Upsilon/BaseTypes/LuaFunction.cs index fe98861..1ba0dd1 100644 --- a/Upsilon/BaseTypes/LuaFunction.cs +++ b/Upsilon/BaseTypes/LuaFunction.cs @@ -1,27 +1,64 @@ using System.Collections.Immutable; +using System.Linq; +using System.Reflection; using Upsilon.Binder; using Upsilon.Evaluator; namespace Upsilon.BaseTypes { - internal class LuaFunction : LuaType, IScopeOwner + internal abstract class LuaFunction : LuaType { - public LuaFunction(ImmutableArray parameters, BoundBlockStatement block, - EvaluationScope parentScope) - { - Parameters = parameters; - Block = block; - EvaluationScope = new EvaluationScope(parentScope); - } - public override Type Type => Type.Function; public override object ToCSharpObject() { return this; } + public abstract LuaType Run(Diagnostics diagnostics, LuaType[] variables); + } + + internal class LuaInternalFunction : LuaFunction, IScopeOwner + { + public BoundBlockStatement Block { get; } public ImmutableArray Parameters { get; } - public BoundBlockStatement Block { get; } public EvaluationScope EvaluationScope { get; } + + public LuaInternalFunction(ImmutableArray parameters, BoundBlockStatement block, + EvaluationScope evaluationScope) + { + Parameters = parameters; + Block = block; + EvaluationScope = evaluationScope; + } + + public override LuaType Run(Diagnostics diagnostics, LuaType[] variables) + { + var innerEvaluator = new Evaluator.Evaluator(diagnostics, EvaluationScope); + for (var i = 0; i < Parameters.Length; i++) + { + var parameterVariable = Parameters[i]; + var parameterValue = variables[i]; + innerEvaluator.Scope.Set(parameterVariable, parameterValue); + } + return innerEvaluator.EvaluateNode(Block); + } + } + + internal class LuaMethodInfoFunction : LuaFunction + { + public LuaMethodInfoFunction(MethodInfo method, object o) + { + _method = method; + _object = o; + } + + private readonly MethodInfo _method; + private readonly object _object; + + public override LuaType Run(Diagnostics diagnostics, LuaType[] variables) + { + var objects = variables.Select(x => x.ToCSharpObject()); + return _method.Invoke(_object, objects.ToArray()).ToLuaType(); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataType.cs b/Upsilon/BaseTypes/UserData/UserDataType.cs index 7b2a11f..f6e03f2 100644 --- a/Upsilon/BaseTypes/UserData/UserDataType.cs +++ b/Upsilon/BaseTypes/UserData/UserDataType.cs @@ -11,11 +11,13 @@ namespace Upsilon.BaseTypes.UserData Type = type; Variables = type.GetFields().ToDictionary(x => x.Name, x => x); Properties = type.GetProperties().ToDictionary(x => x.Name, x => x); + Methods = type.GetMethods().ToDictionary(x => x.Name, x => x); } private System.Type Type { get; } private Dictionary Variables { get; } private Dictionary Properties { get; } + private Dictionary Methods { get; } public LuaType Get(object value, string member) { @@ -29,6 +31,10 @@ namespace Upsilon.BaseTypes.UserData { return property.GetValue(value).ToLuaType(); } + if (Methods.TryGetValue(member, out var method)) + { + return new LuaMethodInfoFunction(method, value); + } return null; } diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 0b739af..02b1659 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -22,7 +22,7 @@ namespace Upsilon.Evaluator Scope = new EvaluationScope(variables); } - private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope) + internal Evaluator(Diagnostics diagnostics, EvaluationScope parentScope) { _diagnostics = diagnostics; Scope = new EvaluationScope(parentScope); @@ -30,7 +30,7 @@ namespace Upsilon.Evaluator public LuaType Evaluate(BoundScript e) { - Evaluate(e.Statement); + EvaluateNode(e.Statement); if (_returnValue == null) return _lastValue; return _returnValue; @@ -38,12 +38,12 @@ namespace Upsilon.Evaluator public LuaType Evaluate(BoundScript e, string functionName, ImmutableArray parameters) { - Evaluate(e.Statement); + EvaluateNode(e.Statement); if (!Scope.TryGet(functionName, out var statement) || statement.Type != Type.Function) { throw new ArgumentException(($"Function '{functionName}' could not be found")); } - var function = (LuaFunction) statement; + var function = (LuaInternalFunction) statement; var innerEvaluator = new Evaluator(_diagnostics, Scope); for (var index = 0; index < parameters.Length; index++) { @@ -52,11 +52,11 @@ namespace Upsilon.Evaluator innerEvaluator.Scope.Set(parameterName, parameter); } - var result = innerEvaluator.Evaluate(function.Block); + var result = innerEvaluator.EvaluateNode(function.Block); return result; } - private LuaType Evaluate(BoundNode b) + internal LuaType EvaluateNode(BoundNode b) { switch (b.Kind) { @@ -126,7 +126,7 @@ namespace Upsilon.Evaluator _lastValue = EvaluateExpression(e.Expression); } - private LuaType EvaluateExpression(BoundExpression e) + internal LuaType EvaluateExpression(BoundExpression e) { switch (e.Kind) { @@ -264,13 +264,13 @@ namespace Upsilon.Evaluator private LuaType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression) { - var func = new LuaFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope); + var func = new LuaInternalFunction(boundFunctionExpression.Parameters, boundFunctionExpression.Block, Scope); return func; } private LuaType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression) { - var func = new LuaFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope); + var func = new LuaInternalFunction(unboundFunctionExpression.Parameters, unboundFunctionExpression.Block, Scope); return func; } @@ -281,16 +281,15 @@ namespace Upsilon.Evaluator { throw new Exception($"Variable is not a function."); } - - var innerEvaluator = new Evaluator(_diagnostics, function.EvaluationScope); - for (var i = 0; i < function.Parameters.Length; i++) + var ls = new List(); + foreach (var t in boundFunctionCallExpression.Parameters) { - var parameterVariable = function.Parameters[i]; - var parameterValue = innerEvaluator.EvaluateExpression(boundFunctionCallExpression.Parameters[i]); - innerEvaluator.Scope.Set(parameterVariable, parameterValue); + var evaluate = EvaluateExpression(t); + ls.Add(evaluate); } - _lastValue = innerEvaluator.Evaluate(function.Block); - return _lastValue; + + var val = function.Run(_diagnostics, ls.ToArray()); + return val; } private void EvaluateReturnStatement(BoundReturnStatement b) @@ -313,7 +312,7 @@ namespace Upsilon.Evaluator case BoundKind.BoundAssignmentStatement: case BoundKind.BoundFunctionExpression: case BoundKind.BoundFunctionAssignmentStatement: - innerEvaluator.Evaluate(boundStatement); + innerEvaluator.EvaluateNode(boundStatement); break; default: { diff --git a/UpsilonTests/UserDataTests.cs b/UpsilonTests/UserDataTests.cs index c390dd8..6fcf411 100644 --- a/UpsilonTests/UserDataTests.cs +++ b/UpsilonTests/UserDataTests.cs @@ -2,6 +2,7 @@ using System; using Upsilon.BaseTypes.UserData; using Upsilon.Evaluator; using Xunit; +// ReSharper disable UnusedMember.Local namespace UpsilonTests { @@ -24,6 +25,18 @@ namespace UpsilonTests { public string FieldString = "TestField"; public string FieldStringSet; + + public bool TestMethodHasRun { get; private set; } + public void TestMethod() + { + TestMethodHasRun = true; + } + + public long Add(long a, long b) + { + return a + b; + } + } #pragma warning restore 414, 649 @@ -60,5 +73,36 @@ end Assert.Equal("Test", obj.FieldStringSet); } + [Fact] + public void RunVoidMethod() + { + var obj = new UserDataHelper(); + const string input = @" +function test(o) + o[""TestMethod""]() +end +"; + var script = new Script(input); + Assert.Empty(script.Diagnostics.Messages); + script.EvaluateFunction("test", new[] {obj}); + Assert.Empty(script.Diagnostics.Messages); + Assert.True(obj.TestMethodHasRun); + } + + [Fact] + public void ReturnMethodWithParameters() + { + var obj = new UserDataHelper(); + const string input = @" +function test(o) + return o[""Add""](100, 20) +end +"; + var script = new Script(input); + Assert.Empty(script.Diagnostics.Messages); + var result = script.EvaluateFunction("test", new[] {obj}); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(result, 120); + } } } \ No newline at end of file