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.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Evaluator; using Upsilon.Evaluator;
namespace Upsilon.BaseTypes 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 Type Type => Type.Function;
public override object ToCSharpObject() public override object ToCSharpObject()
{ {
return this; 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 ImmutableArray<VariableSymbol> Parameters { get; }
public BoundBlockStatement Block { get; }
public EvaluationScope EvaluationScope { 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; Type = type;
Variables = type.GetFields().ToDictionary(x => x.Name, x => x); Variables = type.GetFields().ToDictionary(x => x.Name, x => x);
Properties = type.GetProperties().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 System.Type Type { get; }
private Dictionary<string, FieldInfo> Variables { get; } private Dictionary<string, FieldInfo> Variables { get; }
private Dictionary<string, PropertyInfo> Properties { get; } private Dictionary<string, PropertyInfo> Properties { get; }
private Dictionary<string, MethodInfo> Methods { get; }
public LuaType Get(object value, string member) public LuaType Get(object value, string member)
{ {
@ -29,6 +31,10 @@ namespace Upsilon.BaseTypes.UserData
{ {
return property.GetValue(value).ToLuaType(); return property.GetValue(value).ToLuaType();
} }
if (Methods.TryGetValue(member, out var method))
{
return new LuaMethodInfoFunction(method, value);
}
return null; return null;
} }

View File

@ -22,7 +22,7 @@ namespace Upsilon.Evaluator
Scope = new EvaluationScope(variables); Scope = new EvaluationScope(variables);
} }
private Evaluator(Diagnostics diagnostics, EvaluationScope parentScope) internal Evaluator(Diagnostics diagnostics, EvaluationScope parentScope)
{ {
_diagnostics = diagnostics; _diagnostics = diagnostics;
Scope = new EvaluationScope(parentScope); Scope = new EvaluationScope(parentScope);
@ -30,7 +30,7 @@ namespace Upsilon.Evaluator
public LuaType Evaluate(BoundScript e) public LuaType Evaluate(BoundScript e)
{ {
Evaluate(e.Statement); EvaluateNode(e.Statement);
if (_returnValue == null) if (_returnValue == null)
return _lastValue; return _lastValue;
return _returnValue; return _returnValue;
@ -38,12 +38,12 @@ namespace Upsilon.Evaluator
public LuaType Evaluate(BoundScript e, string functionName, ImmutableArray<LuaType> parameters) 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) if (!Scope.TryGet(functionName, out var statement) || statement.Type != Type.Function)
{ {
throw new ArgumentException(($"Function '{functionName}' could not be found")); throw new ArgumentException(($"Function '{functionName}' could not be found"));
} }
var function = (LuaFunction) statement; var function = (LuaInternalFunction) statement;
var innerEvaluator = new Evaluator(_diagnostics, Scope); var innerEvaluator = new Evaluator(_diagnostics, Scope);
for (var index = 0; index < parameters.Length; index++) for (var index = 0; index < parameters.Length; index++)
{ {
@ -52,11 +52,11 @@ namespace Upsilon.Evaluator
innerEvaluator.Scope.Set(parameterName, parameter); innerEvaluator.Scope.Set(parameterName, parameter);
} }
var result = innerEvaluator.Evaluate(function.Block); var result = innerEvaluator.EvaluateNode(function.Block);
return result; return result;
} }
private LuaType Evaluate(BoundNode b) internal LuaType EvaluateNode(BoundNode b)
{ {
switch (b.Kind) switch (b.Kind)
{ {
@ -126,7 +126,7 @@ namespace Upsilon.Evaluator
_lastValue = EvaluateExpression(e.Expression); _lastValue = EvaluateExpression(e.Expression);
} }
private LuaType EvaluateExpression(BoundExpression e) internal LuaType EvaluateExpression(BoundExpression e)
{ {
switch (e.Kind) switch (e.Kind)
{ {
@ -264,13 +264,13 @@ namespace Upsilon.Evaluator
private LuaType EvaluateBoundFunctionStatement(BoundFunctionExpression boundFunctionExpression) 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; return func;
} }
private LuaType EvaluateUnboundFunctionStatement(UnboundFunctionExpression unboundFunctionExpression) 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; return func;
} }
@ -281,16 +281,15 @@ namespace Upsilon.Evaluator
{ {
throw new Exception($"Variable is not a function."); throw new Exception($"Variable is not a function.");
} }
var ls = new List<LuaType>();
var innerEvaluator = new Evaluator(_diagnostics, function.EvaluationScope); foreach (var t in boundFunctionCallExpression.Parameters)
for (var i = 0; i < function.Parameters.Length; i++)
{ {
var parameterVariable = function.Parameters[i]; var evaluate = EvaluateExpression(t);
var parameterValue = innerEvaluator.EvaluateExpression(boundFunctionCallExpression.Parameters[i]); ls.Add(evaluate);
innerEvaluator.Scope.Set(parameterVariable, parameterValue);
} }
_lastValue = innerEvaluator.Evaluate(function.Block);
return _lastValue; var val = function.Run(_diagnostics, ls.ToArray());
return val;
} }
private void EvaluateReturnStatement(BoundReturnStatement b) private void EvaluateReturnStatement(BoundReturnStatement b)
@ -313,7 +312,7 @@ namespace Upsilon.Evaluator
case BoundKind.BoundAssignmentStatement: case BoundKind.BoundAssignmentStatement:
case BoundKind.BoundFunctionExpression: case BoundKind.BoundFunctionExpression:
case BoundKind.BoundFunctionAssignmentStatement: case BoundKind.BoundFunctionAssignmentStatement:
innerEvaluator.Evaluate(boundStatement); innerEvaluator.EvaluateNode(boundStatement);
break; break;
default: default:
{ {

View File

@ -2,6 +2,7 @@ using System;
using Upsilon.BaseTypes.UserData; using Upsilon.BaseTypes.UserData;
using Upsilon.Evaluator; using Upsilon.Evaluator;
using Xunit; using Xunit;
// ReSharper disable UnusedMember.Local
namespace UpsilonTests namespace UpsilonTests
{ {
@ -24,6 +25,18 @@ namespace UpsilonTests
{ {
public string FieldString = "TestField"; public string FieldString = "TestField";
public string FieldStringSet; 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 #pragma warning restore 414, 649
@ -60,5 +73,36 @@ end
Assert.Equal("Test", obj.FieldStringSet); 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);
}
} }
} }