Work on calling CSharp functions from Lua

This commit is contained in:
Deukhoofd 2018-11-20 12:55:41 +01:00
parent d1b1675bba
commit 066af47b5b
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
4 changed files with 114 additions and 28 deletions

View File

@ -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();
}
}
}

View File

@ -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;
}

View File

@ -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:
{

View File

@ -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);
}
}
}