Work on calling CSharp functions from Lua
This commit is contained in:
parent
d1b1675bba
commit
066af47b5b
|
@ -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<VariableSymbol> 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<VariableSymbol> Parameters { get; }
|
||||
public BoundBlockStatement Block { get; }
|
||||
public EvaluationScope EvaluationScope { get; }
|
||||
|
||||
public LuaInternalFunction(ImmutableArray<VariableSymbol> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<string, FieldInfo> Variables { get; }
|
||||
private Dictionary<string, PropertyInfo> Properties { get; }
|
||||
private Dictionary<string, MethodInfo> 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;
|
||||
}
|
||||
|
|
|
@ -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<LuaType> 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<LuaType>();
|
||||
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:
|
||||
{
|
||||
|
|
|
@ -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<long>("test", new[] {obj});
|
||||
Assert.Empty(script.Diagnostics.Messages);
|
||||
Assert.Equal(result, 120);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue