diff --git a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs index 4f991ad..cda27d8 100644 --- a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs +++ b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs @@ -126,9 +126,9 @@ public static class ExpressionParser digester.Emit(OpCode.ArrayAdd); } else if (canAssign && digester.Match(TokenType.Equal)) { digester.ParseExpression(); - digester.Emit(OpCode.ArraySet); + digester.Emit(OpCode.SetItem); } else { - digester.Emit(OpCode.ArrayGet); + digester.Emit(OpCode.GetItem); } } diff --git a/Qrakhen.Qamp.Core/Execution/OpCode.cs b/Qrakhen.Qamp.Core/Execution/OpCode.cs index f503063..6d19fff 100644 --- a/Qrakhen.Qamp.Core/Execution/OpCode.cs +++ b/Qrakhen.Qamp.Core/Execution/OpCode.cs @@ -56,10 +56,12 @@ public enum OpCode Decrement = 0x71, Array = 0xc0, - ArrayGet = 0xc1, - ArraySet = 0xc2, - ArrayAdd = 0xc3, - ArrayRemove = 0xc4, + List = 0xc1, + Structure = 0xc2, + GetItem = 0xc3, + SetItem = 0xc4, + AddItem = 0xc5, + RemoveItem = 0xc6, Return = 0x80, Jump = 0x81, diff --git a/Qrakhen.Qamp.Core/Execution/Runner.cs b/Qrakhen.Qamp.Core/Execution/Runner.cs index 2c1cbd5..e624589 100644 --- a/Qrakhen.Qamp.Core/Execution/Runner.cs +++ b/Qrakhen.Qamp.Core/Execution/Runner.cs @@ -204,44 +204,48 @@ public class Runner : IDisposable break; } - case Op.ArrayGet: { + case Op.GetItem: { Value index = Pop(); - Value array = Pop(); - if (!array.Is(T.Array)) - return Error($"Can not access {index} of {array} - it is not an array."); + Value items = Pop(); + if (!items.Is(T.ItemProvider, false)) + return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)"); - if (index.IsSigned) - Push(array.Ptr.As()!.Get((int)index.Signed)); - else if (index.IsUnsigned) - Push(array.Ptr.As()!.Get((int)index.Unsigned)); + if (items.Is(T.Array)) + Push(items.Ptr.As()!.Get(index)); + else if (items.Is(T.List)) + Push(items.Ptr.As()!.Get(index)); + else if (items.Is(T.Structure)) + Push(items.Ptr.As()!.Get(index)); else - return Error($"Can not access {index}, index is not an integer."); + return Error($"Unsupported native accessor for type {items.Type}!"); break; } - case Op.ArraySet: { + case Op.SetItem: { Value value = Pop(); Value index = Pop(); - Value array = Pop(); - if (!array.Is(T.Array)) - return Error($"Can not access {index} of {array} - it is not an array."); + Value items = Pop(); + if (!items.Is(T.ItemProvider, false)) + return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)"); - if (index.IsSigned) - array.Ptr.As()!.Set((int)index.Signed, value); - else if (index.IsUnsigned) - array.Ptr.As()!.Set((int)index.Signed, value); + if (items.Is(T.Array)) + items.Ptr.As()!.Set(index, value); + else if (items.Is(T.List)) + items.Ptr.As()!.Set(index, value); + else if (items.Is(T.Structure)) + items.Ptr.As()!.Set(index, value); else - return Error($"Can not access {index}, index is not an integer."); + return Error($"Unsupported native accessor for type {items.Type}!"); break; } - case Op.ArrayAdd: { + case Op.AddItem: { Value value = Pop(); - Value array = Pop(); - if (!array.Is(T.Array)) - return Error($"Can not add {value} to {array} - it is not an array."); + Value items = Pop(); + if (!items.Is(T.List)) + return Error($"Can not add {value} to {items} - only lists :[] support the native add <+ operator."); - array.Ptr.As()!.Add(value); + items.Ptr.As()!.Add(value); break; } diff --git a/Qrakhen.Qamp.Core/Tokenization/Reader.cs b/Qrakhen.Qamp.Core/Tokenization/Reader.cs index aa83965..70b36c0 100644 --- a/Qrakhen.Qamp.Core/Tokenization/Reader.cs +++ b/Qrakhen.Qamp.Core/Tokenization/Reader.cs @@ -279,7 +279,9 @@ public class Reader : IReader, IDisposable ';' => MakeToken(Semicolon, buffer), ':' => Check(':') ? MakeToken(Print, buffer + Next()) : - MakeToken(Colon, buffer), + Check('(') ? + MakeToken(ListOpen, buffer + Next()) : + MakeToken(Colon, buffer), '&' => Check('&') ? MakeToken(And, buffer + Next()) : MakeToken(BitwiseAnd, buffer), diff --git a/Qrakhen.Qamp.Core/Tokenization/TokenType.cs b/Qrakhen.Qamp.Core/Tokenization/TokenType.cs index 4564709..0a08eb2 100644 --- a/Qrakhen.Qamp.Core/Tokenization/TokenType.cs +++ b/Qrakhen.Qamp.Core/Tokenization/TokenType.cs @@ -22,6 +22,7 @@ public enum TokenType ArrayOpen = Bracket | 5, ArrayClose = Bracket | 6, + ListOpen = Bracket | 7, ArrayAdd, ArrayRemove, diff --git a/Qrakhen.Qamp.Core/Values/Objects/Array.cs b/Qrakhen.Qamp.Core/Values/Objects/Array.cs index 945b918..1ddbac7 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/Array.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/Array.cs @@ -1,32 +1,39 @@ -namespace Qrakhen.Qamp.Core.Values.Objects; +using System; -public class Array(IEnumerable data) : Obj(ValueType.Array) +namespace Qrakhen.Qamp.Core.Values.Objects; + +public class Array(IEnumerable data) : ItemProvider(ValueType.Array) { - public List Data = [..data]; - - public void Set(int index, Value value) + public Value[] Data = [..data]; + protected override void InnerSet(int index, Value value) { - AssertWithinBounds(index); Data[index] = value; } - public Value Get(int index) + protected override Value InnerGet(int index) { - AssertWithinBounds(index); return Data[index]; } - public void Add(Value value) + protected override void AssertValidAccesor(int index) { - Data.Add(value); + if (index < 0 || index > Data.Length) + throw new ItemProviderException($"Can not index '{index}' of array, as the index is outside its boundaries.", this); } - private void AssertWithinBounds(int index) + protected override int ExtractIndex(Value value) { - if (index < 0 || index > Data.Count) - throw new QampException($"Can not access element at index {index}, it is outside of this array's bounds."); + int index; + if (value.IsSigned) + index = (int)value.Signed; + else if (value.IsUnsigned) + index = (int)value.Unsigned; + else + throw new ItemProviderException($"Can not use {value} as an accessor, as it is not an integer.", this); + + return index; } public override string ToString() => ToString(true); - public string ToString(bool detail) => detail ? $"[{string.Join(", ", Data)}]" : $"Array[{Data.Count}]"; + public string ToString(bool detail) => detail ? $"[{string.Join(", ", Data)}]" : $"Array[{Data.Length}]"; } \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Objects/Dictionary.cs b/Qrakhen.Qamp.Core/Values/Objects/Dictionary.cs deleted file mode 100644 index 0244179..0000000 --- a/Qrakhen.Qamp.Core/Values/Objects/Dictionary.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Qrakhen.Qamp.Core.Values.Objects; - -public class Dictionary(IEnumerable> data) : Obj(ValueType.Object) -{ - public Dictionary Data = new(data); - - public void Set(string key, Value value) - { - Data[key] = value; - } - - public Value Get(string key) - { - if (Data.TryGetValue(key, out Value value)) - return value; - return Value.Void; - } - - public override string ToString() => $"[{string.Join(", ", Data)}]"; -} \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Objects/ItemProvider.cs b/Qrakhen.Qamp.Core/Values/Objects/ItemProvider.cs new file mode 100644 index 0000000..115c9df --- /dev/null +++ b/Qrakhen.Qamp.Core/Values/Objects/ItemProvider.cs @@ -0,0 +1,26 @@ +namespace Qrakhen.Qamp.Core.Values.Objects; + +public abstract class ItemProvider(ValueType type) : Obj(type) +{ + public void Set(Value index, Value value) + { + T _index = ExtractIndex(index); + AssertValidAccesor(_index); + InnerSet(_index, value); + } + + public Value Get(Value index) + { + T _index = ExtractIndex(index); + AssertValidAccesor(_index); + return InnerGet(_index); + } + + protected abstract Value InnerGet(T index); + protected abstract void InnerSet(T index, Value value); + + protected abstract void AssertValidAccesor(T index); + protected abstract T ExtractIndex(Value value); +} + +public class ItemProviderException(string message, object context) : QampException(message, context); \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Objects/List.cs b/Qrakhen.Qamp.Core/Values/Objects/List.cs new file mode 100644 index 0000000..4afe2a6 --- /dev/null +++ b/Qrakhen.Qamp.Core/Values/Objects/List.cs @@ -0,0 +1,50 @@ +namespace Qrakhen.Qamp.Core.Values.Objects; + +public class List(IEnumerable data) : ItemProvider(ValueType.List) +{ + public List Data = [..data]; + + public void Add(Value value) + { + Data.Add(value); + } + + public void Remove(Value index) + { + int _index = ExtractIndex(index); + AssertValidAccesor(_index); + Data.RemoveAt(_index); + } + + protected override void InnerSet(int index, Value value) + { + Data[index] = value; + } + + protected override Value InnerGet(int index) + { + return Data[index]; + } + + protected override void AssertValidAccesor(int index) + { + if (index < 0 || index > Data.Count) + throw new ItemProviderException($"Can not index '{index}' of list, as the index is outside its boundaries.", this); + } + + protected override int ExtractIndex(Value value) + { + int index; + if (value.IsSigned) + index = (int)value.Signed; + else if (value.IsUnsigned) + index = (int)value.Unsigned; + else + throw new ItemProviderException($"Can not use {value} as an accessor, as it is not an integer.", this); + + return index; + } + + public override string ToString() => ToString(true); + public string ToString(bool detail) => detail ? $":[{string.Join(", ", Data)}]" : $"List[{Data.Count}]"; +} \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Objects/Structure.cs b/Qrakhen.Qamp.Core/Values/Objects/Structure.cs new file mode 100644 index 0000000..08db111 --- /dev/null +++ b/Qrakhen.Qamp.Core/Values/Objects/Structure.cs @@ -0,0 +1,37 @@ +namespace Qrakhen.Qamp.Core.Values.Objects; + +public class Structure(IEnumerable> data) : ItemProvider(ValueType.Structure) +{ + public bool Sealed; + public Dictionary Data = new(data); + + protected override void InnerSet(string index, Value value) + { + Data[index] = value; + } + + protected override Value InnerGet(string index) + { + return Data[index]; + } + + protected override void AssertValidAccesor(string index) + { + if (Sealed && !Data.ContainsKey(index)) + throw new ItemProviderException($"Can not set non-existent key '{index}' of sealed structure.", this); + } + + protected override string ExtractIndex(Value value) + { + string index; + if (value.IsString) + index = value.Ptr.As()!.Value!; + else + throw new ItemProviderException($"Can not use {value} as an accessor, as it is not a string.", this); + + return index; + } + + public override string ToString() => ToString(true); + public string ToString(bool detail) => detail ? $"{{{string.Join("\n,", Data)}}}" : $"Structure{{{Data.Count}}}"; +} \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/ValueType.cs b/Qrakhen.Qamp.Core/Values/ValueType.cs index e1766bf..71ab8d0 100644 --- a/Qrakhen.Qamp.Core/Values/ValueType.cs +++ b/Qrakhen.Qamp.Core/Values/ValueType.cs @@ -23,18 +23,23 @@ public enum ValueType // classes etc. here? - Reference = 0x1000, - Pointer = 0x2000, - Object = Pointer, // coded with & symbol - Native = Object | 0x0001, - String = Object | 0x0002, - Array = Object | 0x0004, - Function = Object | 0x0008, - Context = Object | 0x0010, - Instance = Object | 0x0020, - Class = Object | 0x0100, - Method = Class | Function, - Outer = Object | 0x0200, + Reference = 0x1000, + Pointer = 0x2000, - Module = 0xFFFF + Object = Pointer | 0x4000, + Native = Object | 0x0001, + String = Object | 0x0002, + Function = Object | 0x0020, + Context = Object | 0x0040, + Instance = Object | 0x0080, + Class = Object | 0x0100, + Method = Class | Function, + Outer = Object | 0x0200, + + ItemProvider = Object | 0x8000, // accessible with [n] or :n (getters / setters) + Array = ItemProvider | 0x0001, + List = ItemProvider | 0x0002, + Structure = ItemProvider | 0x0004, + + Module = 0xFFFF } \ No newline at end of file