Work on standard libraries.

- Allows Standard Libraries to work with actual luatypes, to prevent constant back and forth casting
- adds ipairs function, doesn't do anything except maintain compatibility with lua
- several tests
This commit is contained in:
Deukhoofd 2018-11-24 13:35:40 +01:00
parent 806b3d5689
commit c63df3c941
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
13 changed files with 88 additions and 42 deletions

View File

@ -1,3 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -53,20 +56,32 @@ namespace Upsilon.BaseTypes
internal class ScriptMethodInfoFunction : ScriptFunction internal class ScriptMethodInfoFunction : ScriptFunction
{ {
public ScriptMethodInfoFunction(UserDataMethod method, object o) public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation)
{ {
_method = method; _method = method;
_object = o; _object = o;
_directTypeManipulation = directTypeManipulation;
} }
private readonly UserDataMethod _method; private readonly UserDataMethod _method;
private readonly object _object; private readonly object _object;
private readonly bool _directTypeManipulation;
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables) public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables)
{ {
var types = variables.Select(x => x.GetCSharpType()); var types = _directTypeManipulation
var method = _method.GetMethod(types.ToArray()); ? variables.Select(x => x.GetType()).ToArray()
var objects = variables.Select(x => x.ToCSharpObject()).ToList(); : variables.Select(x => x.GetCSharpType()).ToArray();
var method = _method.GetMethod(types);
if (method == null)
{
throw new Exception(
$"No valid function found on type '{_object.GetType()}' with name '{_method.Name}' " +
$"and parameter types: {string.Join(", ", types.Select(x => $"'{x.Name}'"))}");
}
var objects = _directTypeManipulation
? variables.Select(x => (object) x).ToList()
: variables.Select(x => x.ToCSharpObject()).ToList();
var pars = method.GetParameters(); var pars = method.GetParameters();
if (pars.Length != objects.Count) if (pars.Length != objects.Count)
{ {
@ -78,7 +93,10 @@ namespace Upsilon.BaseTypes
try try
{ {
return method.Invoke(_object, objects.ToArray()).ToLuaType(); var result = method.Invoke(_object, objects.ToArray());
if (_directTypeManipulation)
return (ScriptType)result;
return result.ToScriptType();
} }
catch (TargetInvocationException e) catch (TargetInvocationException e)
{ {

View File

@ -6,7 +6,7 @@ namespace Upsilon.BaseTypes
{ {
internal static class TypeConversion internal static class TypeConversion
{ {
public static ScriptType ToLuaType(this object o) public static ScriptType ToScriptType(this object o)
{ {
if (o is ScriptType type) return type; if (o is ScriptType type) return type;
switch (o) switch (o)

View File

@ -18,7 +18,7 @@ namespace Upsilon.BaseTypes.UserData
var key = index.ToCSharpObject(); var key = index.ToCSharpObject();
if (Dictionary.Contains(key)) if (Dictionary.Contains(key))
{ {
return Dictionary[key].ToLuaType(); return Dictionary[key].ToScriptType();
} }
//TODO: log error //TODO: log error
return new ScriptNull(); return new ScriptNull();

View File

@ -32,7 +32,7 @@ namespace Upsilon.BaseTypes.UserData
{ {
return null; return null;
} }
return List[i].ToLuaType(); return List[i].ToScriptType();
} }
public void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value) public void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value)

View File

@ -30,10 +30,12 @@ namespace Upsilon.BaseTypes.UserData
public bool IsOptional { get; } public bool IsOptional { get; }
} }
public string Name { get; }
private List<UserDataMethodPart> MethodParts { get; } private List<UserDataMethodPart> MethodParts { get; }
public UserDataMethod(MethodInfo method) public UserDataMethod(MethodInfo method)
{ {
Name = method.Name;
var part = new UserDataMethodPart(method); var part = new UserDataMethodPart(method);
MethodParts = new List<UserDataMethodPart>() MethodParts = new List<UserDataMethodPart>()
{ {

View File

@ -40,15 +40,15 @@ namespace Upsilon.BaseTypes.UserData
member = member.ToLowerInvariant(); member = member.ToLowerInvariant();
if (Variables.TryGetValue(member, out var info)) if (Variables.TryGetValue(member, out var info))
{ {
return (info.GetValue(value).ToLuaType(), false, null); return (info.GetValue(value).ToScriptType(), false, null);
} }
if (Properties.TryGetValue(member, out var property)) if (Properties.TryGetValue(member, out var property))
{ {
return (property.GetValue(value).ToLuaType(), false, null); return (property.GetValue(value).ToScriptType(), false, null);
} }
if (Methods.TryGetValue(member, out var method)) if (Methods.TryGetValue(member, out var method))
{ {
return (new ScriptMethodInfoFunction(method, value), false, null); return (new ScriptMethodInfoFunction(method, value, false), false, null);
} }
return (null, true, $"Can't find public member '{member}' on type '{Type}'."); return (null, true, $"Can't find public member '{member}' on type '{Type}'.");
@ -85,7 +85,7 @@ namespace Upsilon.BaseTypes.UserData
return (new ScriptNull(), true); return (new ScriptNull(), true);
} }
return (method.Invoke(value, new[] {par1.ToCSharpObject(), par2.ToCSharpObject()}).ToLuaType(), false); return (method.Invoke(value, new[] {par1.ToCSharpObject(), par2.ToCSharpObject()}).ToScriptType(), false);
} }
public (ScriptType Type, bool Failed) UnaryOperator(object value, ScriptType par1, OperatorType op) public (ScriptType Type, bool Failed) UnaryOperator(object value, ScriptType par1, OperatorType op)
{ {
@ -95,7 +95,7 @@ namespace Upsilon.BaseTypes.UserData
return (new ScriptNull(), true); return (new ScriptNull(), true);
} }
return (method.Invoke(value, new[] {par1.ToCSharpObject()}).ToLuaType(), false); return (method.Invoke(value, new[] {par1.ToCSharpObject()}).ToScriptType(), false);
} }
} }
} }

