few things, realized i should actually do the native methods like the extension ones
This commit is contained in:
parent
aaec6375eb
commit
44b8bb8900
|
|
@ -17,7 +17,7 @@ public class Table<TValue> :
|
|||
IToArray<TValue>,
|
||||
IEnumerable<KeyValuePair<Address, TValue>>
|
||||
{
|
||||
public const int BLOCK_SIZE = sizeof(int);
|
||||
public const int BLOCK_SIZE = sizeof(int) * 8;
|
||||
private const uint BLOCK_FULL = (uint)((1UL << 32) - 1);
|
||||
|
||||
private readonly TValue[] _data;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,7 @@ public static class Console
|
|||
Formatter = (p) => {
|
||||
if (p.Length == 0)
|
||||
return [];
|
||||
return p
|
||||
.Select(v => $"{v}".Split('\n'))
|
||||
.Select(v => string.Join($"\n :> ", v))
|
||||
.Select(v => $" :> {v}")
|
||||
.ToArray();
|
||||
return p.Select(x => x.ToString()).ToArray() ?? [];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -58,4 +54,4 @@ public delegate string? InputStreamListenHandler();
|
|||
public delegate void OutputStreamMessageHandler(string? message);
|
||||
public delegate void ErrorStreamMessageHandler(string? message);
|
||||
|
||||
public delegate string[] MessageFormatter(params object[] parameters);
|
||||
public delegate string?[] MessageFormatter(params object[] parameters);
|
||||
|
|
@ -14,15 +14,15 @@ public class ArithmeticResolver : IOperationResolver
|
|||
|
||||
private readonly Register<OpCode, OperationHandler> _operations;
|
||||
|
||||
public bool CanResolve(OpCode opCode) => (opCode & (OpCode.F_Operation | OpCode.F_Compare)) > 0;
|
||||
public bool CanResolve(OpCode opCode) => opCode.IsHandledByAlu();
|
||||
|
||||
private Operation PopOperation(Runner runner, OpCode code)
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method(code);
|
||||
#endif
|
||||
if ((code & OpCode.F_Unary) == OpCode.F_Unary)
|
||||
return new Operation(code, runner.Pop(), default);
|
||||
if (code.IsUnary())
|
||||
return new Operation(code, runner.Pop(), Value.Void);
|
||||
Value right = runner.Pop();
|
||||
Value left = runner.Pop();
|
||||
return new Operation(code, left, right);
|
||||
|
|
|
|||
|
|
@ -1,92 +1,131 @@
|
|||
namespace Qrakhen.Qamp.Core.Execution;
|
||||
|
||||
public class SerializedEnumAttribute(Type serializedType) : Attribute
|
||||
{
|
||||
public Type SerializedType { get; } = serializedType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In order to keep instruction files small, all op codes are crammed into 8 bits,
|
||||
/// and operation flags aren't flags, but rather should be seen as IDs (the first 4 bits)
|
||||
/// and the operation itself is a number made from the last 4 bits.
|
||||
/// & 0b11110000 => Handler
|
||||
/// & 0b00001111 => Operation
|
||||
/// </remarks>
|
||||
[SerializedEnum(typeof(byte))]
|
||||
public enum OpCode
|
||||
{
|
||||
F_Mask = 0xF0,
|
||||
/// <summary>
|
||||
/// Mask bits, to be used like <c>opCode & F_Mask == F_Operation</c>, for example.
|
||||
/// </summary>
|
||||
Mask_Handler = 0xF0,
|
||||
Mask_Operation = 0x0F,
|
||||
|
||||
None = 0x00,
|
||||
Constant = 0x01,
|
||||
Null = 0x02,
|
||||
Pop = 0x03,
|
||||
Cast = 0x04,
|
||||
Error = -1,
|
||||
Void = 0,
|
||||
|
||||
False = 0x0e,
|
||||
True = 0x0f,
|
||||
F_Static = 0x10,
|
||||
None = F_Static | 1,
|
||||
True = F_Static | 2,
|
||||
False = F_Static | 3,
|
||||
Constant = F_Static | 4,
|
||||
Null = F_Static | 5,
|
||||
Cast = F_Static | 6,
|
||||
|
||||
Val = 0x10,
|
||||
Ref = 0x11,
|
||||
Addr = 0x12,
|
||||
Pop = F_Static | 7,
|
||||
|
||||
SetGlobal = 0x20,
|
||||
GetGlobal = 0x21,
|
||||
GetLocal = 0x22,
|
||||
SetLocal = 0x23,
|
||||
GetOuter = 0x24,
|
||||
SetOuter = 0x25,
|
||||
GetMember = 0x26,
|
||||
SetMember = 0x27,
|
||||
F_Retrieve = 0x20,
|
||||
Val = F_Retrieve | 1,
|
||||
Ref = F_Retrieve | 2,
|
||||
Addr = F_Retrieve | 3,
|
||||
|
||||
DefineGlobal = 0x30,
|
||||
CloseOuter = 0x31,
|
||||
F_Names = 0x30,
|
||||
SetGlobal = F_Names | 1,
|
||||
GetGlobal = F_Names | 2,
|
||||
GetLocal = F_Names | 3,
|
||||
SetLocal = F_Names | 4,
|
||||
GetOuter = F_Names | 5,
|
||||
SetOuter = F_Names | 6,
|
||||
GetMember = F_Names | 7,
|
||||
SetMember = F_Names | 8,
|
||||
DefineGlobal = F_Names | 9,
|
||||
CloseOuter = F_Names | 10,
|
||||
|
||||
AssignValue = 0x40, // unsure
|
||||
AssignReference = 0x41, // unsure
|
||||
//AssignValue = 0x0, // unsure
|
||||
//AssignReference = 0x0, // unsure
|
||||
|
||||
F_Operation = 0x60,
|
||||
Add = 0x60,
|
||||
Subtract = 0x61,
|
||||
Multiply = 0x62,
|
||||
Divide = 0x63,
|
||||
Modulo = 0x64,
|
||||
BitwiseAnd = 0x65,
|
||||
BitwiseOr = 0x66,
|
||||
BitwiseXor = 0x67,
|
||||
BitwiseLeft = 0x68,
|
||||
BitwiseRight = 0x69,
|
||||
F_Arithmetic = 0x40,
|
||||
Add = F_Arithmetic | 1,
|
||||
Subtract = F_Arithmetic | 2,
|
||||
Multiply = F_Arithmetic | 3,
|
||||
Divide = F_Arithmetic | 4,
|
||||
Modulo = F_Arithmetic | 5,
|
||||
Negate = F_Arithmetic | 7,
|
||||
|
||||
F_Unary = 0x0a,
|
||||
Not = 0x6a,
|
||||
Negate = 0x6b,
|
||||
BitwiseNot = 0x6c,
|
||||
F_Bitwise = 0x50,
|
||||
BitwiseAnd = F_Bitwise | 1,
|
||||
BitwiseOr = F_Bitwise | 2,
|
||||
BitwiseXor = F_Bitwise | 3,
|
||||
BitwiseLeft = F_Bitwise | 4,
|
||||
BitwiseRight = F_Bitwise | 5,
|
||||
BitwiseNot = F_Bitwise | 6,
|
||||
|
||||
F_Compare = 0x50,
|
||||
Equal = 0x50,
|
||||
Greater = 0x51,
|
||||
Less = 0x52,
|
||||
F_Boolean = 0x60,
|
||||
Not = F_Boolean | 1,
|
||||
Equal = F_Boolean | 2,
|
||||
Greater = F_Boolean | 3,
|
||||
Less = F_Boolean | 4,
|
||||
|
||||
//Increment = 0x70, // basically increment by, ++ simply 'makes up' the 1.
|
||||
// = 0x71, // same here broren min
|
||||
F_Collection = 0x70,
|
||||
Array = F_Collection | 1,
|
||||
List = F_Collection | 2,
|
||||
Structure = F_Collection | 3,
|
||||
GetItem = F_Collection | 4,
|
||||
SetItem = F_Collection | 5,
|
||||
AddItem = F_Collection | 6,
|
||||
RemoveItem = F_Collection | 7,
|
||||
|
||||
Array = 0xc0,
|
||||
List = 0xc1,
|
||||
Structure = 0xc2,
|
||||
GetItem = 0xc3,
|
||||
SetItem = 0xc4,
|
||||
AddItem = 0xc5,
|
||||
RemoveItem = 0xc6,
|
||||
F_Control = 0x80,
|
||||
Return = F_Control | 1,
|
||||
Jump = F_Control | 2,
|
||||
JumpIfFalse = F_Control | 3,
|
||||
Loop = F_Control | 4,
|
||||
Invoke = F_Control | 5,
|
||||
InvokeBase = F_Control | 6,
|
||||
InvokeMember = F_Control | 7,
|
||||
Context = F_Control | 8,
|
||||
|
||||
Return = 0x80,
|
||||
Jump = 0x81,
|
||||
JumpIfFalse = 0x82,
|
||||
Loop = 0x83,
|
||||
Invoke = 0x84,
|
||||
InvokeBase = 0x85,
|
||||
InvokeMember = 0x86,
|
||||
Context = 0x87,
|
||||
F_Class = 0x90,
|
||||
Class = F_Class | 1,
|
||||
Member = F_Class | 2,
|
||||
Base = F_Class | 3,
|
||||
Inherit = F_Class | 4,
|
||||
Method = F_Class | 5,
|
||||
Static = F_Class | 6,
|
||||
|
||||
Class = 0xa0,
|
||||
Member = 0xa1,
|
||||
Base = 0xa2,
|
||||
Inherit = 0xa3,
|
||||
Method = 0xa4,
|
||||
Static = 0xa5,
|
||||
F_Meta = 0xa0,
|
||||
Print = F_Meta | 1,
|
||||
PrintStack = F_Meta | 2,
|
||||
PrintGlobals = F_Meta | 3,
|
||||
PrintExpr = F_Meta | 4,
|
||||
Typeof = F_Meta | 5,
|
||||
|
||||
Print = 0xe0,
|
||||
PrintStack = 0xe1,
|
||||
PrintGlobals = 0xe2,
|
||||
PrintExpr = 0xe3,
|
||||
Typeof = 0xef,
|
||||
F_Module = 0xf0,
|
||||
Export = F_Module | 1, // probably shouldnt be an op code but rather be done during digestion
|
||||
Import = F_Module | 2, // probably shouldnt be an op code but rather be done during digestion
|
||||
}
|
||||
|
||||
Export = 0xfe, // probably shouldnt be an op code but rather be done during digestion
|
||||
Import = 0xff, // probably shouldnt be an op code but rather be done during digestion
|
||||
public static class OpCodeExtensions
|
||||
{
|
||||
public static bool IsUnary(this OpCode op)
|
||||
=> op is OpCode.Not or OpCode.Negate or OpCode.BitwiseNot;
|
||||
|
||||
public static bool IsHandledByAlu(this OpCode op)
|
||||
=> (op & OpCode.Mask_Handler) is OpCode.F_Arithmetic or OpCode.F_Bitwise or OpCode.F_Boolean;
|
||||
|
||||
public static string ToString(this OpCode op)
|
||||
=> $"{op & OpCode.Mask_Handler}: {op} ({(byte)op:x2})";
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ public class Runner : IDisposable
|
|||
return result;
|
||||
}
|
||||
|
||||
private bool TestFlag(Op code, Op flag) => (code & Op.F_Mask) == flag;
|
||||
private bool TestHandler(Op code, Op handler) => (code & Op.Mask_Handler) == handler;
|
||||
|
||||
private ExecutionResult Execute()
|
||||
{
|
||||
|
|
@ -186,7 +186,7 @@ public class Runner : IDisposable
|
|||
case Op.GetGlobal: {
|
||||
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return Error($"tried to set global variable with empty name");
|
||||
return Error($"tried to get global variable with empty name");
|
||||
if (!Globals.Has(name))
|
||||
Push(Value.Void);
|
||||
else
|
||||
|
|
@ -306,14 +306,16 @@ public class Runner : IDisposable
|
|||
if (!items.Is(T.ItemProvider, false))
|
||||
return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)");
|
||||
|
||||
if (items.Is(T.Array))
|
||||
Push(items.Ptr.As<Values.Objects.Array>()!.Get(index));
|
||||
else if (items.Is(T.List))
|
||||
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
|
||||
return Error($"Unsupported native accessor for type {items.Type}!");
|
||||
if (items.Is(T.Array))
|
||||
Push(items.Ptr.As<Values.Objects.Array>()!.Get(index));
|
||||
else if (items.Is(T.List))
|
||||
Push(items.Ptr.As<List>()!.Get(index));
|
||||
else if (items.Is(T.Structure))
|
||||
Push(items.Ptr.As<Structure>()!.Get(index));
|
||||
else if (items.Is(T.String))
|
||||
Push(items.Ptr.As<String>()!.SubString(index, new Value(1L)));
|
||||
else
|
||||
return Error($"Unsupported native accessor for type {items.Type}!");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -682,18 +684,6 @@ public class Runner : IDisposable
|
|||
_stack.Push(value);
|
||||
}
|
||||
|
||||
private Operation PopOperation(Op code)
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method(code);
|
||||
#endif
|
||||
if ((code & Op.F_Unary) == Op.F_Unary)
|
||||
return new Operation(code, Pop(), default);
|
||||
Value right = Pop();
|
||||
Value left = Pop();
|
||||
return new Operation(code, left, right);
|
||||
}
|
||||
|
||||
private ExecutionResult Error(string message, object? context = null, bool @throw = true)
|
||||
{
|
||||
#if LOG
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ using System.Reflection;
|
|||
|
||||
namespace Qrakhen.Qamp.Core.Values;
|
||||
|
||||
// todo: do the same for native methods. make it more abstract. i like the approach.
|
||||
|
||||
public delegate Value ExtensionDelegate(Value target, Value[] parameters);
|
||||
|
||||
public class ExtensionException(string message, object? context = null) : QampException(message, context);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,86 @@ using System.Reflection;
|
|||
|
||||
namespace Qrakhen.Qamp.Core.Values.Native;
|
||||
|
||||
public class NativeMemberAttribute(string? name = null) : Attribute
|
||||
{
|
||||
public string? Name { get; } = name;
|
||||
}
|
||||
|
||||
public class NativeMember(string name, MemberInfo info)
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
public MemberInfo MemberInfo { get; } = info;
|
||||
}
|
||||
|
||||
public class NativeProperty(string name,
|
||||
PropertyInfo info,
|
||||
bool readOnly) : NativeMember(name, info)
|
||||
{
|
||||
public bool ReadOnly { get; } = readOnly;
|
||||
public PropertyInfo PropertyInfo => (MemberInfo as PropertyInfo)!;
|
||||
}
|
||||
|
||||
public class NativeMethod(string name,
|
||||
MethodInfo info) : NativeMember(name, info)
|
||||
{
|
||||
public MethodInfo MethodInfo => (MemberInfo as MethodInfo)!;
|
||||
}
|
||||
|
||||
public static class NativeLinker
|
||||
{
|
||||
private static readonly Dictionary<Type, Dictionary<string, NativeMember>> _linked = [];
|
||||
|
||||
public static Value GetMember<TObj>(TObj obj, string name)
|
||||
{
|
||||
return Value.Void;
|
||||
}
|
||||
|
||||
public static void SetMember<TObj>(TObj obj, string name, Value value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static Value CallMember<TObj>(TObj obj, string name, Value[] args)
|
||||
{
|
||||
|
||||
return Value.Void;
|
||||
}
|
||||
|
||||
private static void Compile(Type type)
|
||||
{
|
||||
var members = type.GetMembers();
|
||||
var table = new Dictionary<string, NativeMember>();
|
||||
foreach (var member in members)
|
||||
{
|
||||
var attr = member.GetCustomAttribute<NativeMemberAttribute>();
|
||||
if (attr == null)
|
||||
continue;
|
||||
|
||||
NativeMember? native = null;
|
||||
if (member is PropertyInfo propertyInfo)
|
||||
{
|
||||
native = new NativeProperty(attr.Name ?? member.Name,
|
||||
propertyInfo,
|
||||
propertyInfo.CanWrite);
|
||||
}
|
||||
|
||||
if (member is MethodInfo methodInfo)
|
||||
{
|
||||
native = new NativeMethod(attr.Name ?? methodInfo.Name,
|
||||
methodInfo);
|
||||
}
|
||||
|
||||
if (native == null)
|
||||
throw new Exception("meh.");
|
||||
|
||||
table[native.Name] = native;
|
||||
}
|
||||
|
||||
_linked[type] = table;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public delegate Value Getter(Obj? self);
|
||||
public delegate void Setter(Obj? self, Value value);
|
||||
public delegate Value Method(Obj? self, Value[] args);
|
||||
|
|
@ -73,4 +153,4 @@ public static class Natives
|
|||
{
|
||||
Register(type, new NativeMethod(info.Name, info.IsStatic, method));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
@ -4,10 +4,12 @@ namespace Qrakhen.Qamp.Core.Values.Objects;
|
|||
|
||||
public class ObjTable(int size = 4096)
|
||||
{
|
||||
public static readonly ObjTable Shared = new ObjTable(4096);
|
||||
public static readonly ObjTable Global = new ObjTable(4096);
|
||||
|
||||
private readonly Table<Obj> _table = new Table<Obj>(size);
|
||||
|
||||
public IEnumerable<KeyValuePair<Address, Obj>> Entries => _table;
|
||||
|
||||
public Obj? Get(Address address) => _table[address];
|
||||
public T? Get<T>(Address address) where T : Obj => _table[address] as T;
|
||||
public bool TryGet(Address address, out Obj? obj) => _table.TryGet(address, out obj);
|
||||
|
|
@ -28,7 +30,7 @@ public class Obj : ITypedValue
|
|||
public Obj(ValueType type)
|
||||
{
|
||||
Type = type;
|
||||
__Address = ObjTable.Shared.Register(this);
|
||||
__Address = ObjTable.Global.Register(this);
|
||||
}
|
||||
|
||||
public static Value Create(Obj obj)
|
||||
|
|
|
|||
|
|
@ -11,12 +11,52 @@ public class String(string? value) : Obj(ValueType.String)
|
|||
|
||||
public override string ToString() => Value ?? "null";
|
||||
|
||||
public Value SubString(Value start, Value length)
|
||||
{
|
||||
if (Value == null)
|
||||
throw new RuntimeException($"Can not call substring on an empty string.");
|
||||
|
||||
int index = start.IsInteger ?
|
||||
(int)start.Signed :
|
||||
throw new RuntimeException($"{nameof(String)}.{nameof(SubString)} requires first parameter 'start' to be an integer.");
|
||||
|
||||
if (index < 0 || index >= Value.Length)
|
||||
throw new RuntimeException($"Parameter 'start' {start} was out of bounds.");
|
||||
|
||||
if (length.IsVoid)
|
||||
return String.Make(Value.Substring(index));
|
||||
|
||||
int _length = length.IsInteger ?
|
||||
(int)length.Signed :
|
||||
throw new RuntimeException($"{nameof(String)}.{nameof(SubString)} requires second parameter 'length' to be an integer.");
|
||||
|
||||
return String.Make(Value.Substring(index, _length));
|
||||
}
|
||||
|
||||
public Value IndexOf(Value needle)
|
||||
{
|
||||
if (!needle.IsString)
|
||||
throw new RuntimeException($"Parameter 'needle' is expected to be of type string, got {needle} instead.");
|
||||
|
||||
return new Value(Value?.IndexOf(needle.Ptr.As<String>()!.Value!) ?? -1L);
|
||||
}
|
||||
|
||||
public Value Split(Value delimiter)
|
||||
{
|
||||
return Values.Value.Void;
|
||||
}
|
||||
|
||||
public Value Length()
|
||||
{
|
||||
return new Value(Value?.Length ?? 0L);
|
||||
}
|
||||
|
||||
public uint GetHash()
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
if (string.IsNullOrEmpty(Value))
|
||||
return 0;
|
||||
|
||||
return Value.GetHash();
|
||||
return Value?.GetHash() ?? 0;
|
||||
}
|
||||
|
||||
public static Value Make(string? value)
|
||||
|
|
|
|||
|
|
@ -9,13 +9,11 @@ namespace Qrakhen.Qamp.Core.Values;
|
|||
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
|
||||
public readonly struct Ptr
|
||||
{
|
||||
private static readonly Table<Obj> _register = []; // should not be needed that anymore tbh.
|
||||
|
||||
[Serialized] public readonly Address Address;
|
||||
|
||||
public Obj? Value {
|
||||
get {
|
||||
if (ObjTable.Shared.TryGet(Address, out Obj? obj))
|
||||
if (ObjTable.Global.TryGet(Address, out Obj? obj))
|
||||
return obj;
|
||||
return null;
|
||||
}
|
||||
|
|
@ -28,7 +26,7 @@ public readonly struct Ptr
|
|||
|
||||
public static Ptr Create(Obj obj)
|
||||
{
|
||||
return new Ptr(_register.Add(obj));
|
||||
return new Ptr(obj.__Address);
|
||||
}
|
||||
|
||||
public T? As<T>(bool throwWhenNull = true) where T : Obj
|
||||
|
|
@ -52,7 +50,7 @@ public readonly struct Ptr
|
|||
public static async Task __GC_Prepare()
|
||||
{
|
||||
await Task.Run(() => {
|
||||
foreach (var obj in _register) {
|
||||
foreach (var obj in ObjTable.Global.Entries) {
|
||||
obj.Value.__GC_Count = 0;
|
||||
}
|
||||
});
|
||||
|
|
@ -61,7 +59,7 @@ public readonly struct Ptr
|
|||
public static byte[] SerializeFunctions()
|
||||
{
|
||||
List<byte> result = new();
|
||||
foreach (var element in _register) {
|
||||
foreach (var element in ObjTable.Global.Entries) {
|
||||
Obj obj = element.Value;
|
||||
if (obj == null || obj.__GC_Marked)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -87,10 +87,12 @@ public readonly struct Value :
|
|||
public T ValueType => Type;
|
||||
public TypeInfo TypeInfo => TypeInfo.FromValue(this);
|
||||
|
||||
public bool IsVoid => Type == T.Void;
|
||||
public bool IsNumber => Type <= T.Decimal;
|
||||
public bool IsSigned => Is(T.Signed);
|
||||
public bool IsUnsigned => Is(T.Unsigned);
|
||||
public bool IsDecimal => Is(T.Decimal);
|
||||
public bool IsInteger => IsSigned || IsUnsigned;
|
||||
public bool IsBool => Is(T.Bool);
|
||||
public bool IsString => Is(T.String);
|
||||
public bool IsRef => Is(T.Reference, false);
|
||||
|
|
|
|||
Loading…
Reference in New Issue