Compare commits
No commits in common. "44b8bb89002ca2bc68754aece8effcc57b42ad8b" and "c42a9a42e6386c7367ea80ef286a025002bec83c" have entirely different histories.
44b8bb8900
...
c42a9a42e6
|
|
@ -33,14 +33,14 @@ public static class Benchmark
|
|||
|
||||
if (!_clocks.TryGetValue(key, out Stopwatch? sw)) {
|
||||
sw = _clocks[key] = new Stopwatch();
|
||||
IO.Console.Write($" ::: Registered new benchmark clock '{key}'");
|
||||
IO.Console.Write($" ::: Registered new benchmark clock '{key}'\n");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
IO.Console.Write($" ::: {key}:{line} > {message}");
|
||||
IO.Console.Write($" ::: {key}:{line} > {message}\n");
|
||||
|
||||
if (sw.IsRunning)
|
||||
IO.Console.Write($" ::: {key}:{line} > already running with an elapsed time of {sw.Elapsed}. clock was reset.");
|
||||
IO.Console.Write($" ::: {key}:{line} > already running with an elapsed time of {sw.Elapsed}. clock was reset.\n");
|
||||
|
||||
sw.Reset();
|
||||
sw.Start();
|
||||
|
|
@ -60,12 +60,16 @@ public static class Benchmark
|
|||
string key = MakeKey(id, member, file);
|
||||
|
||||
if (!_clocks.TryGetValue(key, out Stopwatch sw)) {
|
||||
_logger.Debug($"No clock found for '{key}', start one first using Benchmark.Start()");
|
||||
#if LOG
|
||||
_logger.Debug($"No clock found for '{key}', start one first using Benchmark.Start()");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
IO.Console.Write($" ::: {key}:{line} > {message ?? "Elapsed"}: {sw.Elapsed}");
|
||||
#if LOG
|
||||
_logger.Debug($" ::: {key}:{line} > {message ?? "Elapsed"}: {sw.Elapsed}");
|
||||
#endif
|
||||
if (keep)
|
||||
sw.Start();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,184 +0,0 @@
|
|||
using Qrakhen.Qamp.Core.Collections.Abstractions;
|
||||
using Qrakhen.Qamp.Core.Values;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Qrakhen.Qamp.Core.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Similar to a register with <see cref="Address"/> as key;
|
||||
/// But with the caveat that the memory slots can be allocated or freed up,
|
||||
/// automatically seeking for free addresses when adding data,
|
||||
/// making it much easier to deal with addresses or pointers.
|
||||
/// </summary>
|
||||
public class Table<TValue> :
|
||||
IGetSet<Address, TValue>,
|
||||
IToArray<TValue>,
|
||||
IEnumerable<KeyValuePair<Address, TValue>>
|
||||
{
|
||||
public const int BLOCK_SIZE = sizeof(int) * 8;
|
||||
private const uint BLOCK_FULL = (uint)((1UL << 32) - 1);
|
||||
|
||||
private readonly TValue[] _data;
|
||||
private readonly uint[] _alloc;
|
||||
|
||||
public int Size { get; }
|
||||
public int Blocks => Size / BLOCK_SIZE;
|
||||
|
||||
public TValue this[Address index]
|
||||
{
|
||||
get => Get(index);
|
||||
set => Set(index, value);
|
||||
}
|
||||
|
||||
public Table(int size = 4096)
|
||||
{
|
||||
Size = size;
|
||||
if (size % BLOCK_SIZE != 0)
|
||||
throw new ArgumentException($"{nameof(Table<TValue>)} size parameter must be product (multiple) of its block size {BLOCK_SIZE}.");
|
||||
_data = new TValue[size];
|
||||
_alloc = new uint[size / BLOCK_SIZE];
|
||||
}
|
||||
|
||||
private int GetBlockIndex(Address address)
|
||||
{
|
||||
return (int)(address / BLOCK_SIZE);
|
||||
}
|
||||
|
||||
private int GetBitIndex(Address address)
|
||||
{
|
||||
return (int)(address % BLOCK_SIZE);
|
||||
}
|
||||
|
||||
public bool IsAllocated(Address address)
|
||||
{
|
||||
return (_alloc[GetBlockIndex(address)] & (1 << GetBitIndex(address))) > 0;
|
||||
}
|
||||
|
||||
private void SetAllocBit(Address address, bool value)
|
||||
{
|
||||
int block = GetBlockIndex(address);
|
||||
int index = GetBitIndex(address);
|
||||
if (value)
|
||||
_alloc[block] |= 1U << index;
|
||||
else
|
||||
_alloc[block] &= ~(1U << index);
|
||||
}
|
||||
|
||||
private Address SeekFree(Address from = default)
|
||||
{
|
||||
Address cur = from - (from % BLOCK_SIZE);
|
||||
while (cur < Size)
|
||||
{
|
||||
int block = GetBlockIndex(cur);
|
||||
uint mask = _alloc[block];
|
||||
if (mask < BLOCK_FULL)
|
||||
{
|
||||
int index = 0;
|
||||
while ((mask & (1 << index)) > 0)
|
||||
index++;
|
||||
|
||||
return (Address)((long)block * BLOCK_SIZE + index);
|
||||
}
|
||||
cur += BLOCK_SIZE;
|
||||
}
|
||||
return Address.Void;
|
||||
}
|
||||
|
||||
private bool Validate(Address address)
|
||||
{
|
||||
if (address < 0 || address >= Size)
|
||||
throw new RuntimeException($"Tried to access address outside memory {address}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Free(Address address, int size = 1)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(1, size); // only support single address freeing atm
|
||||
SetAllocBit(address, false);
|
||||
}
|
||||
|
||||
public Address Allocate(int size = 1)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(1, size); // only support single address allocation atm
|
||||
Address address = SeekFree();
|
||||
SetAllocBit(address, true);
|
||||
return address;
|
||||
}
|
||||
|
||||
public TValue[] ToArray()
|
||||
{
|
||||
return [.. _data];
|
||||
}
|
||||
|
||||
public TValue Get(Address index)
|
||||
{
|
||||
Validate(index);
|
||||
if (!IsAllocated(index))
|
||||
return default;
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
public bool TryGet(Address index, [MaybeNullWhen(false)] out TValue? value)
|
||||
{
|
||||
Validate(index);
|
||||
value = default;
|
||||
if (!IsAllocated(index))
|
||||
return false;
|
||||
value = _data[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Set(Address index, TValue value)
|
||||
{
|
||||
Validate(index);
|
||||
SetAllocBit(index, true);
|
||||
_data[index] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="Allocate(int)"/> and then <see cref="Set(Address, TValue)"/>,
|
||||
/// returning the address at which <paramref name="value"/> was stored at.
|
||||
/// </summary>
|
||||
public Address Add(TValue value)
|
||||
{
|
||||
Address free = SeekFree();
|
||||
if (free == Address.Void)
|
||||
throw new QampException($"Tried to add {value} to a memory block, but no free memory could be allocated.");
|
||||
Set(free, value);
|
||||
return free;
|
||||
}
|
||||
|
||||
public string Print()
|
||||
{
|
||||
string r = $"Table [{nameof(TValue)}]:\n";
|
||||
int c = 0;
|
||||
for (int b = 0; b < Blocks; b++)
|
||||
{
|
||||
uint block = _alloc[b];
|
||||
if (block == 0)
|
||||
continue;
|
||||
for (int i = 0; i < BLOCK_SIZE; i++)
|
||||
{
|
||||
Address address = MakeAddress(b, i);
|
||||
if (!IsAllocated(address))
|
||||
continue;
|
||||
c++;
|
||||
r += $" {address}: {Get(address)}\n";
|
||||
}
|
||||
}
|
||||
r += $"{c}/{Size - c} slots allocated.";
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Address MakeAddress(int block, int index) => new((long)block * BLOCK_SIZE + index);
|
||||
|
||||
public IEnumerator<KeyValuePair<Address, TValue>> GetEnumerator()
|
||||
{
|
||||
for (Address addr = 0; addr < Size; addr++)
|
||||
yield return new KeyValuePair<Address, TValue>(addr, Get(addr));
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
|
@ -76,8 +76,6 @@ public class Digester : ISteppable<Token>
|
|||
|
||||
public Function Digest()
|
||||
{
|
||||
Benchmark.Active = true;
|
||||
Benchmark.Start($"Digest begin");
|
||||
#if LOG
|
||||
_logger.Method();
|
||||
#endif
|
||||
|
|
@ -103,7 +101,6 @@ public class Digester : ISteppable<Token>
|
|||
#if LOG
|
||||
_logger.Verbose(_debug.Debug());
|
||||
#endif
|
||||
Benchmark.End($"Digest end");
|
||||
return function;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -299,9 +299,6 @@ public static class ExpressionParser
|
|||
case TokenType.BitwiseNot:
|
||||
digester.Emit(OpCode.BitwiseNot);
|
||||
break;
|
||||
case TokenType.AddressOf:
|
||||
digester.Emit(OpCode.Addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +357,6 @@ public static class ExpressionParser
|
|||
_rules[TokenType.Or] = new Rule(null, Or, Weight.Or);
|
||||
_rules[TokenType.Print] = new Rule(null, null, Weight.None);
|
||||
_rules[TokenType.TypeOf] = new Rule(TypeOf, null, Weight.None);
|
||||
_rules[TokenType.AddressOf] = new Rule(Modifier, null, Weight.Term);
|
||||
_rules[TokenType.Export] = new Rule(null, null, Weight.None);
|
||||
_rules[TokenType.Import] = new Rule(null, null, Weight.None);
|
||||
_rules[TokenType.Return] = new Rule(null, null, Weight.None);
|
||||
|
|
|
|||
|
|
@ -1,57 +1,20 @@
|
|||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace Qrakhen.Qamp.Core.IO;
|
||||
namespace Qrakhen.Qamp.Core.IO;
|
||||
|
||||
public static class Console
|
||||
{
|
||||
public static OutputStreamMessageHandler StdOut;
|
||||
public static ErrorStreamMessageHandler StdErr;
|
||||
public static InputStreamListenHandler StdIn;
|
||||
public static MessageFormatter Formatter;
|
||||
// TEMP
|
||||
public static Action<string>? StdOut;
|
||||
// TEMP
|
||||
|
||||
static Console()
|
||||
public static void Write(object? any)
|
||||
{
|
||||
StdOut = System.Console.Out.WriteLine;
|
||||
StdErr = System.Console.Error.WriteLine;
|
||||
StdIn = System.Console.In.ReadLine;
|
||||
Formatter = (p) => {
|
||||
if (p.Length == 0)
|
||||
return [];
|
||||
return p.Select(x => x.ToString()).ToArray() ?? [];
|
||||
};
|
||||
string[] split = $"{any ?? "null"}".Split('\n');
|
||||
for (int i = 0; i < split.Length; i++) {
|
||||
string line = (i == 0 ? " :> " : " ") + $"{split[i]}\n";
|
||||
if (StdOut == null)
|
||||
System.Console.Write(line);
|
||||
else
|
||||
StdOut(line);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Write(params object[] parameters)
|
||||
{
|
||||
if (StdOut == null)
|
||||
return;
|
||||
|
||||
var lines = Formatter(parameters);
|
||||
foreach (var line in lines)
|
||||
StdOut(line);
|
||||
}
|
||||
|
||||
public static string? Read()
|
||||
{
|
||||
if (StdIn == null)
|
||||
throw new InvalidOperationException($"No stdin provided, can not read input stream.");
|
||||
|
||||
return StdIn();
|
||||
}
|
||||
|
||||
public static void Error(params object[] parameters)
|
||||
{
|
||||
if (StdErr == null)
|
||||
Write(parameters);
|
||||
|
||||
var lines = Formatter(parameters);
|
||||
foreach (var line in lines)
|
||||
StdErr(line);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate string? InputStreamListenHandler();
|
||||
public delegate void OutputStreamMessageHandler(string? message);
|
||||
public delegate void ErrorStreamMessageHandler(string? message);
|
||||
|
||||
public delegate string?[] MessageFormatter(params object[] parameters);
|
||||
}
|
||||
|
|
@ -1,131 +1,90 @@
|
|||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Mask bits, to be used like <c>opCode & F_Mask == F_Operation</c>, for example.
|
||||
/// </summary>
|
||||
Mask_Handler = 0xF0,
|
||||
Mask_Operation = 0x0F,
|
||||
F_Mask = 0xF0,
|
||||
|
||||
Error = -1,
|
||||
Void = 0,
|
||||
None = 0x00,
|
||||
Constant = 0x01,
|
||||
Null = 0x02,
|
||||
Pop = 0x03,
|
||||
Cast = 0x04,
|
||||
|
||||
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,
|
||||
False = 0x0e,
|
||||
True = 0x0f,
|
||||
|
||||
Pop = F_Static | 7,
|
||||
Val = 0x10,
|
||||
Ref = 0x11,
|
||||
|
||||
F_Retrieve = 0x20,
|
||||
Val = F_Retrieve | 1,
|
||||
Ref = F_Retrieve | 2,
|
||||
Addr = F_Retrieve | 3,
|
||||
SetGlobal = 0x20,
|
||||
GetGlobal = 0x21,
|
||||
GetLocal = 0x22,
|
||||
SetLocal = 0x23,
|
||||
GetOuter = 0x24,
|
||||
SetOuter = 0x25,
|
||||
GetMember = 0x26,
|
||||
SetMember = 0x27,
|
||||
|
||||
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,
|
||||
DefineGlobal = 0x30,
|
||||
CloseOuter = 0x31,
|
||||
|
||||
//AssignValue = 0x0, // unsure
|
||||
//AssignReference = 0x0, // unsure
|
||||
AssignValue = 0x40, // unsure
|
||||
AssignReference = 0x41, // unsure
|
||||
|
||||
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_Operation = 0x60,
|
||||
Add = 0x60,
|
||||
Subtract = 0x61,
|
||||
Multiply = 0x62,
|
||||
Divide = 0x63,
|
||||
Modulo = 0x64,
|
||||
BitwiseAnd = 0x65,
|
||||
BitwiseOr = 0x66,
|
||||
BitwiseXor = 0x67,
|
||||
BitwiseLeft = 0x68,
|
||||
BitwiseRight = 0x69,
|
||||
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
|
||||
}
|
||||
|
||||
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})";
|
||||
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
|
||||
}
|
||||
|
|
@ -25,66 +25,17 @@ public class Call
|
|||
}
|
||||
}
|
||||
|
||||
public interface IOperationResolver
|
||||
{
|
||||
bool CanResolve(OpCode code);
|
||||
OperationResult Resolve(Runner runner, OpCode code);
|
||||
}
|
||||
|
||||
public struct OperationResult
|
||||
{
|
||||
public bool Success;
|
||||
public Value Value;
|
||||
public RuntimeException? Error;
|
||||
|
||||
public static OperationResult MakeSuccess(Value value)
|
||||
{
|
||||
return new OperationResult { Success = true, Value = value };
|
||||
}
|
||||
|
||||
public static OperationResult MakeError(RuntimeException exception)
|
||||
{
|
||||
return new OperationResult { Success = false, Value = Value.Void, Error = exception };
|
||||
}
|
||||
}
|
||||
|
||||
public class OperationRouter
|
||||
{
|
||||
private readonly List<IOperationResolver> _handlers = [];
|
||||
private readonly Dictionary<OpCode, IOperationResolver> _cache = [];
|
||||
|
||||
public void Register(IOperationResolver resolver)
|
||||
{
|
||||
_handlers.Add(resolver);
|
||||
}
|
||||
|
||||
public IOperationResolver? Route(Runner runner, OpCode opCode)
|
||||
{
|
||||
if (!_cache.TryGetValue(opCode, out IOperationResolver? resolver)) {
|
||||
resolver = _handlers.FirstOrDefault(h => h.CanResolve(opCode));
|
||||
if (resolver == null)
|
||||
return null;
|
||||
|
||||
_cache[opCode] = resolver;
|
||||
}
|
||||
|
||||
return _cache[opCode];
|
||||
}
|
||||
}
|
||||
|
||||
public class Runner : IDisposable
|
||||
{
|
||||
private readonly ILogger _logger = LoggerService.Get<Runner>();
|
||||
private readonly OperationRouter _router = new OperationRouter();
|
||||
|
||||
public readonly Options Options;
|
||||
|
||||
private List<IOperationResolver> _resolvers = [];
|
||||
private StackLike<Call> _calls;
|
||||
private StackLike<Call> Calls;
|
||||
|
||||
// todo: maybe have a pointer everywhere to stacks or arrays and use those rather than built-in pointers or cursors... ?
|
||||
private StackLike<Value> _stack;
|
||||
private long _cursor => _stack.Position;
|
||||
private StackLike<Value> Stack;
|
||||
private long Cursor => Stack.Position;
|
||||
|
||||
private List<Outer> Outers = [];
|
||||
|
||||
|
|
@ -95,12 +46,9 @@ public class Runner : IDisposable
|
|||
public Runner(Options options)
|
||||
{
|
||||
Options = options;
|
||||
_calls = new StackLike<Call>(options.MaxCalls);
|
||||
_stack = new StackLike<Value>(options.InitialStackSize);
|
||||
IO.Console.StdOut ??= options.StdOut;
|
||||
IO.Console.StdIn ??= options.StdIn;
|
||||
|
||||
_router.Register(new ArithmeticResolver());
|
||||
Calls = new StackLike<Call>(options.MaxCalls);
|
||||
Stack = new StackLike<Value>(options.InitialStackSize);
|
||||
IO.Console.StdOut = options.StdOut;
|
||||
}
|
||||
|
||||
public ExecutionResult Run(Stream stream)
|
||||
|
|
@ -108,36 +56,31 @@ public class Runner : IDisposable
|
|||
#if LOG
|
||||
_logger.Method();
|
||||
#endif
|
||||
if (_stack.Position != 0) {
|
||||
if (Stack.Position != 0) {
|
||||
#if LOG
|
||||
_logger.Warn($"Something went wrong, stack cursor is at {_stack.Position}. Resetting Stack.");
|
||||
_logger.Warn($"Something went wrong, stack cursor is at {Stack.Position}. Resetting Stack.");
|
||||
#endif
|
||||
_stack = new StackLike<Value>(Options.InitialStackSize);
|
||||
Stack = new StackLike<Value>(Options.InitialStackSize);
|
||||
}
|
||||
Benchmark.Start($"Compile Start");
|
||||
using Reader reader = new Reader(stream);
|
||||
Compilation.Digester digester = new(reader);
|
||||
Function function = digester.Digest();
|
||||
Benchmark.End($"Compile End");
|
||||
if (stream.Length > 64)
|
||||
File.WriteAllBytes($"./func.{DateTime.Now:yyyyMMdd_HHmmss}.sqi", function.Serialize(true));
|
||||
Context closure = new Context(function);
|
||||
Push(Obj.Create(closure));
|
||||
Invoke(closure, 0);
|
||||
Benchmark.Start($"Execution Start");
|
||||
ExecutionResult result = Execute();
|
||||
Benchmark.End($"Execution End");
|
||||
return result;
|
||||
return Execute();
|
||||
}
|
||||
|
||||
private bool TestHandler(Op code, Op handler) => (code & Op.Mask_Handler) == handler;
|
||||
private bool TestFlag(Op code, Op flag) => (code & Op.F_Mask) == flag;
|
||||
|
||||
private ExecutionResult Execute()
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method();
|
||||
#endif
|
||||
Call call = _calls.Peek(-1);
|
||||
Call call = Calls.Peek(-1);
|
||||
do {
|
||||
Op opCode = call.Instruction.Next();
|
||||
#if LOG
|
||||
|
|
@ -146,15 +89,15 @@ public class Runner : IDisposable
|
|||
|
||||
//IO.Console.Write($"{opCode}: \n - {string.Join("\n - ", Stack.ToArray().Subset(0, Stack.Count))}");
|
||||
|
||||
IOperationResolver? resolver = _router.Route(this, opCode);
|
||||
if (TestFlag(opCode, Op.F_Operation)) {
|
||||
Value result = ValueOperation.Resolve(PopOperation(opCode));
|
||||
Push(result);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resolver != null)
|
||||
{
|
||||
OperationResult result = resolver.Resolve(this, opCode);
|
||||
if (!result.Success)
|
||||
{
|
||||
_logger.Error(result.Error?.Message);
|
||||
}
|
||||
if (TestFlag(opCode, Op.F_Compare)) {
|
||||
Value result = ValueOperation.Resolve(PopOperation(opCode));
|
||||
Push(result);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -175,18 +118,10 @@ public class Runner : IDisposable
|
|||
Push(Value.False);
|
||||
break;
|
||||
|
||||
case Op.Addr: {
|
||||
Value value = Pop();
|
||||
if (!value.IsObj)
|
||||
return Error($"can not get address of a value on stack");
|
||||
Push(new Value(value.Ptr.Address));
|
||||
break;
|
||||
}
|
||||
|
||||
case Op.GetGlobal: {
|
||||
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return Error($"tried to get global variable with empty name");
|
||||
return Error($"tried to set global variable with empty name");
|
||||
if (!Globals.Has(name))
|
||||
Push(Value.Void);
|
||||
else
|
||||
|
|
@ -306,16 +241,14 @@ 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<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}!");
|
||||
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}!");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -370,7 +303,7 @@ public class Runner : IDisposable
|
|||
byte count = call.Instruction.Next();
|
||||
if (!Invoke(Peek(-(count + 1)), count))
|
||||
return Error($"Could not invoke function.");
|
||||
call = _calls.Peek();
|
||||
call = Calls.Peek();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -379,7 +312,7 @@ public class Runner : IDisposable
|
|||
byte argumentCount = call.Instruction.Next();
|
||||
if (!Invoke(name, argumentCount))
|
||||
return Error($"Could not invoke member {name}.");
|
||||
call = _calls.Peek();
|
||||
call = Calls.Peek();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +322,7 @@ public class Runner : IDisposable
|
|||
Class @class = Pop().Ptr.As<Class>()!;
|
||||
if (!Invoke(@class, name, argumentCount))
|
||||
return Error($"Could not invoke member {name} of base class {@class.Name}!");
|
||||
call = _calls.Peek();
|
||||
call = Calls.Peek();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -410,7 +343,7 @@ public class Runner : IDisposable
|
|||
}
|
||||
|
||||
case Op.CloseOuter: {
|
||||
CloseOuters(new Pointer<Value>(_stack, _cursor));
|
||||
CloseOuters(new Pointer<Value>(Stack, Cursor));
|
||||
Pop();
|
||||
break;
|
||||
}
|
||||
|
|
@ -418,9 +351,9 @@ public class Runner : IDisposable
|
|||
case Op.Return: {
|
||||
Value result = Pop();
|
||||
CloseOuters(call.StackPtr.Branch());
|
||||
_calls.Pop();
|
||||
if (_calls.Count == 0) {
|
||||
if (_stack.Count > 0) {
|
||||
Calls.Pop();
|
||||
if (Calls.Count == 0) {
|
||||
if (Stack.Count > 0) {
|
||||
// todo: very bad fix for our stack problem
|
||||
Pop();
|
||||
} else {
|
||||
|
|
@ -428,9 +361,9 @@ public class Runner : IDisposable
|
|||
}
|
||||
return ExecutionResult.OK;
|
||||
}
|
||||
_stack.Decimate(call.StackPtr.Cursor);
|
||||
Stack.Decimate(call.StackPtr.Cursor);
|
||||
Push(result);
|
||||
call = _calls.Peek();
|
||||
call = Calls.Peek();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -481,8 +414,8 @@ public class Runner : IDisposable
|
|||
}
|
||||
|
||||
case Op.PrintStack: {
|
||||
for (int i = 0; i < _cursor; i++) {
|
||||
IO.Console.Write($"{_stack[i].ToString(true)}");
|
||||
for (int i = 0; i < Cursor; i++) {
|
||||
IO.Console.Write($"{Stack[i].ToString(true)}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -545,7 +478,7 @@ public class Runner : IDisposable
|
|||
values[i] = Peek(-i - 1);
|
||||
}
|
||||
|
||||
_stack.Decimate(_stack.Position - argumentCount - 1);
|
||||
Stack.Decimate(Stack.Position - argumentCount - 1);
|
||||
Value result = extension.Call(target, values);
|
||||
Push(result);
|
||||
return true;
|
||||
|
|
@ -561,13 +494,13 @@ public class Runner : IDisposable
|
|||
return false;
|
||||
}
|
||||
|
||||
if (_calls.Count > Options.MaxCalls) {
|
||||
Error($"Stack overflow {_calls.Count}");
|
||||
if (Calls.Count > Options.MaxCalls) {
|
||||
Error($"Stack overflow {Calls.Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
Call call = new(closure, _stack, _cursor - argumentCount - 1);
|
||||
_calls.Push(call);
|
||||
Call call = new(closure, Stack, Cursor - argumentCount - 1);
|
||||
Calls.Push(call);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -608,7 +541,7 @@ public class Runner : IDisposable
|
|||
|
||||
if (value.Is(T.Method)) {
|
||||
Method method = value.Ptr.As<Method>()!;
|
||||
_stack.Set(_cursor - method.Function.ArgumentCount - 1, method.Receiver);
|
||||
Stack.Set(Cursor - method.Function.ArgumentCount - 1, method.Receiver);
|
||||
return Invoke(method, method.Function.ArgumentCount);
|
||||
} else if (value.Is(T.Context)) {
|
||||
Context? context = value.Ptr.As<Context>()!;
|
||||
|
|
@ -617,7 +550,7 @@ public class Runner : IDisposable
|
|||
throw new NotImplementedException("i dont really want globals in this language so lets see");
|
||||
} else if (value.Is(T.Class)) {
|
||||
Class? @class = value.Ptr.As<Class>()!;
|
||||
_stack.Set(_cursor - argumentCount - 1, Instantiate(@class));
|
||||
Stack.Set(Cursor - argumentCount - 1, Instantiate(@class));
|
||||
if (@class.Members.TryGet(@class.Name, out Value ctor)) {
|
||||
Context? context = ctor.Ptr.As<Context>()!;
|
||||
return Invoke(context, argumentCount);
|
||||
|
|
@ -660,28 +593,40 @@ public class Runner : IDisposable
|
|||
Pop();
|
||||
}
|
||||
|
||||
public Value Peek(int delta = -1)
|
||||
private Value Peek(int delta = -1)
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method();
|
||||
#endif
|
||||
return _stack.Peek(delta);
|
||||
return Stack.Peek(delta);
|
||||
}
|
||||
|
||||
public Value Pop()
|
||||
private Value Pop()
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method();
|
||||
#endif
|
||||
return _stack.Pop();
|
||||
return Stack.Pop();
|
||||
}
|
||||
|
||||
public void Push(Value value)
|
||||
private void Push(Value value)
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method($"{value}");
|
||||
#endif
|
||||
_stack.Push(value);
|
||||
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)
|
||||
|
|
@ -703,7 +648,7 @@ public class Runner : IDisposable
|
|||
#if LOG
|
||||
_logger.Method();
|
||||
#endif
|
||||
_stack.Decimate(0);
|
||||
Stack.Decimate(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -728,14 +673,14 @@ public enum ExecutionResult
|
|||
}
|
||||
|
||||
public readonly struct Options(
|
||||
IO.OutputStreamMessageHandler stdOut = null,
|
||||
IO.InputStreamListenHandler stdIn = null,
|
||||
Action<string>? stdOut = null,
|
||||
Action<string>? stdIn = null,
|
||||
int maxCalls = 0x100,
|
||||
int stackSize = 0x200,
|
||||
int initialStackSize = 0x80)
|
||||
{
|
||||
public readonly IO.OutputStreamMessageHandler StdOut = stdOut;
|
||||
public readonly IO.InputStreamListenHandler StdIn = stdIn;
|
||||
public readonly Action<string>? StdOut = stdOut;
|
||||
public readonly Action<string>? StdIn = stdIn;
|
||||
public readonly int MaxCalls = maxCalls;
|
||||
public readonly int StackSize = stackSize;
|
||||
public readonly int InitialStackSize = initialStackSize;
|
||||
|
|
|
|||
|
|
@ -8,36 +8,19 @@ public readonly record struct Operation(OpCode OpCode, Value Left, Value Right);
|
|||
|
||||
public delegate Value OperationHandler(Operation operation);
|
||||
|
||||
public class ArithmeticResolver : IOperationResolver
|
||||
public static class ValueOperation
|
||||
{
|
||||
private readonly ILogger _logger = LoggerService.Get(nameof(ArithmeticResolver));
|
||||
private static readonly ILogger _logger = LoggerService.Get(nameof(ValueOperation));
|
||||
|
||||
private readonly Register<OpCode, OperationHandler> _operations;
|
||||
private static readonly Register<OpCode, OperationHandler> _operations;
|
||||
|
||||
public bool CanResolve(OpCode opCode) => opCode.IsHandledByAlu();
|
||||
|
||||
private Operation PopOperation(Runner runner, OpCode code)
|
||||
public static Value Resolve(Operation operation)
|
||||
{
|
||||
#if LOG
|
||||
_logger.Method(code);
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
public OperationResult Resolve(Runner runner, OpCode opCode)
|
||||
{
|
||||
Operation operation = PopOperation(runner, opCode);
|
||||
|
||||
#if LOG
|
||||
_logger.Method($"{operation.Left} {operation.OpCode} {operation.Right}");
|
||||
#endif
|
||||
|
||||
// p sure switch is faster here (as opposed to dict lookup)
|
||||
Value result = operation.OpCode switch {
|
||||
return operation.OpCode switch {
|
||||
OpCode.Equal => Equal(operation),
|
||||
OpCode.Greater => Greater(operation),
|
||||
OpCode.Less => Less(operation),
|
||||
|
|
@ -56,16 +39,9 @@ public class ArithmeticResolver : IOperationResolver
|
|||
OpCode.BitwiseXor => BitwiseXor(operation),
|
||||
_ => throw new NotImplementedException($"Unknown operator {operation.OpCode}.")
|
||||
};
|
||||
|
||||
#if LOG
|
||||
_logger.Verbose($"Result: {result}");
|
||||
#endif
|
||||
|
||||
runner.Push(result);
|
||||
return OperationResult.MakeSuccess(result);
|
||||
}
|
||||
|
||||
public ArithmeticResolver()
|
||||
static ValueOperation()
|
||||
{
|
||||
_operations = new Register<OpCode, OperationHandler> {
|
||||
{ OpCode.Equal, Equal },
|
||||
|
|
@ -285,7 +285,7 @@ public class Reader : IReader<Token>, IDisposable
|
|||
'&' => Check('&') ?
|
||||
MakeToken(And, buffer + Next()) :
|
||||
Check(':') ?
|
||||
MakeToken(AddressOf, buffer + Next()) :
|
||||
MakeToken(Address, buffer + Next()) :
|
||||
MakeToken(BitwiseAnd, buffer),
|
||||
'^' => Check('~') ?
|
||||
MakeToken(Base, buffer + Next()) :
|
||||
|
|
@ -318,9 +318,9 @@ public class Reader : IReader<Token>, IDisposable
|
|||
MakeToken(Var, buffer + Next()) :
|
||||
MakeToken(Star, buffer),
|
||||
'=' => Check('=') ?
|
||||
MakeToken(EqualEqual, buffer + Next()) :
|
||||
MakeToken(Equal, buffer),
|
||||
'<' => Peek(0) switch {
|
||||
MakeToken(EqualEqual, buffer + Next()) :
|
||||
MakeToken(Equal, buffer),
|
||||
'<' => Peek(1) switch {
|
||||
'~' => MakeToken(Equal, buffer + Next()),
|
||||
':' => MakeToken(Return, buffer + Next()),
|
||||
'+' => MakeToken(PlusEqual, buffer + Next()),
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public enum TokenType
|
|||
BitwiseNot = Operator | 9,
|
||||
BitwiseLeft = Operator | 10,
|
||||
BitwiseRight = Operator | 11,
|
||||
AddressOf = Operator | 12, // returns the address of a ptr/object as a value
|
||||
Address = Operator | 12, // returns the address of a ptr/object as a value
|
||||
|
||||
Assignment = 1 << 9,
|
||||
Equal = Assignment | 1,
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ namespace Qrakhen.Qamp.Core.Values;
|
|||
[StructLayout(LayoutKind.Sequential, Size = sizeof(long))]
|
||||
public readonly record struct Address(long Value)
|
||||
{
|
||||
public static readonly Address Void = new Address(-1);
|
||||
|
||||
public override string ToString() => Value < 0 ? "&void" : $"&{Value:x8}";
|
||||
public override string ToString() => $"0x{Value:x8}";
|
||||
|
||||
public static implicit operator long(Address address) => address.Value;
|
||||
public static implicit operator Address(long value) => new(value);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ 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,86 +3,6 @@ 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);
|
||||
|
|
@ -153,4 +73,4 @@ public static class Natives
|
|||
{
|
||||
Register(type, new NativeMethod(info.Name, info.IsStatic, method));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
@ -1,42 +1,23 @@
|
|||
using Qrakhen.Qamp.Core.Collections;
|
||||
namespace Qrakhen.Qamp.Core.Values.Objects;
|
||||
|
||||
namespace Qrakhen.Qamp.Core.Values.Objects;
|
||||
|
||||
public class ObjTable(int size = 4096)
|
||||
public class Obj(ValueType type) : IValue
|
||||
{
|
||||
public static readonly ObjTable Global = new ObjTable(4096);
|
||||
public bool __gcMarked = false;
|
||||
public int __gcCount = 1;
|
||||
|
||||
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);
|
||||
public void Free(Address address) => _table.Free(address);
|
||||
public Address Register(Obj obj) => _table.Add(obj);
|
||||
}
|
||||
|
||||
public class Obj : ITypedValue
|
||||
{
|
||||
internal bool __GC_Marked = false;
|
||||
internal int __GC_Count = 1;
|
||||
|
||||
internal readonly Address __Address; // unsure lol
|
||||
public readonly ValueType Type;
|
||||
public readonly ValueType Type = type;
|
||||
|
||||
public ValueType ValueType => Type;
|
||||
|
||||
public Obj(ValueType type)
|
||||
{
|
||||
Type = type;
|
||||
__Address = ObjTable.Global.Register(this);
|
||||
}
|
||||
|
||||
public static Value Create(Obj obj)
|
||||
{
|
||||
return new Value(Ptr.Create(obj));
|
||||
}
|
||||
|
||||
public override string ToString() => "Obj<undefined>";
|
||||
|
||||
static Obj()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,52 +11,12 @@ 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() ?? 0;
|
||||
return Value.GetHash();
|
||||
}
|
||||
|
||||
public static Value Make(string? value)
|
||||
|
|
@ -76,7 +36,7 @@ public class String(string? value) : Obj(ValueType.String)
|
|||
List<byte> result = new();
|
||||
foreach (var pair in _strings) {
|
||||
String str = pair.Value;
|
||||
if (str == null || str.__GC_Marked)
|
||||
if (str == null || str.__gcMarked)
|
||||
continue;
|
||||
byte[] bytes;
|
||||
bytes = Encoding.ASCII.GetBytes(str.Value!);
|
||||
|
|
|
|||
|
|
@ -4,29 +4,36 @@ using Qrakhen.Qamp.Core.Collections;
|
|||
|
||||
namespace Qrakhen.Qamp.Core.Values;
|
||||
|
||||
// generally interesting: GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal));
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
|
||||
public readonly struct Ptr
|
||||
{
|
||||
private static readonly PushStack<Obj> _register = new();
|
||||
|
||||
[Serialized] public readonly Address Address;
|
||||
|
||||
//private readonly IntPtr _pointer;
|
||||
//public IntPtr Pointer => _pointer;
|
||||
|
||||
public Obj? Value {
|
||||
get {
|
||||
if (ObjTable.Global.TryGet(Address, out Obj? obj))
|
||||
if (_register.TryGet(Address, out Obj obj))
|
||||
return obj;
|
||||
return null;
|
||||
//if (_pointer == IntPtr.Zero)
|
||||
// throw new ObjectDisposedException(nameof(Ptr));
|
||||
//return (Obj)GCHandle.FromIntPtr(_pointer).Target!;
|
||||
}
|
||||
}
|
||||
|
||||
private Ptr(Address address)
|
||||
{
|
||||
{
|
||||
//_pointer = GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal));
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public static Ptr Create(Obj obj)
|
||||
{
|
||||
return new Ptr(obj.__Address);
|
||||
return new Ptr(_register.Add(obj));
|
||||
}
|
||||
|
||||
public T? As<T>(bool throwWhenNull = true) where T : Obj
|
||||
|
|
@ -50,8 +57,8 @@ public readonly struct Ptr
|
|||
public static async Task __GC_Prepare()
|
||||
{
|
||||
await Task.Run(() => {
|
||||
foreach (var obj in ObjTable.Global.Entries) {
|
||||
obj.Value.__GC_Count = 0;
|
||||
foreach (var obj in _register) {
|
||||
obj.__gcCount = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -59,9 +66,9 @@ public readonly struct Ptr
|
|||
public static byte[] SerializeFunctions()
|
||||
{
|
||||
List<byte> result = new();
|
||||
foreach (var element in ObjTable.Global.Entries) {
|
||||
Obj obj = element.Value;
|
||||
if (obj == null || obj.__GC_Marked)
|
||||
for (int i = 0; i < _register.Count; i++) {
|
||||
Obj obj = _register.Get(i);
|
||||
if (obj == null || obj.__gcMarked)
|
||||
continue;
|
||||
byte[] bytes;
|
||||
if (obj is Function function) {
|
||||
|
|
@ -72,7 +79,7 @@ public readonly struct Ptr
|
|||
continue;
|
||||
}
|
||||
|
||||
result.AddRange(((long)element.Key).GetBytes());
|
||||
result.AddRange(i.GetBytes());
|
||||
result.AddRange(bytes.Length.GetBytes());
|
||||
result.AddRange(bytes);
|
||||
result.Add(0);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
using Qrakhen.Qamp.Core.Abstractions;
|
||||
using Qrakhen.Qamp.Core.Values.Objects;
|
||||
using String = Qrakhen.Qamp.Core.Values.Objects.String;
|
||||
|
|
@ -8,47 +7,13 @@ using T = Qrakhen.Qamp.Core.Values.ValueType;
|
|||
|
||||
namespace Qrakhen.Qamp.Core.Values;
|
||||
|
||||
|
||||
|
||||
// HEY. IDEA.
|
||||
// have custom structs for types. this way we can handle many more thing and also more clean no????
|
||||
|
||||
public interface IPrimitive
|
||||
{
|
||||
public static abstract string Name { get; }
|
||||
public static abstract int SizeOf { get; }
|
||||
|
||||
public ulong Raw { get; }
|
||||
|
||||
public string Print();
|
||||
}
|
||||
|
||||
public readonly record struct Signed(long Value)// : IPrimitive
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public interface IValue<T>
|
||||
{
|
||||
T Data { get; }
|
||||
}
|
||||
|
||||
public interface ITypedValue
|
||||
public interface IValue
|
||||
{
|
||||
T ValueType { get; }
|
||||
}
|
||||
|
||||
public interface IDynamicValue : IValue<ulong>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||
public readonly struct Value :
|
||||
IDynamicValue,
|
||||
ISerialize<Value>,
|
||||
IDebug<string>
|
||||
public readonly struct Value : IValue, ISerialize<Value>, IDebug<string>
|
||||
{
|
||||
public static readonly Value Void = new Value();
|
||||
public static readonly Value True = new Value(true);
|
||||
|
|
@ -63,8 +28,6 @@ public readonly struct Value :
|
|||
[FieldOffset(0x00)] [Serialized] public readonly Ptr Ptr;
|
||||
[FieldOffset(0x00)] [Serialized] public readonly Ref Ref;
|
||||
|
||||
public ulong Data => Unsigned;
|
||||
|
||||
[FieldOffset(0x08)] [Serialized] public readonly T Type;
|
||||
|
||||
[FieldOffset(0x0c)] [Serialized] public readonly uint Meta; // like lengths of arrays or lists
|
||||
|
|
@ -87,12 +50,10 @@ 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);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public enum ValueType
|
|||
Integer = Unsigned | Signed | Char,
|
||||
Primitive = Integer | Decimal | Bool,
|
||||
|
||||
Address = 0x0020,
|
||||
Address = 0x0020, // coded with : symbol
|
||||
|
||||
// classes etc. here?
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue