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.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 ImmutableArray<VariableSymbol> Parameters { get; }
|
public abstract LuaType Run(Diagnostics diagnostics, LuaType[] variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LuaInternalFunction : LuaFunction, IScopeOwner
|
||||||
|
{
|
||||||
public BoundBlockStatement Block { get; }
|
public BoundBlockStatement Block { get; }
|
||||||
|
public ImmutableArray<VariableSymbol> Parameters { 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue