diff --git a/Qrakhen.Qamp.CLI/Qrakhen.Qamp.CLI.csproj b/Qrakhen.Qamp.CLI/Qrakhen.Qamp.CLI.csproj
index 48b6eb8..941e7a1 100644
--- a/Qrakhen.Qamp.CLI/Qrakhen.Qamp.CLI.csproj
+++ b/Qrakhen.Qamp.CLI/Qrakhen.Qamp.CLI.csproj
@@ -2,9 +2,10 @@
Exe
- net8.0
+ net10.0
enable
enable
+ ..\Build\
diff --git a/Qrakhen.Qamp.CLI/TODO.md b/Qrakhen.Qamp.CLI/TODO.md
new file mode 100644
index 0000000..e2e1941
--- /dev/null
+++ b/Qrakhen.Qamp.CLI/TODO.md
@@ -0,0 +1,21 @@
+# Todo:
+
+### Make Digester OOP-ish
+ - inherit from a base digester that has the continouus stack / reader references as protected information
+ - one digester for every job area (Loops, Functions, Variables, etc.)
+ - time is not as essential here as I want to have the executables pre-compiled to OpCodes anyway
+
+### - Finish runner
+ - add interpretation for all remaining op codes
+ - make variable operations faster with optimized inline lambda expressions
+ - better debugging for the instruction set
+
+### CLI / IDE / VSC
+ - fix 'IDE' in CLI
+ - create syntax highlighting extension for vs code (if i ever manage to)
+
+### Smaller Goals
+ - think of a cleaner way to handle Obj (pointers specifically)
+ - add classic sqript dialect
+ - fix console "IDE"
+ - split runtime, digester and core into separate projects
diff --git a/Qrakhen.Qamp.Core/Collections/Expander.cs b/Qrakhen.Qamp.Core/Collections/Expander.cs
index db0029d..95a8eac 100644
--- a/Qrakhen.Qamp.Core/Collections/Expander.cs
+++ b/Qrakhen.Qamp.Core/Collections/Expander.cs
@@ -17,7 +17,7 @@ public abstract class Expander :
{
protected T[] Data;
- public long Count { get; protected set; }= 0;
+ public long Count { get; protected set; } = 0;
public long Capacity => Data.Length;
protected Expander(int capacity = 0x10)
diff --git a/Qrakhen.Qamp.Core/Collections/FixedArray.cs b/Qrakhen.Qamp.Core/Collections/FixedArray.cs
index 501cb31..95802a8 100644
--- a/Qrakhen.Qamp.Core/Collections/FixedArray.cs
+++ b/Qrakhen.Qamp.Core/Collections/FixedArray.cs
@@ -2,6 +2,10 @@
namespace Qrakhen.Qamp.Core.Collections;
+///
+/// Basically just a normal array, but implements to be used with .
+///
+///
public class FixedArray : IEnumerable, IGetSet
{
protected T[] Data;
diff --git a/Qrakhen.Qamp.Core/Collections/Pointer.cs b/Qrakhen.Qamp.Core/Collections/Pointer.cs
new file mode 100644
index 0000000..1229797
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Collections/Pointer.cs
@@ -0,0 +1,46 @@
+namespace Qrakhen.Qamp.Core.Collections;
+
+///
+/// Dynamic Pointer able to point to any object implementing the interface.
+///
+public class Pointer
+{
+ protected IGetSet Target;
+ public long Ptr;
+
+ public Pointer(IGetSet target, long pointer = 0)
+ {
+ Target = target;
+ Ptr = pointer;
+ }
+
+ public Pointer Branch(long delta = 0)
+ {
+ return new Pointer(Target, Ptr + delta);
+ }
+
+ ///
+ /// Returns the next item from and increases the pointer by 1.
+ ///
+ public T Next() => Target.Get(Ptr++);
+
+ ///
+ /// Sets the value of at .
+ ///
+ public void Set(long position, T value) => Target.Set(position, value);
+
+ ///
+ /// Sets the value of at the current location.
+ ///
+ public void Set(T value) => Set(Ptr, value);
+
+ ///
+ /// Gets the value of at .
+ ///
+ public T Get(long position) => Target.Get(position);
+
+ ///
+ /// Gets the value of at the current location.
+ ///
+ public T Get() => Get(Ptr);
+}
diff --git a/Qrakhen.Qamp.Core/Collections/PopStack.cs b/Qrakhen.Qamp.Core/Collections/PopStack.cs
index fc22283..e1bc3a6 100644
--- a/Qrakhen.Qamp.Core/Collections/PopStack.cs
+++ b/Qrakhen.Qamp.Core/Collections/PopStack.cs
@@ -2,7 +2,13 @@
namespace Qrakhen.Qamp.Core.Collections;
-public class PopStack : IEnumerable, IPop, IPeekable
+///
+/// Pop-Only stack with a fixed array that is gradually reduced.
+///
+public class PopStack :
+ IEnumerable,
+ IPop,
+ IPeekable
{
private readonly T[] _data;
diff --git a/Qrakhen.Qamp.Core/Collections/StackRegister.cs b/Qrakhen.Qamp.Core/Collections/PushStack.cs
similarity index 69%
rename from Qrakhen.Qamp.Core/Collections/StackRegister.cs
rename to Qrakhen.Qamp.Core/Collections/PushStack.cs
index 427bced..bcb2a35 100644
--- a/Qrakhen.Qamp.Core/Collections/StackRegister.cs
+++ b/Qrakhen.Qamp.Core/Collections/PushStack.cs
@@ -1,8 +1,15 @@
namespace Qrakhen.Qamp.Core.Collections;
-public class StackRegister : Expander, IToArray
+///
+/// Push-only Stack that is based on .
+///
+///
+public class PushStack :
+ Expander,
+ IPush,
+ IToArray
{
- public StackRegister(int initialCapacity = 0x10)
+ public PushStack(int initialCapacity = 0x10)
{
Data = new T[initialCapacity];
}
@@ -15,20 +22,22 @@ public class StackRegister : Expander, IToArray
return default;
}
- public void Add(T[] array)
+ public void Push(T[] array)
{
foreach (T value in array) {
Add(value);
}
}
- public void Add(IEnumerable array)
+ public void Push(IEnumerable array)
{
foreach (T value in array) {
Add(value);
}
}
+ public long Push(T value) => Add(value);
+
public bool TryGet(long index, out T value)
{
value = default;
diff --git a/Qrakhen.Qamp.Core/Collections/Register.cs b/Qrakhen.Qamp.Core/Collections/Register.cs
index d070eaa..c2f16bb 100644
--- a/Qrakhen.Qamp.Core/Collections/Register.cs
+++ b/Qrakhen.Qamp.Core/Collections/Register.cs
@@ -1,7 +1,14 @@
-namespace Qrakhen.Qamp.Core.Collections;
+using System.Collections;
+namespace Qrakhen.Qamp.Core.Collections;
+
+///
+/// Dictionary wrapper implementing .
+///
public class Register :
- IGetSet
+ IGetSet,
+ IToArray,
+ IEnumerable>
where TKey : notnull
{
private readonly Dictionary _data = new();
@@ -19,7 +26,21 @@ public class Register :
return key;
}
+ public bool TryGet(TKey key, out TValue? value) => _data.TryGetValue(key, out value);
public TValue Get(TKey key) => _data[key];
public void Set(TKey key, TValue value) => _data[key] = value;
public bool Has(TKey key) => _data.ContainsKey(key);
+
+ public TValue[] ToArray() => _data.Values.ToArray();
+
+ public IEnumerator> GetEnumerator()
+ {
+ foreach (var item in _data)
+ yield return item;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
}
diff --git a/Qrakhen.Qamp.Core/Collections/StackLike.cs b/Qrakhen.Qamp.Core/Collections/StackLike.cs
index 986afe9..5a0c682 100644
--- a/Qrakhen.Qamp.Core/Collections/StackLike.cs
+++ b/Qrakhen.Qamp.Core/Collections/StackLike.cs
@@ -1,10 +1,15 @@
namespace Qrakhen.Qamp.Core.Collections;
+///
+/// Stack-like implementation with a few added features like seeking and setting values.
+///
+///
public class StackLike : Expander,
IPush,
IPop,
ISeekable,
- IPeekable
+ IPeekable,
+ IToArray
{
public long Position => Count;
@@ -39,4 +44,14 @@ public class StackLike : Expander,
public bool CanPeek(int delta = 0)
=> Count + delta > -1 && Count + delta < Count;
+
+ ///
+ /// Decimates this stack down to the given position, overwriting everything beyond that when pushing in the future.
+ ///
+ public void Decimate(long position)
+ {
+ Count = position;
+ }
+
+ public T[] ToArray() => Data.ToArray();
}
diff --git a/Qrakhen.Qamp.Core/Compilation/Builders/Builder.cs b/Qrakhen.Qamp.Core/Compilation/Builders/Builder.cs
index 8c73496..e242c9c 100644
--- a/Qrakhen.Qamp.Core/Compilation/Builders/Builder.cs
+++ b/Qrakhen.Qamp.Core/Compilation/Builders/Builder.cs
@@ -4,10 +4,15 @@ namespace Qrakhen.Qamp.Core.Compilation.Builders;
public class Builder
{
- public Builder? Outer;
+ public Builder? Outer { get; init; }
public FunctionBuilder Function = new();
public StackLike Locals = [];
public StackLike Outers = [];
public int ScopeDepth = 0;
+
+ public Builder(Builder? outer = null)
+ {
+ Outer = outer;
+ }
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Compilation/Builders/IBuilder.cs b/Qrakhen.Qamp.Core/Compilation/Builders/IBuilder.cs
new file mode 100644
index 0000000..f0233d6
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Compilation/Builders/IBuilder.cs
@@ -0,0 +1,6 @@
+namespace Qrakhen.Qamp.Core.Compilation.Builders;
+
+public interface IBuilder
+{
+ public TOut Build();
+}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Compilation/Builders/SegmentBuilder.cs b/Qrakhen.Qamp.Core/Compilation/Builders/SegmentBuilder.cs
index d2c71b3..a2da2aa 100644
--- a/Qrakhen.Qamp.Core/Compilation/Builders/SegmentBuilder.cs
+++ b/Qrakhen.Qamp.Core/Compilation/Builders/SegmentBuilder.cs
@@ -4,10 +4,10 @@ using Qrakhen.Qamp.Core.Values;
namespace Qrakhen.Qamp.Core.Compilation.Builders;
-public readonly struct SegmentBuilder() : IDebug
+public class SegmentBuilder() : IBuilder, IDebug
{
- public readonly StackRegister Instructions = new();
- public readonly StackRegister Constants = new();
+ public readonly PushStack Instructions = new();
+ public readonly PushStack Constants = new();
public Segment Build()
{
diff --git a/Qrakhen.Qamp.Core/Compilation/Digester.cs b/Qrakhen.Qamp.Core/Compilation/Digester.cs
index 2ec5c98..0440924 100644
--- a/Qrakhen.Qamp.Core/Compilation/Digester.cs
+++ b/Qrakhen.Qamp.Core/Compilation/Digester.cs
@@ -37,6 +37,11 @@ public class DigesterProvider
public object ExpressionDigester; // etc.
}
+public abstract class xDigester
+{
+
+}
+
public class Digester : ISteppable
{
private readonly CompilerState _compiler;
@@ -105,7 +110,7 @@ public class Digester : ISteppable
{
Emit(Op.Return);
Function function = Builder.Function.Build();
- Builder = Builder.Outer!;
+ Builder = Builder.Outer;
return function;
}
@@ -114,8 +119,8 @@ public class Digester : ISteppable
_logger.Method();
if (Match(T.Class)) DeclareClass();
else if (Match(T.Function)) DeclareFunction();
- else if (Match(T.Var)) DeclareVariable();
- else Statement();
+ else if (Match(T.Var)) DeclareVariable();
+ else Statement();
}
internal void Statement()
@@ -129,15 +134,19 @@ public class Digester : ISteppable
else if (Match(T.Return)) Return();
else if (Match(T.TypeOf)) TypeOf();
else if (Match(T.Print)) Print();
+ else if (Match(T.PrintStack)) PrintStack();
+ else if (Match(T.PrintGlobals)) PrintGlobals();
+ else if (Match(T.PrintExpr)) PrintExpr();
else if (Match(T.Export)) Export();
else if (Match(T.Import)) Import();
else if (Match(T.ContextOpen)) Context();
- else StatementExpression();
+ else Expression();
}
- internal void StatementExpression()
+ internal void Expression()
{
- Expression();
+ _logger.Method();
+ ParseExpression();
Consume(T.Semicolon, "Expected ';' after statement");
Emit(Op.Pop); // in an expression statement, we definitely do not want to remain the value on stack,
// as it would fuck over the entire stack for the rest of the execution.
@@ -146,7 +155,7 @@ public class Digester : ISteppable
// a tad bit older me: it's a terrible idea as return a = b; wouldn't work anymore
}
- internal void Expression() => WeightedDigest(Weight.Assign);
+ internal void ParseExpression() => WeightedDigest(Weight.Assign);
internal void Context()
{
@@ -221,7 +230,7 @@ public class Digester : ISteppable
ExpressionParser.Variable(this, name, false);
Consume(T.ContextOpen, "Expected '{' for class body declaration.", false);
while (!Check(T.ContextClose) && !Check(T.Eof))
- DeclareMethod();
+ DeclareMember();
Consume(T.ContextClose, "Expected '}' to end class body declaration.", false);
Emit(Op.Pop);
@@ -241,8 +250,9 @@ public class Digester : ISteppable
DefineVariable(global);
}
- internal void DeclareMethod()
+ internal void DeclareMember()
{
+ // todo: only methods allowed atm, add fields and shit too
_logger.Method();
Consume(T.Identifier, "Expected method name.", false);
long identifier = IdentifierConstant(Previous.Value!);
@@ -258,7 +268,7 @@ public class Digester : ISteppable
_logger.Method();
long global = ParseVariable();
if (Match(T.Equal))
- Expression();
+ ParseExpression();
else
Emit(Op.Null);
Consume(T.Semicolon, "missing ; after variable declaration");
@@ -279,7 +289,7 @@ public class Digester : ISteppable
{
_logger.Method();
Consume(T.GroupOpen, "Expected '(' after if");
- Expression();
+ ParseExpression();
Consume(T.GroupClose, "Expected ')' after condition");
long then = EmitJump(Op.JumpIfFalse);
@@ -308,7 +318,7 @@ public class Digester : ISteppable
_logger.Method();
long start = Function.CurrentInstruction;
Consume(T.GroupOpen, "Expected while condition to be placed inside parentheses. Missing '('.", false);
- Expression();
+ ParseExpression();
Consume(T.GroupClose, "Expected while condition to be placed inside parentheses. Missing ')'.", false);
long exit = EmitJump(Op.JumpIfFalse);
Emit(Op.Pop);
@@ -335,13 +345,13 @@ public class Digester : ISteppable
} else if (Match(T.Var)) {
DeclareVariable();
} else {
- StatementExpression();
+ Expression();
}
long start = Function.CurrentInstruction;
long exit = -1;
if (!Match(T.Semicolon)) {
- Expression();
+ ParseExpression();
Consume(T.Semicolon, "Expected ';' after condition");
exit = EmitJump(Op.JumpIfFalse);
Emit(Op.Pop);
@@ -350,7 +360,7 @@ public class Digester : ISteppable
if (!Match(T.GroupClose)) {
long body = EmitJump(Op.Jump);
long increment = Function.Segment.Instructions.Count;
- Expression();
+ ParseExpression();
Emit(Op.Pop);
Consume(T.GroupClose, "Expected ')' after for clause");
@@ -378,15 +388,36 @@ public class Digester : ISteppable
internal void Print()
{
_logger.Method();
- Expression();
+ ParseExpression();
Consume(T.Semicolon, "Expected ';' after print call.");
Emit(Op.Print);
}
+ internal void PrintGlobals()
+ {
+ _logger.Method();
+ Consume(T.Semicolon, "Expected ';' after print call.");
+ Emit(Op.PrintGlobals);
+ }
+
+ internal void PrintStack()
+ {
+ _logger.Method();
+ Consume(T.Semicolon, "Expected ';' after print call.");
+ Emit(Op.PrintStack);
+ }
+
+ internal void PrintExpr()
+ {
+ _logger.Method();
+ Consume(T.Semicolon, "Expected ';' after print call.");
+ Emit(Op.PrintExpr);
+ }
+
internal void TypeOf()
{
_logger.Method();
- Expression();
+ ParseExpression();
Emit(Op.Typeof);
}
@@ -403,10 +434,10 @@ public class Digester : ISteppable
while (Builder.Locals.Count > 0 &&
Builder.Locals.Peek().Depth > Builder.ScopeDepth) {
if (Builder.Locals.Peek().IsCaptured) {
- Emit(OpCode.CloseUpvalue);
+ Emit(Op.CloseOuter);
} else {
- Emit(OpCode.Pop);
- }
+ Emit(Op.Pop);
+ }
Builder.Locals.Pop();
}
}
@@ -425,17 +456,16 @@ public class Digester : ISteppable
DefineVariable(ParseVariable());
} while (Match(T.Comma));
}
-
Consume(T.GroupClose, "Expected ')' after argument list.");
Consume(T.ContextOpen, "Expected function body");
Block();
Function function = FinishBuilder();
- Emit(Op.Closure);
- Emit(MakeConstant(Obj.Create(function)));
+ Emit(Op.Context);
+ EmitDynamic(MakeConstant(Obj.Create(function)));
for (int i = 0; i < function.OuterCount; i++) {
Outer outer = next.Outers.Get(i);
Emit(outer.IsLocal ? 1 : 0);
- Emit(outer.Index);
+ EmitDynamic(outer.Index);
}
}
@@ -551,12 +581,12 @@ public class Digester : ISteppable
}
}
- internal byte ArgumentList()
+ internal byte Arguments()
{
byte count = 0;
if (!Check(T.GroupClose)) {
do {
- Expression();
+ ParseExpression();
if (count++ >= 0xFF)
ErrorAtCurrent("How many arguments do you need?");
} while (Match(T.Comma));
@@ -621,7 +651,8 @@ public class Digester : ISteppable
///
internal void EmitDynamic(byte[] data)
{
- Emit((byte)(data.Length | 0x80));
+ if (data.Length > 1)
+ Emit((byte)(data.Length | 0x80)); // 0x80 flag for length marker, anything else is direct value
Emit(data);
}
diff --git a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs
index cbadfa9..a0d58d5 100644
--- a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs
+++ b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs
@@ -52,8 +52,8 @@ public static class ExpressionParser
static void Call(Digester digester, bool canAssign)
{
- byte count = digester.ArgumentList();
- digester.Emit(OpCode.Call, count);
+ byte count = digester.Arguments();
+ digester.Emit(OpCode.Invoke, count);
}
static void Dot(Digester digester, bool canAssign)
@@ -62,14 +62,14 @@ public static class ExpressionParser
long name = digester.IdentifierConstant(digester.Previous.Value);
if (canAssign && digester.Match(TokenType.Equal)) {
- digester.Expression();
- digester.EmitDynamic(OpCode.SetProperty, name);
+ digester.ParseExpression();
+ digester.EmitDynamic(OpCode.SetMember, name);
} else if (digester.Match(TokenType.GroupOpen)) {
- Byte argCount = 0; //argumentList();
- digester.Emit(OpCode.Invoke, name.GetBytes());
- digester.EmitDynamic(argCount);
+ byte argCount = digester.Arguments();
+ digester.EmitDynamic(OpCode.InvokeMember, name);
+ digester.Emit(argCount);
} else {
- digester.EmitDynamic(OpCode.GetProperty, name);
+ digester.EmitDynamic(OpCode.GetMember, name);
}
}
@@ -92,7 +92,7 @@ public static class ExpressionParser
static void Group(Digester digester, bool canAssign)
{
- digester.Expression();
+ digester.ParseExpression();
digester.Consume(TokenType.GroupClose, "Expected ')' after expression.");
}
@@ -100,7 +100,7 @@ public static class ExpressionParser
{
int length = 0;
while (!digester.Check(TokenType.ArrayClose)) {
- digester.Expression();
+ digester.ParseExpression();
length++;
if (digester.Check(TokenType.Comma))
digester.Next();
@@ -113,19 +113,19 @@ public static class ExpressionParser
static void ArrayAdd(Digester digester, bool canAssign)
{
- digester.Expression();
+ digester.ParseExpression();
digester.Emit(OpCode.ArrayAdd);
}
static void Index(Digester digester, bool canAssign)
{
- digester.Expression();
+ digester.ParseExpression();
digester.Consume(TokenType.ArrayClose, "expected ] after array accessor");
if (canAssign && digester.Match(TokenType.ArrayAdd)) {
- digester.Expression();
+ digester.ParseExpression();
digester.Emit(OpCode.ArrayAdd);
} else if (canAssign && digester.Match(TokenType.Equal)) {
- digester.Expression();
+ digester.ParseExpression();
digester.Emit(OpCode.ArraySet);
} else {
digester.Emit(OpCode.ArrayGet);
@@ -183,7 +183,7 @@ public static class ExpressionParser
}
if (canAssign && digester.Match(TokenType.Equal)) {
- digester.Expression();
+ digester.ParseExpression();
digester.Emit(Set);
digester.EmitDynamic(variable);
} else {
@@ -211,9 +211,9 @@ public static class ExpressionParser
Variable(digester, "this", false);
if (digester.Match(TokenType.GroupOpen))
{
- byte arguments = digester.ArgumentList();
+ byte arguments = digester.Arguments();
Variable(digester, "base", false);
- digester.Emit(OpCode.BaseCall);
+ digester.Emit(OpCode.InvokeBase);
digester.Emit(arguments);
} else {
Variable(digester, "base", false);
@@ -294,7 +294,7 @@ public static class ExpressionParser
_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);
- _rules[TokenType.Super] = new Rule(Base, null, Weight.None);
+ _rules[TokenType.Base] = new Rule(Base, null, Weight.None);
_rules[TokenType.This] = new Rule(This, null, Weight.None);
_rules[TokenType.True] = new Rule(Literal, null, Weight.None);
_rules[TokenType.Var] = new Rule(null, null, Weight.None);
diff --git a/Qrakhen.Qamp.Core/Exceptions.cs b/Qrakhen.Qamp.Core/Exceptions.cs
index 57bf192..bd27ed8 100644
--- a/Qrakhen.Qamp.Core/Exceptions.cs
+++ b/Qrakhen.Qamp.Core/Exceptions.cs
@@ -1,14 +1,12 @@
using Qrakhen.Qamp.Core.Tokenization;
using Qrakhen.Qamp.Core.Values;
-using V = Qrakhen.Qamp.Core.Values.ValueType;
-
namespace Qrakhen.Qamp.Core;
-public class QampException(string message, TokenPosition position = default)
+public class QampException(string message, object? context = null)
: Exception(message)
{
- public readonly TokenPosition Position = position;
+ public readonly object? Context = context;
}
public class ReaderException(string message, IReader reader)
@@ -23,14 +21,14 @@ public class TokenException(string message, IReader reader, Token token)
public readonly Token Token = token;
}
-public class ExecutionException(string message, TokenPosition position = default)
- : QampException(message, position);
+public class RuntimeException(string message, object? context = null)
+ : QampException(message, context);
-public class DivisionByZeroException(string? message, TokenPosition position = default)
- : ExecutionException(message ?? "Can not divide by zero", position);
+public class DivisionByZeroException(string? message, object? context = null)
+ : RuntimeException(message ?? "Can not divide by zero", context);
-public class ConversionException(string message, Value value, TokenPosition position = default)
- : ExecutionException(message, position)
+public class ConversionException(string message, Value value, object? context = null)
+ : RuntimeException(message, context)
{
public readonly Value Value = value;
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Execution/InstructionPtr.cs b/Qrakhen.Qamp.Core/Execution/InstructionPtr.cs
new file mode 100644
index 0000000..968ad81
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Execution/InstructionPtr.cs
@@ -0,0 +1,65 @@
+using Qrakhen.Qamp.Core.Collections;
+using Qrakhen.Qamp.Core.Values;
+using String = Qrakhen.Qamp.Core.Values.Objects.String;
+
+namespace Qrakhen.Qamp.Core.Execution;
+
+public class InstructionPtr : Pointer
+{
+ public Segment Segment;
+
+ public Instruction Instruction => Segment.Instructions[Ptr];
+
+ public InstructionPtr(Segment segment, int position = 0) : base(segment.Instructions, position)
+ {
+ Segment = segment;
+ }
+
+ public long NextLong()
+ {
+ long value = Segment.ReadLong(Ptr);
+ Ptr += sizeof(long);
+ return value;
+ }
+
+ public Value NextConstant()
+ {
+ return Segment.Constants[NextDynamic()];
+ }
+
+ public String NextString()
+ {
+ Value value = NextConstant();
+ if (value.IsString)
+ return value.Ptr.As()!;
+ throw new Exception($"Expected string, got {value}");
+ }
+
+ public long NextDynamic()
+ {
+ var result = Segment.ReadDynamic(Ptr, out int read);
+ Ptr += read;
+ return result;
+ }
+
+ public String? GetStringConstant(long offset)
+ => Segment.Constants[offset].Ptr.Value as String;
+
+ public static Instruction operator +(InstructionPtr ptr, int value)
+ => ptr.Segment.Instructions[ptr.Ptr + value];
+
+ public static Instruction operator -(InstructionPtr ptr, int value)
+ => ptr.Segment.Instructions[ptr.Ptr - value];
+
+ public static InstructionPtr operator ++(InstructionPtr ptr)
+ {
+ ptr.Ptr++;
+ return ptr;
+ }
+
+ public static InstructionPtr operator --(InstructionPtr ptr)
+ {
+ ptr.Ptr--;
+ return ptr;
+ }
+}
diff --git a/Qrakhen.Qamp.Core/Execution/OpCode.cs b/Qrakhen.Qamp.Core/Execution/OpCode.cs
index 73fad7f..f503063 100644
--- a/Qrakhen.Qamp.Core/Execution/OpCode.cs
+++ b/Qrakhen.Qamp.Core/Execution/OpCode.cs
@@ -2,62 +2,87 @@
public enum OpCode
{
- None = 0,
- Constant,
- Null,
- True,
- False,
- Pop,
- Export,
- Import,
- GetLocal,
- SetLocal,
- GetGlobal,
- DefineGlobal,
- SetGlobal,
- GetOuter,
- SetOuter,
- GetProperty,
- SetProperty,
- Property,
- Base,
- Equal,
- Greater,
- Less,
- Add,
- Subtract,
- Multiply,
- Divide,
- Modulo,
- BitwiseAnd,
- BitwiseOr,
- BitwiseXor,
- BitwiseNot,
- BitwiseLeft,
- BitwiseRight,
- Increment,
- Decrement,
- Not,
- Negate,
- Ref,
- Array,
- ArrayGet,
- ArraySet,
- ArrayAdd,
- ArrayRemove,
- Print,
- PrintExpr,
- Typeof,
- Jump,
- JumpIfFalse,
- Loop,
- Call,
- Invoke,
- BaseCall,
- Closure,
- CloseUpvalue,
- Return,
- Class,
- Inherit,
- Method
+ F_Mask = 0xF0,
+
+ None = 0x00,
+ Constant = 0x01,
+ Null = 0x02,
+ Pop = 0x03,
+ Cast = 0x04,
+
+ False = 0x0e,
+ True = 0x0f,
+
+ Val = 0x10,
+ Ref = 0x11,
+
+ SetGlobal = 0x20,
+ GetGlobal = 0x21,
+ GetLocal = 0x22,
+ SetLocal = 0x23,
+ GetOuter = 0x24,
+ SetOuter = 0x25,
+ GetMember = 0x26,
+ SetMember = 0x27,
+
+ DefineGlobal = 0x30,
+ CloseOuter = 0x31,
+
+ AssignValue = 0x40, // unsure
+ AssignReference = 0x41, // unsure
+
+ F_Operation = 0x60,
+ Add = 0x60,
+ Subtract = 0x61,
+ Multiply = 0x62,
+ Divide = 0x63,
+ Modulo = 0x64,
+ BitwiseAnd = 0x65,
+ BitwiseOr = 0x66,
+ BitwiseXor = 0x67,
+ BitwiseLeft = 0x68,
+ BitwiseRight = 0x69,
+ F_Unary = 0x0a,
+ Not = 0x6a,
+ Negate = 0x6b,
+ BitwiseNot = 0x6c,
+
+ F_Compare = 0x50,
+ Equal = 0x50,
+ Greater = 0x51,
+ Less = 0x52,
+
+ Increment = 0x70,
+ Decrement = 0x71,
+
+ Array = 0xc0,
+ ArrayGet = 0xc1,
+ ArraySet = 0xc2,
+ ArrayAdd = 0xc3,
+ ArrayRemove = 0xc4,
+
+ Return = 0x80,
+ Jump = 0x81,
+ JumpIfFalse = 0x82,
+ Loop = 0x83,
+ Invoke = 0x84,
+ InvokeBase = 0x85,
+ InvokeMember = 0x86,
+ Context = 0x87,
+
+ Class = 0xa0,
+ Member = 0xa1,
+ Base = 0xa2,
+ Inherit = 0xa3,
+ Method = 0xa4,
+ Static = 0xa5,
+
+ Print = 0xe0,
+ PrintStack = 0xe1,
+ PrintGlobals = 0xe2,
+ PrintExpr = 0xe3,
+ Typeof = 0xef,
+
+ 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
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Execution/Runner.cs b/Qrakhen.Qamp.Core/Execution/Runner.cs
index 08d9aae..d99931d 100644
--- a/Qrakhen.Qamp.Core/Execution/Runner.cs
+++ b/Qrakhen.Qamp.Core/Execution/Runner.cs
@@ -1,165 +1,47 @@
using Qrakhen.Qamp.Core.Collections;
-using Qrakhen.Qamp.Core.Compilation;
using Qrakhen.Qamp.Core.Logging;
using Qrakhen.Qamp.Core.Tokenization;
using Qrakhen.Qamp.Core.Values;
using Qrakhen.Qamp.Core.Values.Objects;
+using System.Xml.Linq;
using String = Qrakhen.Qamp.Core.Values.Objects.String;
namespace Qrakhen.Qamp.Core.Execution;
using Op = OpCode;
-
-/*
- * Todo:
- * - Make Digester OOP-ish
- * - inherit from a base digester that has the continouus stack / reader references as protected information
- * - one digester for every job area (Loops, Functions, Variables, etc.)
- * - time is not as essential here as I want to have the executables pre-compiled to OpCodes anyway
- * - Think of a cleaner way to handle Obj (Pointers specifically)
- * - Finish runner
- * - add interpretation for all remaining op codes
- * - make variable operations faster with optimized inline lambda expressions
- * - better debugging for the instruction set
- * - Add classic sqript dialect
- * - Fix console "IDE"
- * - Split Runner, Digester and Core into separate projects
- */
-
-///
-/// Dynamic Pointer able to point to any object implementing the interface.
-///
-public class Pointer
-{
- protected IGetSet Target;
- public long Ptr;
-
- public Pointer(IGetSet target, long pointer = 0)
- {
- Target = target;
- Ptr = pointer;
- }
-
- ///
- /// Returns the next item from and increases the pointer by 1.
- ///
- public T Next() => Target.Get(Ptr++);
-
- ///
- /// Sets the value of at .
- ///
- public void Set(long position, T value) => Target.Set(position, value);
-
- ///
- /// Sets the value of at the current location.
- ///
- public void Set(T value) => Set(Ptr, value);
-
- ///
- /// Gets the value of at .
- ///
- public T Get(long position) => Target.Get(position);
-
- ///
- /// Gets the value of at the current location.
- ///
- public T Get() => Get(Ptr);
-}
-
-// this is all a bit cheesy imho
-public class InstructionPtr : Pointer
-{
- public Segment Segment;
-
- public InstructionPtr(Segment segment, int position = 0) : base(segment.Instructions, position)
- {
- Segment = segment;
- }
-
- public long NextLong()
- {
- long value = Segment.ReadLong(Ptr);
- Ptr += sizeof(long);
- return value;
- }
-
- public Value NextConstant()
- {
- return Segment.Constants[NextDynamic()];
- }
-
- public long NextDynamic()
- {
- int length = Segment.Read(Ptr) ^ 0x80;
- Ptr++;
- byte[] data = new byte[8];
- for (int i = 0; i < length; i++)
- data[i] = Segment.Read(Ptr++);
- return data.ToInt64();
- }
-
-
- public String? GetStringConstant(long offset)
- {
- return Segment.Constants[offset].Ptr.Value as String;
- }
-
- public Instruction Instruction => Segment.Instructions[Ptr];
-
- public static Instruction operator +(InstructionPtr ptr, int value)
- => ptr.Segment.Instructions[ptr.Ptr + value];
-
- public static Instruction operator -(InstructionPtr ptr, int value)
- => ptr.Segment.Instructions[ptr.Ptr - value];
-
- public static InstructionPtr operator ++(InstructionPtr ptr)
- {
- ptr.Ptr++;
- return ptr;
- }
-
- public static InstructionPtr operator --(InstructionPtr ptr)
- {
- ptr.Ptr--;
- return ptr;
- }
-}
+using T = Values.ValueType;
public class Call
{
- public Closure Closure;
+ public Context Context;
public InstructionPtr Instruction;
public Pointer StackPtr;
- public Call(Closure closure, StackLike stack, long stackOffset)
+ public Call(Context context, StackLike stack, long stackOffset)
{
- Closure = closure;
- Instruction = new InstructionPtr(Closure.Function.Segment);
+ Context = context;
+ Instruction = new InstructionPtr(Context.Function.Segment);
StackPtr = new Pointer(stack, stackOffset);
}
}
-public unsafe class OuterWrapper
-{
- public Value* Target;
- public Value Stored;
-}
-
public class Runner : IDisposable
{
private readonly ILogger _logger = LoggerService.Get();
public readonly Options Options;
- public StackLike Calls;
+ private StackLike Calls;
// todo: maybe have a pointer everywhere to stacks or arrays and use those rather than built-in pointers or cursors... ?
- public StackLike Stack;
- public long Cursor;
+ private StackLike Stack;
+ private long Cursor => Stack.Position;
- public Register Globals = new Register();
- public Register Imports = new Register();
- public Register Exports = new Register();
+ private List Outers = [];
+
+ private Register Globals = new();
+ private Register Imports = new();
+ private Register Exports = new();
public Runner(Options options)
{
@@ -172,39 +54,74 @@ public class Runner : IDisposable
{
_logger.Method();
using Reader reader = new Reader(stream);
- Digester digester = new Digester(reader);
+ Compilation.Digester digester = new(reader);
Function function = digester.Digest();
File.WriteAllBytes("func.sqi", function.Segment.Serialize());
- Closure closure = new Closure(function);
+ Context closure = new Context(function);
Push(Obj.Create(closure));
- Call(closure, 0);
- return Interpret();
+ Invoke(closure, 0);
+ return Execute();
}
- private ExecutionResult Interpret()
+ private bool TestFlag(Op code, Op flag) => (code & Op.F_Mask) == flag;
+
+ private ExecutionResult Execute()
{
_logger.Method();
Call call = Calls.Peek(-1);
do {
Op opCode = call.Instruction.Next();
_logger.Verbose($"OpCode: {opCode}");
- switch (opCode) {
- case Op.Constant: Push(call.Instruction.NextConstant()); break;
- case Op.Pop: Pop(); break;
- case Op.Not: OpNot(); break;
- case Op.Add: OpAdd(); break;
- case Op.Subtract: OpSubtract(); break;
- case Op.Divide: OpDivide(); break;
- case Op.Modulo: OpModulo(); break;
- case Op.Multiply: OpMultiply(); break;
- case Op.Negate: OpNegate(); break;
- case Op.BitwiseOr: OpBitwiseOr(); break;
- case Op.BitwiseAnd: OpBitwiseAnd(); break;
- case Op.BitwiseXor: OpBitwiseXor(); break;
- case Op.BitwiseNot: OpBitwiseNot(); break;
- case Op.BitwiseLeft: OpBitwiseLeft(); break;
- case Op.BitwiseRight: OpBitwiseRight(); break;
+ if (TestFlag(opCode, Op.F_Operation)) {
+ Value result = ValueOperation.Resolve(PopOperation(opCode));
+ Push(result);
+ continue;
+ }
+
+ if (TestFlag(opCode, Op.F_Compare)) {
+ Value result = ValueOperation.Resolve(PopOperation(opCode));
+ Push(result);
+ continue;
+ }
+
+ switch (opCode) {
+ case Op.Constant: Push(call.Instruction.NextConstant()); break;
+ case Op.Pop: Pop(); break;
+ case Op.True: Push(Value.True); break;
+ case Op.False: Push(Value.False); break;
+
+ case Op.GetGlobal: {
+ string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
+ if (string.IsNullOrEmpty(name))
+ throw new RuntimeException($"tried to set global variable with empty name");
+ if (!Globals.Has(name))
+ Push(Value.Void);
+ else
+ Push(Globals[name]);
+ _logger.Verbose($"got global {name} ({Peek()})");
+ break;
+ }
+
+ case Op.SetGlobal: {
+ string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
+ if (string.IsNullOrEmpty(name))
+ throw new RuntimeException($"tried to set global variable with empty name");
+ if (!Globals.Has(name))
+ throw new RuntimeException($"tried to set a value of non-existing global variable");
+ Globals[name] = Pop();
+ _logger.Verbose($"set global {name} = {Globals[name]}");
+ break;
+ }
+
+ case Op.DefineGlobal: {
+ string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
+ if (string.IsNullOrEmpty(name))
+ throw new RuntimeException($"tried to define global variable with empty name");
+ Globals[name] = Pop();
+ _logger.Verbose($"defined global {name} as {Globals[name]}");
+ break;
+ }
case Op.GetLocal: {
long slot = call.Instruction.NextDynamic();
@@ -220,38 +137,158 @@ public class Runner : IDisposable
break;
}
- case Op.DefineGlobal: {
- string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
- if (string.IsNullOrEmpty(name))
- throw new ExecutionException($"tried to define global variable with empty name");
- Globals[name] = Pop();
- _logger.Verbose($"defined global {name} as {Globals[name]}");
+ case Op.GetOuter: {
+ long slot = call.Instruction.NextDynamic();
+ Push(call.Context.Outers[slot].Value);
break;
}
- case Op.SetGlobal: {
- string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
- if (string.IsNullOrEmpty(name))
- throw new ExecutionException($"tried to set global variable with empty name");
- if (!Globals.Has(name))
- throw new ExecutionException($"tried to set a value of non-existing global variable");
- Globals[name] = Pop();
- _logger.Verbose($"set global {name} = {Globals[name]}");
+ case Op.SetOuter: {
+ long slot = call.Instruction.NextDynamic();
+ call.Context.Outers[slot].Value = Peek();
break;
}
- case Op.GetGlobal: {
- string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
- if (string.IsNullOrEmpty(name))
- throw new ExecutionException($"tried to set global variable with empty name");
- if (!Globals.Has(name))
- Push(Value.Void);
- else
- Push(Globals[name]);
- _logger.Verbose($"got global {name} ({Peek()})");
+ case Op.GetMember: {
+ Value value = Peek();
+ if (!value.Is(T.Instance))
+ return Error("Only instances have members.");
+ Instance instance = value.Ptr.As()!;
+ string name = call.Instruction.NextString().Value!;
+ if (instance.Values.TryGet(name, out Value member)) {
+ Pop(); // remove instance from stack
+ Push(member);
+ } else if (!BindMethod(instance.Class, name)) {
+ return Error($"Could not bind member {name} to instance {instance} of class {instance.Class.Name}!");
+ }
+
break;
}
+ case Op.SetMember: {
+ Value value = Peek(-2);
+ if (!value.Is(T.Instance))
+ return Error("Only instances have members.");
+ Instance instance = value.Ptr.As()!;
+ string name = call.Instruction.NextString().Value!;
+ instance.Values.Set(name, Peek());
+ Value move = Pop();
+ Pop(); // remove instance from stack
+ Push(move);
+ break;
+ }
+
+ case Op.Base: {
+ string name = call.Instruction.NextString().Value!;
+ Class @class = Pop().Ptr.As()!;
+ if (!BindMethod(@class, name))
+ return Error($"Could not bind member {name} to base class {@class.Name}");
+ break;
+ }
+
+ case Op.Jump: {
+ long delta = call.Instruction.NextLong();
+ call.Instruction.Ptr += delta;
+ break;
+ }
+
+ case Op.JumpIfFalse: {
+ long delta = call.Instruction.NextLong();
+ if (Peek(0).IsFalsy)
+ call.Instruction.Ptr += delta;
+ break;
+ }
+
+ case Op.Loop: {
+ long delta = call.Instruction.NextLong();
+ call.Instruction.Ptr -= delta;
+ break;
+ }
+
+ case Op.Invoke: {
+ byte count = call.Instruction.Next();
+ if (!Invoke(Peek(-(count + 1)), count))
+ return Error($"Could not invoke function.");
+ call = Calls.Peek();
+ break;
+ }
+
+ case Op.InvokeMember: {
+ string name = call.Instruction.NextString().Value!;
+ byte argumentCount = call.Instruction.Next();
+ if (!Invoke(name, argumentCount))
+ return Error($"Could not invoke member {name}.");
+ call = Calls.Peek();
+ break;
+ }
+
+ case Op.InvokeBase: {
+ string name = call.Instruction.NextString().Value!;
+ byte argumentCount = call.Instruction.Next();
+ Class @class = Pop().Ptr.As()!;
+ if (!Invoke(@class, name, argumentCount))
+ return Error($"Could not invoke member {name} of base class {@class.Name}!");
+ call = Calls.Peek();
+ break;
+ }
+
+ case Op.Context: {
+ Function function = call.Instruction.NextConstant().Ptr.As()!;
+ Context context = new Context(function);
+ Push(Obj.Create(context));
+ for (int i = 0; i < context.Function.OuterCount; i++) {
+ Outer outer = context.Outers[i];
+ bool isLocal = call.Instruction.Next() == 1;
+ long index = call.Instruction.NextDynamic();
+ if (isLocal)
+ context.Outers.Add(CaptureOuter(call.StackPtr.Branch(index)));
+ else
+ context.Outers.Add(call.Context.Outers[index]);
+ }
+ break;
+ }
+
+ case Op.CloseOuter: {
+ CloseOuters(new Pointer(Stack, Cursor));
+ Pop();
+ break;
+ }
+
+ case Op.Return: // todo: not done
+ Value result = Pop();
+ CloseOuters(call.StackPtr.Branch());
+ Calls.Pop();
+ if (Calls.Count == 0) {
+ Pop();
+ return ExecutionResult.OK;
+ }
+ Stack.Decimate(call.StackPtr.Ptr);
+ Push(result);
+ call = Calls.Peek();
+ break;
+
+ case Op.Class: {
+ Push(Obj.Create(new Class(call.Instruction.NextString().Value!)));
+ break;
+ }
+
+ case Op.Inherit: {
+ Value @class = Peek(-2);
+ if (!@class.Is(T.Class))
+ return Error($"Can not inherit from {@class}, value must be of type Class.");
+ Class baseClass = @class.Ptr.As()!;
+ Class inheritingClass = Peek().Ptr.As()!;
+ foreach (var member in baseClass.Members) {
+ if (!inheritingClass.Members.Has(member.Key))
+ inheritingClass.Members.Add(member.Key, member.Value);
+ }
+ Pop(); //
+ break;
+ }
+
+ case Op.Method:
+ DefineMember(call.Instruction.NextString().Value!);
+ break;
case Op.Typeof: {
Value value = Pop();
@@ -265,17 +302,22 @@ public class Runner : IDisposable
break;
}
- case Op.Return: // todo: not done
- Value result = Pop();
- Calls.Pop();
- if (Calls.Count == 0) {
- Pop();
- return ExecutionResult.OK;
+ case Op.PrintStack: {
+ for (int i = 0; i < Cursor; i++) {
+ Console.WriteLine($"{Stack[i].ToString(true)}");
}
- Cursor = call.StackPtr.Ptr; // well find a better way...
- Push(result);
- call = Calls.Peek();
break;
+ }
+
+ case Op.PrintGlobals: {
+ foreach (var value in Globals) {
+ Console.WriteLine($"{value.Key}: {value.Value.ToString(true)}");
+ }
+ break;
+ }
+
+ default:
+ return Error($"Unexpected or not yet supported OpCode {opCode}!");
}
} while (call.Instruction.Segment.Instructions.Length > call.Instruction.Ptr);
@@ -283,7 +325,34 @@ public class Runner : IDisposable
return ExecutionResult.OK;
}
- private bool Call(Closure closure, int argumentCount)
+ private Outer CaptureOuter(Pointer target)
+ {
+ Outer? outer = null;
+ for (int i = Outers.Count; i-->0;) {
+ outer = Outers[i];
+ if (outer.Target.Ptr <= target.Ptr)
+ break;
+ }
+
+ if (outer != null && outer.Target.Ptr == target.Ptr)
+ return outer;
+
+ return new Outer(target);
+ }
+
+ private void CloseOuters(Pointer last)
+ {
+ for (int i = Outers.Count;i-->0;) {
+ Outer outer = Outers[i];
+ if (outer.IsClosed)
+ continue;
+ if (outer.Target.Ptr < last.Ptr)
+ return;
+ outer.Close();
+ }
+ }
+
+ private bool Invoke(Context closure, int argumentCount)
{
_logger.Method();
if (argumentCount != closure.Function.ArgumentCount)
@@ -297,6 +366,90 @@ public class Runner : IDisposable
return true;
}
+ private bool Invoke(string methodName, int argumentCount)
+ {
+ Value value = Peek(-argumentCount);
+ if (!value.Is(T.Instance)) {
+ Error($"Can not call {methodName} on value of {value}");
+ return false;
+ }
+
+ Instance instance = value.Ptr.As()!;
+ if (instance.Values.TryGet(methodName, out Value method))
+ return Invoke(method, argumentCount);
+
+ return Invoke(instance.Class, methodName, argumentCount);
+ }
+
+ private bool Invoke(Class @class, string methodName, int argumentCount)
+ {
+ if (@class.Members.TryGet(methodName, out Value method))
+ return Invoke(method, argumentCount);
+ Error($"Could not call method {methodName} on class {@class.Name}: Member does not exist");
+ return false;
+ }
+
+ private bool Invoke(Value value, int argumentCount)
+ {
+ if (!value.IsObj) {
+ Error($"Can only call values of object type! Tried to call {value.ToString(true)}", value);
+ return false;
+ }
+
+ if (value.Is(T.Method)) {
+ Method method = value.Ptr.As()!;
+ 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()!;
+ return Invoke(context, context.Function.ArgumentCount);
+ } else if (value.Is(T.Native)) {
+ 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()!;
+ Stack.Set(Cursor - argumentCount - 1, Instantiate(@class));
+ if (@class.Members.TryGet(@class.Name, out Value ctor)) {
+ Context? context = ctor.Ptr.As()!;
+ return Invoke(context, argumentCount);
+ } else if (argumentCount != 0) {
+ Error($"{@class.Name}() expects 0 arguments but received {argumentCount}");
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool BindMethod(Class @class, string name)
+ {
+ if (!@class.Members.TryGet(name, out Value value)) {
+ Error($"Undefined member '{name}' on class {@class.Name}");
+ return false;
+ }
+
+ Method method = new Method(value.Ptr.As()!, Peek());
+ Pop(); // remove instance from stack
+ Push(Obj.Create(method));
+ return true;
+ }
+
+ private Value Instantiate(Class @class)
+ {
+ Instance instance = new Instance(@class);
+ // todo: init fields etc here too
+ return Obj.Create(instance);
+ }
+
+ private void DefineMember(string name)
+ {
+ Value method = Peek();
+ Class @class = Peek(-2).Ptr.As()!;
+ @class.Members.Set(name, method);
+ Pop();
+ }
+
private Value Peek(int delta = -1)
{
_logger.Method();
@@ -315,92 +468,23 @@ public class Runner : IDisposable
Stack.Push(value);
}
- private void OpNot()
- {
- Value value = Pop();
- Push(ValueOperation.Not(value));
- }
-
- private void OpAdd()
+ private Operation PopOperation(Op code)
{
+ _logger.Method(code);
+ if ((code & Op.F_Unary) == Op.F_Unary)
+ return new Operation(code, Pop(), default);
Value right = Pop();
Value left = Pop();
- Push(ValueOperation.Add(left, right));
+ return new Operation(code, left, right);
}
- private void OpSubtract()
+ private ExecutionResult Error(string message, object? context = null, bool @throw = true)
{
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.Subtract(left, right));
- }
-
- private void OpDivide()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.Divide(left, right));
- }
-
- private void OpModulo()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.Modulo(left, right));
- }
-
- private void OpMultiply()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.Multiply(left, right));
- }
-
- private void OpNegate()
- {
- Value value = Pop();
- Push(ValueOperation.Negate(value));
- }
-
- private void OpBitwiseOr()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.BitwiseOr(left, right));
- }
-
- private void OpBitwiseAnd()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.BitwiseAnd(left, right));
- }
-
- private void OpBitwiseXor()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.BitwiseXor(left, right));
- }
-
- private void OpBitwiseLeft()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.BitwiseLeft(left, right));
- }
-
- private void OpBitwiseRight()
- {
- Value right = Pop();
- Value left = Pop();
- Push(ValueOperation.BitwiseRight(left, right));
- }
-
- private void OpBitwiseNot()
- {
- Value value = Pop();
- Push(ValueOperation.BitwiseInvert(value));
+ _logger.Method(message);
+ _logger.Error(message, context);
+ if (@throw)
+ throw new QampException(message, context);
+ return ExecutionResult.Execution;
}
public void Dispose()
@@ -409,6 +493,11 @@ public class Runner : IDisposable
}
}
+public class GarbageCollector
+{
+
+}
+
public enum ExecutionResult
{
OK = 0x0000,
diff --git a/Qrakhen.Qamp.Core/Execution/Segment.cs b/Qrakhen.Qamp.Core/Execution/Segment.cs
index 91e9775..bedfdf7 100644
--- a/Qrakhen.Qamp.Core/Execution/Segment.cs
+++ b/Qrakhen.Qamp.Core/Execution/Segment.cs
@@ -29,18 +29,27 @@ public class Segment(
}
///
- /// Although this always returns a long, the actually stored data in the instruction set has a dynamic width (anything between 1 and 8 bytes)
+ /// Although this always returns a long, the actually stored data in the instruction set has a dynamic width
+ /// (anything between 1 and 9 bytes, 1 if it's just a byte, and 1 + n, where n is up to 8 bytes).
+ /// If the first byte read is masked with 0x80, it means that this byte represents the amount of bytes that
+ /// shall be read. If it is not, the byte is returned directly (for smaller offsets).
///
- ///
- ///
- public long ReadDynamic(long offset, out byte length)
+ ///
+ /// All of this is done to compress compiled instruction size.
+ /// Todo: It will need to be tested wheter the cost on performance is stronger than anticipated.
+ ///
+ public long ReadDynamic(long offset, out int read)
{
- length = (byte)(Read(offset) ^ 0x80);
- byte[] bytes = new byte[8];
- for (int i = 0; i < length; i++) {
- bytes[i] = Instructions[offset + 1 + i];
- }
- return bytes.ToInt64();
+ byte value = Read(offset);
+ read = 1;
+ if (value < 0x80) // 0x80 flag for length, less than 0x80 means direct read
+ return value;
+ int length = value ^ 0x80;
+ read += length;
+ var data = new byte[8];
+ for (int i = 0; i < length; i++)
+ data[i] = Read(offset + i);
+ return data.ToInt64();
}
public short ReadShort(long offset) => BitConverter.ToInt16(Read(offset, 2));
diff --git a/Qrakhen.Qamp.Core/Execution/ValueOperation.cs b/Qrakhen.Qamp.Core/Execution/ValueOperation.cs
new file mode 100644
index 0000000..ab819da
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Execution/ValueOperation.cs
@@ -0,0 +1,197 @@
+using Qrakhen.Qamp.Core.Collections;
+using Qrakhen.Qamp.Core.Compilation;
+using Qrakhen.Qamp.Core.Logging;
+using Qrakhen.Qamp.Core.Values;
+
+namespace Qrakhen.Qamp.Core.Execution;
+
+public readonly record struct Operation(OpCode OpCode, Value Left, Value Right);
+
+public delegate Value OperationHandler(Operation operation);
+
+public static class ValueOperation
+{
+ private static readonly ILogger _logger = LoggerService.Get(nameof(ValueOperation));
+
+ private static readonly Register _operations;
+
+ public static Value Resolve(Operation operation)
+ {
+ _logger.Method($"{operation.Left} {operation.OpCode} {operation.Right}");
+ return _operations[operation.OpCode].Invoke(operation);
+ }
+
+ static ValueOperation()
+ {
+ _operations = new Register();
+ _operations.Add(OpCode.Equal, Equal);
+ _operations.Add(OpCode.Greater, Greater);
+ _operations.Add(OpCode.Less, Less);
+ _operations.Add(OpCode.Not, Not);
+ _operations.Add(OpCode.Add, Add);
+ _operations.Add(OpCode.Subtract, Subtract);
+ _operations.Add(OpCode.Divide, Divide);
+ _operations.Add(OpCode.Modulo, Modulo);
+ _operations.Add(OpCode.Multiply, Multiply);
+ _operations.Add(OpCode.Negate, Negate);
+ _operations.Add(OpCode.BitwiseOr, BitwiseOr);
+ _operations.Add(OpCode.BitwiseAnd, BitwiseAnd);
+ _operations.Add(OpCode.BitwiseLeft, BitwiseLeft);
+ _operations.Add(OpCode.BitwiseRight, BitwiseRight);
+ _operations.Add(OpCode.BitwiseNot, BitwiseInvert);
+ _operations.Add(OpCode.BitwiseXor, BitwiseXor);
+ }
+
+ public static Value Equal(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ return new Value(a.Dynamic == b.Dynamic);
+ }
+
+ public static Value Greater(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ return new Value(a.Dynamic > b.Dynamic);
+ }
+
+ public static Value Less(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ return new Value(a.Dynamic < b.Dynamic);
+ }
+
+ public static Value Not(Operation operation)
+ {
+ Value value = operation.Left;
+ _logger.Method($"{value}");
+ Assert.NotVoid(value);
+ ulong inner = value.Unsigned;
+ return new Value(inner == 0);
+ }
+
+ public static Value Add(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ if (a.IsString)
+ return Values.Objects.String.Make($"{a}{b}");
+ Assert.IsPrimitive(a, b);
+ bool convert = a.Type != b.Type;
+ return new Value(a.Dynamic + b.Dynamic);
+ }
+
+ public static Value Subtract(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ return new Value(a.Dynamic - b.Dynamic);
+ }
+
+ public static Value Divide(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.NotZero(b);
+ return new Value(a.Dynamic / b.Dynamic);
+ }
+
+ public static Value Modulo(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.NotZero(b);
+ return new Value(a.Dynamic % b.Dynamic);
+ }
+
+ public static Value Multiply(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ return new Value(a.Dynamic * b.Dynamic);
+ }
+
+ public static Value Negate(Operation operation)
+ {
+ Value value = operation.Left;
+ Assert.NotVoid(value);
+ Assert.IsPrimitive(value);
+ return new Value(-value.Dynamic);
+ }
+
+ public static Value BitwiseOr(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.IsInteger(a, b);
+ return new Value(a.Dynamic | b.Dynamic);
+ }
+
+ public static Value BitwiseAnd(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.IsInteger(a, b);
+ return new Value(a.Dynamic & b.Dynamic);
+ }
+
+ public static Value BitwiseXor(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.IsInteger(a, b);
+ return new Value(a.Dynamic ^ b.Dynamic);
+ }
+
+ public static Value BitwiseLeft(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.IsInteger(a, b);
+ return new Value(a.Dynamic << (int)(b.Dynamic ?? 0));
+ }
+
+ public static Value BitwiseRight(Operation operation)
+ {
+ Value a = operation.Left;
+ Value b = operation.Right;
+ Assert.NotVoid(a, b);
+ Assert.IsPrimitive(a, b);
+ Assert.IsInteger(a, b);
+ return new Value(a.Dynamic >> (int)(b.Dynamic ?? 0));
+ }
+
+ public static Value BitwiseInvert(Operation operation)
+ {
+ Value a = operation.Left;
+ Assert.NotVoid(a);
+ Assert.IsPrimitive(a);
+ Assert.IsInteger(a);
+ return new Value(~a.Dynamic);
+ }
+}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Injector.cs b/Qrakhen.Qamp.Core/Injector.cs
new file mode 100644
index 0000000..7599da4
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Injector.cs
@@ -0,0 +1,19 @@
+namespace Qrakhen.Qamp.Core.Injector;
+
+///
+/// Class used to export custom .dll implementations as binary instruction files (*.sqi).
+///
+public class Injector
+{
+
+}
+
+public enum Scope
+{
+ /// Within the global scope of the file that is importing this library
+ Global,
+ /// Within the scope of the module this library exports
+ Module,
+ /// An extension of the built-in type
+ Extension
+}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Qrakhen.Qamp.Core.csproj b/Qrakhen.Qamp.Core/Qrakhen.Qamp.Core.csproj
index cdfe8fc..1130182 100644
--- a/Qrakhen.Qamp.Core/Qrakhen.Qamp.Core.csproj
+++ b/Qrakhen.Qamp.Core/Qrakhen.Qamp.Core.csproj
@@ -5,5 +5,6 @@
enable
enable
true
+ ..\Build\
diff --git a/Qrakhen.Qamp.Core/Tokenization/Dialect.cs b/Qrakhen.Qamp.Core/Tokenization/Dialect.cs
index 59b7652..5caed20 100644
--- a/Qrakhen.Qamp.Core/Tokenization/Dialect.cs
+++ b/Qrakhen.Qamp.Core/Tokenization/Dialect.cs
@@ -88,7 +88,7 @@ public class DefaultDialect : Dialect
Define(Ref, "ref");
Define(Function, "function");
Define(Class, "class");
- Define(Super, "base");
+ Define(Base, "base");
Define(TypeOf, "typeof");
Define(Print, "print");
Define(Import, "import");
@@ -109,7 +109,7 @@ public class ClassicDialect : DefaultDialect
Define(Return, "<:");
Define(Ref, "*&");
Define(Function, "fq");
- Define(Super, "^~");
+ Define(Base, "^~");
Define(TypeOf, "?:");
Define(Print, "::");
Define(Import, " PreValues = new();
+ public PushStack Outers = new();
public override string ToString() => "{}";
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Values/Objects/Function.cs b/Qrakhen.Qamp.Core/Values/Objects/Function.cs
index 3b117de..2571709 100644
--- a/Qrakhen.Qamp.Core/Values/Objects/Function.cs
+++ b/Qrakhen.Qamp.Core/Values/Objects/Function.cs
@@ -9,5 +9,5 @@ public class Function(string name, Segment segment, int outerCount, int argument
public readonly int OuterCount = outerCount;
public readonly int ArgumentCount = argumentCount;
- public override string ToString() => $"{name}()";
+ public override string ToString() => $"{Name}()";
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Values/Objects/Instance.cs b/Qrakhen.Qamp.Core/Values/Objects/Instance.cs
new file mode 100644
index 0000000..e78daa8
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Values/Objects/Instance.cs
@@ -0,0 +1,7 @@
+namespace Qrakhen.Qamp.Core.Values.Objects;
+
+public class Instance(Class @class) : Obj(ValueType.Instance)
+{
+ public readonly Class Class = @class;
+ public Table Values = new();
+}
diff --git a/Qrakhen.Qamp.Core/Values/Objects/Method.cs b/Qrakhen.Qamp.Core/Values/Objects/Method.cs
new file mode 100644
index 0000000..553cd4a
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Values/Objects/Method.cs
@@ -0,0 +1,6 @@
+namespace Qrakhen.Qamp.Core.Values.Objects;
+
+public class Method(Function function, Value receiver) : Context(function, ValueType.Method)
+{
+ public Value Receiver = receiver;
+}
diff --git a/Qrakhen.Qamp.Core/Values/Objects/Outer.cs b/Qrakhen.Qamp.Core/Values/Objects/Outer.cs
new file mode 100644
index 0000000..0aa48b9
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Values/Objects/Outer.cs
@@ -0,0 +1,40 @@
+using Qrakhen.Qamp.Core.Collections;
+
+namespace Qrakhen.Qamp.Core.Values.Objects;
+
+using T = ValueType;
+
+///
+/// Wrapper to access values that are spread around the stack inside contexts
+///
+public class Outer(Pointer target) : Obj(T.Outer)
+{
+ private Value _stored = Value.Void;
+ public Pointer Target = target;
+
+ public bool IsClosed { get; private set; }
+
+ ///
+ /// Dynamic getter & setter that returns or writes to either the target value directly,
+ /// or the stored value if this has been closed.
+ ///
+ public Value Value
+ {
+ get => IsClosed ? _stored : Target!.Get();
+ set {
+ if (IsClosed)
+ _stored = value;
+ else
+ Target.Set(value);
+ }
+ }
+
+ public Value Close()
+ {
+ if (IsClosed)
+ throw new Exception($"meh, something not right here (cant close already closed outer)");
+ _stored = Target!.Get();
+ IsClosed = true;
+ return _stored;
+ }
+}
diff --git a/Qrakhen.Qamp.Core/Values/Objects/Table.cs b/Qrakhen.Qamp.Core/Values/Objects/Table.cs
new file mode 100644
index 0000000..a67db93
--- /dev/null
+++ b/Qrakhen.Qamp.Core/Values/Objects/Table.cs
@@ -0,0 +1,8 @@
+using Qrakhen.Qamp.Core.Collections;
+
+namespace Qrakhen.Qamp.Core.Values.Objects;
+
+public class Table : Register
+{
+
+}
diff --git a/Qrakhen.Qamp.Core/Values/Ptr.cs b/Qrakhen.Qamp.Core/Values/Ptr.cs
index ffb4e30..01cdca9 100644
--- a/Qrakhen.Qamp.Core/Values/Ptr.cs
+++ b/Qrakhen.Qamp.Core/Values/Ptr.cs
@@ -1,13 +1,14 @@
using Qrakhen.Qamp.Core.Values.Objects;
using System.Runtime.InteropServices;
using Qrakhen.Qamp.Core.Collections;
+using System.Diagnostics.CodeAnalysis;
namespace Qrakhen.Qamp.Core.Values;
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
public readonly struct Ptr
{
- private static readonly StackRegister _register = new(0x10);
+ private static readonly PushStack _register = new(0x10);
[Serialized] public readonly Address Address;
@@ -31,5 +32,13 @@ public readonly struct Ptr
Address = _register.Add(obj);
}
+ public T? As(bool throwWhenNull = true) where T : Obj
+ {
+ T? value = Value as T;
+ if (value == null && throwWhenNull)
+ throw new RuntimeException($"Could not convert Object {Value} to target type {typeof(T)}.", Value);
+ return value;
+ }
+
public override string ToString() => $"0x{Value}";
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Values/Value.cs b/Qrakhen.Qamp.Core/Values/Value.cs
index f0c1edb..379a9b4 100644
--- a/Qrakhen.Qamp.Core/Values/Value.cs
+++ b/Qrakhen.Qamp.Core/Values/Value.cs
@@ -15,6 +15,8 @@ public interface IValue
public readonly struct Value : IValue, IDebug
{
public static readonly Value Void = new Value();
+ public static readonly Value True = new Value(true);
+ public static readonly Value False = new Value(false);
[FieldOffset(0x00)] [Serialized] public readonly ulong Unsigned;
[FieldOffset(0x00)] [Serialized] public readonly long Signed;
@@ -28,9 +30,7 @@ public readonly struct Value : IValue, IDebug
[FieldOffset(0x08)] [Serialized] public readonly T Type;
[FieldOffset(0x0c)] [Serialized] public readonly uint Meta; // like lengths of arrays or lists
-
- public T ValueType => Type;
-
+
private Value(T type) => Type = type;
public Value() : this(T.Void) { }
@@ -44,7 +44,20 @@ public readonly struct Value : IValue, IDebug
public Value(Ref @ref) : this(T.Reference) => Ref = @ref;
public dynamic? Dynamic => AsDynamic();
-
+
+ public T ValueType => Type;
+
+ public bool IsNumber => IsSigned || IsUnsigned || IsDecimal;
+ public bool IsSigned => Is(T.Signed);
+ public bool IsUnsigned => Is(T.Unsigned);
+ public bool IsDecimal => Is(T.Decimal);
+ public bool IsBool => Is(T.Bool);
+ public bool IsString => Is(T.String);
+ public bool IsRef => Is(T.Reference, false);
+ public bool IsObj => Is(T.Object, false);
+
+ public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0;
+
public bool Is(T type, bool exact = true)
{
return exact ? (Type & type) == type : (Type & type) > 0;
@@ -52,7 +65,10 @@ public readonly struct Value : IValue, IDebug
private unsafe dynamic? AsDynamic(bool throwWhenNull = true)
{
- if (Type.HasFlag(T.Object))
+ if (IsString)
+ return Ptr.As()!.Value;
+
+ if (IsObj)
return Ptr.Value;
return Type switch {
@@ -88,13 +104,18 @@ public readonly struct Value : IValue, IDebug
public override int GetHashCode() => Unsigned.GetHashCode();
- public override unsafe string ToString()
+ public override string ToString() => ToString(false);
+
+ public string ToString(bool includeType)
{
+ if (!includeType)
+ return $"{AsDynamic(false)}";
+
string type = Type.ToString();
if (Type.HasFlag(T.Pointer))
type = Ptr.Value?.Type.ToString() ?? "NullPtr";
-
- return $"[{type}, {AsDynamic(false) ?? "null"}]";
+
+ return $"<{type}, {AsDynamic(false) ?? "null"}>";
}
public string Debug(DebugLevel level = DebugLevel.None)
diff --git a/Qrakhen.Qamp.Core/Values/ValueOperation.cs b/Qrakhen.Qamp.Core/Values/ValueOperation.cs
deleted file mode 100644
index 1bd3ef9..0000000
--- a/Qrakhen.Qamp.Core/Values/ValueOperation.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using Qrakhen.Qamp.Core.Logging;
-
-namespace Qrakhen.Qamp.Core.Values;
-
-public static class ValueOperation
-{
- private static ILogger _logger = LoggerService.Get(nameof(ValueOperation));
-
- public static Value Not(Value v)
- {
- _logger.Method($"{v}");
- Assert.NotVoid(v);
- ulong inner = v.Unsigned;
- return new Value(inner == 0);
- }
-
- public static Value Add(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- bool convert = a.Type != b.Type; // meh, lets make c# do that for us for now
- return new Value(a.Dynamic + b.Dynamic);
- }
-
- public static Value Subtract(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- return new Value(a.Dynamic - b.Dynamic);
- }
-
- public static Value Divide(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.NotZero(b);
- return new Value(a.Dynamic / b.Dynamic);
- }
-
- public static Value Modulo(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.NotZero(b);
- return new Value(a.Dynamic % b.Dynamic);
- }
-
- public static Value Multiply(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- return new Value(a.Dynamic * b.Dynamic);
- }
-
- public static Value Negate(Value a)
- {
- _logger.Method($"{a}");
- Assert.NotVoid(a);
- Assert.IsPrimitive(a);
- return new Value(-a.Dynamic);
- }
-
- public static Value BitwiseOr(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.IsInteger(a, b);
- return new Value(a.Dynamic | b.Dynamic);
- }
-
- public static Value BitwiseAnd(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.IsInteger(a, b);
- return new Value(a.Dynamic & b.Dynamic);
- }
-
- public static Value BitwiseXor(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.IsInteger(a, b);
- return new Value(a.Dynamic ^ b.Dynamic);
- }
-
- public static Value BitwiseLeft(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.IsInteger(a, b);
- return new Value(a.Dynamic << (int)(b.Dynamic ?? 0));
- }
-
- public static Value BitwiseRight(Value a, Value b)
- {
- _logger.Method($"{a}, {b}");
- Assert.NotVoid(a, b);
- Assert.IsPrimitive(a, b);
- Assert.IsInteger(a, b);
- return new Value(a.Dynamic >> (int)(b.Dynamic ?? 0));
- }
-
- public static Value BitwiseInvert(Value a)
- {
- _logger.Method($"{a}");
- Assert.NotVoid(a);
- Assert.IsPrimitive(a);
- Assert.IsInteger(a);
- return new Value(~a.Dynamic);
- }
-}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Core/Values/ValueType.cs b/Qrakhen.Qamp.Core/Values/ValueType.cs
index 97090ba..2f0b51b 100644
--- a/Qrakhen.Qamp.Core/Values/ValueType.cs
+++ b/Qrakhen.Qamp.Core/Values/ValueType.cs
@@ -1,5 +1,9 @@
namespace Qrakhen.Qamp.Core.Values;
+///
+/// todo: make value type a byte instead to save some memory on values
+/// edit: if that's even possible with c#'s memory padding
+///
[Flags]
public enum ValueType
{
@@ -11,6 +15,7 @@ public enum ValueType
Decimal = 0x0008,
Bool = 0x0010,
+ Number = Unsigned | Signed | Decimal,
Integer = Unsigned | Signed | Char,
Primitive = Integer | Decimal | Bool,
@@ -29,7 +34,7 @@ public enum ValueType
Instance = Object | 0x0020,
Class = Object | 0x0100,
Method = Class | Function,
- PreValue = Object | 0x0200,
+ Outer = Object | 0x0200,
Module = 0xFFFF
}
\ No newline at end of file
diff --git a/Qrakhen.Qamp.Digest/Class1.cs b/Qrakhen.Qamp.Digest/Class1.cs
new file mode 100644
index 0000000..1a0484c
--- /dev/null
+++ b/Qrakhen.Qamp.Digest/Class1.cs
@@ -0,0 +1,6 @@
+namespace Qrakhen.Qamp.Digest;
+
+public class Class1
+{
+
+}
diff --git a/Qrakhen.Qamp.Digest/Qrakhen.Qamp.Digest.csproj b/Qrakhen.Qamp.Digest/Qrakhen.Qamp.Digest.csproj
new file mode 100644
index 0000000..359c961
--- /dev/null
+++ b/Qrakhen.Qamp.Digest/Qrakhen.Qamp.Digest.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net10.0
+ enable
+ enable
+ ..\Build\
+
+
+
+
+
+
+
diff --git a/Qrakhen.Qamp.Runtime/Class1.cs b/Qrakhen.Qamp.Runtime/Class1.cs
new file mode 100644
index 0000000..585c8e4
--- /dev/null
+++ b/Qrakhen.Qamp.Runtime/Class1.cs
@@ -0,0 +1,6 @@
+namespace Qrakhen.Qamp.Runtime;
+
+public class Class1
+{
+
+}
diff --git a/Qrakhen.Qamp.Runtime/Qrakhen.Qamp.Runtime.csproj b/Qrakhen.Qamp.Runtime/Qrakhen.Qamp.Runtime.csproj
new file mode 100644
index 0000000..5c59036
--- /dev/null
+++ b/Qrakhen.Qamp.Runtime/Qrakhen.Qamp.Runtime.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net10.0
+ enable
+ enable
+ True
+ ..\Build\
+
+
+
+
+
+
+
diff --git a/Qrakhen.Qamp.sln b/Qrakhen.Qamp.sln
index b5d19c9..ce2534f 100644
--- a/Qrakhen.Qamp.sln
+++ b/Qrakhen.Qamp.sln
@@ -1,12 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
-VisualStudioVersion = 18.0.11123.170 d18.0
+VisualStudioVersion = 18.0.11123.170
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Core", "Qrakhen.Qamp.Core\Qrakhen.Qamp.Core.csproj", "{CA6F4D4E-AF35-4CA6-BC53-C83277EEE46C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.CLI", "Qrakhen.Qamp.CLI\Qrakhen.Qamp.CLI.csproj", "{1D355F76-9D35-4CB2-BF8C-944B0842BC30}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Digest", "Qrakhen.Qamp.Digest\Qrakhen.Qamp.Digest.csproj", "{E836DAE4-9F53-4E3D-BEF7-ADA84BE356CE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qrakhen.Qamp.Runtime", "Qrakhen.Qamp.Runtime\Qrakhen.Qamp.Runtime.csproj", "{4B0C0717-6529-4B70-A3EA-C3426F1B3FDC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +25,14 @@ Global
{1D355F76-9D35-4CB2-BF8C-944B0842BC30}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D355F76-9D35-4CB2-BF8C-944B0842BC30}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D355F76-9D35-4CB2-BF8C-944B0842BC30}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E836DAE4-9F53-4E3D-BEF7-ADA84BE356CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E836DAE4-9F53-4E3D-BEF7-ADA84BE356CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E836DAE4-9F53-4E3D-BEF7-ADA84BE356CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E836DAE4-9F53-4E3D-BEF7-ADA84BE356CE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4B0C0717-6529-4B70-A3EA-C3426F1B3FDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4B0C0717-6529-4B70-A3EA-C3426F1B3FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4B0C0717-6529-4B70-A3EA-C3426F1B3FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4B0C0717-6529-4B70-A3EA-C3426F1B3FDC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..b622a75
Binary files /dev/null and b/icon.png differ