Special UserData types for ILists(includes arrays) and IDictionaries

This commit is contained in:
Deukhoofd 2018-11-21 14:49:59 +01:00
parent 1d24be85d6
commit 105c40bc05
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
11 changed files with 242 additions and 24 deletions

View File

@ -5,8 +5,8 @@ namespace Upsilon.BaseTypes
{ {
internal interface IIndexable internal interface IIndexable
{ {
LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope); LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope);
void Set(Diagnostics diagnostics, TextSpan span, string s, LuaType value); void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value);
} }
internal interface IScopeOwner internal interface IScopeOwner

View File

@ -84,13 +84,27 @@ namespace Upsilon.BaseTypes
return Value; return Value;
} }
public LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope) public LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope)
{ {
var i = int.Parse(s); int i;
if (index.Type == Type.String)
{
var str = (LuaString)index;
i = int.Parse(str.Value);
}
else if (index.Type == Type.Number)
{
var ind = (Number.NumberLong) index;
i = (int) ind.Value;
}
else
{
return null;
}
return new LuaString(Value[i - 1].ToString()); return new LuaString(Value[i - 1].ToString());
} }
public void Set(Diagnostics diagnostics, TextSpan span, string s, LuaType value) public void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }

View File

@ -27,8 +27,9 @@ namespace Upsilon.BaseTypes
return typeof(Dictionary<string, object>); return typeof(Dictionary<string, object>);
} }
public LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope) public LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope)
{ {
var s = index.ToString();
if (EvaluationScope.TryGet(s, out var o)) if (EvaluationScope.TryGet(s, out var o))
{ {
return o; return o;
@ -37,8 +38,9 @@ namespace Upsilon.BaseTypes
return new LuaNull(); return new LuaNull();
} }
public void Set(Diagnostics diagnostics, TextSpan span, string s, LuaType value) public void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value)
{ {
var s = index.ToString();
EvaluationScope.Set(new VariableSymbol(s, value.Type, false), value); EvaluationScope.Set(new VariableSymbol(s, value.Type, false), value);
} }
} }

View File

@ -1,4 +1,6 @@
using System.Collections;
using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.Number;
using Upsilon.BaseTypes.UserData;
namespace Upsilon.BaseTypes namespace Upsilon.BaseTypes
{ {
@ -23,8 +25,21 @@ namespace Upsilon.BaseTypes
case null: case null:
return new LuaNull(); return new LuaNull();
default: default:
return new UserData.UserData(o); return ConvertUserData(o);
} }
} }
private static LuaType ConvertUserData(object o)
{
if (o is IList ls)
{
return new ListUserData(ls);
}
if (o is IDictionary dic)
{
return new DictionaryUserData(dic);
}
return new GenericUserData(o);
}
} }
} }

View File

@ -0,0 +1,51 @@
using System.Collections;
using Upsilon.Evaluator;
using Upsilon.Text;
namespace Upsilon.BaseTypes.UserData
{
internal class DictionaryUserData : LuaType, IUserData
{
public IDictionary Dictionary { get; }
public DictionaryUserData(IDictionary dictionary)
{
Dictionary = dictionary;
}
public LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope)
{
var key = index.ToCSharpObject();
if (Dictionary.Contains(key))
{
return Dictionary[key].ToLuaType();
}
//TODO: log error
return new LuaNull();
}
public void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value)
{
var key = index.ToCSharpObject();
if (Dictionary.Contains(key))
{
Dictionary[key] = value.ToCSharpObject();
}
else
{
Dictionary.Add(key, value.ToCSharpObject());
}
}
public override Type Type => Type.UserData;
public override object ToCSharpObject()
{
return Dictionary;
}
public override System.Type GetCSharpType()
{
return Dictionary.GetType();
}
}
}

View File

@ -3,16 +3,16 @@ using Upsilon.Text;
namespace Upsilon.BaseTypes.UserData namespace Upsilon.BaseTypes.UserData
{ {
internal class UserData : LuaType, IIndexable internal class GenericUserData : LuaType, IUserData
{ {
public UserData(object o) public GenericUserData(object dictionary)
{ {
Value = o; Value = dictionary;
_typeInfo = UserDataTypeHandler.GetTypeInfo(o.GetType()); _typeInfo = UserDataTypeHandler.GetTypeInfo(dictionary.GetType());
} }
public override Type Type => Type.UserData; public override Type Type => Type.UserData;
private object Value { get; } protected virtual object Value { get; }
private readonly UserDataType _typeInfo; private readonly UserDataType _typeInfo;
public override object ToCSharpObject() public override object ToCSharpObject()
@ -25,8 +25,9 @@ namespace Upsilon.BaseTypes.UserData
return Value.GetType(); return Value.GetType();
} }
public LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope) public virtual LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope)
{ {
var s = index.ToCSharpObject().ToString();
var (type, failed, error) = _typeInfo.Get(Value, s); var (type, failed, error) = _typeInfo.Get(Value, s);
if (failed) if (failed)
{ {
@ -35,8 +36,9 @@ namespace Upsilon.BaseTypes.UserData
return type; return type;
} }
public void Set(Diagnostics diagnostics, TextSpan span, string s, LuaType value) public virtual void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value)
{ {
var s = index.ToCSharpObject().ToString();
var (failed, error) = _typeInfo.Set(Value, s, value); var (failed, error) = _typeInfo.Set(Value, s, value);
if (failed) if (failed)
{ {

View File

@ -0,0 +1,9 @@
namespace Upsilon.BaseTypes.UserData
{
internal interface IUserData : IIndexable
{
object ToCSharpObject();
System.Type GetCSharpType();
}
}

View File

@ -0,0 +1,52 @@
using System.Collections;
using Upsilon.Evaluator;
using Upsilon.Text;
namespace Upsilon.BaseTypes.UserData
{
internal class ListUserData : LuaType, IUserData
{
private IList List { get; }
public ListUserData(IList list)
{
List = list;
}
public LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope)
{
int i;
if (index.Type == Type.String)
{
var str = (LuaString)index;
i = int.Parse(str.Value) - 1;
}
else if (index.Type == Type.Number)
{
var ind = (Number.NumberLong) index;
i = (int) ind.Value - 1;
}
else
{
return null;
}
return List[i].ToLuaType();
}
public void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value)
{
throw new System.NotImplementedException();
}
public override Type Type => Type.UserData;
public override object ToCSharpObject()
{
return List;
}
public override System.Type GetCSharpType()
{
return List.GetType();
}
}
}

View File

@ -169,7 +169,7 @@ namespace Upsilon.Evaluator
return -((Number)operand); return -((Number)operand);
else if (operand.Type == Type.UserData) else if (operand.Type == Type.UserData)
{ {
var ud = (UserData) operand; var ud = (GenericUserData) operand;
var (type, failed) = ud.UnaryOperator(operand, OperatorType.UnaryNegation); var (type, failed) = ud.UnaryOperator(operand, OperatorType.UnaryNegation);
if (failed) goto default; if (failed) goto default;
return type; return type;
@ -180,7 +180,7 @@ namespace Upsilon.Evaluator
return !(LuaBoolean) operand; return !(LuaBoolean) operand;
else if (operand.Type == Type.UserData) else if (operand.Type == Type.UserData)
{ {
var ud = (UserData) operand; var ud = (GenericUserData) operand;
var (type, failed) = ud.UnaryOperator(operand, OperatorType.LogicalNot); var (type, failed) = ud.UnaryOperator(operand, OperatorType.LogicalNot);
if (failed) goto default; if (failed) goto default;
return type; return type;
@ -207,7 +207,7 @@ namespace Upsilon.Evaluator
} }
else if (left.Type == Type.UserData) else if (left.Type == Type.UserData)
{ {
var ud = (UserData) left; var ud = (GenericUserData) left;
var (type, failed) = ud.BinaryOperator(left, OperatorType.Addition, right); var (type, failed) = ud.BinaryOperator(left, OperatorType.Addition, right);
if (failed) goto default; if (failed) goto default;
return type; return type;
@ -220,7 +220,7 @@ namespace Upsilon.Evaluator
} }
else if (left.Type == Type.UserData) else if (left.Type == Type.UserData)
{ {
var ud = (UserData) left; var ud = (GenericUserData) left;
var (type, failed) = ud.BinaryOperator(left, OperatorType.Subtraction, right); var (type, failed) = ud.BinaryOperator(left, OperatorType.Subtraction, right);
if (failed) goto default; if (failed) goto default;
return type; return type;
@ -233,7 +233,7 @@ namespace Upsilon.Evaluator
} }
else if (left.Type == Type.UserData) else if (left.Type == Type.UserData)
{ {
var ud = (UserData) left; var ud = (GenericUserData) left;
var (type, failed) = ud.BinaryOperator(left, OperatorType.Multiplication, right); var (type, failed) = ud.BinaryOperator(left, OperatorType.Multiplication, right);
if (failed) goto default; if (failed) goto default;
return type; return type;
@ -246,7 +246,7 @@ namespace Upsilon.Evaluator
} }
else if (left.Type == Type.UserData) else if (left.Type == Type.UserData)
{ {
var ud = (UserData) left; var ud = (GenericUserData) left;
var (type, failed) = ud.BinaryOperator(left, OperatorType.Division, right); var (type, failed) = ud.BinaryOperator(left, OperatorType.Division, right);
if (failed) goto default; if (failed) goto default;
return type; return type;
@ -406,7 +406,7 @@ namespace Upsilon.Evaluator
scope = scopeOwner.EvaluationScope; scope = scopeOwner.EvaluationScope;
} }
var indexer = EvaluateExpression(e.Index); var indexer = EvaluateExpression(e.Index);
return indexable.Get(_diagnostics, e.Span, indexer.ToString(), scope); return indexable.Get(_diagnostics, e.Span, indexer, scope);
} }
private LuaType EvaluateFullStopIndexExpression(BoundFullStopIndexExpression e) private LuaType EvaluateFullStopIndexExpression(BoundFullStopIndexExpression e)
{ {
@ -422,7 +422,7 @@ namespace Upsilon.Evaluator
scope = scopeOwner.EvaluationScope; scope = scopeOwner.EvaluationScope;
} }
return indexable.Get(_diagnostics, e.Span, e.Index, scope); return indexable.Get(_diagnostics, e.Span, e.Index.ToLuaType(), scope);
} }
private void EvaluateTableAssignmentStatement(BoundTableAssigmentStatement e) private void EvaluateTableAssignmentStatement(BoundTableAssigmentStatement e)
@ -445,7 +445,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(), value); indexable.Set(_diagnostics, e.Span, index.ToString().ToLuaType(), value);
} }
} }
} }

