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)) {
|
if (!_clocks.TryGetValue(key, out Stopwatch? sw)) {
|
||||||
sw = _clocks[key] = new Stopwatch();
|
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))
|
if (!string.IsNullOrEmpty(message))
|
||||||
IO.Console.Write($" ::: {key}:{line} > {message}");
|
IO.Console.Write($" ::: {key}:{line} > {message}\n");
|
||||||
|
|
||||||
if (sw.IsRunning)
|
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.Reset();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
@ -60,12 +60,16 @@ public static class Benchmark
|
||||||
string key = MakeKey(id, member, file);
|
string key = MakeKey(id, member, file);
|
||||||
|
|
||||||
if (!_clocks.TryGetValue(key, out Stopwatch sw)) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
IO.Console.Write($" ::: {key}:{line} > {message ?? "Elapsed"}: {sw.Elapsed}");
|
#if LOG
|
||||||
|
_logger.Debug($" ::: {key}:{line} > {message ?? "Elapsed"}: {sw.Elapsed}");
|
||||||
|
#endif
|
||||||
if (keep)
|
if (keep)
|
||||||
sw.Start();
|
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()
|
public Function Digest()
|
||||||
{
|
{
|
||||||
Benchmark.Active = true;
|
|
||||||
Benchmark.Start($"Digest begin");
|
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -103,7 +101,6 @@ public class Digester : ISteppable<Token>
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Verbose(_debug.Debug());
|
_logger.Verbose(_debug.Debug());
|
||||||
#endif
|
#endif
|
||||||
Benchmark.End($"Digest end");
|
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -299,9 +299,6 @@ public static class ExpressionParser
|
||||||
case TokenType.BitwiseNot:
|
case TokenType.BitwiseNot:
|
||||||
digester.Emit(OpCode.BitwiseNot);
|
digester.Emit(OpCode.BitwiseNot);
|
||||||
break;
|
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.Or] = new Rule(null, Or, Weight.Or);
|
||||||
_rules[TokenType.Print] = new Rule(null, null, Weight.None);
|
_rules[TokenType.Print] = new Rule(null, null, Weight.None);
|
||||||
_rules[TokenType.TypeOf] = new Rule(TypeOf, 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.Export] = new Rule(null, null, Weight.None);
|
||||||
_rules[TokenType.Import] = new Rule(null, null, Weight.None);
|
_rules[TokenType.Import] = new Rule(null, null, Weight.None);
|
||||||
_rules[TokenType.Return] = 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 class Console
|
||||||
{
|
{
|
||||||
public static OutputStreamMessageHandler StdOut;
|
// TEMP
|
||||||
public static ErrorStreamMessageHandler StdErr;
|
public static Action<string>? StdOut;
|
||||||
public static InputStreamListenHandler StdIn;
|
// TEMP
|
||||||
public static MessageFormatter Formatter;
|
|
||||||
|
|
||||||
static Console()
|
public static void Write(object? any)
|
||||||
{
|
{
|
||||||
StdOut = System.Console.Out.WriteLine;
|
string[] split = $"{any ?? "null"}".Split('\n');
|
||||||
StdErr = System.Console.Error.WriteLine;
|
for (int i = 0; i < split.Length; i++) {
|
||||||
StdIn = System.Console.In.ReadLine;
|
string line = (i == 0 ? " :> " : " ") + $"{split[i]}\n";
|
||||||
Formatter = (p) => {
|
if (StdOut == null)
|
||||||
if (p.Length == 0)
|
System.Console.Write(line);
|
||||||
return [];
|
else
|
||||||
return p.Select(x => x.ToString()).ToArray() ?? [];
|
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;
|
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
|
public enum OpCode
|
||||||
{
|
{
|
||||||
/// <summary>
|
F_Mask = 0xF0,
|
||||||
/// Mask bits, to be used like <c>opCode & F_Mask == F_Operation</c>, for example.
|
|
||||||
/// </summary>
|
|
||||||
Mask_Handler = 0xF0,
|
|
||||||
Mask_Operation = 0x0F,
|
|
||||||
|
|
||||||
Error = -1,
|
None = 0x00,
|
||||||
Void = 0,
|
Constant = 0x01,
|
||||||
|
Null = 0x02,
|
||||||
|
Pop = 0x03,
|
||||||
|
Cast = 0x04,
|
||||||
|
|
||||||
F_Static = 0x10,
|
False = 0x0e,
|
||||||
None = F_Static | 1,
|
True = 0x0f,
|
||||||
True = F_Static | 2,
|
|
||||||
False = F_Static | 3,
|
|
||||||
Constant = F_Static | 4,
|
|
||||||
Null = F_Static | 5,
|
|
||||||
Cast = F_Static | 6,
|
|
||||||
|
|
||||||
Pop = F_Static | 7,
|
Val = 0x10,
|
||||||
|
Ref = 0x11,
|
||||||
|
|
||||||
F_Retrieve = 0x20,
|
SetGlobal = 0x20,
|
||||||
Val = F_Retrieve | 1,
|
GetGlobal = 0x21,
|
||||||
Ref = F_Retrieve | 2,
|
GetLocal = 0x22,
|
||||||
Addr = F_Retrieve | 3,
|
SetLocal = 0x23,
|
||||||
|
GetOuter = 0x24,
|
||||||
|
SetOuter = 0x25,
|
||||||
|
GetMember = 0x26,
|
||||||
|
SetMember = 0x27,
|
||||||
|
|
||||||
F_Names = 0x30,
|
DefineGlobal = 0x30,
|
||||||
SetGlobal = F_Names | 1,
|
CloseOuter = 0x31,
|
||||||
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 = 0x0, // unsure
|
AssignValue = 0x40, // unsure
|
||||||
//AssignReference = 0x0, // unsure
|
AssignReference = 0x41, // unsure
|
||||||
|
|
||||||
F_Arithmetic = 0x40,
|
F_Operation = 0x60,
|
||||||
Add = F_Arithmetic | 1,
|
Add = 0x60,
|
||||||
Subtract = F_Arithmetic | 2,
|
Subtract = 0x61,
|
||||||
Multiply = F_Arithmetic | 3,
|
Multiply = 0x62,
|
||||||
Divide = F_Arithmetic | 4,
|
Divide = 0x63,
|
||||||
Modulo = F_Arithmetic | 5,
|
Modulo = 0x64,
|
||||||
Negate = F_Arithmetic | 7,
|
BitwiseAnd = 0x65,
|
||||||
|
BitwiseOr = 0x66,
|
||||||
|
BitwiseXor = 0x67,
|
||||||
|
BitwiseLeft = 0x68,
|
||||||
|
BitwiseRight = 0x69,
|
||||||
|
F_Unary = 0x0a,
|
||||||
|
Not = 0x6a,
|
||||||
|
Negate = 0x6b,
|
||||||
|
BitwiseNot = 0x6c,
|
||||||
|
|
||||||
F_Bitwise = 0x50,
|
F_Compare = 0x50,
|
||||||
BitwiseAnd = F_Bitwise | 1,
|
Equal = 0x50,
|
||||||
BitwiseOr = F_Bitwise | 2,
|
Greater = 0x51,
|
||||||
BitwiseXor = F_Bitwise | 3,
|
Less = 0x52,
|
||||||
BitwiseLeft = F_Bitwise | 4,
|
|
||||||
BitwiseRight = F_Bitwise | 5,
|
|
||||||
BitwiseNot = F_Bitwise | 6,
|
|
||||||
|
|
||||||
F_Boolean = 0x60,
|
//Increment = 0x70, // basically increment by, ++ simply 'makes up' the 1.
|
||||||
Not = F_Boolean | 1,
|
// = 0x71, // same here broren min
|
||||||
Equal = F_Boolean | 2,
|
|
||||||
Greater = F_Boolean | 3,
|
|
||||||
Less = F_Boolean | 4,
|
|
||||||
|
|
||||||
F_Collection = 0x70,
|
Array = 0xc0,
|
||||||
Array = F_Collection | 1,
|
List = 0xc1,
|
||||||
List = F_Collection | 2,
|
Structure = 0xc2,
|
||||||
Structure = F_Collection | 3,
|
GetItem = 0xc3,
|
||||||
GetItem = F_Collection | 4,
|
SetItem = 0xc4,
|
||||||
SetItem = F_Collection | 5,
|
AddItem = 0xc5,
|
||||||
AddItem = F_Collection | 6,
|
RemoveItem = 0xc6,
|
||||||
RemoveItem = F_Collection | 7,
|
|
||||||
|
|
||||||
F_Control = 0x80,
|
Return = 0x80,
|
||||||
Return = F_Control | 1,
|
Jump = 0x81,
|
||||||
Jump = F_Control | 2,
|
JumpIfFalse = 0x82,
|
||||||
JumpIfFalse = F_Control | 3,
|
Loop = 0x83,
|
||||||
Loop = F_Control | 4,
|
Invoke = 0x84,
|
||||||
Invoke = F_Control | 5,
|
InvokeBase = 0x85,
|
||||||
InvokeBase = F_Control | 6,
|
InvokeMember = 0x86,
|
||||||
InvokeMember = F_Control | 7,
|
Context = 0x87,
|
||||||
Context = F_Control | 8,
|
|
||||||
|
|
||||||
F_Class = 0x90,
|
Class = 0xa0,
|
||||||
Class = F_Class | 1,
|
Member = 0xa1,
|
||||||
Member = F_Class | 2,
|
Base = 0xa2,
|
||||||
Base = F_Class | 3,
|
Inherit = 0xa3,
|
||||||
Inherit = F_Class | 4,
|
Method = 0xa4,
|
||||||
Method = F_Class | 5,
|
Static = 0xa5,
|
||||||
Static = F_Class | 6,
|
|
||||||
|
|
||||||
F_Meta = 0xa0,
|
Print = 0xe0,
|
||||||
Print = F_Meta | 1,
|
PrintStack = 0xe1,
|
||||||
PrintStack = F_Meta | 2,
|
PrintGlobals = 0xe2,
|
||||||
PrintGlobals = F_Meta | 3,
|
PrintExpr = 0xe3,
|
||||||
PrintExpr = F_Meta | 4,
|
Typeof = 0xef,
|
||||||
Typeof = F_Meta | 5,
|
|
||||||
|
|
||||||
F_Module = 0xf0,
|
Export = 0xfe, // probably shouldnt be an op code but rather be done during digestion
|
||||||
Export = F_Module | 1, // 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
|
||||||
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})";
|
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
public class Runner : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger = LoggerService.Get<Runner>();
|
private readonly ILogger _logger = LoggerService.Get<Runner>();
|
||||||
private readonly OperationRouter _router = new OperationRouter();
|
|
||||||
|
|
||||||
public readonly Options Options;
|
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... ?
|
// 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 StackLike<Value> Stack;
|
||||||
private long _cursor => _stack.Position;
|
private long Cursor => Stack.Position;
|
||||||
|
|
||||||
private List<Outer> Outers = [];
|
private List<Outer> Outers = [];
|
||||||
|
|
||||||
|
|
@ -95,12 +46,9 @@ public class Runner : IDisposable
|
||||||
public Runner(Options options)
|
public Runner(Options options)
|
||||||
{
|
{
|
||||||
Options = options;
|
Options = options;
|
||||||
_calls = new StackLike<Call>(options.MaxCalls);
|
Calls = new StackLike<Call>(options.MaxCalls);
|
||||||
_stack = new StackLike<Value>(options.InitialStackSize);
|
Stack = new StackLike<Value>(options.InitialStackSize);
|
||||||
IO.Console.StdOut ??= options.StdOut;
|
IO.Console.StdOut = options.StdOut;
|
||||||
IO.Console.StdIn ??= options.StdIn;
|
|
||||||
|
|
||||||
_router.Register(new ArithmeticResolver());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExecutionResult Run(Stream stream)
|
public ExecutionResult Run(Stream stream)
|
||||||
|
|
@ -108,36 +56,31 @@ public class Runner : IDisposable
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
#endif
|
#endif
|
||||||
if (_stack.Position != 0) {
|
if (Stack.Position != 0) {
|
||||||
#if LOG
|
#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
|
#endif
|
||||||
_stack = new StackLike<Value>(Options.InitialStackSize);
|
Stack = new StackLike<Value>(Options.InitialStackSize);
|
||||||
}
|
}
|
||||||
Benchmark.Start($"Compile Start");
|
|
||||||
using Reader reader = new Reader(stream);
|
using Reader reader = new Reader(stream);
|
||||||
Compilation.Digester digester = new(reader);
|
Compilation.Digester digester = new(reader);
|
||||||
Function function = digester.Digest();
|
Function function = digester.Digest();
|
||||||
Benchmark.End($"Compile End");
|
|
||||||
if (stream.Length > 64)
|
if (stream.Length > 64)
|
||||||
File.WriteAllBytes($"./func.{DateTime.Now:yyyyMMdd_HHmmss}.sqi", function.Serialize(true));
|
File.WriteAllBytes($"./func.{DateTime.Now:yyyyMMdd_HHmmss}.sqi", function.Serialize(true));
|
||||||
Context closure = new Context(function);
|
Context closure = new Context(function);
|
||||||
Push(Obj.Create(closure));
|
Push(Obj.Create(closure));
|
||||||
Invoke(closure, 0);
|
Invoke(closure, 0);
|
||||||
Benchmark.Start($"Execution Start");
|
return Execute();
|
||||||
ExecutionResult result = Execute();
|
|
||||||
Benchmark.End($"Execution End");
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
private ExecutionResult Execute()
|
||||||
{
|
{
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
#endif
|
#endif
|
||||||
Call call = _calls.Peek(-1);
|
Call call = Calls.Peek(-1);
|
||||||
do {
|
do {
|
||||||
Op opCode = call.Instruction.Next();
|
Op opCode = call.Instruction.Next();
|
||||||
#if LOG
|
#if LOG
|
||||||
|
|
@ -146,15 +89,15 @@ public class Runner : IDisposable
|
||||||
|
|
||||||
//IO.Console.Write($"{opCode}: \n - {string.Join("\n - ", Stack.ToArray().Subset(0, Stack.Count))}");
|
//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)
|
if (TestFlag(opCode, Op.F_Compare)) {
|
||||||
{
|
Value result = ValueOperation.Resolve(PopOperation(opCode));
|
||||||
OperationResult result = resolver.Resolve(this, opCode);
|
Push(result);
|
||||||
if (!result.Success)
|
|
||||||
{
|
|
||||||
_logger.Error(result.Error?.Message);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,18 +118,10 @@ public class Runner : IDisposable
|
||||||
Push(Value.False);
|
Push(Value.False);
|
||||||
break;
|
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: {
|
case Op.GetGlobal: {
|
||||||
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
||||||
if (string.IsNullOrEmpty(name))
|
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))
|
if (!Globals.Has(name))
|
||||||
Push(Value.Void);
|
Push(Value.Void);
|
||||||
else
|
else
|
||||||
|
|
@ -306,16 +241,14 @@ public class Runner : IDisposable
|
||||||
if (!items.Is(T.ItemProvider, false))
|
if (!items.Is(T.ItemProvider, false))
|
||||||
return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)");
|
return Error($"Can not access {index} of {items} - it is not an ItemProvider! (Array, List, etc.)");
|
||||||
|
|
||||||
if (items.Is(T.Array))
|
if (items.Is(T.Array))
|
||||||
Push(items.Ptr.As<Values.Objects.Array>()!.Get(index));
|
Push(items.Ptr.As<Values.Objects.Array>()!.Get(index));
|
||||||
else if (items.Is(T.List))
|
else if (items.Is(T.List))
|
||||||
Push(items.Ptr.As<List>()!.Get(index));
|
Push(items.Ptr.As<Values.Objects.List>()!.Get(index));
|
||||||
else if (items.Is(T.Structure))
|
else if (items.Is(T.Structure))
|
||||||
Push(items.Ptr.As<Structure>()!.Get(index));
|
Push(items.Ptr.As<Values.Objects.Structure>()!.Get(index));
|
||||||
else if (items.Is(T.String))
|
else
|
||||||
Push(items.Ptr.As<String>()!.SubString(index, new Value(1L)));
|
return Error($"Unsupported native accessor for type {items.Type}!");
|
||||||
else
|
|
||||||
return Error($"Unsupported native accessor for type {items.Type}!");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,7 +303,7 @@ public class Runner : IDisposable
|
||||||
byte count = call.Instruction.Next();
|
byte count = call.Instruction.Next();
|
||||||
if (!Invoke(Peek(-(count + 1)), count))
|
if (!Invoke(Peek(-(count + 1)), count))
|
||||||
return Error($"Could not invoke function.");
|
return Error($"Could not invoke function.");
|
||||||
call = _calls.Peek();
|
call = Calls.Peek();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,7 +312,7 @@ public class Runner : IDisposable
|
||||||
byte argumentCount = call.Instruction.Next();
|
byte argumentCount = call.Instruction.Next();
|
||||||
if (!Invoke(name, argumentCount))
|
if (!Invoke(name, argumentCount))
|
||||||
return Error($"Could not invoke member {name}.");
|
return Error($"Could not invoke member {name}.");
|
||||||
call = _calls.Peek();
|
call = Calls.Peek();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,7 +322,7 @@ public class Runner : IDisposable
|
||||||
Class @class = Pop().Ptr.As<Class>()!;
|
Class @class = Pop().Ptr.As<Class>()!;
|
||||||
if (!Invoke(@class, name, argumentCount))
|
if (!Invoke(@class, name, argumentCount))
|
||||||
return Error($"Could not invoke member {name} of base class {@class.Name}!");
|
return Error($"Could not invoke member {name} of base class {@class.Name}!");
|
||||||
call = _calls.Peek();
|
call = Calls.Peek();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -410,7 +343,7 @@ public class Runner : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
case Op.CloseOuter: {
|
case Op.CloseOuter: {
|
||||||
CloseOuters(new Pointer<Value>(_stack, _cursor));
|
CloseOuters(new Pointer<Value>(Stack, Cursor));
|
||||||
Pop();
|
Pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -418,9 +351,9 @@ public class Runner : IDisposable
|
||||||
case Op.Return: {
|
case Op.Return: {
|
||||||
Value result = Pop();
|
Value result = Pop();
|
||||||
CloseOuters(call.StackPtr.Branch());
|
CloseOuters(call.StackPtr.Branch());
|
||||||
_calls.Pop();
|
Calls.Pop();
|
||||||
if (_calls.Count == 0) {
|
if (Calls.Count == 0) {
|
||||||
if (_stack.Count > 0) {
|
if (Stack.Count > 0) {
|
||||||
// todo: very bad fix for our stack problem
|
// todo: very bad fix for our stack problem
|
||||||
Pop();
|
Pop();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -428,9 +361,9 @@ public class Runner : IDisposable
|
||||||
}
|
}
|
||||||
return ExecutionResult.OK;
|
return ExecutionResult.OK;
|
||||||
}
|
}
|
||||||
_stack.Decimate(call.StackPtr.Cursor);
|
Stack.Decimate(call.StackPtr.Cursor);
|
||||||
Push(result);
|
Push(result);
|
||||||
call = _calls.Peek();
|
call = Calls.Peek();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,8 +414,8 @@ public class Runner : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
case Op.PrintStack: {
|
case Op.PrintStack: {
|
||||||
for (int i = 0; i < _cursor; i++) {
|
for (int i = 0; i < Cursor; i++) {
|
||||||
IO.Console.Write($"{_stack[i].ToString(true)}");
|
IO.Console.Write($"{Stack[i].ToString(true)}");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -545,7 +478,7 @@ public class Runner : IDisposable
|
||||||
values[i] = Peek(-i - 1);
|
values[i] = Peek(-i - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
_stack.Decimate(_stack.Position - argumentCount - 1);
|
Stack.Decimate(Stack.Position - argumentCount - 1);
|
||||||
Value result = extension.Call(target, values);
|
Value result = extension.Call(target, values);
|
||||||
Push(result);
|
Push(result);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -561,13 +494,13 @@ public class Runner : IDisposable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_calls.Count > Options.MaxCalls) {
|
if (Calls.Count > Options.MaxCalls) {
|
||||||
Error($"Stack overflow {_calls.Count}");
|
Error($"Stack overflow {Calls.Count}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Call call = new(closure, _stack, _cursor - argumentCount - 1);
|
Call call = new(closure, Stack, Cursor - argumentCount - 1);
|
||||||
_calls.Push(call);
|
Calls.Push(call);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -608,7 +541,7 @@ public class Runner : IDisposable
|
||||||
|
|
||||||
if (value.Is(T.Method)) {
|
if (value.Is(T.Method)) {
|
||||||
Method method = value.Ptr.As<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);
|
return Invoke(method, method.Function.ArgumentCount);
|
||||||
} else if (value.Is(T.Context)) {
|
} else if (value.Is(T.Context)) {
|
||||||
Context? context = value.Ptr.As<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");
|
throw new NotImplementedException("i dont really want globals in this language so lets see");
|
||||||
} else if (value.Is(T.Class)) {
|
} else if (value.Is(T.Class)) {
|
||||||
Class? @class = value.Ptr.As<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)) {
|
if (@class.Members.TryGet(@class.Name, out Value ctor)) {
|
||||||
Context? context = ctor.Ptr.As<Context>()!;
|
Context? context = ctor.Ptr.As<Context>()!;
|
||||||
return Invoke(context, argumentCount);
|
return Invoke(context, argumentCount);
|
||||||
|
|
@ -660,28 +593,40 @@ public class Runner : IDisposable
|
||||||
Pop();
|
Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Value Peek(int delta = -1)
|
private Value Peek(int delta = -1)
|
||||||
{
|
{
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
#endif
|
#endif
|
||||||
return _stack.Peek(delta);
|
return Stack.Peek(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Value Pop()
|
private Value Pop()
|
||||||
{
|
{
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
#endif
|
#endif
|
||||||
return _stack.Pop();
|
return Stack.Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Push(Value value)
|
private void Push(Value value)
|
||||||
{
|
{
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method($"{value}");
|
_logger.Method($"{value}");
|
||||||
#endif
|
#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)
|
private ExecutionResult Error(string message, object? context = null, bool @throw = true)
|
||||||
|
|
@ -703,7 +648,7 @@ public class Runner : IDisposable
|
||||||
#if LOG
|
#if LOG
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
#endif
|
#endif
|
||||||
_stack.Decimate(0);
|
Stack.Decimate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -728,14 +673,14 @@ public enum ExecutionResult
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct Options(
|
public readonly struct Options(
|
||||||
IO.OutputStreamMessageHandler stdOut = null,
|
Action<string>? stdOut = null,
|
||||||
IO.InputStreamListenHandler stdIn = null,
|
Action<string>? stdIn = null,
|
||||||
int maxCalls = 0x100,
|
int maxCalls = 0x100,
|
||||||
int stackSize = 0x200,
|
int stackSize = 0x200,
|
||||||
int initialStackSize = 0x80)
|
int initialStackSize = 0x80)
|
||||||
{
|
{
|
||||||
public readonly IO.OutputStreamMessageHandler StdOut = stdOut;
|
public readonly Action<string>? StdOut = stdOut;
|
||||||
public readonly IO.InputStreamListenHandler StdIn = stdIn;
|
public readonly Action<string>? StdIn = stdIn;
|
||||||
public readonly int MaxCalls = maxCalls;
|
public readonly int MaxCalls = maxCalls;
|
||||||
public readonly int StackSize = stackSize;
|
public readonly int StackSize = stackSize;
|
||||||
public readonly int InitialStackSize = initialStackSize;
|
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 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();
|
public static Value Resolve(Operation operation)
|
||||||
|
|
||||||
private Operation PopOperation(Runner runner, OpCode code)
|
|
||||||
{
|
{
|
||||||
#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
|
#if LOG
|
||||||
_logger.Method($"{operation.Left} {operation.OpCode} {operation.Right}");
|
_logger.Method($"{operation.Left} {operation.OpCode} {operation.Right}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// p sure switch is faster here (as opposed to dict lookup)
|
// 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.Equal => Equal(operation),
|
||||||
OpCode.Greater => Greater(operation),
|
OpCode.Greater => Greater(operation),
|
||||||
OpCode.Less => Less(operation),
|
OpCode.Less => Less(operation),
|
||||||
|
|
@ -56,16 +39,9 @@ public class ArithmeticResolver : IOperationResolver
|
||||||
OpCode.BitwiseXor => BitwiseXor(operation),
|
OpCode.BitwiseXor => BitwiseXor(operation),
|
||||||
_ => throw new NotImplementedException($"Unknown operator {operation.OpCode}.")
|
_ => 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> {
|
_operations = new Register<OpCode, OperationHandler> {
|
||||||
{ OpCode.Equal, Equal },
|
{ OpCode.Equal, Equal },
|
||||||
|
|
@ -285,7 +285,7 @@ public class Reader : IReader<Token>, IDisposable
|
||||||
'&' => Check('&') ?
|
'&' => Check('&') ?
|
||||||
MakeToken(And, buffer + Next()) :
|
MakeToken(And, buffer + Next()) :
|
||||||
Check(':') ?
|
Check(':') ?
|
||||||
MakeToken(AddressOf, buffer + Next()) :
|
MakeToken(Address, buffer + Next()) :
|
||||||
MakeToken(BitwiseAnd, buffer),
|
MakeToken(BitwiseAnd, buffer),
|
||||||
'^' => Check('~') ?
|
'^' => Check('~') ?
|
||||||
MakeToken(Base, buffer + Next()) :
|
MakeToken(Base, buffer + Next()) :
|
||||||
|
|
@ -318,9 +318,9 @@ public class Reader : IReader<Token>, IDisposable
|
||||||
MakeToken(Var, buffer + Next()) :
|
MakeToken(Var, buffer + Next()) :
|
||||||
MakeToken(Star, buffer),
|
MakeToken(Star, buffer),
|
||||||
'=' => Check('=') ?
|
'=' => Check('=') ?
|
||||||
MakeToken(EqualEqual, buffer + Next()) :
|
MakeToken(EqualEqual, buffer + Next()) :
|
||||||
MakeToken(Equal, buffer),
|
MakeToken(Equal, buffer),
|
||||||
'<' => Peek(0) switch {
|
'<' => Peek(1) switch {
|
||||||
'~' => MakeToken(Equal, buffer + Next()),
|
'~' => MakeToken(Equal, buffer + Next()),
|
||||||
':' => MakeToken(Return, buffer + Next()),
|
':' => MakeToken(Return, buffer + Next()),
|
||||||
'+' => MakeToken(PlusEqual, buffer + Next()),
|
'+' => MakeToken(PlusEqual, buffer + Next()),
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ public enum TokenType
|
||||||
BitwiseNot = Operator | 9,
|
BitwiseNot = Operator | 9,
|
||||||
BitwiseLeft = Operator | 10,
|
BitwiseLeft = Operator | 10,
|
||||||
BitwiseRight = Operator | 11,
|
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,
|
Assignment = 1 << 9,
|
||||||
Equal = Assignment | 1,
|
Equal = Assignment | 1,
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ namespace Qrakhen.Qamp.Core.Values;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(long))]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(long))]
|
||||||
public readonly record struct Address(long Value)
|
public readonly record struct Address(long Value)
|
||||||
{
|
{
|
||||||
public static readonly Address Void = new Address(-1);
|
public override string ToString() => $"0x{Value:x8}";
|
||||||
|
|
||||||
public override string ToString() => Value < 0 ? "&void" : $"&{Value:x8}";
|
|
||||||
|
|
||||||
public static implicit operator long(Address address) => address.Value;
|
public static implicit operator long(Address address) => address.Value;
|
||||||
public static implicit operator Address(long value) => new(value);
|
public static implicit operator Address(long value) => new(value);
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ using System.Reflection;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Core.Values;
|
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 delegate Value ExtensionDelegate(Value target, Value[] parameters);
|
||||||
|
|
||||||
public class ExtensionException(string message, object? context = null) : QampException(message, context);
|
public class ExtensionException(string message, object? context = null) : QampException(message, context);
|
||||||
|
|
|
||||||
|
|
@ -3,86 +3,6 @@ using System.Reflection;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Core.Values.Native;
|
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 Value Getter(Obj? self);
|
||||||
public delegate void Setter(Obj? self, Value value);
|
public delegate void Setter(Obj? self, Value value);
|
||||||
public delegate Value Method(Obj? self, Value[] args);
|
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));
|
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 Obj(ValueType type) : IValue
|
||||||
|
|
||||||
public class ObjTable(int size = 4096)
|
|
||||||
{
|
{
|
||||||
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 readonly ValueType Type = type;
|
||||||
|
|
||||||
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 ValueType ValueType => Type;
|
public ValueType ValueType => Type;
|
||||||
|
|
||||||
public Obj(ValueType type)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
__Address = ObjTable.Global.Register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Value Create(Obj obj)
|
public static Value Create(Obj obj)
|
||||||
{
|
{
|
||||||
return new Value(Ptr.Create(obj));
|
return new Value(Ptr.Create(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => "Obj<undefined>";
|
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 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()
|
public uint GetHash()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(Value))
|
if (string.IsNullOrEmpty(value))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return Value?.GetHash() ?? 0;
|
return Value.GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Value Make(string? value)
|
public static Value Make(string? value)
|
||||||
|
|
@ -76,7 +36,7 @@ public class String(string? value) : Obj(ValueType.String)
|
||||||
List<byte> result = new();
|
List<byte> result = new();
|
||||||
foreach (var pair in _strings) {
|
foreach (var pair in _strings) {
|
||||||
String str = pair.Value;
|
String str = pair.Value;
|
||||||
if (str == null || str.__GC_Marked)
|
if (str == null || str.__gcMarked)
|
||||||
continue;
|
continue;
|
||||||
byte[] bytes;
|
byte[] bytes;
|
||||||
bytes = Encoding.ASCII.GetBytes(str.Value!);
|
bytes = Encoding.ASCII.GetBytes(str.Value!);
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,36 @@ using Qrakhen.Qamp.Core.Collections;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Core.Values;
|
namespace Qrakhen.Qamp.Core.Values;
|
||||||
|
|
||||||
// generally interesting: GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal));
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
|
||||||
public readonly struct Ptr
|
public readonly struct Ptr
|
||||||
{
|
{
|
||||||
|
private static readonly PushStack<Obj> _register = new();
|
||||||
|
|
||||||
[Serialized] public readonly Address Address;
|
[Serialized] public readonly Address Address;
|
||||||
|
|
||||||
|
//private readonly IntPtr _pointer;
|
||||||
|
//public IntPtr Pointer => _pointer;
|
||||||
|
|
||||||
public Obj? Value {
|
public Obj? Value {
|
||||||
get {
|
get {
|
||||||
if (ObjTable.Global.TryGet(Address, out Obj? obj))
|
if (_register.TryGet(Address, out Obj obj))
|
||||||
return obj;
|
return obj;
|
||||||
return null;
|
return null;
|
||||||
|
//if (_pointer == IntPtr.Zero)
|
||||||
|
// throw new ObjectDisposedException(nameof(Ptr));
|
||||||
|
//return (Obj)GCHandle.FromIntPtr(_pointer).Target!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Ptr(Address address)
|
private Ptr(Address address)
|
||||||
{
|
{
|
||||||
|
//_pointer = GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal));
|
||||||
Address = address;
|
Address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Ptr Create(Obj obj)
|
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
|
public T? As<T>(bool throwWhenNull = true) where T : Obj
|
||||||
|
|
@ -50,8 +57,8 @@ public readonly struct Ptr
|
||||||
public static async Task __GC_Prepare()
|
public static async Task __GC_Prepare()
|
||||||
{
|
{
|
||||||
await Task.Run(() => {
|
await Task.Run(() => {
|
||||||
foreach (var obj in ObjTable.Global.Entries) {
|
foreach (var obj in _register) {
|
||||||
obj.Value.__GC_Count = 0;
|
obj.__gcCount = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -59,9 +66,9 @@ public readonly struct Ptr
|
||||||
public static byte[] SerializeFunctions()
|
public static byte[] SerializeFunctions()
|
||||||
{
|
{
|
||||||
List<byte> result = new();
|
List<byte> result = new();
|
||||||
foreach (var element in ObjTable.Global.Entries) {
|
for (int i = 0; i < _register.Count; i++) {
|
||||||
Obj obj = element.Value;
|
Obj obj = _register.Get(i);
|
||||||
if (obj == null || obj.__GC_Marked)
|
if (obj == null || obj.__gcMarked)
|
||||||
continue;
|
continue;
|
||||||
byte[] bytes;
|
byte[] bytes;
|
||||||
if (obj is Function function) {
|
if (obj is Function function) {
|
||||||
|
|
@ -72,7 +79,7 @@ public readonly struct Ptr
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.AddRange(((long)element.Key).GetBytes());
|
result.AddRange(i.GetBytes());
|
||||||
result.AddRange(bytes.Length.GetBytes());
|
result.AddRange(bytes.Length.GetBytes());
|
||||||
result.AddRange(bytes);
|
result.AddRange(bytes);
|
||||||
result.Add(0);
|
result.Add(0);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.AccessControl;
|
|
||||||
using Qrakhen.Qamp.Core.Abstractions;
|
using Qrakhen.Qamp.Core.Abstractions;
|
||||||
using Qrakhen.Qamp.Core.Values.Objects;
|
using Qrakhen.Qamp.Core.Values.Objects;
|
||||||
using String = Qrakhen.Qamp.Core.Values.Objects.String;
|
using String = Qrakhen.Qamp.Core.Values.Objects.String;
|
||||||
|
|
@ -8,47 +7,13 @@ using T = Qrakhen.Qamp.Core.Values.ValueType;
|
||||||
|
|
||||||
namespace Qrakhen.Qamp.Core.Values;
|
namespace Qrakhen.Qamp.Core.Values;
|
||||||
|
|
||||||
|
public interface IValue
|
||||||
|
|
||||||
// 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
|
|
||||||
{
|
{
|
||||||
T ValueType { get; }
|
T ValueType { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IDynamicValue : IValue<ulong>
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||||
public readonly struct Value :
|
public readonly struct Value : IValue, ISerialize<Value>, IDebug<string>
|
||||||
IDynamicValue,
|
|
||||||
ISerialize<Value>,
|
|
||||||
IDebug<string>
|
|
||||||
{
|
{
|
||||||
public static readonly Value Void = new Value();
|
public static readonly Value Void = new Value();
|
||||||
public static readonly Value True = new Value(true);
|
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 Ptr Ptr;
|
||||||
[FieldOffset(0x00)] [Serialized] public readonly Ref Ref;
|
[FieldOffset(0x00)] [Serialized] public readonly Ref Ref;
|
||||||
|
|
||||||
public ulong Data => Unsigned;
|
|
||||||
|
|
||||||
[FieldOffset(0x08)] [Serialized] public readonly T Type;
|
[FieldOffset(0x08)] [Serialized] public readonly T Type;
|
||||||
|
|
||||||
[FieldOffset(0x0c)] [Serialized] public readonly uint Meta; // like lengths of arrays or lists
|
[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 T ValueType => Type;
|
||||||
public TypeInfo TypeInfo => TypeInfo.FromValue(this);
|
public TypeInfo TypeInfo => TypeInfo.FromValue(this);
|
||||||
|
|
||||||
public bool IsVoid => Type == T.Void;
|
|
||||||
public bool IsNumber => Type <= T.Decimal;
|
public bool IsNumber => Type <= T.Decimal;
|
||||||
public bool IsSigned => Is(T.Signed);
|
public bool IsSigned => Is(T.Signed);
|
||||||
public bool IsUnsigned => Is(T.Unsigned);
|
public bool IsUnsigned => Is(T.Unsigned);
|
||||||
public bool IsDecimal => Is(T.Decimal);
|
public bool IsDecimal => Is(T.Decimal);
|
||||||
public bool IsInteger => IsSigned || IsUnsigned;
|
|
||||||
public bool IsBool => Is(T.Bool);
|
public bool IsBool => Is(T.Bool);
|
||||||
public bool IsString => Is(T.String);
|
public bool IsString => Is(T.String);
|
||||||
public bool IsRef => Is(T.Reference, false);
|
public bool IsRef => Is(T.Reference, false);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ public enum ValueType
|
||||||
Integer = Unsigned | Signed | Char,
|
Integer = Unsigned | Signed | Char,
|
||||||
Primitive = Integer | Decimal | Bool,
|
Primitive = Integer | Decimal | Bool,
|
||||||
|
|
||||||
Address = 0x0020,
|
Address = 0x0020, // coded with : symbol
|
||||||
|
|
||||||
// classes etc. here?
|
// classes etc. here?
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue