add lists, itemprovider logic, structs

This commit is contained in:
Qrakhen 2025-11-18 22:17:30 +01:00
parent 53fdea4d18
commit 367b334b79
11 changed files with 191 additions and 77 deletions

View File

@ -126,9 +126,9 @@ public static class ExpressionParser
digester.Emit(OpCode.ArrayAdd); digester.Emit(OpCode.ArrayAdd);
} else if (canAssign && digester.Match(TokenType.Equal)) { } else if (canAssign && digester.Match(TokenType.Equal)) {
digester.ParseExpression(); digester.ParseExpression();
digester.Emit(OpCode.ArraySet); digester.Emit(OpCode.SetItem);
} else { } else {
digester.Emit(OpCode.ArrayGet); digester.Emit(OpCode.GetItem);
} }
} }

View File

@ -56,10 +56,12 @@ public enum OpCode
Decrement = 0x71, Decrement = 0x71,
Array = 0xc0, Array = 0xc0,
ArrayGet = 0xc1, List = 0xc1,
ArraySet = 0xc2, Structure = 0xc2,
ArrayAdd = 0xc3, GetItem = 0xc3,
ArrayRemove = 0xc4, SetItem = 0xc4,
AddItem = 0xc5,
RemoveItem = 0xc6,
Return = 0x80, Return = 0x80,
Jump = 0x81, Jump = 0x81,

View File

@ -204,44 +204,48 @@ public class Runner : IDisposable
break; break;
} }
case Op.ArrayGet: { case Op.GetItem: {
Value index = Pop(); Value index = Pop();
Value array = Pop(); Value items = Pop();
if (!array.Is(T.Array)) if (!items.Is(T.ItemProvider, false))
return Error($"Can not access {index} of {array} - it is not an array."); return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)");
if (index.IsSigned) if (items.Is(T.Array))
Push(array.Ptr.As<Values.Objects.Array>()!.Get((int)index.Signed)); Push(items.Ptr.As<Values.Objects.Array>()!.Get(index));
else if (index.IsUnsigned) else if (items.Is(T.List))
Push(array.Ptr.As<Values.Objects.Array>()!.Get((int)index.Unsigned)); Push(items.Ptr.As<Values.Objects.List>()!.Get(index));
else if (items.Is(T.Structure))
Push(items.Ptr.As<Values.Objects.Structure>()!.Get(index));
else else
return Error($"Can not access {index}, index is not an integer."); return Error($"Unsupported native accessor for type {items.Type}!");
break; break;
} }
case Op.ArraySet: { case Op.SetItem: {
Value value = Pop(); Value value = Pop();
Value index = Pop(); Value index = Pop();
Value array = Pop(); Value items = Pop();
if (!array.Is(T.Array)) if (!items.Is(T.ItemProvider, false))
return Error($"Can not access {index} of {array} - it is not an array."); return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)");
if (index.IsSigned) if (items.Is(T.Array))
array.Ptr.As<Values.Objects.Array>()!.Set((int)index.Signed, value); items.Ptr.As<Values.Objects.Array>()!.Set(index, value);
else if (index.IsUnsigned) else if (items.Is(T.List))
array.Ptr.As<Values.Objects.Array>()!.Set((int)index.Signed, value); items.Ptr.As<Values.Objects.List>()!.Set(index, value);
else if (items.Is(T.Structure))
items.Ptr.As<Values.Objects.Structure>()!.Set(index, value);
else else
return Error($"Can not access {index}, index is not an integer."); return Error($"Unsupported native accessor for type {items.Type}!");
break; break;
} }
case Op.ArrayAdd: { case Op.AddItem: {
Value value = Pop(); Value value = Pop();
Value array = Pop(); Value items = Pop();
if (!array.Is(T.Array)) if (!items.Is(T.List))
return Error($"Can not add {value} to {array} - it is not an array."); return Error($"Can not add {value} to {items} - only lists :[] support the native add <+ operator.");
array.Ptr.As<Values.Objects.Array>()!.Add(value); items.Ptr.As<Values.Objects.List>()!.Add(value);
break; break;
} }

View File

@ -279,6 +279,8 @@ public class Reader : IReader<Token>, IDisposable
';' => MakeToken(Semicolon, buffer), ';' => MakeToken(Semicolon, buffer),
':' => Check(':') ? ':' => Check(':') ?
MakeToken(Print, buffer + Next()) : MakeToken(Print, buffer + Next()) :
Check('(') ?
MakeToken(ListOpen, buffer + Next()) :
MakeToken(Colon, buffer), MakeToken(Colon, buffer),
'&' => Check('&') ? '&' => Check('&') ?
MakeToken(And, buffer + Next()) : MakeToken(And, buffer + Next()) :

View File

@ -22,6 +22,7 @@ public enum TokenType
ArrayOpen = Bracket | 5, ArrayOpen = Bracket | 5,
ArrayClose = Bracket | 6, ArrayClose = Bracket | 6,
ListOpen = Bracket | 7,
ArrayAdd, ArrayAdd,
ArrayRemove, ArrayRemove,

View File

@ -1,32 +1,39 @@
namespace Qrakhen.Qamp.Core.Values.Objects; using System;
public class Array(IEnumerable<Value> data) : Obj(ValueType.Array) namespace Qrakhen.Qamp.Core.Values.Objects;
{
public List<Value> Data = [..data];
public void Set(int index, Value value) public class Array(IEnumerable<Value> data) : ItemProvider<int>(ValueType.Array)
{
public Value[] Data = [..data];
protected override void InnerSet(int index, Value value)
{ {
AssertWithinBounds(index);
Data[index] = value; Data[index] = value;
} }
public Value Get(int index) protected override Value InnerGet(int index)
{ {
AssertWithinBounds(index);
return Data[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) int index;
throw new QampException($"Can not access element at index {index}, it is outside of this array's bounds."); 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 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}]";
} }

View File

@ -1,20 +0,0 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
public class Dictionary(IEnumerable<KeyValuePair<string, Value>> data) : Obj(ValueType.Object)
{
public Dictionary<string, Value> 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)}]";
}

View File

@ -0,0 +1,26 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
public abstract class ItemProvider<T>(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);

View File

@ -0,0 +1,50 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
public class List(IEnumerable<Value> data) : ItemProvider<int>(ValueType.List)
{
public List<Value> 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}]";
}

View File

@ -0,0 +1,37 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
public class Structure(IEnumerable<KeyValuePair<string, Value>> data) : ItemProvider<string>(ValueType.Structure)
{
public bool Sealed;
public Dictionary<string, Value> 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<String>()!.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}}}";
}

View File

@ -25,16 +25,21 @@ public enum ValueType
Reference = 0x1000, Reference = 0x1000,
Pointer = 0x2000, Pointer = 0x2000,
Object = Pointer, // coded with & symbol
Object = Pointer | 0x4000,
Native = Object | 0x0001, Native = Object | 0x0001,
String = Object | 0x0002, String = Object | 0x0002,
Array = Object | 0x0004, Function = Object | 0x0020,
Function = Object | 0x0008, Context = Object | 0x0040,
Context = Object | 0x0010, Instance = Object | 0x0080,
Instance = Object | 0x0020,
Class = Object | 0x0100, Class = Object | 0x0100,
Method = Class | Function, Method = Class | Function,
Outer = Object | 0x0200, Outer = Object | 0x0200,
ItemProvider = Object | 0x8000, // accessible with [n] or :n (getters / setters)
Array = ItemProvider | 0x0001,
List = ItemProvider | 0x0002,
Structure = ItemProvider | 0x0004,
Module = 0xFFFF Module = 0xFFFF
} }