using System; using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BoundTypes; using Upsilon.Evaluator; using Upsilon.Exceptions; using Upsilon.Text; namespace Upsilon.BaseTypes.UserData { internal class ListUserData : ScriptType, IUserData, IIterable, ILengthType { public IList List { get; } public string TypeName { get; } public ListUserData(IList list) { List = list; var type = list.GetType(); System.Type elementType; if (type.IsArray) { elementType = type.GetElementType(); TypeName = BoundTypeHandler.GetTypeName(elementType); } else { elementType = type.GetGenericArguments()[0]; TypeName = BoundTypeHandler.GetTypeName(elementType); } if (TypeName == null) { Type = new CompositeTypeContainer(new TypeContainer[] {BaseTypes.Type.Number, new UndefinedUserDataTypeContainer(elementType)} .ToImmutableArray()); } else { Type = new CompositeTypeContainer(new TypeContainer[] {BaseTypes.Type.Number, new TypeContainer(TypeName)} .ToImmutableArray()); } } public ScriptType Get(Diagnostics diagnostics, TextSpan span, ScriptType index, EvaluationScope scope) { int i; if (index.Type == BaseTypes.Type.String) { var str = (ScriptString)index; i = int.Parse(str.Value) - 1; } else if (index.Type == BaseTypes.Type.Number) { var ind = (Number.ScriptNumberLong) index; i = (int) ind.Value - 1; } else { return null; } return List[i].ToScriptType(); } public void Set(Diagnostics diagnostics, TextSpan span, ScriptType scriptIndex, ScriptType value) { var index = -1; if (scriptIndex.Type == BaseTypes.Type.Number || scriptIndex.Type == BaseTypes.Type.Unknown) { index = Convert.ToInt32(scriptIndex.ToCSharpObject()); } else if (scriptIndex.Type == BaseTypes.Type.String) { index = int.Parse(((ScriptString) scriptIndex).Value); } else { throw new ScriptRuntimeException(null, $"Tried indexing a CSharp list with a value that's not an integer. Value: {scriptIndex.ToCSharpObject()}", span, diagnostics.ScriptString.GetSpan(span)); } index--; if (index > List.Count) { throw new Exception( $"Tried adding an item to a list at position {index}, but list is only of length {List.Count}"); } if (index == List.Count) { List.Add(value.ToCSharpObject()); } else { List[index] = value.ToCSharpObject(); } } public override TypeContainer Type { get; } public override object ToCSharpObject() { return List; } public override System.Type GetCSharpType() { return List.GetType(); } public ScriptType GetValueFromIndex(ScriptType index) { var num = (ScriptNumberLong)index; return List[(int) num.Value].ToScriptType(); } public IEnumerator GetScriptEnumerator() { for (int i = 0; i < List.Count; i++) { yield return new ScriptNumberLong(i); } } public ScriptNumberLong Length() { return new ScriptNumberLong(List.Count); } protected bool Equals(ListUserData other) { return List.Equals(other.List); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() == List.GetType()) { return List.Equals(obj); } if (obj.GetType() != this.GetType()) return false; return Equals((ListUserData) obj); } public override string ToString() { return string.Join(", ", List); } } }