View File

@ -0,0 +1,31 @@
using System.Collections.Generic;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
{
public class UserDataDictionaryTests
{
[Fact]
public void BasicStringKeyed()
{
var arr = new Dictionary<string, int>
{
{"test", 100},
{"1234", 90},
{"here", 1683},
};
const string input = @"
function getValue(arr)
return arr[""here""]
end
";
var script = new Script(input);
Assert.Empty(script.Diagnostics.Messages);
var evaluated = script.EvaluateFunction<long>("getValue", new[] {arr});
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(1683, evaluated);
}
}
}

View File

@ -0,0 +1,42 @@
using System.Collections.Generic;
using Upsilon.Evaluator;
using Xunit;
namespace UpsilonTests
{
public class UserDataListTests
{
[Fact]
public void BasicArrayTest()
{
var arr = new[] {100, 30, 56, 213, 76787};
const string input = @"
function getValue(arr)
return arr[3]
end
";
var script = new Script(input);
Assert.Empty(script.Diagnostics.Messages);
var evaluated = script.EvaluateFunction<long>("getValue", new[] {arr});
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(56, evaluated);
}
[Fact]
public void BasicListTest()
{
var arr = new List<int> {100, 30, 56, 213, 76787};
const string input = @"
function getValue(arr)
return arr[2]
end
";
var script = new Script(input);
Assert.Empty(script.Diagnostics.Messages);
var evaluated = script.EvaluateFunction<long>("getValue", new[] {arr});
Assert.Empty(script.Diagnostics.Messages);
Assert.Equal(30, evaluated);
}
}
}