View File

@ -476,7 +476,7 @@ namespace Upsilon.Evaluator
scope = scopeOwner.EvaluationScope; scope = scopeOwner.EvaluationScope;
} }
return indexable.Get(_diagnostics, e.Span, e.Index.ToLuaType(), scope); return indexable.Get(_diagnostics, e.Span, e.Index.ToScriptType(), scope);
} }
private void EvaluateTableAssignmentStatement(BoundTableAssigmentStatement e) private void EvaluateTableAssignmentStatement(BoundTableAssigmentStatement e)
@ -491,7 +491,7 @@ namespace Upsilon.Evaluator
else else
{ {
table = EvaluateExpression(((BoundFullStopIndexExpression)e.TableIndexExpression).Expression); table = EvaluateExpression(((BoundFullStopIndexExpression)e.TableIndexExpression).Expression);
index = ((BoundFullStopIndexExpression) e.TableIndexExpression).Index.ToLuaType(); index = ((BoundFullStopIndexExpression) e.TableIndexExpression).Index.ToScriptType();
} }
var value = EvaluateExpression(e.Value); var value = EvaluateExpression(e.Value);
@ -499,7 +499,7 @@ namespace Upsilon.Evaluator
{ {
throw new Exception("Cant assign to that"); throw new Exception("Cant assign to that");
} }
indexable.Set(_diagnostics, e.Span, index.ToString().ToLuaType(), value); indexable.Set(_diagnostics, e.Span, index.ToString().ToScriptType(), value);
} }
private void EvaluateNumericForStatement(BoundNumericForStatement e) private void EvaluateNumericForStatement(BoundNumericForStatement e)
@ -550,7 +550,7 @@ namespace Upsilon.Evaluator
{ {
var (key, value) = enumerator.Current; var (key, value) = enumerator.Current;
if (e.Variables[0].Name != "_") if (e.Variables[0].Name != "_")
innerEvaluator.Scope.CreateLocal(e.Variables[0], key.ToLuaType()); innerEvaluator.Scope.CreateLocal(e.Variables[0], key.ToScriptType());
if (e.Variables[1].Name != "_") if (e.Variables[1].Name != "_")
innerEvaluator.Scope.CreateLocal(e.Variables[1], value); innerEvaluator.Scope.CreateLocal(e.Variables[1], value);
innerEvaluator.EvaluateBoundBlockStatement((BoundBlockStatement) e.Block); innerEvaluator.EvaluateBoundBlockStatement((BoundBlockStatement) e.Block);

View File

@ -69,7 +69,7 @@ namespace Upsilon.Evaluator
{ {
foreach (var parameter in parameters) foreach (var parameter in parameters)
{ {
luaParameters.Add(parameter.ToLuaType()); luaParameters.Add(parameter.ToScriptType());
} }
} }
return Convert(Evaluator.Evaluate(Bind(), functionName, luaParameters.ToImmutable())); return Convert(Evaluator.Evaluate(Bind(), functionName, luaParameters.ToImmutable()));
@ -103,7 +103,7 @@ namespace Upsilon.Evaluator
{ {
foreach (var parameter in parameters) foreach (var parameter in parameters)
{ {
luaParameters.Add(parameter.ToLuaType()); luaParameters.Add(parameter.ToScriptType());
} }
} }
return Convert<T>(Evaluator.Evaluate(Bind(), functionName, luaParameters.ToImmutable())); return Convert<T>(Evaluator.Evaluate(Bind(), functionName, luaParameters.ToImmutable()));

View File

@ -12,36 +12,31 @@ namespace Upsilon.StandardLibraries
{ {
internal class BasicFunctions : LuaLibrary internal class BasicFunctions : LuaLibrary
{ {
[LuaFunction("assert")] [StandardLibraryScriptFunction("assert")]
public void Assert(bool boolean, string message = null) public void Assert(ScriptBoolean boolean, ScriptString message = null)
{ {
if (!boolean) if (!boolean)
{ {
Error(message ?? "assertion failed!"); Error(message ?? new ScriptString("assertion failed!"));
} }
} }
[LuaFunction("error")] [StandardLibraryScriptFunction("error")]
public void Error(string message) public void Error(ScriptString message)
{ {
throw new Exception(message); throw new Exception(message.Value);
} }
/* [StandardLibraryScriptFunction("ipairs")]
[LuaFunction("ipairs")] public IIterable Pairs(IIterable table)
public ScriptType Pairs(IIterable table)
{ {
if (!table.Next(out var key, out var next)) return table;
{
return new ScriptNull();
} }
return new SimpleScriptTable(new List<ScriptType>() {key, next}); [StandardLibraryScriptFunction("tonumber")]
}*/ public ScriptNumber ToNumber(ScriptString obj)
[LuaFunction("tonumber")]
public ScriptNumber ToNumber(string str)
{ {
var str = obj.Value;
if (str.Contains(".")) if (str.Contains("."))
{ {
return new ScriptNumberDouble(double.Parse(str)); return new ScriptNumberDouble(double.Parse(str));

View File

@ -13,10 +13,10 @@ namespace Upsilon.StandardLibraries
var methods = GetType().GetMethods(); var methods = GetType().GetMethods();
foreach (var methodInfo in methods) foreach (var methodInfo in methods)
{ {
var attr = methodInfo.GetCustomAttribute<LuaFunctionAttribute>(); var attr = methodInfo.GetCustomAttribute<StandardLibraryScriptFunctionAttribute>();
if (attr != null) if (attr != null)
{ {
dictionary.Add(attr.Name, new ScriptMethodInfoFunction(new UserDataMethod(methodInfo), this)); dictionary.Add(attr.Name, new ScriptMethodInfoFunction(new UserDataMethod(methodInfo), this, true));
} }
} }

View File

@ -3,9 +3,9 @@ using System;
namespace Upsilon.StandardLibraries namespace Upsilon.StandardLibraries
{ {
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class LuaFunctionAttribute : Attribute public class StandardLibraryScriptFunctionAttribute : Attribute
{ {
public LuaFunctionAttribute(string name) public StandardLibraryScriptFunctionAttribute(string name)
{ {
Name = name; Name = name;
} }

View File

@ -54,7 +54,7 @@ namespace Upsilon.StandardLibraries
public static void RegisterStaticVariable(string name, object value) public static void RegisterStaticVariable(string name, object value)
{ {
var luaVariable = value.ToLuaType(); var luaVariable = value.ToScriptType();
var varSymbol = new VariableSymbol(name, luaVariable.Type, false); var varSymbol = new VariableSymbol(name, luaVariable.Type, false);
BoundScope.AssignToNearest(varSymbol); BoundScope.AssignToNearest(varSymbol);
Scope.AssignToNearest(varSymbol, luaVariable); Scope.AssignToNearest(varSymbol, luaVariable);

View File

@ -25,5 +25,36 @@ namespace UpsilonTests.StandardLibraryTests
Assert.Equal("test_error", e.Message); Assert.Equal("test_error", e.Message);
} }
[Fact]
public void IPairsTest()
{
const string input = @"
arr = {100, 56, 28}
value = 0
for key, val in ipairs(arr) do
value = value + val
end
return value
";
var script = new Script(input, BoundScope, StaticScope);
Assert.Empty(script.Diagnostics.Messages);
var result = script.Evaluate<long>();
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(184, result);
}
[Fact]
public void ToNumberTest()
{
const string input = @"
return tonumber(""100"")
";
var script = new Script(input, BoundScope, StaticScope);
Assert.Empty(script.Diagnostics.Messages);
var result = script.Evaluate<long>();
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(100, result);
}
} }
} }