diff --git a/Upsilon/BaseTypes/IIndexable.cs b/Upsilon/BaseTypes/IIndexable.cs index 6dc6062..aae4275 100644 --- a/Upsilon/BaseTypes/IIndexable.cs +++ b/Upsilon/BaseTypes/IIndexable.cs @@ -5,8 +5,8 @@ namespace Upsilon.BaseTypes { internal interface IIndexable { - LuaType Get(Diagnostics diagnostics, TextSpan span, string s, EvaluationScope scope); - void Set(Diagnostics diagnostics, TextSpan span, string s, LuaType value); + LuaType Get(Diagnostics diagnostics, TextSpan span, LuaType index, EvaluationScope scope); + void Set(Diagnostics diagnostics, TextSpan span, LuaType index, LuaType value); } internal interface IScopeOwner diff --git a/Upsilon/BaseTypes/LuaString.cs b/Upsilon/BaseTypes/LuaString.cs index 83a9dc4..428a539 100644 --- a/Upsilon/BaseTypes/LuaString.cs +++ b/Upsilon/BaseTypes/LuaString.cs @@ -84,13 +84,27 @@ namespace Upsilon.BaseTypes 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()); } - 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(); } diff --git a/Upsilon/BaseTypes/LuaTable.cs b/Upsilon/BaseTypes/LuaTable.cs index bebfcdc..65de10f 100644 --- a/Upsilon/BaseTypes/LuaTable.cs +++ b/Upsilon/BaseTypes/LuaTable.cs @@ -27,8 +27,9 @@ namespace Upsilon.BaseTypes return typeof(Dictionary); } - 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)) { return o; @@ -37,8 +38,9 @@ namespace Upsilon.BaseTypes 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); } } diff --git a/Upsilon/BaseTypes/TypeConversion.cs b/Upsilon/BaseTypes/TypeConversion.cs index 5d18a2a..49c8eb4 100644 --- a/Upsilon/BaseTypes/TypeConversion.cs +++ b/Upsilon/BaseTypes/TypeConversion.cs @@ -1,4 +1,6 @@ +using System.Collections; using Upsilon.BaseTypes.Number; +using Upsilon.BaseTypes.UserData; namespace Upsilon.BaseTypes { @@ -23,8 +25,21 @@ namespace Upsilon.BaseTypes case null: return new LuaNull(); 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); + } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/DictionaryUserData.cs b/Upsilon/BaseTypes/UserData/DictionaryUserData.cs new file mode 100644 index 0000000..ffc8b5b --- /dev/null +++ b/Upsilon/BaseTypes/UserData/DictionaryUserData.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserData.cs b/Upsilon/BaseTypes/UserData/GenericUserData.cs similarity index 66% rename from Upsilon/BaseTypes/UserData/UserData.cs rename to Upsilon/BaseTypes/UserData/GenericUserData.cs index 592fa85..785cdd0 100644 --- a/Upsilon/BaseTypes/UserData/UserData.cs +++ b/Upsilon/BaseTypes/UserData/GenericUserData.cs @@ -3,16 +3,16 @@ using Upsilon.Text; namespace Upsilon.BaseTypes.UserData { - internal class UserData : LuaType, IIndexable + internal class GenericUserData : LuaType, IUserData { - public UserData(object o) + public GenericUserData(object dictionary) { - Value = o; - _typeInfo = UserDataTypeHandler.GetTypeInfo(o.GetType()); + Value = dictionary; + _typeInfo = UserDataTypeHandler.GetTypeInfo(dictionary.GetType()); } public override Type Type => Type.UserData; - private object Value { get; } + protected virtual object Value { get; } private readonly UserDataType _typeInfo; public override object ToCSharpObject() @@ -25,8 +25,9 @@ namespace Upsilon.BaseTypes.UserData 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); if (failed) { @@ -35,8 +36,9 @@ namespace Upsilon.BaseTypes.UserData 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); if (failed) { diff --git a/Upsilon/BaseTypes/UserData/IUserData.cs b/Upsilon/BaseTypes/UserData/IUserData.cs new file mode 100644 index 0000000..622c503 --- /dev/null +++ b/Upsilon/BaseTypes/UserData/IUserData.cs @@ -0,0 +1,9 @@ +namespace Upsilon.BaseTypes.UserData +{ + internal interface IUserData : IIndexable + { + object ToCSharpObject(); + System.Type GetCSharpType(); + + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/ListUserData.cs b/Upsilon/BaseTypes/UserData/ListUserData.cs new file mode 100644 index 0000000..f94dbef --- /dev/null +++ b/Upsilon/BaseTypes/UserData/ListUserData.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 43e2063..1096408 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -169,7 +169,7 @@ namespace Upsilon.Evaluator return -((Number)operand); else if (operand.Type == Type.UserData) { - var ud = (UserData) operand; + var ud = (GenericUserData) operand; var (type, failed) = ud.UnaryOperator(operand, OperatorType.UnaryNegation); if (failed) goto default; return type; @@ -180,7 +180,7 @@ namespace Upsilon.Evaluator return !(LuaBoolean) operand; else if (operand.Type == Type.UserData) { - var ud = (UserData) operand; + var ud = (GenericUserData) operand; var (type, failed) = ud.UnaryOperator(operand, OperatorType.LogicalNot); if (failed) goto default; return type; @@ -207,7 +207,7 @@ namespace Upsilon.Evaluator } else if (left.Type == Type.UserData) { - var ud = (UserData) left; + var ud = (GenericUserData) left; var (type, failed) = ud.BinaryOperator(left, OperatorType.Addition, right); if (failed) goto default; return type; @@ -220,7 +220,7 @@ namespace Upsilon.Evaluator } else if (left.Type == Type.UserData) { - var ud = (UserData) left; + var ud = (GenericUserData) left; var (type, failed) = ud.BinaryOperator(left, OperatorType.Subtraction, right); if (failed) goto default; return type; @@ -233,7 +233,7 @@ namespace Upsilon.Evaluator } else if (left.Type == Type.UserData) { - var ud = (UserData) left; + var ud = (GenericUserData) left; var (type, failed) = ud.BinaryOperator(left, OperatorType.Multiplication, right); if (failed) goto default; return type; @@ -246,7 +246,7 @@ namespace Upsilon.Evaluator } else if (left.Type == Type.UserData) { - var ud = (UserData) left; + var ud = (GenericUserData) left; var (type, failed) = ud.BinaryOperator(left, OperatorType.Division, right); if (failed) goto default; return type; @@ -406,7 +406,7 @@ namespace Upsilon.Evaluator scope = scopeOwner.EvaluationScope; } 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) { @@ -422,7 +422,7 @@ namespace Upsilon.Evaluator 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) @@ -445,7 +445,7 @@ namespace Upsilon.Evaluator { throw new Exception("Cant assign to that"); } - indexable.Set(_diagnostics, e.Span, index.ToString(), value); + indexable.Set(_diagnostics, e.Span, index.ToString().ToLuaType(), value); } } } \ No newline at end of file diff --git a/UpsilonTests/UserDataDictionaryTests.cs b/UpsilonTests/UserDataDictionaryTests.cs new file mode 100644 index 0000000..436357f --- /dev/null +++ b/UpsilonTests/UserDataDictionaryTests.cs @@ -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 + { + {"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("getValue", new[] {arr}); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(1683, evaluated); + } + + } +} \ No newline at end of file diff --git a/UpsilonTests/UserDataListTests.cs b/UpsilonTests/UserDataListTests.cs new file mode 100644 index 0000000..efd3297 --- /dev/null +++ b/UpsilonTests/UserDataListTests.cs @@ -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("getValue", new[] {arr}); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(56, evaluated); + } + + [Fact] + public void BasicListTest() + { + var arr = new List {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("getValue", new[] {arr}); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(30, evaluated); + } + + } +} \ No newline at end of file