fix formatting everywhere and make operation resolving faster

This commit is contained in:
Qrakhen 2025-11-23 07:38:22 +01:00
parent fad93a7973
commit a39096014c
94 changed files with 3941 additions and 3862 deletions

View File

@ -1,6 +1,6 @@
public enum ConsoleCode public enum ConsoleCode
{ {
Error = -1, Error = -1,
OK = 0, OK = 0,
Exit = 1 Exit = 1
} }

View File

@ -1,10 +1,10 @@
public static class ConsoleExtensions public static class ConsoleExtensions
{ {
public static bool Check(this ConsoleKeyInfo keyInfo, ConsoleKey key, ConsoleModifiers modifier = default, bool exactModifier = false) public static bool Check(this ConsoleKeyInfo keyInfo, ConsoleKey key, ConsoleModifiers modifier = default, bool exactModifier = false)
{ {
return ((exactModifier ? return ((exactModifier ?
keyInfo.Modifiers == modifier : keyInfo.Modifiers == modifier :
(keyInfo.Modifiers & modifier) == modifier) (keyInfo.Modifiers & modifier) == modifier)
&& keyInfo.Key == key); && keyInfo.Key == key);
} }
} }

View File

@ -4,54 +4,54 @@ using System.Text;
public class ConsoleRenderer public class ConsoleRenderer
{ {
private readonly IReader<Token> _reader; private readonly IReader<Token> _reader;
private readonly Stream _stream; private readonly Stream _stream;
public ConsoleRenderer(Stream stream) public ConsoleRenderer(Stream stream)
{ {
_stream = new MemoryStream(); _stream = new MemoryStream();
stream.Position = 0; stream.Position = 0;
stream.CopyTo(_stream); stream.CopyTo(_stream);
_reader = new Reader(_stream); _reader = new Reader(_stream);
} }
public void Render((int x, int y) position) public void Render((int x, int y) position)
{ {
Console.CursorVisible = false; Console.CursorVisible = false;
Console.SetCursorPosition(position.x, position.y); Console.SetCursorPosition(position.x, position.y);
List<Token> tokens = []; List<Token> tokens = [];
while (!_reader.Done) { while (!_reader.Done) {
tokens.Add(_reader.NextToken(true)); tokens.Add(_reader.NextToken(true));
} }
_stream.Position = 0; _stream.Position = 0;
foreach (var token in tokens) { foreach (var token in tokens) {
ConsoleColor color = ConsoleColor.Gray; ConsoleColor color = ConsoleColor.Gray;
if (token.Type.IsBoolean()) if (token.Type.IsBoolean())
color = ConsoleColor.Blue; color = ConsoleColor.Blue;
if (token.Type.IsString()) if (token.Type.IsString())
color = ConsoleColor.Cyan; color = ConsoleColor.Cyan;
if (token.Type.IsNumber()) if (token.Type.IsNumber())
color = ConsoleColor.DarkCyan; color = ConsoleColor.DarkCyan;
if (token.Type.IsOperator()) if (token.Type.IsOperator())
color = ConsoleColor.DarkGreen; color = ConsoleColor.DarkGreen;
if (token.Type.IsBracket()) if (token.Type.IsBracket())
color = ConsoleColor.Red; color = ConsoleColor.Red;
if (token.Type.IsIdentifier()) if (token.Type.IsIdentifier())
color = ConsoleColor.White; color = ConsoleColor.White;
if (token.Type.IsControl()) if (token.Type.IsControl())
color = ConsoleColor.DarkYellow; color = ConsoleColor.DarkYellow;
if (token.Type == TokenType.Error) if (token.Type == TokenType.Error)
color = ConsoleColor.DarkRed; color = ConsoleColor.DarkRed;
Console.ForegroundColor = color; Console.ForegroundColor = color;
byte[] buffer = new byte[token.Span.Length]; byte[] buffer = new byte[token.Span.Length];
_stream.ReadExactly(buffer, 0, (int)token.Span.Length); _stream.ReadExactly(buffer, 0, (int)token.Span.Length);
string str = Encoding.Default.GetString(buffer); string str = Encoding.Default.GetString(buffer);
Console.Write(str); Console.Write(str);
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
if (token.Type == TokenType.NewLine) if (token.Type == TokenType.NewLine)
Console.Write(" : "); Console.Write(" : ");
} }
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
Console.CursorVisible = true; Console.CursorVisible = true;
} }
} }

View File

@ -2,141 +2,141 @@
public class ConsoleWriter public class ConsoleWriter
{ {
private const string SYMBOL_INPUT = " <: "; private const string SYMBOL_INPUT = " <: ";
private const string SYMBOL_INPUT_CONTINUE = " : "; private const string SYMBOL_INPUT_CONTINUE = " : ";
private readonly List<char> _buffer = []; private readonly List<char> _buffer = [];
private readonly Stack<char[]> _history = []; private readonly Stack<char[]> _history = [];
private readonly Encoding _encoding; private readonly Encoding _encoding;
private int _position; private int _position;
private (int x, int y) _origin; private (int x, int y) _origin;
private (int x, int y) _cursorPosition => Console.GetCursorPosition(); private (int x, int y) _cursorPosition => Console.GetCursorPosition();
private int _cursorOffset => SYMBOL_INPUT.Length; private int _cursorOffset => SYMBOL_INPUT.Length;
private int _cursorLeft { get => Console.CursorLeft; set => Console.CursorLeft = value; } private int _cursorLeft { get => Console.CursorLeft; set => Console.CursorLeft = value; }
private int _cursorTop { get => Console.CursorTop; set => Console.CursorTop = value; } private int _cursorTop { get => Console.CursorTop; set => Console.CursorTop = value; }
public bool UseSyntaxHighlighting { get; set; } = false; public bool UseSyntaxHighlighting { get; set; } = false;
public ConsoleWriter(Encoding encoding) public ConsoleWriter(Encoding encoding)
{ {
_encoding = encoding; _encoding = encoding;
} }
private void Write(char c) => Console.Write(c); private void Write(char c) => Console.Write(c);
private void Write(string str) => Console.Write(str); private void Write(string str) => Console.Write(str);
private void SetCursor((int x, int y) position) => Console.SetCursorPosition(position.x, position.y); private void SetCursor((int x, int y) position) => Console.SetCursorPosition(position.x, position.y);
private void SetColor(ConsoleColor color, ConsoleColor background = ConsoleColor.Black) private void SetColor(ConsoleColor color, ConsoleColor background = ConsoleColor.Black)
{ {
Console.ForegroundColor = color; Console.ForegroundColor = color;
Console.BackgroundColor = background; Console.BackgroundColor = background;
} }
private void Move(int x, int y = 0) private void Move(int x, int y = 0)
{ {
_position = Math.Min(_buffer.Count, Math.Max(0, _position + x)); _position = Math.Min(_buffer.Count, Math.Max(0, _position + x));
_cursorLeft -= 1; //???? _cursorLeft -= 1; //????
} }
private void ClearLine() private void ClearLine()
{ {
int left = _cursorLeft; int left = _cursorLeft;
while (_cursorLeft < Console.BufferWidth - 1) while (_cursorLeft < Console.BufferWidth - 1)
Write(' '); Write(' ');
_cursorLeft = left; _cursorLeft = left;
} }
private void Print((int x, int y) origin, (int x, int y)? remain = null) private void Print((int x, int y) origin, (int x, int y)? remain = null)
{ {
SetColor(ConsoleColor.White); SetColor(ConsoleColor.White);
SetCursor(origin); SetCursor(origin);
Write(" <: "); Write(" <: ");
foreach (var c in _buffer) { foreach (var c in _buffer) {
if (c == '\n') { if (c == '\n') {
ClearLine(); ClearLine();
Write("\n : "); Write("\n : ");
} else { } else {
Write(c); Write(c);
}
}
ClearLine();
if (remain.HasValue)
SetCursor(remain.Value);
}
private Stream Dispatch()
{
var stream = new MemoryStream();
var data = _buffer.ToArray();
stream.Write(_encoding.GetBytes(data));
_history.Push(data);
_buffer.Clear();
return stream;
}
public ConsoleCode Read(out Stream? stream)
{
stream = null;
ConsoleKeyInfo input;
_origin = _cursorPosition;
_position = 0;
Print(_origin);
do {
if (UseSyntaxHighlighting)
new ConsoleRenderer(new MemoryStream(_encoding.GetBytes(_buffer.ToArray()))).Render(_origin); // ~ so efficient ~ XD
Print(_origin, _cursorPosition);
input = Console.ReadKey(true);
if (input.Modifiers == ConsoleModifiers.Control) {
if (input.Key == ConsoleKey.C)
return ConsoleCode.Exit;
if (input.Key == ConsoleKey.H) {
UseSyntaxHighlighting = !UseSyntaxHighlighting;
Write($"\n #: {nameof(UseSyntaxHighlighting)} = {UseSyntaxHighlighting}\n <: ");
_origin = _cursorPosition;
} }
}
ClearLine();
if (remain.HasValue)
SetCursor(remain.Value);
}
private Stream Dispatch()
{
var stream = new MemoryStream();
var data = _buffer.ToArray();
stream.Write(_encoding.GetBytes(data));
_history.Push(data);
_buffer.Clear();
return stream;
}
public ConsoleCode Read(out Stream? stream)
{
stream = null;
ConsoleKeyInfo input;
_origin = _cursorPosition;
_position = 0;
Print(_origin);
do {
if (UseSyntaxHighlighting)
new ConsoleRenderer(new MemoryStream(_encoding.GetBytes(_buffer.ToArray()))).Render(_origin); // ~ so efficient ~ XD
Print(_origin, _cursorPosition);
input = Console.ReadKey(true);
if (input.Modifiers == ConsoleModifiers.Control) {
if (input.Key == ConsoleKey.C)
return ConsoleCode.Exit;
if (input.Key == ConsoleKey.H) {
UseSyntaxHighlighting = !UseSyntaxHighlighting;
Write($"\n #: {nameof(UseSyntaxHighlighting)} = {UseSyntaxHighlighting}\n <: ");
_origin = _cursorPosition;
}
if (input.Key == ConsoleKey.LeftArrow) {
}
continue;
}
if (input.Key == ConsoleKey.LeftArrow) { if (input.Key == ConsoleKey.LeftArrow) {
if (_position > 0) {
_position--;
_cursorLeft--;
}
continue;
}
if (input.Key == ConsoleKey.RightArrow) {
if (_position < _buffer.Count) {
_position++;
_cursorLeft++;
}
continue;
} }
continue;
}
if (input.Key == ConsoleKey.Backspace) { if (input.Key == ConsoleKey.LeftArrow) {
if (_position > 0) { if (_position > 0) {
_cursorLeft--; _position--;
_buffer.RemoveRange(--_position, 1); _cursorLeft--;
}
continue;
} else if (input.Check(ConsoleKey.Enter, ConsoleModifiers.Shift)) {
_buffer.Insert(_position++, '\n');
_cursorLeft = _cursorOffset;
_cursorTop++;
continue;
} else {
_buffer.Insert(_position++, input.KeyChar);
_cursorLeft++;
} }
} while (!input.Check(ConsoleKey.Enter, ConsoleModifiers.None, true)); continue;
stream = Dispatch(); }
return ConsoleCode.OK;
} if (input.Key == ConsoleKey.RightArrow) {
if (_position < _buffer.Count) {
_position++;
_cursorLeft++;
}
continue;
}
if (input.Key == ConsoleKey.Backspace) {
if (_position > 0) {
_cursorLeft--;
_buffer.RemoveRange(--_position, 1);
}
continue;
} else if (input.Check(ConsoleKey.Enter, ConsoleModifiers.Shift)) {
_buffer.Insert(_position++, '\n');
_cursorLeft = _cursorOffset;
_cursorTop++;
continue;
} else {
_buffer.Insert(_position++, input.KeyChar);
_cursorLeft++;
}
} while (!input.Check(ConsoleKey.Enter, ConsoleModifiers.None, true));
stream = Dispatch();
return ConsoleCode.OK;
}
} }

View File

@ -47,123 +47,121 @@ bool useSyntaxHighlighting = false;
ConsoleCode code = ConsoleCode.Error; ConsoleCode code = ConsoleCode.Error;
Runner runner = new(new Options()); Runner runner = new(new Options());
do { do {
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
MemoryStream stream = new MemoryStream(); MemoryStream stream = new MemoryStream();
Console.Write(" <: "); Console.Write(" <: ");
cursor = Console.GetCursorPosition(); cursor = Console.GetCursorPosition();
do { do {
if (useSyntaxHighlighting) if (useSyntaxHighlighting)
new ConsoleRenderer(stream).Render(cursor); new ConsoleRenderer(stream).Render(cursor);
input = Console.ReadKey(true); input = Console.ReadKey(true);
if (input.Modifiers == ConsoleModifiers.Control) { if (input.Modifiers == ConsoleModifiers.Control) {
if (input.Key == ConsoleKey.H) { if (input.Key == ConsoleKey.H) {
useSyntaxHighlighting = !useSyntaxHighlighting; useSyntaxHighlighting = !useSyntaxHighlighting;
Console.Write($"\n #: {nameof(useSyntaxHighlighting)} = {useSyntaxHighlighting}\n <: "); Console.Write($"\n #: {nameof(useSyntaxHighlighting)} = {useSyntaxHighlighting}\n <: ");
cursor = Console.GetCursorPosition(); cursor = Console.GetCursorPosition();
} }
if (input.Key == ConsoleKey.L) { if (input.Key == ConsoleKey.L) {
LoggerService.Default = LoggerService.Default < LogLevel.All ? LogLevel.All : LogLevel.Info; LoggerService.Default = LoggerService.Default < LogLevel.All ? LogLevel.All : LogLevel.Info;
Console.Write($"\n #: LogLevel = {LoggerService.Default}\n <: "); Console.Write($"\n #: LogLevel = {LoggerService.Default}\n <: ");
cursor = Console.GetCursorPosition(); cursor = Console.GetCursorPosition();
} }
continue; continue;
} }
if (input.Key == ConsoleKey.LeftArrow) { if (input.Key == ConsoleKey.LeftArrow) {
if (stream.Position > 0) { if (stream.Position > 0) {
stream.Position--; stream.Position--;
Console.CursorLeft--; Console.CursorLeft--;
} }
continue; continue;
} }
if (input.Key == ConsoleKey.RightArrow) { if (input.Key == ConsoleKey.RightArrow) {
if (stream.Position < stream.Length) { if (stream.Position < stream.Length) {
stream.Position++; stream.Position++;
Console.CursorLeft++; Console.CursorLeft++;
} }
continue; continue;
} }
if (input.Key == ConsoleKey.UpArrow) { if (input.Key == ConsoleKey.UpArrow) {
historyCursor = Math.Min(History.Count - 1, historyCursor + 1); historyCursor = Math.Min(History.Count - 1, historyCursor + 1);
byte[] bytes = History[historyCursor]; byte[] bytes = History[historyCursor];
continue; continue;
} }
if (input.Key == ConsoleKey.DownArrow) { if (input.Key == ConsoleKey.DownArrow) {
historyCursor = Math.Max(0, historyCursor - 1); historyCursor = Math.Max(0, historyCursor - 1);
byte[] bytes = History[historyCursor]; byte[] bytes = History[historyCursor];
continue; continue;
} }
if (input.Key == ConsoleKey.Backspace && stream.Position > 0) { if (input.Key == ConsoleKey.Backspace && stream.Position > 0) {
Console.CursorLeft -= 1;
Console.Write(' ');
if (!useSyntaxHighlighting)
Console.CursorLeft -= 1; Console.CursorLeft -= 1;
Console.Write(' '); //stream.Position--;
if (!useSyntaxHighlighting) //stream.SetLength(stream.Position);
Console.CursorLeft -= 1; stream.Remove(stream.Position - 1, 1);
//stream.Position--; } else if (input.Key == ConsoleKey.Enter && input.Modifiers == ConsoleModifiers.Shift) {
//stream.SetLength(stream.Position); if (!useSyntaxHighlighting)
stream.Remove(stream.Position - 1, 1); Console.Write("\n : ");
} else if (input.Key == ConsoleKey.Enter && input.Modifiers == ConsoleModifiers.Shift) { stream.Insert(Encoding.Default.GetBytes(Environment.NewLine));
if (!useSyntaxHighlighting) } else if (Ignored.IndexOf(input.KeyChar) < 0) {
Console.Write("\n : "); if (!useSyntaxHighlighting)
stream.Insert(Encoding.Default.GetBytes(Environment.NewLine)); Console.Write(input.KeyChar);
} else if (Ignored.IndexOf(input.KeyChar) < 0) { stream.Insert(Encoding.Default.GetBytes([input.KeyChar], 0, 1));
if (!useSyntaxHighlighting) }
Console.Write(input.KeyChar); } while (input.Key != ConsoleKey.Enter || input.Modifiers == ConsoleModifiers.Shift);
stream.Insert(Encoding.Default.GetBytes([input.KeyChar], 0, 1)); try {
} History.Insert(0, stream.GetBuffer());
} while (input.Key != ConsoleKey.Enter || input.Modifiers == ConsoleModifiers.Shift); Console.WriteLine();
try { runner.Run(stream);
History.Insert(0, stream.GetBuffer()); stream.Dispose();
Console.WriteLine(); } catch (QampException e) {
runner.Run(stream); Console.ForegroundColor = ConsoleColor.Yellow;
stream.Dispose(); Console.WriteLine($" !> {e.Message}");
} if (e.StackTrace != null) {
catch (QampException e) { Console.Write($" ! ");
Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
Console.WriteLine($" !> {e.Message}"); }
if (e.StackTrace != null) { } catch (Exception e) {
Console.Write($" ! "); Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n'))); Console.WriteLine($" !? {e.Message}");
} if (e.StackTrace != null) {
} Console.Write($" ! ");
catch (Exception e) { Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
Console.ForegroundColor = ConsoleColor.DarkRed; }
Console.WriteLine($" !? {e.Message}"); }
if (e.StackTrace != null) { } while (code != ConsoleCode.Exit);
Console.Write($" ! ");
Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
}
}
} while(code != ConsoleCode.Exit);
public class Command public class Command
{ {
public class Parameters public class Parameters
{ {
} }
public class Definition public class Definition
{ {
public string Name { get; init; } public string Name { get; init; }
public string Alias { get; init; } public string Alias { get; init; }
public class Parameter public class Parameter
{ {
public string Name { get; init; } public string Name { get; init; }
public string Alias { get; init; } public string Alias { get; init; }
} }
} }
} }
public class ArgumentAttribute(string name, string alias, string description) public class ArgumentAttribute(string name, string alias, string description)
{ {
public string Name { get; } = name; public string Name { get; } = name;
public string Alias { get; } = alias; public string Alias { get; } = alias;
public string Description { get; } = description; public string Description { get; } = description;
} }

View File

@ -6,38 +6,38 @@ using System.Text;
class C class C
{ {
void X() void X()
{ {
ConsoleCode code = ConsoleCode.Exit; ConsoleCode code = ConsoleCode.Exit;
Runner runner = new Runner(new Options()); Runner runner = new Runner(new Options());
ConsoleWriter cli = new ConsoleWriter(Encoding.Default); ConsoleWriter cli = new ConsoleWriter(Encoding.Default);
do { do {
try { try {
code = cli.Read(out Stream? stream); code = cli.Read(out Stream? stream);
if (code == ConsoleCode.OK) { if (code == ConsoleCode.OK) {
if (stream == null) if (stream == null)
throw new QampException($"Stream from CLI was null {code}"); throw new QampException($"Stream from CLI was null {code}");
Console.WriteLine(); Console.WriteLine();
runner.Run(stream); runner.Run(stream);
}
if (code == ConsoleCode.Error)
throw new QampException($"CLI returned {code}");
} catch (QampException e) {
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($" !> {e.Message}");
if (e.StackTrace != null) {
Console.Write($" ! ");
Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
}
} catch (Exception e) {
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine($" !? {e.Message}");
if (e.StackTrace != null) {
Console.Write($" ! ");
Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
}
} }
} while (code != ConsoleCode.Exit); if (code == ConsoleCode.Error)
} throw new QampException($"CLI returned {code}");
} catch (QampException e) {
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($" !> {e.Message}");
if (e.StackTrace != null) {
Console.Write($" ! ");
Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
}
} catch (Exception e) {
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine($" !? {e.Message}");
if (e.StackTrace != null) {
Console.Write($" ! ");
Console.WriteLine(string.Join("\n ! ", e.StackTrace.Split('\n')));
}
}
} while (code != ConsoleCode.Exit);
}
} }

View File

@ -2,9 +2,9 @@
public class UnitTest1 public class UnitTest1
{ {
[Fact] [Fact]
public void Test1() public void Test1()
{ {
} }
} }

View File

@ -4,26 +4,26 @@ namespace Qrakhen.Qamp.Core.Abstractions;
public interface IDebug<out T> public interface IDebug<out T>
{ {
T Debug(DebugLevel level = DebugLevel.None); T Debug(DebugLevel level = DebugLevel.None);
} }
public interface IDebug<in TContext, out TOut> public interface IDebug<in TContext, out TOut>
{ {
TOut Debug(TContext? context, DebugLevel level = DebugLevel.None); TOut Debug(TContext? context, DebugLevel level = DebugLevel.None);
} }
public enum DebugLevel public enum DebugLevel
{ {
None = 0, None = 0,
Mild = 1, Mild = 1,
Strong = 2, Strong = 2,
Verbose = 3 Verbose = 3
} }
public static class Debugger public static class Debugger
{ {
public static string GetContextString(object? origin, [CallerMemberName] string? caller = null) public static string GetContextString(object? origin, [CallerMemberName] string? caller = null)
{ {
return $"{{Debugger::Builder}}: [{origin?.ToString() ?? "null"}] <{origin?.GetType().Name ?? "null"}> ({caller ?? "null"})"; return $"{{Debugger::Builder}}: [{origin?.ToString() ?? "null"}] <{origin?.GetType().Name ?? "null"}> ({caller ?? "null"})";
} }
} }

View File

@ -2,6 +2,6 @@
public interface ISerialize<TSelf> public interface ISerialize<TSelf>
{ {
byte[] Serialize(); byte[] Serialize();
static abstract TSelf Deserialize(byte[] data); static abstract TSelf Deserialize(byte[] data);
} }

View File

@ -7,49 +7,49 @@ namespace Qrakhen.Qamp.Core;
public static class Assert public static class Assert
{ {
public static void IsPrimitive(params Value[] values) public static void IsPrimitive(params Value[] values)
{ {
AssertAndThrow(() => values.All(value => value.Is(V.Primitive, false)), values); AssertAndThrow(() => values.All(value => value.Is(V.Primitive, false)), values);
} }
public static void NotZero(params Value[] values) public static void NotZero(params Value[] values)
{ {
AssertAndThrow(() => values.All(value => value.Signed != 0), values); AssertAndThrow(() => values.All(value => value.Signed != 0), values);
} }
public static void IsInteger(params Value[] values) public static void IsInteger(params Value[] values)
{ {
AssertAndThrow(() => values.All(value => value.Is(V.Integer, false)), values); AssertAndThrow(() => values.All(value => value.Is(V.Integer, false)), values);
} }
public static void NotVoid(params Value[] values) public static void NotVoid(params Value[] values)
{ {
AssertAndThrow(() => values.All(value => value.Type != V.Void), values); AssertAndThrow(() => values.All(value => value.Type != V.Void), values);
} }
private static void AssertAndThrow(Func<bool> assertion, object? parameter, [CallerMemberName] string? method = "") => private static void AssertAndThrow(Func<bool> assertion, object? parameter, [CallerMemberName] string? method = "") =>
AssertAndThrow(assertion, [parameter], method); AssertAndThrow(assertion, [parameter], method);
private static void AssertAndThrow( private static void AssertAndThrow(
Func<bool> assertion, Func<bool> assertion,
object?[] parameters, object?[] parameters,
[CallerMemberName] string? method = "") [CallerMemberName] string? method = "")
{ {
if (!assertion.Invoke()) if (!assertion.Invoke())
throw new AssertionException(method ?? "", parameters); throw new AssertionException(method ?? "", parameters);
} }
public class AssertionException : Exception public class AssertionException : Exception
{ {
public readonly string Assertion; public readonly string Assertion;
public readonly object?[] Parameters; public readonly object?[] Parameters;
public AssertionException(string assertion, params object?[] parameters) public AssertionException(string assertion, params object?[] parameters)
: base($"Assertion {assertion} failed for parameters:{Environment.NewLine}" + : base($"Assertion {assertion} failed for parameters:{Environment.NewLine}" +
$"{string.Join(Environment.NewLine, parameters.Select(p => $" - <{p?.GetType().Name ?? "null"}>{p?.ToString() ?? "null"}"))}") $"{string.Join(Environment.NewLine, parameters.Select(p => $" - <{p?.GetType().Name ?? "null"}>{p?.ToString() ?? "null"}"))}")
{ {
Assertion = assertion; Assertion = assertion;
Parameters = parameters; Parameters = parameters;
} }
} }
} }

View File

@ -6,88 +6,86 @@ namespace Qrakhen.Qamp.Core;
public static class Benchmark public static class Benchmark
{ {
private static readonly Dictionary<string, Stopwatch> _clocks = new(); private static readonly Dictionary<string, Stopwatch> _clocks = new();
public static bool Active { get; set; } public static bool Active { get; set; }
private static readonly ILogger _logger = LoggerService.Get(nameof(Benchmark)); private static readonly ILogger _logger = LoggerService.Get(nameof(Benchmark));
private static string MakeKey(string? id, string? member, string? file) private static string MakeKey(string? id, string? member, string? file)
{ {
if (member == null) if (member == null)
return "global"; return "global";
return $"{file?.Split('\\').Last() ?? "unknown"}{(id == null ? "" : $"<{id}>")}::{member}"; return $"{file?.Split('\\').Last() ?? "unknown"}{(id == null ? "" : $"<{id}>")}::{member}";
} }
public static void Start( public static void Start(
string? message = null, string? message = null,
string? id = null, string? id = null,
[CallerMemberName] string? member = null, [CallerMemberName] string? member = null,
[CallerFilePath] string? file = null, [CallerFilePath] string? file = null,
[CallerLineNumber] int? line = null) [CallerLineNumber] int? line = null)
{ {
if (!Active) if (!Active)
return; return;
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)) {
{ sw = _clocks[key] = new Stopwatch();
sw = _clocks[key] = new Stopwatch(); Console.WriteLine($" ::: Registered new benchmark clock '{key}'");
Console.WriteLine($" ::: Registered new benchmark clock '{key}'"); }
}
if (!string.IsNullOrEmpty(message)) if (!string.IsNullOrEmpty(message))
Console.WriteLine($" ::: {key}:{line} > {message}"); Console.WriteLine($" ::: {key}:{line} > {message}");
if (sw.IsRunning) if (sw.IsRunning)
Console.WriteLine($" ::: {key}:{line} > already running with an elapsed time of {sw.Elapsed}. clock was reset."); Console.WriteLine($" ::: {key}:{line} > already running with an elapsed time of {sw.Elapsed}. clock was reset.");
sw.Reset(); sw.Reset();
sw.Start(); sw.Start();
} }
public static long Report( public static long Report(
string? message = null, string? message = null,
string? id = null, string? id = null,
bool keep = true, bool keep = true,
[CallerMemberName] string? member = null, [CallerMemberName] string? member = null,
[CallerFilePath] string? file = null, [CallerFilePath] string? file = null,
[CallerLineNumber] int? line = null) [CallerLineNumber] int? line = null)
{ {
if (!Active) if (!Active)
return 0; return 0;
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)) {
{
#if LOG #if LOG
_logger.Debug($"No clock found for '{key}', start one first using Benchmark.Start()"); _logger.Debug($"No clock found for '{key}', start one first using Benchmark.Start()");
#endif #endif
return 0; return 0;
} }
sw.Stop(); sw.Stop();
#if LOG #if LOG
_logger.Debug($" ::: {key}:{line} > {message ?? "Elapsed"}: {sw.Elapsed}"); _logger.Debug($" ::: {key}:{line} > {message ?? "Elapsed"}: {sw.Elapsed}");
#endif #endif
if (keep) if (keep)
sw.Start(); sw.Start();
return sw.ElapsedMilliseconds; return sw.ElapsedMilliseconds;
} }
public static long End( public static long End(
string? message = null, string? message = null,
string? id = null, string? id = null,
[CallerMemberName] string? member = null, [CallerMemberName] string? member = null,
[CallerFilePath] string? file = null, [CallerFilePath] string? file = null,
[CallerLineNumber] int? line = null) [CallerLineNumber] int? line = null)
{ {
if (!Active) if (!Active)
return 0; return 0;
return Report(message, id, false, member, file, line); return Report(message, id, false, member, file, line);
} }
} }

View File

@ -4,5 +4,5 @@ namespace Qrakhen.Qamp.Core.Collections.Abstractions;
public interface IAdd<out TKey, in TValue> public interface IAdd<out TKey, in TValue>
{ {
TKey Add(TValue value); TKey Add(TValue value);
} }

View File

@ -2,7 +2,7 @@
public interface ICaptureBuffer<TData, THandle> public interface ICaptureBuffer<TData, THandle>
{ {
THandle BeginCapture(); THandle BeginCapture();
TData[] EndCapture(THandle handle); TData[] EndCapture(THandle handle);
int Captured(THandle handle); int Captured(THandle handle);
} }

View File

@ -2,5 +2,5 @@
public interface IConsumable<in T> public interface IConsumable<in T>
{ {
void Consume(T expected, string? message = null); void Consume(T expected, string? message = null);
} }

View File

@ -2,5 +2,5 @@
public interface IGet<in TKey, out TValue> public interface IGet<in TKey, out TValue>
{ {
TValue Get(TKey index); TValue Get(TKey index);
} }

View File

@ -2,5 +2,5 @@
public interface IPeekable<out T> public interface IPeekable<out T>
{ {
T Peek(int delta = -1); T Peek(int delta = -1);
} }

View File

@ -2,6 +2,6 @@
public interface IReadable<out T> public interface IReadable<out T>
{ {
T Read(int position); T Read(int position);
T[] Read(int position, int length); T[] Read(int position, int length);
} }

View File

@ -4,7 +4,7 @@ namespace Qrakhen.Qamp.Core.Collections.Abstractions;
public interface IReader<out T> public interface IReader<out T>
{ {
TokenPosition CurrentPosition { get; } TokenPosition CurrentPosition { get; }
bool Done { get; } bool Done { get; }
T NextToken(bool includeCompilationIrrelevant = false); T NextToken(bool includeCompilationIrrelevant = false);
} }

View File

@ -2,6 +2,6 @@
public interface ISeekable<TKey, out TValue> public interface ISeekable<TKey, out TValue>
{ {
TKey Position { get; } TKey Position { get; }
TValue Seek(TKey position); TValue Seek(TKey position);
} }

View File

@ -2,7 +2,7 @@
public interface ISet<in TKey, in TValue> public interface ISet<in TKey, in TValue>
{ {
TValue this[TKey index] { set; } TValue this[TKey index] { set; }
void Set(TKey index, TValue value); void Set(TKey index, TValue value);
} }

View File

@ -4,10 +4,10 @@ public interface IStack<out TKey, TValue> : IPush<TKey, TValue>, IPop<TValue>;
public interface IPush<out TKey, in T> public interface IPush<out TKey, in T>
{ {
TKey Push(T value); TKey Push(T value);
} }
public interface IPop<out T> public interface IPop<out T>
{ {
T Pop(); T Pop();
} }

View File

@ -2,6 +2,6 @@
public interface ISteppable<out T> public interface ISteppable<out T>
{ {
bool Done { get; } bool Done { get; }
T Next(); T Next();
} }

View File

@ -2,5 +2,5 @@
public interface IToArray<out T> public interface IToArray<out T>
{ {
T[] ToArray(); T[] ToArray();
} }

View File

@ -16,58 +16,58 @@ public abstract class Expander<T> :
IGetSet<long, T>, IGetSet<long, T>,
IAdd<long, T> IAdd<long, T>
{ {
protected T[] Data; protected T[] Data;
public long Count { get; protected set; } = 0; public long Count { get; protected set; } = 0;
public long Capacity => Data.Length; public long Capacity => Data.Length;
protected Expander(int capacity = 0x10) protected Expander(int capacity = 0x10)
{ {
Data = new T[capacity]; Data = new T[capacity];
} }
protected Expander(IEnumerable<T> data) protected Expander(IEnumerable<T> data)
{ {
Data = data.ToArray(); Data = data.ToArray();
Count = Data.Length; Count = Data.Length;
} }
public T this[long index] { public T this[long index] {
get => Get(index); get => Get(index);
set => Set(index, value); set => Set(index, value);
} }
public T Get(long index) => Data[index]; public T Get(long index) => Data[index];
public void Set(long index, T value) public void Set(long index, T value)
{ {
Prepare(index); Prepare(index);
Data[index] = value; Data[index] = value;
} }
public long Add(T value) public long Add(T value)
{ {
Prepare(Count); Prepare(Count);
Data[Count] = value; Data[Count] = value;
return Count++; return Count++;
} }
protected void Prepare(long position) protected void Prepare(long position)
{ {
while (position >= Data.Length) { while (position >= Data.Length) {
T[] grown = new T[Data.Length * 2]; T[] grown = new T[Data.Length * 2];
Array.Copy(Data, grown, Count); Array.Copy(Data, grown, Count);
Data = grown; Data = grown;
} }
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
foreach (T? item in Data) { foreach (T? item in Data) {
yield return item; yield return item;
} }
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator(); => GetEnumerator();
} }

View File

@ -9,33 +9,33 @@ namespace Qrakhen.Qamp.Core.Collections;
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class FixedArray<T> : IEnumerable<T>, IGetSet<long, T> public class FixedArray<T> : IEnumerable<T>, IGetSet<long, T>
{ {
protected T[] Data; protected T[] Data;
public long Length => Data.LongLength; public long Length => Data.LongLength;
public T this[long index] { public T this[long index] {
get => Get(index); get => Get(index);
set => Set(index, value); set => Set(index, value);
} }
public FixedArray(IEnumerable<T> data) public FixedArray(IEnumerable<T> data)
{ {
Data = data.ToArray(); Data = data.ToArray();
} }
public T Get(long index) => Data[index]; public T Get(long index) => Data[index];
public void Set(long index, T value) public void Set(long index, T value)
{ {
Data[index] = value; Data[index] = value;
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
foreach (var item in Data) foreach (var item in Data)
yield return item; yield return item;
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator(); => GetEnumerator();
} }

View File

@ -7,52 +7,52 @@ namespace Qrakhen.Qamp.Core.Collections;
/// </summary> /// </summary>
public class Pointer<T> public class Pointer<T>
{ {
protected IGetSet<long, T> Target; protected IGetSet<long, T> Target;
public long Cursor; public long Cursor;
public T Current { public T Current {
get => Get(); get => Get();
set => Set(value); set => Set(value);
} }
public Pointer(IGetSet<long, T> target, long pointer = 0) public Pointer(IGetSet<long, T> target, long pointer = 0)
{ {
Target = target; Target = target;
Cursor = pointer; Cursor = pointer;
} }
public Pointer<T> Branch(long delta = 0) public Pointer<T> Branch(long delta = 0)
{ {
return new Pointer<T>(Target, Cursor + delta); return new Pointer<T>(Target, Cursor + delta);
} }
/// <summary> /// <summary>
/// Returns the next item from <see cref="Target"/> and increases the pointer by 1. /// Returns the next item from <see cref="Target"/> and increases the pointer by 1.
/// </summary> /// </summary>
public T Next() => Target.Get(Cursor++); public T Next() => Target.Get(Cursor++);
/// <summary> /// <summary>
/// Sets the value of <see cref="Target"/> RELATIVE from the current position (<paramref name="delta"/>). /// Sets the value of <see cref="Target"/> RELATIVE from the current position (<paramref name="delta"/>).
/// </summary> /// </summary>
public void Set(long delta, T value) => Target.Set(Cursor + delta, value); public void Set(long delta, T value) => Target.Set(Cursor + delta, value);
/// <summary> /// <summary>
/// Sets the value of <see cref="Target"/> at the current <see cref="Cursor"/> location. /// Sets the value of <see cref="Target"/> at the current <see cref="Cursor"/> location.
/// </summary> /// </summary>
public void Set(T value) => Set(Cursor, value); public void Set(T value) => Set(Cursor, value);
/// <summary> /// <summary>
/// Gets the value of <see cref="Target"/> RELATIVE from the current position (<paramref name="delta"/>). /// Gets the value of <see cref="Target"/> RELATIVE from the current position (<paramref name="delta"/>).
/// </summary> /// </summary>
public T Get(long delta) => Target.Get(Cursor + delta); public T Get(long delta) => Target.Get(Cursor + delta);
/// <summary> /// <summary>
/// Gets the value of <see cref="Target"/> at the current <see cref="Cursor"/> location. /// Gets the value of <see cref="Target"/> at the current <see cref="Cursor"/> location.
/// </summary> /// </summary>
public T Get() => Get(Cursor); public T Get() => Get(Cursor);
public override string ToString() public override string ToString()
{ {
return $"&{Cursor:X4}={Current}"; return $"&{Cursor:X4}={Current}";
} }
} }

View File

@ -11,33 +11,33 @@ public class PopStack<T> :
IPop<T>, IPop<T>,
IPeekable<T> IPeekable<T>
{ {
private readonly T[] _data; private readonly T[] _data;
private int _position; private int _position;
public int Position => _position; public int Position => _position;
public int Length => _data.Length; public int Length => _data.Length;
public bool Done => _position >= _data.Length; public bool Done => _position >= _data.Length;
public PopStack(IEnumerable<T> data) public PopStack(IEnumerable<T> data)
{ {
_data = data.ToArray(); _data = data.ToArray();
_position = 0; _position = 0;
} }
public T Pop() => _data[_position++]; public T Pop() => _data[_position++];
public T Peek(int delta = 0) => _data[_position + delta]; public T Peek(int delta = 0) => _data[_position + delta];
public bool CanPeek(int delta = 0) public bool CanPeek(int delta = 0)
=> _position + delta > -1 && _position + delta < _data.Length; => _position + delta > -1 && _position + delta < _data.Length;
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
foreach (var item in _data) foreach (var item in _data)
yield return item; yield return item;
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator(); => GetEnumerator();
} }

View File

@ -11,50 +11,50 @@ public class PushStack<T> :
IPush<long, T>, IPush<long, T>,
IToArray<T> IToArray<T>
{ {
public PushStack(int initialCapacity = 0x10) public PushStack(int initialCapacity = 0x10)
{ {
Data = new T[initialCapacity]; Data = new T[initialCapacity];
} }
public T? Find(Func<T, bool> callback) public T? Find(Func<T, bool> callback)
{ {
for (int i = 0; i < Count; i++) for (int i = 0; i < Count; i++)
if (callback(Data[i])) if (callback(Data[i]))
return Data[i]; return Data[i];
return default; return default;
} }
public void Push(T[] array) public void Push(T[] array)
{ {
foreach (T value in array) { foreach (T value in array) {
Add(value); Add(value);
} }
} }
public void Push(IEnumerable<T> array) public void Push(IEnumerable<T> array)
{ {
foreach (T value in array) { foreach (T value in array) {
Add(value); Add(value);
} }
} }
public long Push(T value) => Add(value); public long Push(T value) => Add(value);
public bool TryGet(long index, out T value) public bool TryGet(long index, out T value)
{ {
value = default; value = default;
if (index < 0 || index >= Count) if (index < 0 || index >= Count)
return false; return false;
value = Data[index]; value = Data[index];
return true; return true;
} }
public T[] ToArray() => Data.Subset(0, Count); public T[] ToArray() => Data.Subset(0, Count);
public T[] Subset(long from, int length) public T[] Subset(long from, int length)
{ {
T[] result = new T[length]; T[] result = new T[length];
Array.Copy(Data, from, result, 0, length); Array.Copy(Data, from, result, 0, length);
return result; return result;
} }
} }

View File

@ -13,36 +13,36 @@ public class Register<TKey, TValue> :
IEnumerable<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>
where TKey : notnull where TKey : notnull
{ {
private readonly Dictionary<TKey, TValue> _data = new(); private readonly Dictionary<TKey, TValue> _data = new();
public int Length => _data.Count; public int Length => _data.Count;
public TValue this[TKey key] { public TValue this[TKey key] {
get => Get(key); get => Get(key);
set => Set(key, value); set => Set(key, value);
} }
public TKey Add(TKey key, TValue value) public TKey Add(TKey key, TValue value)
{ {
_data.Add(key, value); _data.Add(key, value);
return key; return key;
} }
public bool TryGet(TKey key, [MaybeNullWhen(false)] out TValue value) => _data.TryGetValue(key, out value); public bool TryGet(TKey key, [MaybeNullWhen(false)] out TValue value) => _data.TryGetValue(key, out value);
public TValue Get(TKey key) => _data[key]; public TValue Get(TKey key) => _data[key];
public void Set(TKey key, TValue value) => _data[key] = value; public void Set(TKey key, TValue value) => _data[key] = value;
public bool Has(TKey key) => _data.ContainsKey(key); public bool Has(TKey key) => _data.ContainsKey(key);
public TValue[] ToArray() => _data.Values.ToArray(); public TValue[] ToArray() => _data.Values.ToArray();
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{ {
foreach (var item in _data) foreach (var item in _data)
yield return item; yield return item;
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
} }

View File

@ -13,47 +13,47 @@ public class StackLike<T> : Expander<T>,
IPeekable<T>, IPeekable<T>,
IToArray<T> IToArray<T>
{ {
public long Position => Count; public long Position => Count;
public T this[int position] { public T this[int position] {
get => Data[position]; get => Data[position];
set => Data[position] = value; set => Data[position] = value;
} }
public StackLike(int capacity = 0x10) public StackLike(int capacity = 0x10)
{ {
Data = new T[capacity]; Data = new T[capacity];
Count = 0; Count = 0;
} }
public StackLike(IEnumerable<T> data) public StackLike(IEnumerable<T> data)
{ {
Data = data.ToArray(); Data = data.ToArray();
Count = 0; Count = 0;
} }
public long Push(T value) => Add(value); public long Push(T value) => Add(value);
public T Pop() => Data[--Count]; public T Pop() => Data[--Count];
public T Seek(long position) public T Seek(long position)
{ {
Count = position; Count = position;
return this[Count]; return this[Count];
} }
public T Peek(int delta = -1) => Data[Count + delta]; public T Peek(int delta = -1) => Data[Count + delta];
public bool CanPeek(int delta = 0) public bool CanPeek(int delta = 0)
=> Count + delta > -1 && Count + delta < Count; => Count + delta > -1 && Count + delta < Count;
/// <summary> /// <summary>
/// Decimates this stack down to the given position, overwriting everything beyond that when pushing in the future. /// Decimates this stack down to the given position, overwriting everything beyond that when pushing in the future.
/// </summary> /// </summary>
public void Decimate(long position) public void Decimate(long position)
{ {
Count = position; Count = position;
} }
public T[] ToArray() => Data.ToArray(); public T[] ToArray() => Data.ToArray();
} }

View File

@ -4,17 +4,17 @@ namespace Qrakhen.Qamp.Core.Compilation.Builders;
public class Builder public class Builder
{ {
public Builder? Outer { get; init; } public Builder? Outer { get; init; }
public FunctionBuilder Function = new(); public FunctionBuilder Function = new();
public readonly FunctionType Type; public readonly FunctionType Type;
public StackLike<Local> Locals = []; public StackLike<Local> Locals = [];
public StackLike<Outer> Outers = []; public StackLike<Outer> Outers = [];
public int ScopeDepth = 0; public int ScopeDepth = 0;
public Builder(FunctionType type = FunctionType.Code, Builder? outer = null) public Builder(FunctionType type = FunctionType.Code, Builder? outer = null)
{ {
Type = type; Type = type;
Outer = outer; Outer = outer;
} }
} }

View File

@ -2,8 +2,8 @@
public class ClassBuilder public class ClassBuilder
{ {
public string Name; public string Name;
public ClassBuilder? Outer; public ClassBuilder? Outer;
public bool IsDerived; public bool IsDerived;
} }

View File

@ -4,15 +4,15 @@ namespace Qrakhen.Qamp.Core.Compilation.Builders;
public class FunctionBuilder public class FunctionBuilder
{ {
public SegmentBuilder Segment = new(); public SegmentBuilder Segment = new();
public string Name; public string Name;
public int OuterCount; public int OuterCount;
public int ArgumentCount; public int ArgumentCount;
public long CurrentInstruction => Segment.Instructions.Count; public long CurrentInstruction => Segment.Instructions.Count;
public Function Build() public Function Build()
{ {
return new Function(Name, Segment.Build(), OuterCount, ArgumentCount); return new Function(Name, Segment.Build(), OuterCount, ArgumentCount);
} }
} }

View File

@ -2,5 +2,5 @@
public interface IBuilder<TOut> public interface IBuilder<TOut>
{ {
public TOut Build(); public TOut Build();
} }

View File

@ -7,34 +7,33 @@ namespace Qrakhen.Qamp.Core.Compilation.Builders;
public class SegmentBuilder() : IBuilder<Segment>, IDebug<string> public class SegmentBuilder() : IBuilder<Segment>, IDebug<string>
{ {
public readonly PushStack<Instruction> Instructions = new(); public readonly PushStack<Instruction> Instructions = new();
public readonly PushStack<Value> Constants = new(); public readonly PushStack<Value> Constants = new();
public Segment Build() public Segment Build()
{ {
return new Segment(Instructions.ToArray(), Constants.ToArray()); return new Segment(Instructions.ToArray(), Constants.ToArray());
} }
public string Debug(DebugLevel level = DebugLevel.None) public string Debug(DebugLevel level = DebugLevel.None)
{ {
string str = Debugger.GetContextString(this); string str = Debugger.GetContextString(this);
for (int i = 0; i < Instructions.Count; i++) for (int i = 0; i < Instructions.Count; i++) {
{ var instruction = Instructions[i];
var instruction = Instructions[i]; /*if ((instruction.Code & 0x80) == 1) {
/*if ((instruction.Code & 0x80) == 1) { // dynamic
// dynamic int length = instruction.Code & (0x80 - 1);
int length = instruction.Code & (0x80 - 1); byte[] index = Instructions.Subset(i + 1, length).Select(n => n.Code).ToArray();
byte[] index = Instructions.Subset(i + 1, length).Select(n => n.Code).ToArray(); str += $"\n [{i:x4}]: Dynamic ({string.Join(' ', index)})";
str += $"\n [{i:x4}]: Dynamic ({string.Join(' ', index)})"; i += length;
i += length; } else*/
} else*/ str += $"\n [{i:x4}]: {instruction}".PadRight(32);
str += $"\n [{i:x4}]: {instruction}".PadRight(32); /*if (instruction.OpCode == OpCode.Constant) {
/*if (instruction.OpCode == OpCode.Constant) { i++; // i know, i know
i++; // i know, i know str += $" : {Constants[Instructions.Subset(i, 8).Select(n => n.Code).ToArray().ToInt64()]}";
str += $" : {Constants[Instructions.Subset(i, 8).Select(n => n.Code).ToArray().ToInt64()]}"; i += 8;
i += 8; }*/
}*/ }
} return str;
return str; }
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -8,5 +8,5 @@ namespace Qrakhen.Qamp.Core.Compilation.Digesters;
internal interface IDigester internal interface IDigester
{ {
void Digest(CompilerState state); void Digest(CompilerState state);
} }

View File

@ -7,304 +7,345 @@ namespace Qrakhen.Qamp.Core.Compilation;
public static class ExpressionParser public static class ExpressionParser
{ {
private static readonly Dictionary<TokenType, Rule> _rules = new(); private static readonly Dictionary<TokenType, Rule> _rules = new();
public static Rule Get(TokenType type) => _rules[type]; public static Rule Get(TokenType type) => _rules[type];
public static void And(Digester digester, bool canAssign) public static void And(Digester digester, bool canAssign)
{ {
long endJump = digester.EmitJump(OpCode.JumpIfFalse); long endJump = digester.EmitJump(OpCode.JumpIfFalse);
digester.Emit(OpCode.Pop); digester.Emit(OpCode.Pop);
digester.WeightedDigest(Weight.And); digester.WeightedDigest(Weight.And);
digester.PatchJump(endJump); digester.PatchJump(endJump);
} }
static void Binary(Digester digester, bool canAssign) static void Binary(Digester digester, bool canAssign)
{ {
TokenType operatorType = digester.Previous.Type; TokenType operatorType = digester.Previous.Type;
Rule rule = Get(operatorType); Rule rule = Get(operatorType);
digester.WeightedDigest(rule.Weight + 1); digester.WeightedDigest(rule.Weight + 1);
switch (operatorType) { switch (operatorType) {
case TokenType.BangEqual: digester.Emit(OpCode.Equal, OpCode.Not); break; case TokenType.BangEqual:
case TokenType.EqualEqual: digester.Emit(OpCode.Equal); break; digester.Emit(OpCode.Equal, OpCode.Not);
case TokenType.Greater: digester.Emit(OpCode.Greater); break; break;
case TokenType.GreaterEqual: digester.Emit(OpCode.Less, OpCode.Not); break; case TokenType.EqualEqual:
case TokenType.Less: digester.Emit(OpCode.Less); break; digester.Emit(OpCode.Equal);
case TokenType.LessEqual: digester.Emit(OpCode.Greater, OpCode.Not); break; break;
case TokenType.Plus: digester.Emit(OpCode.Add); break; case TokenType.Greater:
case TokenType.Minus: digester.Emit(OpCode.Subtract); break; digester.Emit(OpCode.Greater);
case TokenType.Star: digester.Emit(OpCode.Multiply); break; break;
case TokenType.Slash: digester.Emit(OpCode.Divide); break; case TokenType.GreaterEqual:
case TokenType.BitwiseAnd: digester.Emit(OpCode.BitwiseAnd); break; digester.Emit(OpCode.Less, OpCode.Not);
case TokenType.BitwiseOr: digester.Emit(OpCode.BitwiseOr); break; break;
case TokenType.BitwiseXor: digester.Emit(OpCode.BitwiseXor); break; case TokenType.Less:
case TokenType.BitwiseNot: digester.Emit(OpCode.BitwiseNot); break; digester.Emit(OpCode.Less);
case TokenType.BitwiseLeft: digester.Emit(OpCode.BitwiseLeft); break; break;
case TokenType.BitwiseRight: digester.Emit(OpCode.BitwiseRight); break; case TokenType.LessEqual:
case TokenType.Increment: digester.Emit(OpCode.Increment); break; digester.Emit(OpCode.Greater, OpCode.Not);
case TokenType.Decrement: digester.Emit(OpCode.Decrement); break; break;
default: return; case TokenType.Plus:
} digester.Emit(OpCode.Add);
} break;
case TokenType.Minus:
digester.Emit(OpCode.Subtract);
break;
case TokenType.Star:
digester.Emit(OpCode.Multiply);
break;
case TokenType.Slash:
digester.Emit(OpCode.Divide);
break;
case TokenType.BitwiseAnd:
digester.Emit(OpCode.BitwiseAnd);
break;
case TokenType.BitwiseOr:
digester.Emit(OpCode.BitwiseOr);
break;
case TokenType.BitwiseXor:
digester.Emit(OpCode.BitwiseXor);
break;
case TokenType.BitwiseNot:
digester.Emit(OpCode.BitwiseNot);
break;
case TokenType.BitwiseLeft:
digester.Emit(OpCode.BitwiseLeft);
break;
case TokenType.BitwiseRight:
digester.Emit(OpCode.BitwiseRight);
break;
case TokenType.Increment:
digester.Emit(OpCode.Increment);
break;
case TokenType.Decrement:
digester.Emit(OpCode.Decrement);
break;
default:
return;
}
}
static void Call(Digester digester, bool canAssign) static void Call(Digester digester, bool canAssign)
{ {
byte count = digester.Arguments(); byte count = digester.Arguments();
digester.Emit(OpCode.Invoke, count); digester.Emit(OpCode.Invoke, count);
} }
static void Dot(Digester digester, bool canAssign) static void Dot(Digester digester, bool canAssign)
{ {
digester.Consume(TokenType.Identifier, "Expected property name after '.'."); digester.Consume(TokenType.Identifier, "Expected property name after '.'.");
long name = digester.IdentifierConstant(digester.Previous.Value); long name = digester.IdentifierConstant(digester.Previous.Value);
if (canAssign && digester.Match(TokenType.Equal)) { if (canAssign && digester.Match(TokenType.Equal)) {
digester.ParseExpression(); digester.ParseExpression();
digester.EmitDynamic(OpCode.SetMember, name); digester.EmitDynamic(OpCode.SetMember, name);
} else if (digester.Match(TokenType.GroupOpen)) { } else if (digester.Match(TokenType.GroupOpen)) {
byte argCount = digester.Arguments(); byte argCount = digester.Arguments();
digester.EmitDynamic(OpCode.InvokeMember, name); digester.EmitDynamic(OpCode.InvokeMember, name);
digester.Emit(argCount); digester.Emit(argCount);
} else { } else {
digester.EmitDynamic(OpCode.GetMember, name); digester.EmitDynamic(OpCode.GetMember, name);
} }
} }
static void Literal(Digester digester, bool canAssign) static void Literal(Digester digester, bool canAssign)
{ {
switch (digester.Previous.Type) { switch (digester.Previous.Type) {
case TokenType.False: case TokenType.False:
digester.Emit(OpCode.False); digester.Emit(OpCode.False);
break; break;
case TokenType.Null: case TokenType.Null:
digester.Emit(OpCode.Null); digester.Emit(OpCode.Null);
break; break;
case TokenType.True: case TokenType.True:
digester.Emit(OpCode.True); digester.Emit(OpCode.True);
break; break;
default: default:
break; break;
} }
} }
static void Group(Digester digester, bool canAssign) static void Group(Digester digester, bool canAssign)
{ {
digester.ParseExpression(); digester.ParseExpression();
digester.Consume(TokenType.GroupClose, "Expected ')' after expression."); digester.Consume(TokenType.GroupClose, "Expected ')' after expression.");
} }
static void Array(Digester digester, bool canAssign) static void Array(Digester digester, bool canAssign)
{ {
int length = 0; int length = 0;
while (!digester.Check(TokenType.ArrayClose)) { while (!digester.Check(TokenType.ArrayClose)) {
digester.ParseExpression(); digester.ParseExpression();
length++; length++;
if (digester.Check(TokenType.Comma)) if (digester.Check(TokenType.Comma))
digester.Next(); digester.Next();
else else
break; break;
} }
digester.Consume(TokenType.ArrayClose, "Expected ']' after array declaration"); digester.Consume(TokenType.ArrayClose, "Expected ']' after array declaration");
digester.EmitDynamic(OpCode.Array, length); // digester.MakeConstant(new Value((long)length))); digester.EmitDynamic(OpCode.Array, length); // digester.MakeConstant(new Value((long)length)));
} }
static void AddItem(Digester digester, bool canAssign) static void AddItem(Digester digester, bool canAssign)
{ {
digester.ParseExpression(); digester.ParseExpression();
digester.Emit(OpCode.AddItem); digester.Emit(OpCode.AddItem);
} }
static void Accessor(Digester digester, bool canAssign) static void Accessor(Digester digester, bool canAssign)
{ {
digester.ParseExpression(); digester.ParseExpression();
digester.Consume(TokenType.ArrayClose, "expected ] after array accessor"); digester.Consume(TokenType.ArrayClose, "expected ] after array accessor");
if (canAssign && digester.Match(TokenType.AddItem)) { if (canAssign && digester.Match(TokenType.AddItem)) {
digester.ParseExpression(); digester.ParseExpression();
digester.Emit(OpCode.AddItem); digester.Emit(OpCode.AddItem);
} else if (canAssign && digester.Match(TokenType.Equal)) { } else if (canAssign && digester.Match(TokenType.Equal)) {
digester.ParseExpression(); digester.ParseExpression();
digester.Emit(OpCode.SetItem); digester.Emit(OpCode.SetItem);
} else { } else {
digester.Emit(OpCode.GetItem); digester.Emit(OpCode.GetItem);
} }
} }
static void Number(Digester digester, bool canAssign) static void Number(Digester digester, bool canAssign)
{ {
Token token = digester.Previous; Token token = digester.Previous;
string number = token.Value ?? ""; string number = token.Value ?? "";
TokenType type = token.Type; TokenType type = token.Type;
Value value = default; Value value = default;
if (type == TokenType.Hexadecimal) if (type == TokenType.Hexadecimal)
value = new Value(long.Parse(number, NumberStyles.HexNumber)); value = new Value(long.Parse(number, NumberStyles.HexNumber));
else if (type == TokenType.Decimal) else if (type == TokenType.Decimal)
value = new Value(double.Parse(number, NumberStyles.Float, CultureInfo.InvariantCulture)); value = new Value(double.Parse(number, NumberStyles.Float, CultureInfo.InvariantCulture));
else if (type == TokenType.Integer) else if (type == TokenType.Integer)
value = new Value(long.Parse(number, NumberStyles.Integer)); value = new Value(long.Parse(number, NumberStyles.Integer));
else else
digester.ErrorAt(token, $"Could not parse number {number}"); digester.ErrorAt(token, $"Could not parse number {number}");
digester.EmitConstant(value); digester.EmitConstant(value);
} }
static void Or(Digester digester, bool canAssign) static void Or(Digester digester, bool canAssign)
{ {
long elseJump = digester.EmitJump(OpCode.JumpIfFalse); long elseJump = digester.EmitJump(OpCode.JumpIfFalse);
long endJump = digester.EmitJump(OpCode.Jump); long endJump = digester.EmitJump(OpCode.Jump);
digester.PatchJump(elseJump); digester.PatchJump(elseJump);
digester.Emit(OpCode.Pop); digester.Emit(OpCode.Pop);
digester.WeightedDigest(Weight.Or); digester.WeightedDigest(Weight.Or);
digester.PatchJump(endJump); digester.PatchJump(endJump);
} }
static void String(Digester digester, bool canAssign) static void String(Digester digester, bool canAssign)
{ {
digester.EmitConstant(Values.Objects.String.Make(digester.Previous.Value)); digester.EmitConstant(Values.Objects.String.Make(digester.Previous.Value));
} }
public static void Variable(Digester digester, string name, bool canAssign) public static void Variable(Digester digester, string name, bool canAssign)
{ {
OpCode Get, Set; OpCode Get, Set;
long variable = digester.ResolveLocal(digester.Builder, name); long variable = digester.ResolveLocal(digester.Builder, name);
if (variable > -1) { if (variable > -1) {
Get = OpCode.GetLocal; Get = OpCode.GetLocal;
Set = OpCode.SetLocal; Set = OpCode.SetLocal;
} else if ((variable = digester.ResolveOuter(digester.Builder, name)) > -1) { } else if ((variable = digester.ResolveOuter(digester.Builder, name)) > -1) {
Get = OpCode.GetOuter; Get = OpCode.GetOuter;
Set = OpCode.SetOuter; Set = OpCode.SetOuter;
} else { } else {
variable = digester.IdentifierConstant(name); variable = digester.IdentifierConstant(name);
Get = OpCode.GetGlobal; Get = OpCode.GetGlobal;
Set = OpCode.SetGlobal; Set = OpCode.SetGlobal;
} }
if (canAssign && digester.Match(TokenType.Equal)) { if (canAssign && digester.Match(TokenType.Equal)) {
digester.ParseExpression(); digester.ParseExpression();
digester.Emit(Set); digester.Emit(Set);
digester.EmitDynamic(variable); digester.EmitDynamic(variable);
} else { } else {
digester.Emit(Get); digester.Emit(Get);
digester.EmitDynamic(variable); digester.EmitDynamic(variable);
} }
} }
public static void Variable(Digester digester, bool canAssign) public static void Variable(Digester digester, bool canAssign)
{ {
Variable(digester, digester.Previous.Value!, canAssign); Variable(digester, digester.Previous.Value!, canAssign);
} }
static void Base(Digester digester, bool canAssign) static void Base(Digester digester, bool canAssign)
{ {
if (digester.ClassBuilder == null) if (digester.ClassBuilder == null)
digester.ErrorAtPrevious("We're currently not in a class. 'base' only works inside of a class context."); digester.ErrorAtPrevious("We're currently not in a class. 'base' only works inside of a class context.");
else if (digester.ClassBuilder.Outer == null) else if (digester.ClassBuilder.Outer == null)
digester.ErrorAtPrevious($"{digester.ClassBuilder.Name} is not inherited from any class, so the 'base' keyword points to nothing."); digester.ErrorAtPrevious($"{digester.ClassBuilder.Name} is not inherited from any class, so the 'base' keyword points to nothing.");
else else {
{ digester.Consume(TokenType.Dot, "Expected '.' after 'base' keyword.");
digester.Consume(TokenType.Dot, "Expected '.' after 'base' keyword."); digester.Consume(TokenType.Identifier, "Expected an identifier after 'base' keyword.");
digester.Consume(TokenType.Identifier, "Expected an identifier after 'base' keyword."); long name = digester.IdentifierConstant(digester.Previous.Value);
long name = digester.IdentifierConstant(digester.Previous.Value); Variable(digester, "this", false);
Variable(digester, "this", false); if (digester.Match(TokenType.GroupOpen)) {
if (digester.Match(TokenType.GroupOpen)) byte arguments = digester.Arguments();
{ Variable(digester, "base", false);
byte arguments = digester.Arguments(); digester.Emit(OpCode.InvokeBase);
Variable(digester, "base", false); digester.Emit(arguments);
digester.Emit(OpCode.InvokeBase); } else {
digester.Emit(arguments); Variable(digester, "base", false);
} else { digester.Emit(OpCode.Base);
Variable(digester, "base", false); }
digester.Emit(OpCode.Base); }
} }
}
}
static void This(Digester digester, bool canAssign) static void This(Digester digester, bool canAssign)
{ {
if (digester.ClassBuilder == null) if (digester.ClassBuilder == null)
digester.ErrorAtPrevious("We're currently not in a class. 'this' only refers to the current instance of one."); digester.ErrorAtPrevious("We're currently not in a class. 'this' only refers to the current instance of one.");
else else
Variable(digester, false); Variable(digester, false);
} }
static void Modifier(Digester digester, bool canAssign) static void Modifier(Digester digester, bool canAssign)
{ {
Token token = digester.Previous; Token token = digester.Previous;
digester.WeightedDigest(Weight.Modifier); digester.WeightedDigest(Weight.Modifier);
switch (token.Type) { switch (token.Type) {
case TokenType.Bang: digester.Emit(OpCode.Not); break; case TokenType.Bang:
case TokenType.Minus: digester.Emit(OpCode.Negate); break; digester.Emit(OpCode.Not);
case TokenType.BitwiseNot: digester.Emit(OpCode.BitwiseNot); break; break;
} case TokenType.Minus:
} digester.Emit(OpCode.Negate);
break;
case TokenType.BitwiseNot:
digester.Emit(OpCode.BitwiseNot);
break;
}
}
static void TypeOf(Digester digester, bool canAssign) static void TypeOf(Digester digester, bool canAssign)
{ {
digester.TypeOf(); digester.TypeOf();
} }
static ExpressionParser() static ExpressionParser()
{ {
_rules[TokenType.GroupOpen] = new Rule(Group, Call, Weight.Call); _rules[TokenType.GroupOpen] = new Rule(Group, Call, Weight.Call);
_rules[TokenType.GroupClose] = new Rule(null, null, Weight.None); _rules[TokenType.GroupClose] = new Rule(null, null, Weight.None);
_rules[TokenType.ContextOpen] = new Rule(null, null, Weight.None); _rules[TokenType.ContextOpen] = new Rule(null, null, Weight.None);
_rules[TokenType.ContextClose] = new Rule(null, null, Weight.None); _rules[TokenType.ContextClose] = new Rule(null, null, Weight.None);
_rules[TokenType.ArrayOpen] = new Rule(Array, Accessor, Weight.Call); _rules[TokenType.ArrayOpen] = new Rule(Array, Accessor, Weight.Call);
_rules[TokenType.ArrayClose] = new Rule(null, null, Weight.None); _rules[TokenType.ArrayClose] = new Rule(null, null, Weight.None);
_rules[TokenType.AddItem] = new Rule(null, AddItem, Weight.None); _rules[TokenType.AddItem] = new Rule(null, AddItem, Weight.None);
_rules[TokenType.RemoveItem] = new Rule(null, null, Weight.None); _rules[TokenType.RemoveItem] = new Rule(null, null, Weight.None);
_rules[TokenType.Colon] = new Rule(null, Dot, Weight.Call); _rules[TokenType.Colon] = new Rule(null, Dot, Weight.Call);
_rules[TokenType.Comma] = new Rule(null, null, Weight.None); _rules[TokenType.Comma] = new Rule(null, null, Weight.None);
_rules[TokenType.Dot] = new Rule(null, Dot, Weight.Call); _rules[TokenType.Dot] = new Rule(null, Dot, Weight.Call);
_rules[TokenType.Minus] = new Rule(Modifier, Binary, Weight.Term); _rules[TokenType.Minus] = new Rule(Modifier, Binary, Weight.Term);
_rules[TokenType.Plus] = new Rule(null, Binary, Weight.Term); _rules[TokenType.Plus] = new Rule(null, Binary, Weight.Term);
_rules[TokenType.Increment] = new Rule(Binary, Binary, Weight.Call); _rules[TokenType.Increment] = new Rule(Binary, Binary, Weight.Call);
_rules[TokenType.Decrement] = new Rule(Binary, Binary, Weight.Call); _rules[TokenType.Decrement] = new Rule(Binary, Binary, Weight.Call);
_rules[TokenType.BitwiseAnd] = new Rule(Modifier, Binary, Weight.Term); _rules[TokenType.BitwiseAnd] = new Rule(Modifier, Binary, Weight.Term);
_rules[TokenType.BitwiseOr] = new Rule(null, Binary, Weight.Term); _rules[TokenType.BitwiseOr] = new Rule(null, Binary, Weight.Term);
_rules[TokenType.BitwiseXor] = new Rule(null, Binary, Weight.Term); _rules[TokenType.BitwiseXor] = new Rule(null, Binary, Weight.Term);
_rules[TokenType.BitwiseLeft] = new Rule(null, Binary, Weight.Term); _rules[TokenType.BitwiseLeft] = new Rule(null, Binary, Weight.Term);
_rules[TokenType.BitwiseRight] = new Rule(null, Binary, Weight.Term); _rules[TokenType.BitwiseRight] = new Rule(null, Binary, Weight.Term);
_rules[TokenType.BitwiseNot] = new Rule(Modifier, null, Weight.Term); _rules[TokenType.BitwiseNot] = new Rule(Modifier, null, Weight.Term);
_rules[TokenType.Semicolon] = new Rule(null, null, Weight.None); _rules[TokenType.Semicolon] = new Rule(null, null, Weight.None);
_rules[TokenType.Slash] = new Rule(null, Binary, Weight.Factor); _rules[TokenType.Slash] = new Rule(null, Binary, Weight.Factor);
_rules[TokenType.Star] = new Rule(null, Binary, Weight.Factor); _rules[TokenType.Star] = new Rule(null, Binary, Weight.Factor);
_rules[TokenType.Bang] = new Rule(Modifier, null, Weight.None); _rules[TokenType.Bang] = new Rule(Modifier, null, Weight.None);
_rules[TokenType.BangEqual] = new Rule(null, Binary, Weight.Equal); _rules[TokenType.BangEqual] = new Rule(null, Binary, Weight.Equal);
_rules[TokenType.Equal] = new Rule(null, null, Weight.None); _rules[TokenType.Equal] = new Rule(null, null, Weight.None);
_rules[TokenType.EqualEqual] = new Rule(null, Binary, Weight.Equal); _rules[TokenType.EqualEqual] = new Rule(null, Binary, Weight.Equal);
_rules[TokenType.Greater] = new Rule(null, Binary, Weight.Compare); _rules[TokenType.Greater] = new Rule(null, Binary, Weight.Compare);
_rules[TokenType.GreaterEqual] = new Rule(null, Binary, Weight.Compare); _rules[TokenType.GreaterEqual] = new Rule(null, Binary, Weight.Compare);
_rules[TokenType.Less] = new Rule(null, Binary, Weight.Compare); _rules[TokenType.Less] = new Rule(null, Binary, Weight.Compare);
_rules[TokenType.LessEqual] = new Rule(null, Binary, Weight.Compare); _rules[TokenType.LessEqual] = new Rule(null, Binary, Weight.Compare);
_rules[TokenType.Identifier] = new Rule(Variable, null, Weight.None); _rules[TokenType.Identifier] = new Rule(Variable, null, Weight.None);
_rules[TokenType.String] = new Rule(String, null, Weight.None); _rules[TokenType.String] = new Rule(String, null, Weight.None);
_rules[TokenType.Integer] = new Rule(Number, null, Weight.None); _rules[TokenType.Integer] = new Rule(Number, null, Weight.None);
_rules[TokenType.Hexadecimal] = new Rule(Number, null, Weight.None); _rules[TokenType.Hexadecimal] = new Rule(Number, null, Weight.None);
_rules[TokenType.Decimal] = new Rule(Number, null, Weight.None); _rules[TokenType.Decimal] = new Rule(Number, null, Weight.None);
_rules[TokenType.And] = new Rule(null, And, Weight.And); _rules[TokenType.And] = new Rule(null, And, Weight.And);
_rules[TokenType.Class] = new Rule(null, null, Weight.None); _rules[TokenType.Class] = new Rule(null, null, Weight.None);
_rules[TokenType.Else] = new Rule(null, null, Weight.None); _rules[TokenType.Else] = new Rule(null, null, Weight.None);
_rules[TokenType.False] = new Rule(Literal, null, Weight.None); _rules[TokenType.False] = new Rule(Literal, null, Weight.None);
_rules[TokenType.For] = new Rule(null, null, Weight.None); _rules[TokenType.For] = new Rule(null, null, Weight.None);
_rules[TokenType.Function] = new Rule(null, null, Weight.None); _rules[TokenType.Function] = new Rule(null, null, Weight.None);
_rules[TokenType.If] = new Rule(null, null, Weight.None); _rules[TokenType.If] = new Rule(null, null, Weight.None);
_rules[TokenType.Null] = new Rule(Literal, null, Weight.None); _rules[TokenType.Null] = new Rule(Literal, null, Weight.None);
_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.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);
_rules[TokenType.Base] = 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.This] = new Rule(This, null, Weight.None);
_rules[TokenType.True] = new Rule(Literal, null, Weight.None); _rules[TokenType.True] = new Rule(Literal, null, Weight.None);
_rules[TokenType.Var] = new Rule(null, null, Weight.None); _rules[TokenType.Var] = new Rule(null, null, Weight.None);
_rules[TokenType.While] = new Rule(null, null, Weight.None); _rules[TokenType.While] = new Rule(null, null, Weight.None);
_rules[TokenType.Error] = new Rule(null, null, Weight.None); _rules[TokenType.Error] = new Rule(null, null, Weight.None);
_rules[TokenType.Eof] = new Rule(null, null, Weight.None); _rules[TokenType.Eof] = new Rule(null, null, Weight.None);
} }
} }

View File

@ -4,7 +4,7 @@ public delegate void RuleDelegate(Digester digester, bool canAssign);
public readonly struct Rule(RuleDelegate? prefix = null, RuleDelegate? infix = null, Weight weight = Weight.None) public readonly struct Rule(RuleDelegate? prefix = null, RuleDelegate? infix = null, Weight weight = Weight.None)
{ {
public readonly RuleDelegate? Prefix = prefix; public readonly RuleDelegate? Prefix = prefix;
public readonly RuleDelegate? Infix = infix; public readonly RuleDelegate? Infix = infix;
public readonly Weight Weight = weight; public readonly Weight Weight = weight;
} }

View File

@ -2,15 +2,15 @@
public enum Weight public enum Weight
{ {
None = 0, None = 0,
Assign = 1, Assign = 1,
Or = 2, Or = 2,
And = 3, And = 3,
Equal = 4, Equal = 4,
Compare = 5, Compare = 5,
Term = 6, Term = 6,
Factor = 7, Factor = 7,
Modifier = 8, Modifier = 8,
Call = 9, Call = 9,
Primary = 10 Primary = 10
} }

View File

@ -2,11 +2,11 @@
public static class Console public static class Console
{ {
public static void Write(object? any) public static void Write(object? any)
{ {
string[] split = $"{any ?? "null"}".Split('\n'); string[] split = $"{any ?? "null"}".Split('\n');
for (int i = 0; i < split.Length; i++) { for (int i = 0; i < split.Length; i++) {
System.Console.Write((i == 0 ? " :> " : " ") + $"{split[i]}\n"); System.Console.Write((i == 0 ? " :> " : " ") + $"{split[i]}\n");
} }
} }
} }

View File

@ -7,19 +7,19 @@ namespace Qrakhen.Qamp.Core;
public class QampException(string message, object? context = null) public class QampException(string message, object? context = null)
: Exception(message) : Exception(message)
{ {
public readonly object? Context = context; public readonly object? Context = context;
} }
public class ReaderException(string message, IReader<Token> reader) public class ReaderException(string message, IReader<Token> reader)
: QampException(message, reader.CurrentPosition) : QampException(message, reader.CurrentPosition)
{ {
public readonly IReader<Token> Reader = reader; public readonly IReader<Token> Reader = reader;
} }
public class TokenException(string message, IReader<Token> reader, Token token) public class TokenException(string message, IReader<Token> reader, Token token)
: ReaderException($"TokenError {token} at {token.Position}: {message}", reader) : ReaderException($"TokenError {token} at {token.Position}: {message}", reader)
{ {
public readonly Token Token = token; public readonly Token Token = token;
} }
public class RuntimeException(string message, object? context = null) public class RuntimeException(string message, object? context = null)
@ -31,5 +31,5 @@ public class DivisionByZeroException(string? message, object? context = null)
public class ConversionException(string message, Value value, object? context = null) public class ConversionException(string message, Value value, object? context = null)
: RuntimeException(message, context) : RuntimeException(message, context)
{ {
public readonly Value Value = value; public readonly Value Value = value;
} }

View File

@ -2,15 +2,15 @@
public readonly record struct Instruction(byte Code) public readonly record struct Instruction(byte Code)
{ {
public OpCode OpCode => (OpCode)Code; public OpCode OpCode => (OpCode)Code;
public static implicit operator byte(Instruction instruction) => instruction.Code; public static implicit operator byte(Instruction instruction) => instruction.Code;
public static implicit operator Instruction(byte code) => new(code); public static implicit operator Instruction(byte code) => new(code);
public static implicit operator OpCode(Instruction instruction) => (OpCode)instruction.Code; public static implicit operator OpCode(Instruction instruction) => (OpCode)instruction.Code;
public static implicit operator Instruction(OpCode code) => new((byte)code); public static implicit operator Instruction(OpCode code) => new((byte)code);
public override string ToString() public override string ToString()
{ {
return $"<{Code:X4} | {OpCode}>"; return $"<{Code:X4} | {OpCode}>";
} }
} }

View File

@ -6,60 +6,60 @@ namespace Qrakhen.Qamp.Core.Execution;
public class InstructionPtr : Pointer<Instruction> public class InstructionPtr : Pointer<Instruction>
{ {
public Segment Segment; public Segment Segment;
public Instruction Instruction => Segment.Instructions[Cursor]; public Instruction Instruction => Segment.Instructions[Cursor];
public InstructionPtr(Segment segment, int position = 0) : base(segment.Instructions, position) public InstructionPtr(Segment segment, int position = 0) : base(segment.Instructions, position)
{ {
Segment = segment; Segment = segment;
} }
public long NextLong() public long NextLong()
{ {
long value = Segment.ReadLong(Cursor); long value = Segment.ReadLong(Cursor);
Cursor += sizeof(long); Cursor += sizeof(long);
return value; return value;
} }
public Value NextConstant() public Value NextConstant()
{ {
return Segment.Constants[NextDynamic()]; return Segment.Constants[NextDynamic()];
} }
public String NextString() public String NextString()
{ {
Value value = NextConstant(); Value value = NextConstant();
if (value.IsString) if (value.IsString)
return value.Ptr.As<String>()!; return value.Ptr.As<String>()!;
throw new Exception($"Expected string, got {value}"); throw new Exception($"Expected string, got {value}");
} }
public long NextDynamic() public long NextDynamic()
{ {
var result = Segment.ReadDynamic(Cursor, out int read); var result = Segment.ReadDynamic(Cursor, out int read);
Cursor += read; Cursor += read;
return result; return result;
} }
public String? GetStringConstant(long offset) public String? GetStringConstant(long offset)
=> Segment.Constants[offset].Ptr.Value as String; => Segment.Constants[offset].Ptr.Value as String;
public static Instruction operator +(InstructionPtr ptr, int value) public static Instruction operator +(InstructionPtr ptr, int value)
=> ptr.Segment.Instructions[ptr.Cursor + value]; => ptr.Segment.Instructions[ptr.Cursor + value];
public static Instruction operator -(InstructionPtr ptr, int value) public static Instruction operator -(InstructionPtr ptr, int value)
=> ptr.Segment.Instructions[ptr.Cursor - value]; => ptr.Segment.Instructions[ptr.Cursor - value];
public static InstructionPtr operator ++(InstructionPtr ptr) public static InstructionPtr operator ++(InstructionPtr ptr)
{ {
ptr.Cursor++; ptr.Cursor++;
return ptr; return ptr;
} }
public static InstructionPtr operator --(InstructionPtr ptr) public static InstructionPtr operator --(InstructionPtr ptr)
{ {
ptr.Cursor--; ptr.Cursor--;
return ptr; return ptr;
} }
} }

View File

@ -7,30 +7,30 @@ public class Instructions<T>(IEnumerable<T> instructions) :
IPeekable<T>, IPeekable<T>,
ISeekable<long, T> ISeekable<long, T>
{ {
private readonly T[] _instructions = instructions.ToArray(); private readonly T[] _instructions = instructions.ToArray();
public long Position { get; private set; } = 0; public long Position { get; private set; } = 0;
public long Length => _instructions.LongLength; public long Length => _instructions.LongLength;
public bool Done => Position >= _instructions.Length; public bool Done => Position >= _instructions.Length;
public T this[long offset] => offset < Length ? public T this[long offset] => offset < Length ?
_instructions[offset] : _instructions[offset] :
throw new ArgumentOutOfRangeException($"Tried to access offset beyond instruction length {offset}:{Length}"); throw new ArgumentOutOfRangeException($"Tried to access offset beyond instruction length {offset}:{Length}");
public T Seek(long position) public T Seek(long position)
{ {
Position = position; Position = position;
return _instructions[Position]; return _instructions[Position];
} }
public T Next() public T Next()
{ {
return _instructions[Position++]; return _instructions[Position++];
} }
public T Peek(int delta = 0) public T Peek(int delta = 0)
{ {
return _instructions[Position + delta]; return _instructions[Position + delta];
} }
} }

View File

@ -2,89 +2,89 @@
public enum OpCode public enum OpCode
{ {
F_Mask = 0xF0, F_Mask = 0xF0,
None = 0x00, None = 0x00,
Constant = 0x01, Constant = 0x01,
Null = 0x02, Null = 0x02,
Pop = 0x03, Pop = 0x03,
Cast = 0x04, Cast = 0x04,
False = 0x0e, False = 0x0e,
True = 0x0f, True = 0x0f,
Val = 0x10, Val = 0x10,
Ref = 0x11, Ref = 0x11,
SetGlobal = 0x20, SetGlobal = 0x20,
GetGlobal = 0x21, GetGlobal = 0x21,
GetLocal = 0x22, GetLocal = 0x22,
SetLocal = 0x23, SetLocal = 0x23,
GetOuter = 0x24, GetOuter = 0x24,
SetOuter = 0x25, SetOuter = 0x25,
GetMember = 0x26, GetMember = 0x26,
SetMember = 0x27, SetMember = 0x27,
DefineGlobal = 0x30, DefineGlobal = 0x30,
CloseOuter = 0x31, CloseOuter = 0x31,
AssignValue = 0x40, // unsure AssignValue = 0x40, // unsure
AssignReference = 0x41, // unsure AssignReference = 0x41, // unsure
F_Operation = 0x60, F_Operation = 0x60,
Add = 0x60, Add = 0x60,
Subtract = 0x61, Subtract = 0x61,
Multiply = 0x62, Multiply = 0x62,
Divide = 0x63, Divide = 0x63,
Modulo = 0x64, Modulo = 0x64,
BitwiseAnd = 0x65, BitwiseAnd = 0x65,
BitwiseOr = 0x66, BitwiseOr = 0x66,
BitwiseXor = 0x67, BitwiseXor = 0x67,
BitwiseLeft = 0x68, BitwiseLeft = 0x68,
BitwiseRight = 0x69, BitwiseRight = 0x69,
F_Unary = 0x0a, F_Unary = 0x0a,
Not = 0x6a, Not = 0x6a,
Negate = 0x6b, Negate = 0x6b,
BitwiseNot = 0x6c, BitwiseNot = 0x6c,
F_Compare = 0x50, F_Compare = 0x50,
Equal = 0x50, Equal = 0x50,
Greater = 0x51, Greater = 0x51,
Less = 0x52, Less = 0x52,
Increment = 0x70, Increment = 0x70,
Decrement = 0x71, Decrement = 0x71,
Array = 0xc0, Array = 0xc0,
List = 0xc1, List = 0xc1,
Structure = 0xc2, Structure = 0xc2,
GetItem = 0xc3, GetItem = 0xc3,
SetItem = 0xc4, SetItem = 0xc4,
AddItem = 0xc5, AddItem = 0xc5,
RemoveItem = 0xc6, RemoveItem = 0xc6,
Return = 0x80, Return = 0x80,
Jump = 0x81, Jump = 0x81,
JumpIfFalse = 0x82, JumpIfFalse = 0x82,
Loop = 0x83, Loop = 0x83,
Invoke = 0x84, Invoke = 0x84,
InvokeBase = 0x85, InvokeBase = 0x85,
InvokeMember = 0x86, InvokeMember = 0x86,
Context = 0x87, Context = 0x87,
Class = 0xa0, Class = 0xa0,
Member = 0xa1, Member = 0xa1,
Base = 0xa2, Base = 0xa2,
Inherit = 0xa3, Inherit = 0xa3,
Method = 0xa4, Method = 0xa4,
Static = 0xa5, Static = 0xa5,
Print = 0xe0, Print = 0xe0,
PrintStack = 0xe1, PrintStack = 0xe1,
PrintGlobals = 0xe2, PrintGlobals = 0xe2,
PrintExpr = 0xe3, PrintExpr = 0xe3,
Typeof = 0xef, Typeof = 0xef,
Export = 0xfe, // probably shouldnt be an op code but rather be done during digestion Export = 0xfe, // probably shouldnt be an op code but rather be done during digestion
Import = 0xff, // probably shouldnt be an op code but rather be done during digestion Import = 0xff, // probably shouldnt be an op code but rather be done during digestion
} }

File diff suppressed because it is too large Load Diff

View File

@ -5,81 +5,81 @@ using Qrakhen.Qamp.Core.Values.Objects;
namespace Qrakhen.Qamp.Core.Execution; namespace Qrakhen.Qamp.Core.Execution;
using Unsigned = ulong; using Unsigned = ulong;
using Signed = long; using Signed = long;
using Char = char; using Char = char;
using Byte = byte; using Byte = byte;
using Bool = bool; using Bool = bool;
using Decimal = double; using Decimal = double;
public class Segment( public class Segment(
IEnumerable<Instruction> instructions, IEnumerable<Instruction> instructions,
IEnumerable<Value> constants) : ISerialize<Segment> IEnumerable<Value> constants) : ISerialize<Segment>
{ {
public readonly FixedArray<Instruction> Instructions = new(instructions); public readonly FixedArray<Instruction> Instructions = new(instructions);
public readonly FixedArray<Value> Constants = new(constants); public readonly FixedArray<Value> Constants = new(constants);
public byte Read(long offset) => Instructions[offset]; public byte Read(long offset) => Instructions[offset];
public byte[] Read(long offset, int length) public byte[] Read(long offset, int length)
{ {
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + length, Instructions.Length); ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + length, Instructions.Length);
byte[] bytes = new byte[length]; byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
bytes[i] = Instructions[offset + i]; bytes[i] = Instructions[offset + i];
return bytes; return bytes;
} }
/// <summary> /// <summary>
/// Although this always returns a long, the actually stored data in the instruction set has a dynamic width /// 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).<br/> /// (anything between 1 and 9 bytes, 1 if it's just a byte, and 1 + n, where n is up to 8 bytes).<br/>
/// If the first byte read is masked with 0x80, it means that this byte represents the amount of bytes that /// 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). /// shall be read. If it is not, the byte is returned directly (for smaller offsets).
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// All of this is done to compress compiled instruction size. /// 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. /// Todo: It will need to be tested wheter the cost on performance is stronger than anticipated.
/// </remarks> /// </remarks>
public long ReadDynamic(long offset, out int read) public long ReadDynamic(long offset, out int read)
{ {
byte value = Read(offset); byte value = Read(offset);
read = 1; read = 1;
if (value < 0x80) // 0x80 flag for length, less than 0x80 means direct read if (value < 0x80) // 0x80 flag for length, less than 0x80 means direct read
return value; return value;
int length = value ^ 0x80; int length = value ^ 0x80;
read += length; read += length;
var data = new byte[8]; var data = new byte[8];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
data[i] = Read(offset + i); data[i] = Read(offset + i);
return data.ToInt64(); return data.ToInt64();
} }
public short ReadShort(long offset) => BitConverter.ToInt16(Read(offset, 2)); public short ReadShort(long offset) => BitConverter.ToInt16(Read(offset, 2));
public int ReadInt(long offset) => BitConverter.ToInt32(Read(offset, 4)); public int ReadInt(long offset) => BitConverter.ToInt32(Read(offset, 4));
public long ReadLong(long offset) => BitConverter.ToInt64(Read(offset, 8)); public long ReadLong(long offset) => BitConverter.ToInt64(Read(offset, 8));
public byte[] Serialize() public byte[] Serialize()
{ {
var opCodes = Instructions.Select(i => (byte)i).ToArray(); var opCodes = Instructions.Select(i => (byte)i).ToArray();
var constants = Constants.ToArray(); var constants = Constants.ToArray();
byte[] bytes = new byte[sizeof(long) + opCodes.Length]; byte[] bytes = new byte[sizeof(long) + opCodes.Length];
System.Array.Copy(opCodes.LongLength.GetBytes(), 0, bytes, 0, sizeof(long)); System.Array.Copy(opCodes.LongLength.GetBytes(), 0, bytes, 0, sizeof(long));
System.Array.Copy(opCodes, 0, bytes, sizeof(long), opCodes.Length); System.Array.Copy(opCodes, 0, bytes, sizeof(long), opCodes.Length);
var list = new List<byte>(bytes); var list = new List<byte>(bytes);
foreach (var constant in constants) { foreach (var constant in constants) {
var data = constant.Signed.GetDynamicBytes(); var data = constant.Signed.GetDynamicBytes();
if (data.Length == 1) if (data.Length == 1)
list.Add(data[0]); list.Add(data[0]);
else { else {
list.Add((byte)(data.Length | 0x80)); list.Add((byte)(data.Length | 0x80));
list.AddRange(data); list.AddRange(data);
} }
} }
return list.ToArray(); return list.ToArray();
} }
public static Segment Deserialize(byte[] data) public static Segment Deserialize(byte[] data)
{ {
return null; return null;
} }
} }

View File

@ -19,7 +19,6 @@ public static class ValueOperation
#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)
return operation.OpCode switch { return operation.OpCode switch {
OpCode.Equal => Equal(operation), OpCode.Equal => Equal(operation),
@ -94,9 +93,6 @@ public static class ValueOperation
public static Value Not(Operation operation) public static Value Not(Operation operation)
{ {
Value value = operation.Left; Value value = operation.Left;
#if LOG
_logger.Method($"{value}");
#endif
Assert.NotVoid(value); Assert.NotVoid(value);
ulong inner = value.Unsigned; ulong inner = value.Unsigned;
return new Value(inner == 0); return new Value(inner == 0);

View File

@ -5,97 +5,97 @@ namespace Qrakhen.Qamp.Core;
public static class Extensions public static class Extensions
{ {
public static uint GetHash(this string str) public static uint GetHash(this string str)
{ {
if (string.IsNullOrEmpty(str)) if (string.IsNullOrEmpty(str))
return 0; return 0;
byte[] bytes = Encoding.ASCII.GetBytes(str); byte[] bytes = Encoding.ASCII.GetBytes(str);
unchecked { unchecked {
uint hash = 216613661u; uint hash = 216613661u;
for (int i = 0; i < bytes.Length; i++) { for (int i = 0; i < bytes.Length; i++) {
hash ^= bytes[i]; hash ^= bytes[i];
hash *= 16777619; hash *= 16777619;
} }
return hash; return hash;
} }
} }
public static bool TryMatch(this Regex regex, string str, out Match match) public static bool TryMatch(this Regex regex, string str, out Match match)
{ {
match = regex.Match(str); match = regex.Match(str);
return match.Success; return match.Success;
} }
public static T[] Subset<T>(this T[] array, long from, long length) public static T[] Subset<T>(this T[] array, long from, long length)
{ {
var result = new T[length]; var result = new T[length];
Array.Copy(array, from, result, 0, length); Array.Copy(array, from, result, 0, length);
return result; return result;
} }
/// <summary> /// <summary>
/// Expensive. /// Expensive.
/// </summary> /// </summary>
public static void Insert(this Stream stream, byte[] data, long position = -1) public static void Insert(this Stream stream, byte[] data, long position = -1)
{ {
if (position < 0) if (position < 0)
position = stream.Position; position = stream.Position;
byte[] buffer = new byte[stream.Length - position]; byte[] buffer = new byte[stream.Length - position];
if (buffer.Length > 0) { if (buffer.Length > 0) {
stream.Position = position; stream.Position = position;
stream.ReadExactly(buffer); stream.ReadExactly(buffer);
stream.Write(buffer); stream.Write(buffer);
} }
stream.Position = position; stream.Position = position;
stream.Write(data); stream.Write(data);
} }
public static void Remove(this Stream stream, long position, int amount) public static void Remove(this Stream stream, long position, int amount)
{ {
byte[] buffer = new byte[stream.Length - (position + amount)]; byte[] buffer = new byte[stream.Length - (position + amount)];
if (buffer.Length > 0) { if (buffer.Length > 0) {
stream.Position = position + amount; stream.Position = position + amount;
stream.ReadExactly(buffer); stream.ReadExactly(buffer);
stream.Position = position; stream.Position = position;
stream.Write(buffer); stream.Write(buffer);
} }
stream.Position = position; stream.Position = position;
stream.SetLength(stream.Length - amount); stream.SetLength(stream.Length - amount);
} }
public static byte[] GetDynamicBytes(this long value) public static byte[] GetDynamicBytes(this long value)
{ {
byte[] data = value.GetBytes(); byte[] data = value.GetBytes();
return data.Subset(0, GetPrimitiveLength(value)); return data.Subset(0, GetPrimitiveLength(value));
} }
public static int GetPrimitiveLength(this long value) public static int GetPrimitiveLength(this long value)
{ {
int length = 8; int length = 8;
for (int i = 1; i < 8; i++) { for (int i = 1; i < 8; i++) {
if (value <= (1L << (i * 8))) { if (value <= (1L << (i * 8))) {
length = i; length = i;
break; break;
} }
} }
return length; return length;
} }
public static byte[] GetBytes(this short value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this short value) => BitConverter.GetBytes(value);
public static byte[] GetBytes(this ushort value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this ushort value) => BitConverter.GetBytes(value);
public static byte[] GetBytes(this int value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this int value) => BitConverter.GetBytes(value);
public static byte[] GetBytes(this uint value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this uint value) => BitConverter.GetBytes(value);
public static byte[] GetBytes(this long value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this long value) => BitConverter.GetBytes(value);
public static byte[] GetBytes(this ulong value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this ulong value) => BitConverter.GetBytes(value);
public static byte[] GetBytes(this double value) => BitConverter.GetBytes(value); public static byte[] GetBytes(this double value) => BitConverter.GetBytes(value);
public static short ToInt16(this byte[] bytes) => BitConverter.ToInt16(bytes); public static short ToInt16(this byte[] bytes) => BitConverter.ToInt16(bytes);
public static ushort ToUInt16(this byte[] bytes) => BitConverter.ToUInt16(bytes); public static ushort ToUInt16(this byte[] bytes) => BitConverter.ToUInt16(bytes);
public static int ToInt32(this byte[] bytes) => BitConverter.ToInt32(bytes); public static int ToInt32(this byte[] bytes) => BitConverter.ToInt32(bytes);
public static uint ToUInt32(this byte[] bytes) => BitConverter.ToUInt32(bytes); public static uint ToUInt32(this byte[] bytes) => BitConverter.ToUInt32(bytes);
public static long ToInt64(this byte[] bytes) => BitConverter.ToInt64(bytes); public static long ToInt64(this byte[] bytes) => BitConverter.ToInt64(bytes);
public static ulong ToUInt64(this byte[] bytes) => BitConverter.ToUInt64(bytes); public static ulong ToUInt64(this byte[] bytes) => BitConverter.ToUInt64(bytes);
public static double ToDouble(this byte[] bytes) => BitConverter.ToDouble(bytes); public static double ToDouble(this byte[] bytes) => BitConverter.ToDouble(bytes);
} }

View File

@ -10,10 +10,10 @@ public class Injector
public enum Scope public enum Scope
{ {
/// <summary>Within the global scope of the file that is importing this library</summary> /// <summary>Within the global scope of the file that is importing this library</summary>
Global, Global,
/// <summary>Within the scope of the module this library exports</summary> /// <summary>Within the scope of the module this library exports</summary>
Module, Module,
/// <summary>An extension of the built-in <see cref="Values.Value"/> type</summary> /// <summary>An extension of the built-in <see cref="Values.Value"/> type</summary>
Extension Extension
} }

View File

@ -5,79 +5,79 @@ namespace Qrakhen.Qamp.Core.Logging;
public interface ILogger public interface ILogger
{ {
LogLevel Level { get; set; } LogLevel Level { get; set; }
void Log(LogLevel level, params object?[] values); void Log(LogLevel level, params object?[] values);
void Critical(params object?[] values); void Critical(params object?[] values);
void Error(params object?[] values); void Error(params object?[] values);
void Warn(params object?[] values); void Warn(params object?[] values);
void Info(params object?[] values); void Info(params object?[] values);
void Debug(params object?[] values); void Debug(params object?[] values);
void Trace(params object?[] values); void Trace(params object?[] values);
void Verbose(params object?[] values); void Verbose(params object?[] values);
void Method(object? value = null, string? caller = null); void Method(object? value = null, string? caller = null);
} }
[Flags] [Flags]
public enum LogLevel public enum LogLevel
{ {
Mute = 0x00, Mute = 0x00,
Critical = 0x01, Critical = 0x01,
Error = 0x02, Error = 0x02,
Warn = 0x04, Warn = 0x04,
Info = 0x08, Info = 0x08,
Debug = 0x10, Debug = 0x10,
Trace = 0x20, Trace = 0x20,
Verbose = 0x40, Verbose = 0x40,
All = Critical | Error | Warn | Info | Debug | Trace | Verbose All = Critical | Error | Warn | Info | Debug | Trace | Verbose
} }
public interface ILogFormatter public interface ILogFormatter
{ {
string[] Format(object? value, int maxWidth = 80); string[] Format(object? value, int maxWidth = 80);
} }
public interface ILoggerService public interface ILoggerService
{ {
ILogger Get<T>(string? name = null, LogLevel? level = null); ILogger Get<T>(string? name = null, LogLevel? level = null);
ILogger Get(string name, LogLevel? level = null); ILogger Get(string name, LogLevel? level = null);
} }
public class LoggerService public class LoggerService
{ {
private static readonly Dictionary<string, ILogger> _loggers = new Dictionary<string, ILogger>(); private static readonly Dictionary<string, ILogger> _loggers = new Dictionary<string, ILogger>();
public static LogLevel Default = LogLevel.All; public static LogLevel Default = LogLevel.All;
public static ILogger Get<T>(string? name = null, LogLevel? level = null) public static ILogger Get<T>(string? name = null, LogLevel? level = null)
{ {
level ??= Default; level ??= Default;
string _name = nameof(T); string _name = nameof(T);
if (!string.IsNullOrEmpty(name)) if (!string.IsNullOrEmpty(name))
_name = $"{_name}:{name}"; _name = $"{_name}:{name}";
return Get(_name, level); return Get(_name, level);
} }
public static ILogger Get(string name, LogLevel? level = null) public static ILogger Get(string name, LogLevel? level = null)
{ {
level ??= Default; level ??= Default;
ILogger? logger; ILogger? logger;
if (_loggers.TryGetValue(name, out logger)) if (_loggers.TryGetValue(name, out logger))
return logger; return logger;
logger = new Logger(name, level.Value); logger = new Logger(name, level.Value);
_loggers.Add(name, logger); _loggers.Add(name, logger);
return logger; return logger;
} }
} }
public class Logger : ILogger, ILogFormatter public class Logger : ILogger, ILogFormatter
{ {
public string Name { get; private set; } public string Name { get; private set; }
private LogLevel _level; private LogLevel _level;
public LogLevel Level { get => true ? LoggerService.Default : _level; set => _level = value; } public LogLevel Level { get => true ? LoggerService.Default : _level; set => _level = value; }
private static Dictionary<LogLevel, (string First, string Extra)> headers = new() { private static Dictionary<LogLevel, (string First, string Extra)> headers = new() {
{ LogLevel.Critical, (" !? ", " !? ") }, { LogLevel.Critical, (" !? ", " !? ") },
{ LogLevel.Error, (" !! ", " ! ") }, { LogLevel.Error, (" !! ", " ! ") },
{ LogLevel.Warn, (" !> ", " ! ") }, { LogLevel.Warn, (" !> ", " ! ") },
@ -87,7 +87,7 @@ public class Logger : ILogger, ILogFormatter
{ LogLevel.Verbose, (" > ", " ") }, { LogLevel.Verbose, (" > ", " ") },
}; };
private static Dictionary<LogLevel, ConsoleColor> colors = new() private static Dictionary<LogLevel, ConsoleColor> colors = new()
{ {
{ LogLevel.Critical, ConsoleColor.DarkRed }, { LogLevel.Critical, ConsoleColor.DarkRed },
{ LogLevel.Error, ConsoleColor.Red }, { LogLevel.Error, ConsoleColor.Red },
@ -98,66 +98,63 @@ public class Logger : ILogger, ILogFormatter
{ LogLevel.Verbose, ConsoleColor.DarkGray }, { LogLevel.Verbose, ConsoleColor.DarkGray },
}; };
public Logger(string name, LogLevel level) public Logger(string name, LogLevel level)
{ {
Name = name; Name = name;
Level = level; Level = level;
} }
public string[] Format(object? value, int maxWidth = 80) public string[] Format(object? value, int maxWidth = 80)
{ {
List<string> result = []; List<string> result = [];
if (value == null) if (value == null)
result.Add("null"); result.Add("null");
else if (value is string str) else if (value is string str)
result.AddRange(str.Split('\n')); result.AddRange(str.Split('\n'));
else if (value is Exception e) else if (value is Exception e) {
{ result.Add(e.Message);
result.Add(e.Message); if (!string.IsNullOrEmpty(e.StackTrace))
if (!string.IsNullOrEmpty(e.StackTrace)) result.AddRange(e.StackTrace.Split('\n'));
result.AddRange(e.StackTrace.Split('\n')); } else
} else result.Add(value?.ToString() ?? "null");
result.Add(value?.ToString() ?? "null");
return result.ToArray(); return result.ToArray();
} }
public void Log(LogLevel level, params object?[] values) public void Log(LogLevel level, params object?[] values)
{ {
if ((level & Level) != level) if ((level & Level) != level)
return; return;
var header = headers[level]; var header = headers[level];
Console.ForegroundColor = colors[level]; Console.ForegroundColor = colors[level];
foreach (var value in values) foreach (var value in values) {
{ var lines = Format(value);
var lines = Format(value); for (int i = 0; i < lines.Length; i++)
for (int i = 0; i < lines.Length; i++) Console.WriteLine($"{(i == 0 ? header.First : header.Extra)}{lines[i]}");
Console.WriteLine($"{(i == 0 ? header.First : header.Extra)}{lines[i]}"); }
} Console.ForegroundColor = ConsoleColor.White;
Console.ForegroundColor = ConsoleColor.White; }
}
public void Critical(params object?[] values) => Log(LogLevel.Critical, values); public void Critical(params object?[] values) => Log(LogLevel.Critical, values);
public void Error(params object?[] values) => Log(LogLevel.Error, values); public void Error(params object?[] values) => Log(LogLevel.Error, values);
public void Warn(params object?[] values) => Log(LogLevel.Warn, values); public void Warn(params object?[] values) => Log(LogLevel.Warn, values);
public void Info(params object?[] values) => Log(LogLevel.Info, values); public void Info(params object?[] values) => Log(LogLevel.Info, values);
public void Debug(params object?[] values) => Log(LogLevel.Debug, values); public void Debug(params object?[] values) => Log(LogLevel.Debug, values);
public void Trace(params object?[] values) => Log(LogLevel.Trace, values); public void Trace(params object?[] values) => Log(LogLevel.Trace, values);
public void Verbose(params object?[] values) => Log(LogLevel.Verbose, values); public void Verbose(params object?[] values) => Log(LogLevel.Verbose, values);
public void Method(object? value = null, [CallerMemberName] string? caller = null) public void Method(object? value = null, [CallerMemberName] string? caller = null)
{ {
if (Level < LogLevel.Trace) if (Level < LogLevel.Trace)
return; // save some ticks here return; // save some ticks here
if (caller == null) if (caller == null) {
{ var st = new StackTrace();
var st = new StackTrace(); var previous = st.GetFrame(1)?.GetMethod();
var previous = st.GetFrame(1)?.GetMethod(); if (previous != null)
if (previous != null) caller = $"{previous.DeclaringType?.Name}.{previous.Name}";
caller = $"{previous.DeclaringType?.Name}.{previous.Name}"; }
} Log(LogLevel.Trace, [$"[{caller}] {value}"]);
Log(LogLevel.Trace, [ $"[{caller}] {value}" ]); }
}
} }

View File

@ -5,106 +5,106 @@ using static TokenType;
public class Dialect public class Dialect
{ {
// suuuper slow but meh, who cares // suuuper slow but meh, who cares
private Register<string, TokenType> _register = new(); private Register<string, TokenType> _register = new();
public void Define(TokenType type, string sequence) public void Define(TokenType type, string sequence)
=> _register[sequence] = type; => _register[sequence] = type;
public TokenType Get(string sequence) public TokenType Get(string sequence)
=> _register[sequence]; => _register[sequence];
} }
public class DefaultDialect : Dialect public class DefaultDialect : Dialect
{ {
public DefaultDialect() public DefaultDialect()
{ {
Define(Null, "null"); Define(Null, "null");
Define(GroupOpen, "("); Define(GroupOpen, "(");
Define(GroupClose, ")"); Define(GroupClose, ")");
Define(ContextOpen, "{"); Define(ContextOpen, "{");
Define(ContextClose, "}"); Define(ContextClose, "}");
Define(ArrayOpen, "["); Define(ArrayOpen, "[");
Define(ArrayClose, "]"); Define(ArrayClose, "]");
Define(AddItem, "<+"); Define(AddItem, "<+");
Define(RemoveItem, "->"); Define(RemoveItem, "->");
Define(Comma, ","); Define(Comma, ",");
Define(Dot, "."); Define(Dot, ".");
Define(Colon, ":"); Define(Colon, ":");
Define(Semicolon, ";"); Define(Semicolon, ";");
Define(Minus, "-"); Define(Minus, "-");
Define(Plus, "+"); Define(Plus, "+");
Define(MinusEqual, "-="); Define(MinusEqual, "-=");
Define(PlusEqual, "+="); Define(PlusEqual, "+=");
Define(Decrement, "++"); Define(Decrement, "++");
Define(Increment, "--"); Define(Increment, "--");
Define(Slash, "/"); Define(Slash, "/");
Define(SlashEqual, "/="); Define(SlashEqual, "/=");
Define(Star, "*"); Define(Star, "*");
Define(StarEqual, "*="); Define(StarEqual, "*=");
Define(Modulo, "%"); Define(Modulo, "%");
Define(ModuloEqual, "%="); Define(ModuloEqual, "%=");
Define(BitwiseAnd, "&"); Define(BitwiseAnd, "&");
Define(BitwiseOr, "|"); Define(BitwiseOr, "|");
Define(BitwiseXor, "^"); Define(BitwiseXor, "^");
Define(BitwiseNot, "~"); Define(BitwiseNot, "~");
Define(BitwiseLeft, "<<"); Define(BitwiseLeft, "<<");
Define(BitwiseRight, ">>"); Define(BitwiseRight, ">>");
Define(Bang, "!"); Define(Bang, "!");
Define(BangEqual, "!="); Define(BangEqual, "!=");
Define(Equal, "="); Define(Equal, "=");
Define(EqualEqual, "=="); Define(EqualEqual, "==");
Define(Greater, ">"); Define(Greater, ">");
Define(GreaterEqual, ">="); Define(GreaterEqual, ">=");
Define(Less, "<"); Define(Less, "<");
Define(LessEqual, "<="); Define(LessEqual, "<=");
Define(Question, "?"); Define(Question, "?");
Define(DoubleQuestion, "??"); Define(DoubleQuestion, "??");
Define(Identifier, "([_a-zA-Z][_a-zA-Z0-9]+?)"); Define(Identifier, "([_a-zA-Z][_a-zA-Z0-9]+?)");
Define(String, "(:?('.+')|\".+\")"); Define(String, "(:?('.+')|\".+\")");
Define(Integer, "(\\d+)"); Define(Integer, "(\\d+)");
Define(Decimal, ""); Define(Decimal, "");
Define(Hexadecimal, "0x([a-fA-F0-9]+)"); Define(Hexadecimal, "0x([a-fA-F0-9]+)");
Define(And, "&&"); Define(And, "&&");
Define(Else, "else"); Define(Else, "else");
Define(False, "false"); Define(False, "false");
Define(For, "for"); Define(For, "for");
Define(If, "if"); Define(If, "if");
Define(Or, "||"); Define(Or, "||");
Define(This, "this"); Define(This, "this");
Define(True, "true"); Define(True, "true");
Define(Var, "var"); Define(Var, "var");
Define(While, "while"); Define(While, "while");
Define(Do, "do"); Define(Do, "do");
Define(Return, "return"); Define(Return, "return");
Define(Ref, "ref"); Define(Ref, "ref");
Define(Function, "function"); Define(Function, "function");
Define(Class, "class"); Define(Class, "class");
Define(Base, "base"); Define(Base, "base");
Define(TypeOf, "typeof"); Define(TypeOf, "typeof");
Define(Print, "print"); Define(Print, "print");
Define(Import, "import"); Define(Import, "import");
Define(Export, "export"); Define(Export, "export");
} }
} }
public class ClassicDialect : DefaultDialect public class ClassicDialect : DefaultDialect
{ {
public ClassicDialect() public ClassicDialect()
{ {
Define(Null, "_"); Define(Null, "_");
Define(AddItem, "<+"); Define(AddItem, "<+");
Define(RemoveItem, "->"); Define(RemoveItem, "->");
Define(Equal, "<~"); Define(Equal, "<~");
Define(This, ".~"); Define(This, ".~");
Define(Var, "*~"); Define(Var, "*~");
Define(Return, "<:"); Define(Return, "<:");
Define(Ref, "*&"); Define(Ref, "*&");
Define(Function, "fq"); Define(Function, "fq");
Define(Base, "^~"); Define(Base, "^~");
Define(TypeOf, "?:"); Define(TypeOf, "?:");
Define(Print, "::"); Define(Print, "::");
Define(Import, "<!"); Define(Import, "<!");
Define(Export, "!>"); Define(Export, "!>");
} }
} }

View File

@ -8,371 +8,371 @@ namespace Qrakhen.Qamp.Core.Tokenization;
internal static partial class ReaderPatterns internal static partial class ReaderPatterns
{ {
[GeneratedRegex(@"((?:\d+)?\.(?:\d+))")] [GeneratedRegex(@"((?:\d+)?\.(?:\d+))")]
public static partial Regex IsDecimal(); public static partial Regex IsDecimal();
[GeneratedRegex(@"(\d+)")] [GeneratedRegex(@"(\d+)")]
public static partial Regex IsInteger(); public static partial Regex IsInteger();
[GeneratedRegex(@"0x([a-fA-F0-9]+)")] [GeneratedRegex(@"0x([a-fA-F0-9]+)")]
public static partial Regex IsHexadecimal(); public static partial Regex IsHexadecimal();
public static readonly Dictionary<string, TokenType> Keywords = new(); public static readonly Dictionary<string, TokenType> Keywords = new();
public static readonly Dictionary<string, string> Aliases = new(); public static readonly Dictionary<string, string> Aliases = new();
private static void Define(string key, TokenType type) private static void Define(string key, TokenType type)
{ {
// not ideal, but momentarily better than setting up complex dialects // not ideal, but momentarily better than setting up complex dialects
if (Keywords.ContainsValue(type)) if (Keywords.ContainsValue(type))
Aliases[key] = Keywords.First(v => v.Value == type).Key; Aliases[key] = Keywords.First(v => v.Value == type).Key;
Keywords[key] = type; Keywords[key] = type;
} }
static ReaderPatterns() static ReaderPatterns()
{ {
Define("false", False); Define("false", False);
Define("true", True); Define("true", True);
Define("null", Null); Define("null", Null);
Define("void", Null); Define("void", Null);
Define("and", And); Define("and", And);
Define("else", Else); Define("else", Else);
Define("for", For); Define("for", For);
Define("if", If); Define("if", If);
Define("or", Or); Define("or", Or);
Define("this", This); Define("this", This);
Define(".~", This); Define(".~", This);
Define("var", Var); Define("var", Var);
Define("*~", Var); Define("*~", Var);
Define("while", While); Define("while", While);
Define("do", Do); Define("do", Do);
Define("ref", Ref); Define("ref", Ref);
Define("function", Function); Define("function", Function);
Define("funqtion", Function); Define("funqtion", Function);
Define("fq", Function); Define("fq", Function);
Define("funq", Function); Define("funq", Function);
Define("return", Return); Define("return", Return);
Define("<:", Return); Define("<:", Return);
Define("class", Class); Define("class", Class);
Define("base", Base); Define("base", Base);
Define("^~", Base); Define("^~", Base);
Define("typeof", TypeOf); Define("typeof", TypeOf);
Define("?:", TypeOf); Define("?:", TypeOf);
Define("print", Print); Define("print", Print);
Define("::", Print); Define("::", Print);
Define("globals", PrintGlobals); Define("globals", PrintGlobals);
Define("stack", PrintStack); Define("stack", PrintStack);
Define("expr", PrintExpr); Define("expr", PrintExpr);
Define("import", Import); Define("import", Import);
Define("export", Export); Define("export", Export);
} }
} }
public class Reader : IReader<Token>, IDisposable public class Reader : IReader<Token>, IDisposable
{ {
private const char EofByte = (char)0xFF; private const char EofByte = (char)0xFF;
private const int ChunkSize = 0x20; private const int ChunkSize = 0x20;
private readonly Stream _stream; private readonly Stream _stream;
private readonly Tokens _tokens; private readonly Tokens _tokens;
private readonly List<char> _buffer; private readonly List<char> _buffer;
private readonly ILogger _logger = LoggerService.Get<Reader>(); private readonly ILogger _logger = LoggerService.Get<Reader>();
private int _line = 0; private int _line = 0;
private int _column = 0; private int _column = 0;
private int _current = 0; private int _current = 0;
private long _startIndex = 0; private long _startIndex = 0;
private TokenPosition _startPosition = default; private TokenPosition _startPosition = default;
public bool Done => _stream.Position >= _stream.Length && public bool Done => _stream.Position >= _stream.Length &&
_current >= _buffer.Count; _current >= _buffer.Count;
public TokenPosition CurrentPosition => new TokenPosition(_line, _column); public TokenPosition CurrentPosition => new TokenPosition(_line, _column);
public Reader(Stream stream) public Reader(Stream stream)
{ {
_stream = stream; _stream = stream;
_stream.Position = 0; _stream.Position = 0;
_tokens = new Tokens(); _tokens = new Tokens();
_buffer = []; _buffer = [];
} }
public void Dispose() public void Dispose()
{ {
_stream.Dispose(); _stream.Dispose();
} }
public Token NextToken(bool includeCompilationIrrelevant = false) public Token NextToken(bool includeCompilationIrrelevant = false)
{ {
if (Done) if (Done)
return Token.Eof(); return Token.Eof();
Token token = ReadToken(); Token token = ReadToken();
while (!includeCompilationIrrelevant && token.IsCompilationIrrelevant && !Done) while (!includeCompilationIrrelevant && token.IsCompilationIrrelevant && !Done)
token = ReadToken(); token = ReadToken();
if (!includeCompilationIrrelevant && token.IsCompilationIrrelevant && Done) if (!includeCompilationIrrelevant && token.IsCompilationIrrelevant && Done)
return Token.Eof(); return Token.Eof();
return token; return token;
} }
private char Next() private char Next()
{ {
if (!Done && _current >= _buffer.Count) if (!Done && _current >= _buffer.Count)
Continue(); Continue();
if (Done) if (Done)
return EofByte; return EofByte;
_column++; _column++;
return _buffer[_current++]; return _buffer[_current++];
} }
private char Peek(int delta = 0) private char Peek(int delta = 0)
{ {
if (_current + delta >= _buffer.Count) if (_current + delta >= _buffer.Count)
Continue(); Continue();
if (_current + delta >= _buffer.Count) if (_current + delta >= _buffer.Count)
return EofByte; return EofByte;
return _buffer[_current + delta]; return _buffer[_current + delta];
} }
private Token ReadToken() private Token ReadToken()
{ {
_startIndex = _current; _startIndex = _current;
_startPosition = CurrentPosition; _startPosition = CurrentPosition;
string buffer = string.Empty; string buffer = string.Empty;
char c = Next(); char c = Next();
buffer += c; buffer += c;
if (c == '\n' || c == '\r') { if (c == '\n' || c == '\r') {
var position = CurrentPosition; var position = CurrentPosition;
if (c == '\r') if (c == '\r')
buffer += Next(); buffer += Next();
_line++; _line++;
_column = 0; _column = 0;
return MakeToken(NewLine, buffer); return MakeToken(NewLine, buffer);
} }
if (c == '#') { if (c == '#') {
var position = CurrentPosition; var position = CurrentPosition;
do { do {
c = Next();
buffer += c;
} while (c != '\n' && c != EofByte);
return MakeToken(Comment, buffer);
}
if (c == ' ') {
var position = CurrentPosition;
while (Match(' '))
buffer += ' ';
return MakeToken(Whitespace, buffer);
}
if (c is '\'' or '"') {
string str = ReadUntil(buffer, c);
if (str[^1] != c || str.Length < 2)
return MakeToken(Error, str);
return MakeToken(TokenType.String, str.Substring(1, str.Length - 2));
}
if (IsDigit(c) || (c == '.' && IsDigit(Peek()))) {
do {
c = Peek(0);
if (IsDigit(c) || IsHex(c) || c == 'x' || c == '.')
buffer += Next();
else
break;
} while (c != EofByte);
if (ReaderPatterns.IsDecimal().TryMatch(buffer, out Match match))
return MakeToken(TokenType.Decimal, match.Groups[1].Value);
if (ReaderPatterns.IsHexadecimal().TryMatch(buffer, out match))
return MakeToken(Hexadecimal, match.Groups[1].Value);
if (ReaderPatterns.IsInteger().TryMatch(buffer, out match))
return MakeToken(Integer, match.Groups[1].Value);
throw new ReaderException($"Unrecognizable number detected <{buffer}>", this);
}
if (IsLetter(c)) {
do {
c = Peek();
if (IsLetter(c))
buffer += Next();
else
break;
} while (c != EofByte);
return MakeIdentifier(buffer);
}
return MakeOperator(buffer);
}
private string ReadUntil(string buffer, char until)
{
char c;
do {
c = Next(); c = Next();
if (c == EofByte)
return buffer;
/*throw new ReaderException(
$"Unexpected end of feed while trying to read string <{buffer}>",
this);*/
buffer += c; buffer += c;
} while (c != until || Peek(-2) == '\\'); } while (c != '\n' && c != EofByte);
return buffer; return MakeToken(Comment, buffer);
} }
private Token MakeIdentifier(string buffer) if (c == ' ') {
{ var position = CurrentPosition;
if (ReaderPatterns.Keywords.TryGetValue(buffer, out TokenType type)) while (Match(' '))
return MakeToken(type, buffer); buffer += ' ';
return MakeToken(Identifier, buffer); return MakeToken(Whitespace, buffer);
} }
private bool Check(char c) => Peek(0) == c; if (c is '\'' or '"') {
string str = ReadUntil(buffer, c);
if (str[^1] != c || str.Length < 2)
return MakeToken(Error, str);
return MakeToken(TokenType.String, str.Substring(1, str.Length - 2));
}
private bool Match(char c) if (IsDigit(c) || (c == '.' && IsDigit(Peek()))) {
{ do {
if (!Check(c)) c = Peek(0);
if (IsDigit(c) || IsHex(c) || c == 'x' || c == '.')
buffer += Next();
else
break;
} while (c != EofByte);
if (ReaderPatterns.IsDecimal().TryMatch(buffer, out Match match))
return MakeToken(TokenType.Decimal, match.Groups[1].Value);
if (ReaderPatterns.IsHexadecimal().TryMatch(buffer, out match))
return MakeToken(Hexadecimal, match.Groups[1].Value);
if (ReaderPatterns.IsInteger().TryMatch(buffer, out match))
return MakeToken(Integer, match.Groups[1].Value);
throw new ReaderException($"Unrecognizable number detected <{buffer}>", this);
}
if (IsLetter(c)) {
do {
c = Peek();
if (IsLetter(c))
buffer += Next();
else
break;
} while (c != EofByte);
return MakeIdentifier(buffer);
}
return MakeOperator(buffer);
}
private string ReadUntil(string buffer, char until)
{
char c;
do {
c = Next();
if (c == EofByte)
return buffer;
/*throw new ReaderException(
$"Unexpected end of feed while trying to read string <{buffer}>",
this);*/
buffer += c;
} while (c != until || Peek(-2) == '\\');
return buffer;
}
private Token MakeIdentifier(string buffer)
{
if (ReaderPatterns.Keywords.TryGetValue(buffer, out TokenType type))
return MakeToken(type, buffer);
return MakeToken(Identifier, buffer);
}
private bool Check(char c) => Peek(0) == c;
private bool Match(char c)
{
if (!Check(c))
return false;
Next();
return true;
}
private bool MatchSequence(char[] sequence, string buffer, TokenType type, out Token token)
{
token = Token.Void;
string _buffer = buffer;
for (int i = 0; i < sequence.Length; i++) {
char c = buffer.Length > i ? buffer[i] : Peek(i - buffer.Length);
if (sequence[i] != c)
return false; return false;
_buffer += c;
}
Next(); token = MakeToken(type, _buffer);
return true; return true;
} }
private bool MatchSequence(char[] sequence, string buffer, TokenType type, out Token token) private bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or '_';
{
token = Token.Void;
string _buffer = buffer;
for (int i = 0; i < sequence.Length; i++) {
char c = buffer.Length > i ? buffer[i] : Peek(i - buffer.Length);
if (sequence[i] != c)
return false;
_buffer += c;
}
token = MakeToken(type, _buffer); private bool IsDigit(char c) => c is >= '0' and <= '9';
return true;
}
private bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or '_'; private bool IsHex(char c) => c is >= 'a' and <= 'f' or >= 'A' and <= 'F' || IsDigit(c);
private bool IsDigit(char c) => c is >= '0' and <= '9'; private Token MakeOperator(string buffer)
{
return buffer[0] switch {
'[' => MakeToken(ArrayOpen, buffer),
']' => MakeToken(ArrayClose, buffer),
'{' => MakeToken(ContextOpen, buffer),
'}' => MakeToken(ContextClose, buffer),
'(' => MakeToken(GroupOpen, buffer),
')' => MakeToken(GroupClose, buffer),
'.' => Check('~') ?
MakeToken(This, buffer + Next()) :
MakeToken(Dot, buffer),
',' => MakeToken(Comma, buffer),
';' => MakeToken(Semicolon, buffer),
':' => Check(':') ?
MakeToken(Print, buffer + Next()) :
Check('[') ?
MakeToken(ListOpen, buffer + Next()) :
MakeToken(Colon, buffer),
'&' => Check('&') ?
MakeToken(And, buffer + Next()) :
MakeToken(BitwiseAnd, buffer),
'^' => Check('~') ?
MakeToken(Base, buffer + Next()) :
MakeToken(BitwiseXor, buffer),
'%' => Check('=') ?
MakeToken(ModuloEqual, buffer + Next()) :
MakeToken(Modulo, buffer),
'|' => Check('|') ?
MakeToken(Or, buffer + Next()) :
MakeToken(BitwiseOr, buffer),
'!' => Check('=') ?
MakeToken(BangEqual, buffer + Next()) :
MakeToken(Bang, buffer),
'+' => Check('+') ?
MakeToken(Increment, buffer + Next()) :
Check('=') ?
MakeToken(PlusEqual, buffer + Next()) :
MakeToken(Plus, buffer),
'-' => Check('-') ?
MakeToken(Decrement, buffer + Next()) :
Check('=') ?
MakeToken(MinusEqual, buffer + Next()) :
MakeToken(Minus, buffer),
'/' => Check('=') ?
MakeToken(SlashEqual, buffer + Next()) :
MakeToken(Slash, buffer),
'*' => Check('=') ?
MakeToken(StarEqual, buffer + Next()) :
Check('~') ?
MakeToken(Var, buffer + Next()) :
MakeToken(Star, buffer),
'=' => Check('=') ?
MakeToken(EqualEqual, buffer + Next()) :
MakeToken(Equal, buffer),
'<' => Check('~') ?
MakeToken(Equal, buffer + Next()) :
Check(':') ?
MakeToken(Return, buffer + Next()) :
Check('+') ?
MakeToken(AddItem, buffer + Next()) :
Check('<') ?
MakeToken(BitwiseLeft, buffer + Next()) :
Check('=') ?
MakeToken(LessEqual, buffer + Next()) :
Check('>') ?
MakeToken(TernaryElse, buffer + Next()) :
MakeToken(Less, buffer),
'>' => Check('>') ?
MakeToken(BitwiseRight, buffer + Next()) :
Check('=') ?
MakeToken(GreaterEqual, buffer + Next()) :
MakeToken(Greater, buffer),
'~' => Check('(') ?
MakeToken(Lambda, buffer) :
MakeToken(BitwiseNot, buffer),
'?' => Check('?') ?
MakeToken(DoubleQuestion, buffer + Next()) :
Check(':') ?
MakeToken(TypeOf, buffer + Next()) :
MakeToken(Question, buffer),
_ => MakeToken(Error, buffer) //throw new ReaderException($"Could not identify operator <{buffer}>", this)
};
}
private bool IsHex(char c) => c is >= 'a' and <= 'f' or >= 'A' and <= 'F' || IsDigit(c); private Token MakeToken(TokenType type, string buffer)
{
return new Token(
type,
buffer,
_startPosition,
new StreamSpan(_startIndex, _current - _startIndex));
}
private Token MakeOperator(string buffer) private void Continue()
{ {
return buffer[0] switch { if (Done)
'[' => MakeToken(ArrayOpen, buffer), return;
']' => MakeToken(ArrayClose, buffer), long size = _stream.Length - _stream.Position;
'{' => MakeToken(ContextOpen, buffer), if (size > ChunkSize)
'}' => MakeToken(ContextClose, buffer), size = ChunkSize;
'(' => MakeToken(GroupOpen, buffer), byte[] buffer = new byte[size];
')' => MakeToken(GroupClose, buffer), int read = _stream.Read(buffer, 0, (int)size);
'.' => Check('~') ? if (read < 0)
MakeToken(This, buffer + Next()) : return;
MakeToken(Dot, buffer), _buffer.AddRange(Encoding.UTF8.GetChars(buffer));
',' => MakeToken(Comma, buffer), }
';' => MakeToken(Semicolon, buffer),
':' => Check(':') ?
MakeToken(Print, buffer + Next()) :
Check('[') ?
MakeToken(ListOpen, buffer + Next()) :
MakeToken(Colon, buffer),
'&' => Check('&') ?
MakeToken(And, buffer + Next()) :
MakeToken(BitwiseAnd, buffer),
'^' => Check('~') ?
MakeToken(Base, buffer + Next()) :
MakeToken(BitwiseXor, buffer),
'%' => Check('=') ?
MakeToken(ModuloEqual, buffer + Next()) :
MakeToken(Modulo, buffer),
'|' => Check('|') ?
MakeToken(Or, buffer + Next()) :
MakeToken(BitwiseOr, buffer),
'!' => Check('=') ?
MakeToken(BangEqual, buffer + Next()) :
MakeToken(Bang, buffer),
'+' => Check('+') ?
MakeToken(Increment, buffer + Next()) :
Check('=') ?
MakeToken(PlusEqual, buffer + Next()) :
MakeToken(Plus, buffer),
'-' => Check('-') ?
MakeToken(Decrement, buffer + Next()) :
Check('=') ?
MakeToken(MinusEqual, buffer + Next()) :
MakeToken(Minus, buffer),
'/' => Check('=') ?
MakeToken(SlashEqual, buffer + Next()) :
MakeToken(Slash, buffer),
'*' => Check('=') ?
MakeToken(StarEqual, buffer + Next()) :
Check('~') ?
MakeToken(Var, buffer + Next()) :
MakeToken(Star, buffer),
'=' => Check('=') ?
MakeToken(EqualEqual, buffer + Next()) :
MakeToken(Equal, buffer),
'<' => Check('~') ?
MakeToken(Equal, buffer + Next()) :
Check(':') ?
MakeToken(Return, buffer + Next()) :
Check('+') ?
MakeToken(AddItem, buffer + Next()) :
Check('<') ?
MakeToken(BitwiseLeft, buffer + Next()) :
Check('=') ?
MakeToken(LessEqual, buffer + Next()) :
Check('>') ?
MakeToken(TernaryElse, buffer + Next()) :
MakeToken(Less, buffer),
'>' => Check('>') ?
MakeToken(BitwiseRight, buffer + Next()) :
Check('=') ?
MakeToken(GreaterEqual, buffer + Next()) :
MakeToken(Greater, buffer),
'~' => Check('(') ?
MakeToken(Lambda, buffer) :
MakeToken(BitwiseNot, buffer),
'?' => Check('?') ?
MakeToken(DoubleQuestion, buffer + Next()) :
Check(':') ?
MakeToken(TypeOf, buffer + Next()) :
MakeToken(Question, buffer),
_ => MakeToken(Error, buffer) //throw new ReaderException($"Could not identify operator <{buffer}>", this)
};
}
private Token MakeToken(TokenType type, string buffer) private readonly struct CaptureHandle(long start)
{ {
return new Token( public readonly long Start = start;
type, }
buffer,
_startPosition,
new StreamSpan(_startIndex, _current - _startIndex));
}
private void Continue()
{
if (Done)
return;
long size = _stream.Length - _stream.Position;
if (size > ChunkSize)
size = ChunkSize;
byte[] buffer = new byte[size];
int read = _stream.Read(buffer, 0, (int)size);
if (read < 0)
return;
_buffer.AddRange(Encoding.UTF8.GetChars(buffer));
}
private readonly struct CaptureHandle(long start)
{
public readonly long Start = start;
}
} }

View File

@ -7,36 +7,36 @@ public readonly record struct Token(
StreamSpan Span = default StreamSpan Span = default
) )
{ {
public static Token Void = new(TokenType.Void); public static Token Void = new(TokenType.Void);
public static Token Error(string? message = null) => new Token(TokenType.Error, message); public static Token Error(string? message = null) => new Token(TokenType.Error, message);
public static Token Eof(string? message = null) => new Token(TokenType.Eof, message); public static Token Eof(string? message = null) => new Token(TokenType.Eof, message);
public override string ToString() public override string ToString()
{ {
return $"[@{Position}] <{Type}> ({Value ?? "?"})"; return $"[@{Position}] <{Type}> ({Value ?? "?"})";
} }
public bool IsCompilationIrrelevant => Type == TokenType.NewLine || Type == TokenType.Comment || Type == TokenType.Whitespace; public bool IsCompilationIrrelevant => Type == TokenType.NewLine || Type == TokenType.Comment || Type == TokenType.Whitespace;
} }
public readonly record struct StreamSpan(long Start = -1, long Length = 0); public readonly record struct StreamSpan(long Start = -1, long Length = 0);
public readonly record struct TokenPosition(int Line = -1, int Column = -1) public readonly record struct TokenPosition(int Line = -1, int Column = -1)
{ {
public static readonly TokenPosition None = new(-1, -1); public static readonly TokenPosition None = new(-1, -1);
public bool IsNone => this == None; public bool IsNone => this == None;
public override string ToString() public override string ToString()
{ {
return $"{Line}:{Column}"; return $"{Line}:{Column}";
} }
public TokenPosition Previous => new(Line, Column - 1); public TokenPosition Previous => new(Line, Column - 1);
public static TokenPosition operator +(TokenPosition a, TokenPosition b) => public static TokenPosition operator +(TokenPosition a, TokenPosition b) =>
new(a.Line + b.Line, a.Column + b.Column); new(a.Line + b.Line, a.Column + b.Column);
public static TokenPosition operator -(TokenPosition a, TokenPosition b) => public static TokenPosition operator -(TokenPosition a, TokenPosition b) =>
new(a.Line - b.Line, a.Column - b.Column); new(a.Line - b.Line, a.Column - b.Column);
} }

View File

@ -108,29 +108,29 @@ public enum TokenType
public static class TokenTypeExtensions // iknow, i know, gotta make this flagged rather than THIS public static class TokenTypeExtensions // iknow, i know, gotta make this flagged rather than THIS
{ {
public static bool IsBracket(this TokenType type) public static bool IsBracket(this TokenType type)
=> type is GroupClose or GroupOpen or ContextClose or ContextOpen or ArrayClose or ArrayOpen or ListOpen or Lambda; => type is GroupClose or GroupOpen or ContextClose or ContextOpen or ArrayClose or ArrayOpen or ListOpen or Lambda;
public static bool IsString(this TokenType type) public static bool IsString(this TokenType type)
=> type is TokenType.String; => type is TokenType.String;
public static bool IsNumber(this TokenType type) public static bool IsNumber(this TokenType type)
=> type is Integer or Decimal; => type is Integer or Decimal;
public static bool IsIdentifier(this TokenType type) public static bool IsIdentifier(this TokenType type)
=> type is Identifier; => type is Identifier;
public static bool IsControl(this TokenType type) public static bool IsControl(this TokenType type)
=> type is If or Var or Class or Else or For or While or Do or Return or And or Or or TypeOf or Print; => type is If or Var or Class or Else or For or While or Do or Return or And or Or or TypeOf or Print;
public static bool IsBoolean(this TokenType type) public static bool IsBoolean(this TokenType type)
=> type is True or False; => type is True or False;
public static bool IsOperator(this TokenType type) public static bool IsOperator(this TokenType type)
=> !type.IsBracket() && => !type.IsBracket() &&
!type.IsString() && !type.IsString() &&
!type.IsNumber() && !type.IsNumber() &&
!type.IsIdentifier() && !type.IsIdentifier() &&
!type.IsControl() && !type.IsControl() &&
!type.IsBoolean(); !type.IsBoolean();
} }

View File

@ -4,19 +4,18 @@ namespace Qrakhen.Qamp.Core.Tokenization;
public class Tokens : IDebug<string> public class Tokens : IDebug<string>
{ {
private readonly List<Token> _tokens = new(); private readonly List<Token> _tokens = new();
public Token[] GetTokens() => _tokens.ToArray(); public Token[] GetTokens() => _tokens.ToArray();
public void Feed(Token token) => _tokens.Add(token); public void Feed(Token token) => _tokens.Add(token);
public string Debug(DebugLevel level = DebugLevel.None) public string Debug(DebugLevel level = DebugLevel.None)
{ {
string str = Debugger.GetContextString(this); string str = Debugger.GetContextString(this);
foreach (var token in _tokens) foreach (var token in _tokens) {
{ str += $"\n {token}";
str += $"\n {token}";
} }
return str; return str;
} }
} }

View File

@ -5,8 +5,8 @@ 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 override string ToString() => $"0x{Value:x8}"; public override string ToString() => $"0x{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);
} }

View File

@ -11,42 +11,42 @@ public class NativeExtension(
ExtensionDelegate callback, ExtensionDelegate callback,
string[] parameters) string[] parameters)
{ {
private static readonly Register<ValueType, Register<string, NativeExtension>> _register = new(); private static readonly Register<ValueType, Register<string, NativeExtension>> _register = new();
public readonly string Name = name; public readonly string Name = name;
public readonly ValueType TargetType = targetType; public readonly ValueType TargetType = targetType;
public readonly ExtensionDelegate Callback = callback; public readonly ExtensionDelegate Callback = callback;
public readonly string[] Parameters = parameters.ToArray(); public readonly string[] Parameters = parameters.ToArray();
public static NativeExtension? Get(ValueType targetType, string name) public static NativeExtension? Get(ValueType targetType, string name)
{ {
if (!_register.Has(targetType)) if (!_register.Has(targetType))
return null; return null;
if (!_register[targetType].Has(name)) if (!_register[targetType].Has(name))
return null; return null;
return _register[targetType][name]; return _register[targetType][name];
} }
public static void Register(ValueType targetType, public static void Register(ValueType targetType,
string name, string name,
ExtensionDelegate callback, ExtensionDelegate callback,
params string[] parameters) params string[] parameters)
{ {
if (!_register.Has(targetType)) if (!_register.Has(targetType))
_register.Add(targetType, new()); _register.Add(targetType, new());
if (_register[targetType].Has(name)) if (_register[targetType].Has(name))
throw new QampException($"Extension {name} already exists for type {targetType}."); throw new QampException($"Extension {name} already exists for type {targetType}.");
_register[targetType].Add(name, new NativeExtension(targetType, name, callback, parameters)); _register[targetType].Add(name, new NativeExtension(targetType, name, callback, parameters));
} }
static NativeExtension() static NativeExtension()
{ {
Register(ValueType.String, "Length", (v, p) => new Value((long)v.Ptr.As<Objects.String>()!.Value!.Length), []); Register(ValueType.String, "Length", (v, p) => new Value((long)v.Ptr.As<Objects.String>()!.Value!.Length), []);
Register(ValueType.String, "IndexOf", (v, p) => new Value((long)v.Ptr.As<Objects.String>()!.Value!.IndexOf(p[0].Ptr.As<Objects.String>()!.Value!)), [ "needle" ]); Register(ValueType.String, "IndexOf", (v, p) => new Value((long)v.Ptr.As<Objects.String>()!.Value!.IndexOf(p[0].Ptr.As<Objects.String>()!.Value!)), ["needle"]);
} }
public override string ToString() public override string ToString()
{ {
return $"{TargetType}.{Name}({string.Join(", ", Parameters)}) (NativeExtension)"; return $"{TargetType}.{Name}({string.Join(", ", Parameters)}) (NativeExtension)";
} }
} }

View File

@ -4,36 +4,36 @@ namespace Qrakhen.Qamp.Core.Values.Objects;
public class Array(IEnumerable<Value> data) : ItemProvider<int>(ValueType.Array) public class Array(IEnumerable<Value> data) : ItemProvider<int>(ValueType.Array)
{ {
public Value[] Data = [..data]; public Value[] Data = [..data];
protected override void InnerSet(int index, Value value) protected override void InnerSet(int index, Value value)
{ {
Data[index] = value; Data[index] = value;
} }
protected override Value InnerGet(int index) protected override Value InnerGet(int index)
{ {
return Data[index]; return Data[index];
} }
protected override void AssertValidAccesor(int index) protected override void AssertValidAccesor(int index)
{ {
if (index < 0 || index > Data.Length) if (index < 0 || index > Data.Length)
throw new ItemProviderException($"Can not index '{index}' of array, as the index is outside its boundaries.", this); throw new ItemProviderException($"Can not index '{index}' of array, as the index is outside its boundaries.", this);
} }
protected override int ExtractIndex(Value value) protected override int ExtractIndex(Value value)
{ {
int index; int index;
if (value.IsSigned) if (value.IsSigned)
index = (int)value.Signed; index = (int)value.Signed;
else if (value.IsUnsigned) else if (value.IsUnsigned)
index = (int)value.Unsigned; index = (int)value.Unsigned;
else else
throw new ItemProviderException($"Can not use {value} as an accessor, as it is not an integer.", this); throw new ItemProviderException($"Can not use {value} as an accessor, as it is not an integer.", this);
return index; return index;
} }
public override string ToString() => ToString(true); public override string ToString() => ToString(true);
public string ToString(bool detail) => detail ? $"[{string.Join(", ", Data)}]" : $"Array[{Data.Length}]"; public string ToString(bool detail) => detail ? $"[{string.Join(", ", Data)}]" : $"Array[{Data.Length}]";
} }

View File

@ -2,11 +2,11 @@
public class Class(string name) : Obj(ValueType.Class) public class Class(string name) : Obj(ValueType.Class)
{ {
public readonly string Name = name; public readonly string Name = name;
public Table Members = new(); public Table Members = new();
public override string ToString() public override string ToString()
{ {
return $"{Name} (Class)"; return $"{Name} (Class)";
} }
} }

View File

@ -4,8 +4,8 @@ namespace Qrakhen.Qamp.Core.Values.Objects;
public class Context(Function function, ValueType type = ValueType.Context) : Obj(type) public class Context(Function function, ValueType type = ValueType.Context) : Obj(type)
{ {
public Function Function = function; public Function Function = function;
public PushStack<Outer> Outers = new(); public PushStack<Outer> Outers = new();
public override string ToString() => $"{Function.Name}(){{}}"; public override string ToString() => $"{Function.Name}(){{}}";
} }

View File

@ -2,10 +2,10 @@
public class Extension(ValueType targetType, Function function) : Context(function) public class Extension(ValueType targetType, Function function) : Context(function)
{ {
public ValueType TargetType = targetType; public ValueType TargetType = targetType;
public override string ToString() public override string ToString()
{ {
return $"{TargetType}.{Function.Name}() (Extension)"; return $"{TargetType}.{Function.Name}() (Extension)";
} }
} }

View File

@ -5,16 +5,16 @@ namespace Qrakhen.Qamp.Core.Values.Objects;
public class Function(string name, Segment segment, int outerCount, int argumentCount) : Obj(ValueType.Function) public class Function(string name, Segment segment, int outerCount, int argumentCount) : Obj(ValueType.Function)
{ {
public readonly string Name = name; public readonly string Name = name;
public Segment Segment = segment; public Segment Segment = segment;
public readonly int OuterCount = outerCount; public readonly int OuterCount = outerCount;
public readonly int ArgumentCount = argumentCount; public readonly int ArgumentCount = argumentCount;
public override string ToString() => $"{Name}()"; public override string ToString() => $"{Name}()";
public byte[] Serialize(bool topLevel = false) public byte[] Serialize(bool topLevel = false)
{ {
List<byte> result = List<byte> result =
[ [
.. string.IsNullOrEmpty(Name) ? [] : Encoding.ASCII.GetBytes(Name), .. string.IsNullOrEmpty(Name) ? [] : Encoding.ASCII.GetBytes(Name),
0, 0,
@ -22,13 +22,13 @@ public class Function(string name, Segment segment, int outerCount, int argument
.. OuterCount.GetBytes(), .. OuterCount.GetBytes(),
.. Segment.Serialize() .. Segment.Serialize()
]; ];
if (topLevel) { if (topLevel) {
result.AddRange([0xFF, 0xF0]); result.AddRange([0xFF, 0xF0]);
result.AddRange(String.SerializeStrings()); result.AddRange(String.SerializeStrings());
result.AddRange([0x0F, 0xFF, 0xFF, 0xF1]); result.AddRange([0x0F, 0xFF, 0xFF, 0xF1]);
result.AddRange(Ptr.SerializeFunctions()); result.AddRange(Ptr.SerializeFunctions());
result.AddRange([0x0F, 0xFF]); result.AddRange([0x0F, 0xFF]);
} }
return result.ToArray(); return result.ToArray();
} }
} }

View File

@ -2,18 +2,18 @@
public class Instance(Class @class) : Obj(ValueType.Instance) public class Instance(Class @class) : Obj(ValueType.Instance)
{ {
public readonly Class Class = @class; public readonly Class Class = @class;
public Table Values = new(); public Table Values = new();
public override string ToString() public override string ToString()
{ {
string str = $"{Class.Name} {{\n"; string str = $"{Class.Name} {{\n";
foreach (var member in Class.Members) { foreach (var member in Class.Members) {
str += $" {member.Key}: {member.Value},\n"; str += $" {member.Key}: {member.Value},\n";
} }
foreach (var value in Values) { foreach (var value in Values) {
str += $" {value.Key}: {value.Value},\n"; str += $" {value.Key}: {value.Value},\n";
} }
return $"{str}}}"; return $"{str}}}";
} }
} }

View File

@ -2,25 +2,25 @@
public abstract class ItemProvider<T>(ValueType type) : Obj(type) public abstract class ItemProvider<T>(ValueType type) : Obj(type)
{ {
public void Set(Value index, Value value) public void Set(Value index, Value value)
{ {
T _index = ExtractIndex(index); T _index = ExtractIndex(index);
AssertValidAccesor(_index); AssertValidAccesor(_index);
InnerSet(_index, value); InnerSet(_index, value);
} }
public Value Get(Value index) public Value Get(Value index)
{ {
T _index = ExtractIndex(index); T _index = ExtractIndex(index);
AssertValidAccesor(_index); AssertValidAccesor(_index);
return InnerGet(_index); return InnerGet(_index);
} }
protected abstract Value InnerGet(T index); protected abstract Value InnerGet(T index);
protected abstract void InnerSet(T index, Value value); protected abstract void InnerSet(T index, Value value);
protected abstract void AssertValidAccesor(T index); protected abstract void AssertValidAccesor(T index);
protected abstract T ExtractIndex(Value value); protected abstract T ExtractIndex(Value value);
} }
public class ItemProviderException(string message, object context) : QampException(message, context); public class ItemProviderException(string message, object context) : QampException(message, context);

View File

@ -2,49 +2,49 @@
public class List(IEnumerable<Value> data) : ItemProvider<int>(ValueType.List) public class List(IEnumerable<Value> data) : ItemProvider<int>(ValueType.List)
{ {
public List<Value> Data = [..data]; public List<Value> Data = [..data];
public void Add(Value value) public void Add(Value value)
{ {
Data.Add(value); Data.Add(value);
} }
public void Remove(Value index) public void Remove(Value index)
{ {
int _index = ExtractIndex(index); int _index = ExtractIndex(index);
AssertValidAccesor(_index); AssertValidAccesor(_index);
Data.RemoveAt(_index); Data.RemoveAt(_index);
} }
protected override void InnerSet(int index, Value value) protected override void InnerSet(int index, Value value)
{ {
Data[index] = value; Data[index] = value;
} }
protected override Value InnerGet(int index) protected override Value InnerGet(int index)
{ {
return Data[index]; return Data[index];
} }
protected override void AssertValidAccesor(int index) protected override void AssertValidAccesor(int index)
{ {
if (index < 0 || index > Data.Count) if (index < 0 || index > Data.Count)
throw new ItemProviderException($"Can not index '{index}' of list, as the index is outside its boundaries.", this); throw new ItemProviderException($"Can not index '{index}' of list, as the index is outside its boundaries.", this);
} }
protected override int ExtractIndex(Value value) protected override int ExtractIndex(Value value)
{ {
int index; int index;
if (value.IsSigned) if (value.IsSigned)
index = (int)value.Signed; index = (int)value.Signed;
else if (value.IsUnsigned) else if (value.IsUnsigned)
index = (int)value.Unsigned; index = (int)value.Unsigned;
else else
throw new ItemProviderException($"Can not use {value} as an accessor, as it is not an integer.", this); throw new ItemProviderException($"Can not use {value} as an accessor, as it is not an integer.", this);
return index; return index;
} }
public override string ToString() => ToString(true); public override string ToString() => ToString(true);
public string ToString(bool detail) => detail ? $":[{string.Join(", ", Data)}]" : $"List[{Data.Count}]"; public string ToString(bool detail) => detail ? $":[{string.Join(", ", Data)}]" : $"List[{Data.Count}]";
} }

View File

@ -2,5 +2,5 @@
public class Method(Function function, Value receiver) : Context(function, ValueType.Method) public class Method(Function function, Value receiver) : Context(function, ValueType.Method)
{ {
public Value Receiver = receiver; public Value Receiver = receiver;
} }

View File

@ -2,8 +2,8 @@
public class Native(string name) : Class(name) public class Native(string name) : Class(name)
{ {
public override string ToString() public override string ToString()
{ {
return $"{Name} (Native)"; return $"{Name} (Native)";
} }
} }

View File

@ -2,17 +2,17 @@
public class Obj(ValueType type) : IValue public class Obj(ValueType type) : IValue
{ {
public bool __gcMarked { get; private set; } public bool __gcMarked { get; private set; }
public int __gcCount { get; private set; } = 1; public int __gcCount { get; private set; } = 1;
public readonly ValueType Type = type; public readonly ValueType Type = type;
public ValueType ValueType => Type; public ValueType ValueType => Type;
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>";
} }

View File

@ -9,32 +9,31 @@ using T = ValueType;
/// </summary> /// </summary>
public class Outer(Pointer<Value> target) : Obj(T.Outer) public class Outer(Pointer<Value> target) : Obj(T.Outer)
{ {
private Value _stored = Value.Void; private Value _stored = Value.Void;
public Pointer<Value> Target = target; public Pointer<Value> Target = target;
public bool IsClosed { get; private set; } public bool IsClosed { get; private set; }
/// <summary> /// <summary>
/// Dynamic getter & setter that returns or writes to either the target value directly, /// Dynamic getter & setter that returns or writes to either the target value directly,
/// or the stored value if this <see cref="Outer"/> has been closed. /// or the stored value if this <see cref="Outer"/> has been closed.
/// </summary> /// </summary>
public Value Value public Value Value {
{ get => IsClosed ? _stored : Target!.Get();
get => IsClosed ? _stored : Target!.Get(); set {
set { if (IsClosed)
if (IsClosed) _stored = value;
_stored = value; else
else Target.Set(value);
Target.Set(value); }
} }
}
public Value Close() public Value Close()
{ {
if (IsClosed) if (IsClosed)
throw new Exception($"meh, something not right here (cant close already closed outer)"); throw new Exception($"meh, something not right here (cant close already closed outer)");
_stored = Target!.Get(); _stored = Target!.Get();
IsClosed = true; IsClosed = true;
return _stored; return _stored;
} }
} }

View File

@ -5,46 +5,46 @@ namespace Qrakhen.Qamp.Core.Values.Objects;
public class String(string? value) : Obj(ValueType.String) public class String(string? value) : Obj(ValueType.String)
{ {
private static readonly Register<uint, String> _strings = new(); private static readonly Register<uint, String> _strings = new();
public string? Value = value; public string? Value = value;
public override string ToString() => Value ?? "null"; public override string ToString() => Value ?? "null";
public uint GetHash() public uint GetHash()
{ {
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
return 0; return 0;
return Value.GetHash(); return Value.GetHash();
} }
public static Value Make(string? value) public static Value Make(string? value)
{ {
String? str; String? str;
var hash = value?.GetHash() ?? 0; var hash = value?.GetHash() ?? 0;
if (!_strings.TryGet(hash, out str)) { if (!_strings.TryGet(hash, out str)) {
str = new String(value); str = new String(value);
_strings.Add(hash, str); _strings.Add(hash, str);
} }
return Create(str); return Create(str);
} }
public static byte[] SerializeStrings() public static byte[] SerializeStrings()
{ {
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.__gcMarked) if (str == null || str.__gcMarked)
continue; continue;
byte[] bytes; byte[] bytes;
bytes = Encoding.ASCII.GetBytes(str.Value!); bytes = Encoding.ASCII.GetBytes(str.Value!);
result.AddRange(pair.Key.GetBytes()); result.AddRange(pair.Key.GetBytes());
result.AddRange(bytes.Length.GetBytes()); result.AddRange(bytes.Length.GetBytes());
result.AddRange(bytes); result.AddRange(bytes);
result.Add(0); result.Add(0);
} }
return result.ToArray(); return result.ToArray();
} }
} }

View File

@ -2,36 +2,36 @@
public class Structure(IEnumerable<KeyValuePair<string, Value>> data) : ItemProvider<string>(ValueType.Structure) public class Structure(IEnumerable<KeyValuePair<string, Value>> data) : ItemProvider<string>(ValueType.Structure)
{ {
public bool Sealed; public bool Sealed;
public Dictionary<string, Value> Data = new(data); public Dictionary<string, Value> Data = new(data);
protected override void InnerSet(string index, Value value) protected override void InnerSet(string index, Value value)
{ {
Data[index] = value; Data[index] = value;
} }
protected override Value InnerGet(string index) protected override Value InnerGet(string index)
{ {
return Data[index]; return Data[index];
} }
protected override void AssertValidAccesor(string index) protected override void AssertValidAccesor(string index)
{ {
if (Sealed && !Data.ContainsKey(index)) if (Sealed && !Data.ContainsKey(index))
throw new ItemProviderException($"Can not set non-existent key '{index}' of sealed structure.", this); throw new ItemProviderException($"Can not set non-existent key '{index}' of sealed structure.", this);
} }
protected override string ExtractIndex(Value value) protected override string ExtractIndex(Value value)
{ {
string index; string index;
if (value.IsString) if (value.IsString)
index = value.Ptr.As<String>()!.Value!; index = value.Ptr.As<String>()!.Value!;
else else
throw new ItemProviderException($"Can not use {value} as an accessor, as it is not a string.", this); throw new ItemProviderException($"Can not use {value} as an accessor, as it is not a string.", this);
return index; return index;
} }
public override string ToString() => ToString(true); public override string ToString() => ToString(true);
public string ToString(bool detail) => detail ? $"{{{string.Join("\n,", Data)}}}" : $"Structure{{{Data.Count}}}"; public string ToString(bool detail) => detail ? $"{{{string.Join("\n,", Data)}}}" : $"Structure{{{Data.Count}}}";
} }

View File

@ -7,66 +7,66 @@ namespace Qrakhen.Qamp.Core.Values;
[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(); private static readonly PushStack<Obj> _register = new();
[Serialized] public readonly Address Address; [Serialized] public readonly Address Address;
//private readonly IntPtr _pointer; //private readonly IntPtr _pointer;
//public IntPtr Pointer => _pointer; //public IntPtr Pointer => _pointer;
public Obj? Value { public Obj? Value {
get { get {
if (_register.TryGet(Address, out Obj obj)) if (_register.TryGet(Address, out Obj obj))
return obj; return obj;
return null; return null;
//if (_pointer == IntPtr.Zero) //if (_pointer == IntPtr.Zero)
// throw new ObjectDisposedException(nameof(Ptr)); // throw new ObjectDisposedException(nameof(Ptr));
//return (Obj)GCHandle.FromIntPtr(_pointer).Target!; //return (Obj)GCHandle.FromIntPtr(_pointer).Target!;
} }
} }
private Ptr(Address address) private Ptr(Address address)
{ {
//_pointer = GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Normal)); //_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(_register.Add(obj)); 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
{ {
T? value = Value as T; T? value = Value as T;
if (value == null && throwWhenNull) if (value == null && throwWhenNull)
throw new RuntimeException($"Could not convert Object {Value} to target type {typeof(T)}.", Value); throw new RuntimeException($"Could not convert Object {Value} to target type {typeof(T)}.", Value);
return value; return value;
} }
public override string ToString() => $"0x{Value}"; public override string ToString() => $"0x{Value}";
public static byte[] SerializeFunctions() public static byte[] SerializeFunctions()
{ {
List<byte> result = new(); List<byte> result = new();
for (int i = 0; i < _register.Count; i++) { for (int i = 0; i < _register.Count; i++) {
Obj obj = _register.Get(i); Obj obj = _register.Get(i);
if (obj == null || obj.__gcMarked) if (obj == null || obj.__gcMarked)
continue; continue;
byte[] bytes; byte[] bytes;
if (obj is Function function) { if (obj is Function function) {
bytes = function.Serialize(); bytes = function.Serialize();
} else if (obj is Context context) { } else if (obj is Context context) {
bytes = context.Function.Serialize(); bytes = context.Function.Serialize();
} else { } else {
continue; continue;
} }
result.AddRange(i.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);
} }
return result.ToArray(); return result.ToArray();
} }
} }

View File

@ -5,7 +5,7 @@ namespace Qrakhen.Qamp.Core.Values;
[StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))] [StructLayout(LayoutKind.Sequential, Size = sizeof(ulong))]
public readonly unsafe struct Ref(Value* value) public readonly unsafe struct Ref(Value* value)
{ {
[Serialized] public readonly Value* Value = value; [Serialized] public readonly Value* Value = value;
public override string ToString() => $"0x{*Value}"; public override string ToString() => $"0x{*Value}";
} }

View File

@ -9,125 +9,125 @@ namespace Qrakhen.Qamp.Core.Values;
public interface IValue public interface IValue
{ {
ValueType ValueType { get; } ValueType ValueType { get; }
} }
[StructLayout(LayoutKind.Explicit, Size = 0x10)] [StructLayout(LayoutKind.Explicit, Size = 0x10)]
public readonly struct Value : IValue, ISerialize<Value>, IDebug<string> public readonly struct Value : IValue, 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);
public static readonly Value False = new Value(false); public static readonly Value False = new Value(false);
[FieldOffset(0x00)] [Serialized] public readonly ulong Unsigned; [FieldOffset(0x00)] [Serialized] public readonly ulong Unsigned;
[FieldOffset(0x00)] [Serialized] public readonly long Signed; [FieldOffset(0x00)] [Serialized] public readonly long Signed;
[FieldOffset(0x00)] [Serialized] public readonly bool Bool; [FieldOffset(0x00)] [Serialized] public readonly bool Bool;
[FieldOffset(0x00)] [Serialized] public readonly char Char; [FieldOffset(0x00)] [Serialized] public readonly char Char;
[FieldOffset(0x00)] [Serialized] public readonly double Decimal; [FieldOffset(0x00)] [Serialized] public readonly double Decimal;
[FieldOffset(0x00)] [Serialized] public readonly Address Address; [FieldOffset(0x00)] [Serialized] public readonly Address Address;
[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;
[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
private Value(T type) => Type = type; private Value(T type) => Type = type;
public Value() : this(T.Void) { } public Value() : this(T.Void) { }
public Value(ulong unsigned) : this(T.Unsigned) => Unsigned = unsigned; public Value(ulong unsigned) : this(T.Unsigned) => Unsigned = unsigned;
public Value(long signed) : this(T.Signed) => Signed = signed; public Value(long signed) : this(T.Signed) => Signed = signed;
public Value(char character) : this(T.Char) => Char = character; public Value(char character) : this(T.Char) => Char = character;
public Value(bool boolean) : this(T.Bool) => Bool = boolean; public Value(bool boolean) : this(T.Bool) => Bool = boolean;
public Value(double @decimal) : this(T.Decimal) => Decimal = @decimal; public Value(double @decimal) : this(T.Decimal) => Decimal = @decimal;
public Value(Address address) : this(T.Address) => Address = address; public Value(Address address) : this(T.Address) => Address = address;
public Value(Ptr ptr) : this(ptr.Value?.Type ?? T.Pointer) => Ptr = ptr; public Value(Ptr ptr) : this(ptr.Value?.Type ?? T.Pointer) => Ptr = ptr;
public Value(Ref @ref) : this(T.Reference) => Ref = @ref; public Value(Ref @ref) : this(T.Reference) => Ref = @ref;
public dynamic? Dynamic => AsDynamic(); public dynamic? Dynamic => AsDynamic();
public T ValueType => Type; public T ValueType => Type;
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 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);
public bool IsObj => Is(T.Object, false); public bool IsObj => Is(T.Object, false);
public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0; public bool IsFalsy => IsBool ? Bool == false : Unsigned == 0;
public bool Is(T type, bool exact = true) public bool Is(T type, bool exact = true)
{ {
return exact ? (Type & type) == type : (Type & type) > 0; return exact ? (Type & type) == type : (Type & type) > 0;
} }
private unsafe dynamic? AsDynamic(bool throwWhenNull = true) private unsafe dynamic? AsDynamic(bool throwWhenNull = true)
{ {
if (IsString) if (IsString)
return Ptr.As<String>()!.Value; return Ptr.As<String>()!.Value;
if (IsObj) if (IsObj)
return Ptr.Value; return Ptr.Value;
return Type switch { return Type switch {
T.Void => throwWhenNull ? throw new NullReferenceException() : null, T.Void => throwWhenNull ? throw new NullReferenceException() : null,
T.Unsigned => Unsigned, T.Unsigned => Unsigned,
T.Signed => Signed, T.Signed => Signed,
T.Decimal => Decimal, T.Decimal => Decimal,
T.Bool => Bool, T.Bool => Bool,
T.Address => Address, T.Address => Address,
T.Reference => (Value)(*Ref.Value), T.Reference => (Value)(*Ref.Value),
_ => throwWhenNull ? throw new NullReferenceException() : null, _ => throwWhenNull ? throw new NullReferenceException() : null,
}; };
} }
public static Value FromAny(object? any = null) public static Value FromAny(object? any = null)
{ {
if (any == null) if (any == null)
return new Value(); return new Value();
Type type = any.GetType(); Type type = any.GetType();
return new Value(); return new Value();
} }
public void __gcMark() public void __gcMark()
{ {
if (IsObj && Ptr.Value != null) { } if (IsObj && Ptr.Value != null) { }
} }
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
return obj switch { return obj switch {
Value value when Type == value.Type => Unsigned == value.Unsigned, Value value when Type == value.Type => Unsigned == value.Unsigned,
Value value => Dynamic?.Equals(value.Dynamic), Value value => Dynamic?.Equals(value.Dynamic),
_ => false _ => false
}; };
} }
public override int GetHashCode() => Unsigned.GetHashCode(); public override int GetHashCode() => Unsigned.GetHashCode();
public override string ToString() => ToString(false); public override string ToString() => ToString(false);
public string ToString(bool includeType) public string ToString(bool includeType)
{ {
if (!includeType) if (!includeType)
return $"{AsDynamic(false)}"; return $"{AsDynamic(false)}";
string type = Type.ToString(); string type = Type.ToString();
if (Type.HasFlag(T.Pointer)) if (Type.HasFlag(T.Pointer))
type = Ptr.Value?.Type.ToString() ?? "NullPtr"; type = Ptr.Value?.Type.ToString() ?? "NullPtr";
return $"<{type}, {AsDynamic(false) ?? "null"}>"; return $"<{type}, {AsDynamic(false) ?? "null"}>";
} }
public string Debug(DebugLevel level = DebugLevel.None) public string Debug(DebugLevel level = DebugLevel.None)
{ {
if (level < DebugLevel.Strong) if (level < DebugLevel.Strong)
return Debugger.GetContextString(this); return Debugger.GetContextString(this);
string str = $"{Debugger.GetContextString(this)}\n"; string str = $"{Debugger.GetContextString(this)}\n";
@ -145,22 +145,22 @@ public readonly struct Value : IValue, ISerialize<Value>, IDebug<string>
]; ];
return str + string.Join("\n - ", lines); return str + string.Join("\n - ", lines);
} }
public byte[] Serialize() public byte[] Serialize()
{ {
List<byte> result = [.. ((ushort)Type).GetBytes() ]; List<byte> result = [.. ((ushort)Type).GetBytes() ];
byte[] bytes = Signed.GetBytes(); byte[] bytes = Signed.GetBytes();
result.Add((byte)bytes.Length); result.Add((byte)bytes.Length);
result.AddRange(bytes); result.AddRange(bytes);
result.Add(0x7F); result.Add(0x7F);
return result.ToArray(); return result.ToArray();
} }
public static Value Deserialize(byte[] data) public static Value Deserialize(byte[] data)
{ {
return Void; return Void;
} }
} }
public class SerializedAttribute : Attribute; public class SerializedAttribute : Attribute;

View File

@ -7,39 +7,39 @@
[Flags] [Flags]
public enum ValueType public enum ValueType
{ {
Undefined = -1, Undefined = -1,
Void = 0x0000, Void = 0x0000,
Unsigned = 0x0001, Unsigned = 0x0001,
Signed = 0x0002, Signed = 0x0002,
Decimal = 0x0004, Decimal = 0x0004,
Char = 0x0008, Char = 0x0008,
Bool = 0x0010, Bool = 0x0010,
Number = Unsigned | Signed | Decimal, Number = Unsigned | Signed | Decimal,
Integer = Unsigned | Signed | Char, Integer = Unsigned | Signed | Char,
Primitive = Integer | Decimal | Bool, Primitive = Integer | Decimal | Bool,
Address = 0x0020, // coded with : symbol Address = 0x0020, // coded with : symbol
// classes etc. here? // classes etc. here?
Reference = 0x1000, Reference = 0x1000,
Pointer = 0x2000, Pointer = 0x2000,
Object = Pointer | 0x4000, Object = Pointer | 0x4000,
Native = Object | 0x0001, Native = Object | 0x0001,
String = Object | 0x0002, String = Object | 0x0002,
Function = Object | 0x0020, Function = Object | 0x0020,
Context = Object | 0x0040, Context = Object | 0x0040,
Instance = Object | 0x0080, Instance = Object | 0x0080,
Class = Object | 0x0100, Class = Object | 0x0100,
Method = Class | Function, Method = Class | Function,
Outer = Object | 0x0200, Outer = Object | 0x0200,
ItemProvider = Object | 0x8000, // accessible with [n] or :n (getters / setters) ItemProvider = Object | 0x8000, // accessible with [n] or :n (getters / setters)
Array = ItemProvider | 0x0001, Array = ItemProvider | 0x0001,
List = ItemProvider | 0x0002, List = ItemProvider | 0x0002,
Structure = ItemProvider | 0x0004, Structure = ItemProvider | 0x0004,
Module = 0xFFFF Module = 0xFFFF
} }

View File

@ -8,13 +8,13 @@ namespace Qrakhen.Qamp.Editor;
/// </summary> /// </summary>
public partial class App : Application public partial class App : Application
{ {
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
{ {
MainWindow window = new MainWindow() { MainWindow window = new MainWindow() {
DataContext = new ViewModel.EditorFrameViewModel() DataContext = new ViewModel.EditorFrameViewModel()
}; };
window.Show(); window.Show();
} }
} }

View File

@ -4,27 +4,27 @@ namespace Qrakhen.Qamp.Editor.Commands;
public class RelayCommand : ICommand public class RelayCommand : ICommand
{ {
private readonly Action<object?> _execute; private readonly Action<object?> _execute;
private readonly Func<object?, bool> _canExecute; private readonly Func<object?, bool> _canExecute;
public RelayCommand(Action<object?> execute) : this(execute, null) { } public RelayCommand(Action<object?> execute) : this(execute, null) { }
public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute) public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute)
{ {
_execute = execute ?? throw new ArgumentNullException(nameof(execute)); _execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute ?? (p => true); _canExecute = canExecute ?? (p => true);
} }
public RelayCommand(Action execute) : this(p => execute()) { } public RelayCommand(Action execute) : this(p => execute()) { }
public RelayCommand(Action execute, Func<bool> canExecute) : this(p => execute(), p => canExecute()) { } public RelayCommand(Action execute, Func<bool> canExecute) : this(p => execute(), p => canExecute()) { }
public event EventHandler? CanExecuteChanged { public event EventHandler? CanExecuteChanged {
add => CommandManager.RequerySuggested += value; add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value; remove => CommandManager.RequerySuggested -= value;
} }
public bool CanExecute(object? parameter) => _canExecute(parameter); public bool CanExecute(object? parameter) => _canExecute(parameter);
public void Execute(object? parameter) => _execute(parameter); public void Execute(object? parameter) => _execute(parameter);
} }

View File

@ -18,42 +18,42 @@ public class Caret : Adorner
private DispatcherTimer _blinkTimer; private DispatcherTimer _blinkTimer;
private DispatcherTimer _moveTimer; private DispatcherTimer _moveTimer;
public Caret(UIElement adornedElement, Brush brush, double height = 18, double thickness = 1.72) : base(adornedElement) public Caret(UIElement adornedElement, Brush brush, double height = 18, double thickness = 1.72) : base(adornedElement)
{ {
IsHitTestVisible = false; IsHitTestVisible = false;
_enabled = true; _enabled = true;
_height = height; _height = height;
_brush = brush; _brush = brush;
_pen = new Pen(brush, 1.92); _pen = new Pen(brush, 1.92);
_blinkTimer = new DispatcherTimer(); _blinkTimer = new DispatcherTimer();
_blinkTimer.Interval = TimeSpan.FromMilliseconds(324); _blinkTimer.Interval = TimeSpan.FromMilliseconds(324);
_blinkTimer.Tick += new EventHandler((s, e) => { _blinkTimer.Tick += new EventHandler((s, e) => {
Toggle(_blink = !_blink); Toggle(_blink = !_blink);
}); });
_blinkTimer.Start(); _blinkTimer.Start();
_moveTimer = new DispatcherTimer(); _moveTimer = new DispatcherTimer();
_moveTimer.Interval = TimeSpan.FromMilliseconds(12); _moveTimer.Interval = TimeSpan.FromMilliseconds(12);
_moveTimer.Tick += new EventHandler((s, e) => { _moveTimer.Tick += new EventHandler((s, e) => {
var delta = _targetPosition - _cursorPosition; var delta = _targetPosition - _cursorPosition;
if (delta.Length > 2.4) if (delta.Length > 2.4)
_cursorPosition += delta * .84; _cursorPosition += delta * .84;
else { else {
_cursorPosition = _targetPosition; _cursorPosition = _targetPosition;
_moveTimer.Stop(); _moveTimer.Stop();
_blinkTimer.Start(); _blinkTimer.Start();
} }
InvalidateVisual(); InvalidateVisual();
}); });
} }
public void Toggle(bool hide) public void Toggle(bool hide)
{ {
_pen.Brush = hide ? Brushes.Transparent : _brush; _pen.Brush = hide ? Brushes.Transparent : _brush;
InvalidateVisual(); InvalidateVisual();
} }
public void Disable() public void Disable()
{ {
@ -77,10 +77,10 @@ public class Caret : Adorner
_blinkTimer.Start(); _blinkTimer.Start();
} }
protected override void OnRender(DrawingContext drawingContext) protected override void OnRender(DrawingContext drawingContext)
{ {
Point startPoint = new(_cursorPosition.X, _cursorPosition.Y); Point startPoint = new(_cursorPosition.X, _cursorPosition.Y);
Point endPoint = new(_cursorPosition.X, _cursorPosition.Y + _height); Point endPoint = new(_cursorPosition.X, _cursorPosition.Y + _height);
drawingContext.DrawLine(_pen, startPoint, endPoint); drawingContext.DrawLine(_pen, startPoint, endPoint);
} }
} }

View File

@ -143,8 +143,7 @@ public partial class EditorFrame : UserControl
AutoScroll(offset); AutoScroll(offset);
else else
HideCaret(); HideCaret();
} } else
else
MoveCaret(position); MoveCaret(position);
if (!CurrentSelection.IsVoid) { if (!CurrentSelection.IsVoid) {

View File

@ -22,38 +22,38 @@ public interface IInputHandler
public enum ActionId public enum ActionId
{ {
None, None,
Undo, Undo,
Redo, Redo,
Copy, Copy,
Paste, Paste,
Delete, Delete,
MoveUp, MoveUp,
MoveDown, MoveDown,
MoveRight, MoveRight,
MoveLeft, MoveLeft,
Submit, Submit,
Close Close
} }
public delegate void KeyEventListener(); public delegate void KeyEventListener();
public class InputService : IInputHandler public class InputService : IInputHandler
{ {
public static bool LeftCtrlHeld => Keyboard.IsKeyDown(Key.LeftCtrl); public static bool LeftCtrlHeld => Keyboard.IsKeyDown(Key.LeftCtrl);
public static bool LeftShiftHeld => Keyboard.IsKeyDown(Key.LeftShift); public static bool LeftShiftHeld => Keyboard.IsKeyDown(Key.LeftShift);
public static bool LeftAltHeld => Keyboard.IsKeyDown(Key.LeftAlt); public static bool LeftAltHeld => Keyboard.IsKeyDown(Key.LeftAlt);
public static bool RightCtrlHeld => Keyboard.IsKeyDown(Key.RightCtrl); public static bool RightCtrlHeld => Keyboard.IsKeyDown(Key.RightCtrl);
public static bool RightShiftHeld => Keyboard.IsKeyDown(Key.RightShift); public static bool RightShiftHeld => Keyboard.IsKeyDown(Key.RightShift);
public static bool RightAltHeld => Keyboard.IsKeyDown(Key.RightAlt); public static bool RightAltHeld => Keyboard.IsKeyDown(Key.RightAlt);
public static bool CtrlHeld => LeftCtrlHeld || RightCtrlHeld; public static bool CtrlHeld => LeftCtrlHeld || RightCtrlHeld;
public static bool ShiftHeld => LeftShiftHeld || RightShiftHeld; public static bool ShiftHeld => LeftShiftHeld || RightShiftHeld;
public static bool AltHeld => LeftAltHeld || RightAltHeld; public static bool AltHeld => LeftAltHeld || RightAltHeld;
public static void AddKeyListener(Key key, params Key[] modifiers) public static void AddKeyListener(Key key, params Key[] modifiers)
{ {
} }
} }
public static class KeyHelper public static class KeyHelper
@ -74,51 +74,51 @@ public static class KeyHelper
_chars[Key.Q] = ['q', 'Q', '@']; _chars[Key.Q] = ['q', 'Q', '@'];
Add(Key.None); Add(Key.None);
Add(Key.Cancel); Add(Key.Cancel);
Add(Key.Back); Add(Key.Back);
Add(Key.Tab, '\t'); Add(Key.Tab, '\t');
Add(Key.LineFeed); Add(Key.LineFeed);
Add(Key.Clear); Add(Key.Clear);
Add(Key.Enter, '\n'); Add(Key.Enter, '\n');
Add(Key.Return); Add(Key.Return);
Add(Key.Pause); Add(Key.Pause);
Add(Key.Capital); Add(Key.Capital);
Add(Key.CapsLock); Add(Key.CapsLock);
Add(Key.HangulMode); Add(Key.HangulMode);
Add(Key.KanaMode); Add(Key.KanaMode);
Add(Key.JunjaMode); Add(Key.JunjaMode);
Add(Key.FinalMode); Add(Key.FinalMode);
Add(Key.HanjaMode); Add(Key.HanjaMode);
Add(Key.KanjiMode); Add(Key.KanjiMode);
Add(Key.Escape); Add(Key.Escape);
Add(Key.ImeConvert); Add(Key.ImeConvert);
Add(Key.ImeNonConvert); Add(Key.ImeNonConvert);
Add(Key.ImeAccept); Add(Key.ImeAccept);
Add(Key.ImeModeChange); Add(Key.ImeModeChange);
Add(Key.Space, ' '); Add(Key.Space, ' ');
Add(Key.PageUp); Add(Key.PageUp);
Add(Key.Prior); Add(Key.Prior);
Add(Key.Next); Add(Key.Next);
Add(Key.PageDown); Add(Key.PageDown);
Add(Key.End); Add(Key.End);
Add(Key.Home); Add(Key.Home);
Add(Key.Left); Add(Key.Left);
Add(Key.Up); Add(Key.Up);
Add(Key.Right); Add(Key.Right);
Add(Key.Down); Add(Key.Down);
Add(Key.Select); Add(Key.Select);
Add(Key.Print); Add(Key.Print);
Add(Key.Execute); Add(Key.Execute);
Add(Key.PrintScreen); Add(Key.PrintScreen);
Add(Key.Snapshot); Add(Key.Snapshot);
Add(Key.Insert); Add(Key.Insert);
Add(Key.Delete); Add(Key.Delete);
Add(Key.Help); Add(Key.Help);
Add(Key.LWin); Add(Key.LWin);
Add(Key.RWin); Add(Key.RWin);
Add(Key.Apps); Add(Key.Apps);
Add(Key.Sleep); Add(Key.Sleep);
Add(Key.D0, '0', '=', '}'); Add(Key.D0, '0', '=', '}');
Add(Key.D1, '1', '!'); Add(Key.D1, '1', '!');
Add(Key.D2, '2', '"', '²'); Add(Key.D2, '2', '"', '²');

View File

@ -4,8 +4,8 @@ namespace Qrakhen.Qamp.Editor;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
} }
} }

View File

@ -4,18 +4,18 @@ public readonly record struct BufferPosition(int Line = 0, int Column = 0)
{ {
public bool IsInitial => Line == 0 && Column == 0; public bool IsInitial => Line == 0 && Column == 0;
public static readonly BufferPosition Initial = new(0, 0); public static readonly BufferPosition Initial = new(0, 0);
public static readonly BufferPosition Left = new(0, -1); public static readonly BufferPosition Left = new(0, -1);
public static readonly BufferPosition Right = new(0, 1); public static readonly BufferPosition Right = new(0, 1);
public static readonly BufferPosition Up = new(-1, 0); public static readonly BufferPosition Up = new(-1, 0);
public static readonly BufferPosition Down = new(1, 0); public static readonly BufferPosition Down = new(1, 0);
public static BufferPosition operator +(BufferPosition left, BufferPosition right) public static BufferPosition operator +(BufferPosition left, BufferPosition right)
=> new(left.Line + right.Line, left.Column + right.Column); => new(left.Line + right.Line, left.Column + right.Column);
public static BufferPosition operator -(BufferPosition left, BufferPosition right) public static BufferPosition operator -(BufferPosition left, BufferPosition right)
=> new(left.Line - right.Line, left.Column - right.Column); => new(left.Line - right.Line, left.Column - right.Column);
public static BufferPosition operator *(BufferPosition left, int factor) public static BufferPosition operator *(BufferPosition left, int factor)
=> new(left.Line * factor, left.Column * factor); => new(left.Line * factor, left.Column * factor);
} }

View File

@ -6,6 +6,13 @@ public struct BufferRegion(BufferPosition from, BufferPosition to)
public bool IsVoid => (From - To).IsInitial; public bool IsVoid => (From - To).IsInitial;
public BufferPosition From = from; public readonly BufferPosition From = from;
public BufferPosition To = to; public BufferPosition To = to;
public static BufferRegion operator +(BufferRegion left, BufferPosition right)
=> new(left.From, left.To + right);
public static BufferRegion operator -(BufferRegion left, BufferPosition right)
=> new(left.From, left.To - right);
} }

View File

@ -5,25 +5,25 @@ namespace Qrakhen.Qamp.Editor;
public static class TextHelper // temp public static class TextHelper // temp
{ {
public static double Size { get; set; } = 14; public static double Size { get; set; } = 14;
public static double LineHeight { get; set; } = 18; public static double LineHeight { get; set; } = 18;
public static Brush DefaultColor { get; set; } = Brushes.AntiqueWhite; public static Brush DefaultColor { get; set; } = Brushes.AntiqueWhite;
public static Typeface Typeface { get; set; } = new( public static Typeface Typeface { get; set; } = new(
new FontFamily("Consolas"), new FontFamily("Consolas"),
FontStyles.Normal, FontStyles.Normal,
FontWeights.Normal, FontWeights.Normal,
FontStretches.Normal); FontStretches.Normal);
public static FormattedText GetText(string text, double size, Brush? brush = null, double dpiScale = 1f) public static FormattedText GetText(string text, double size, Brush? brush = null, double dpiScale = 1f)
{ {
return new FormattedText( return new FormattedText(
text, text,
System.Globalization.CultureInfo.CurrentCulture, System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight, FlowDirection.LeftToRight,
Typeface, Typeface,
size, size,
brush ?? DefaultColor, brush ?? DefaultColor,
dpiScale dpiScale
); );
} }
} }

View File

@ -3,6 +3,7 @@ using Qrakhen.Qamp.Editor.Primitives;
using Qrakhen.Qamp.Memory; using Qrakhen.Qamp.Memory;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
@ -16,6 +17,12 @@ public class SelectableLineBuffer(LineBuffer buffer)
public class EditorFrameViewModel : ObservableObject public class EditorFrameViewModel : ObservableObject
{ {
private FileInfo _fileInfo;
public FileInfo FileInfo {
get => _fileInfo;
set => SetProperty(ref _fileInfo, value);
}
private BufferPosition _cursorPosition; private BufferPosition _cursorPosition;
public BufferPosition CursorPosition { public BufferPosition CursorPosition {
get => _cursorPosition; get => _cursorPosition;
@ -56,8 +63,6 @@ public class EditorFrameViewModel : ObservableObject
ClipBoardCommand = new RelayCommand(() => { }); ClipBoardCommand = new RelayCommand(() => { });
} }
public void SetCursor(int line, int column) => SetCursor(new BufferPosition(line, column)); public void SetCursor(int line, int column) => SetCursor(new BufferPosition(line, column));
public void SetCursor(BufferPosition position) public void SetCursor(BufferPosition position)
{ {
@ -111,6 +116,20 @@ public class EditorFrameViewModel : ObservableObject
} }
} }
public void Select(BufferPosition relativePosition, bool jump = false)
{
if (CurrentSelection.IsVoid) {
CurrentSelection = new BufferRegion(CursorPosition, CursorPosition);
}
CurrentSelection += relativePosition;
}
public void Deselect()
{
CurrentSelection = BufferRegion.Void;
}
/* /*
asdasdy asdasdy

View File

@ -6,20 +6,20 @@ namespace Qrakhen.Qamp.Editor.ViewModel;
public abstract class ObservableObject : INotifyPropertyChanged public abstract class ObservableObject : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null) protected bool SetProperty<T>([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
{ {
if (EqualityComparer<T>.Default.Equals(field, newValue)) if (EqualityComparer<T>.Default.Equals(field, newValue))
return false; return false;
field = newValue; field = newValue;
OnPropertyChanged(propertyName); OnPropertyChanged(propertyName);
return true; return true;
} }
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
} }

View File

@ -31,32 +31,32 @@ public struct BufferSpan
public class LineBuffer : TailBuffer<byte>, INotifyPropertyChanged public class LineBuffer : TailBuffer<byte>, INotifyPropertyChanged
{ {
public Encoding Encoding { get; } public Encoding Encoding { get; }
public string Cached { get; private set; } = ""; public string Cached { get; private set; } = "";
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public LineBuffer(Encoding encoding, int size = 0x80) : base(size) public LineBuffer(Encoding encoding, int size = 0x80) : base(size)
{ {
Encoding = encoding; Encoding = encoding;
} }
public LineBuffer(string data) : this(data, Encoding.ASCII) { } public LineBuffer(string data) : this(data, Encoding.ASCII) { }
public LineBuffer(string data, Encoding encoding) : this(encoding.GetBytes(data), encoding) { } public LineBuffer(string data, Encoding encoding) : this(encoding.GetBytes(data), encoding) { }
public LineBuffer(byte[] data, Encoding encoding) : base(data) public LineBuffer(byte[] data, Encoding encoding) : base(data)
{ {
Encoding = encoding; Encoding = encoding;
Cached = ToString(); Cached = ToString();
} }
protected override void SetTail(int tail = -1) protected override void SetTail(int tail = -1)
{ {
base.SetTail(tail); base.SetTail(tail);
Cached = ToString(); Cached = ToString();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Cached))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Cached)));
} }
public void Insert(int cursor, string data) public void Insert(int cursor, string data)
=> Insert(cursor, Encoding.GetBytes(data)); => Insert(cursor, Encoding.GetBytes(data));
public string ToString(BufferSpan span) public string ToString(BufferSpan span)
{ {
@ -66,17 +66,17 @@ public class LineBuffer : TailBuffer<byte>, INotifyPropertyChanged
return Encoding.GetString(Data[synced.Start..synced.End]); return Encoding.GetString(Data[synced.Start..synced.End]);
} }
public override string ToString() public override string ToString()
{ {
if (Tail == 0 || Encoding == null) if (Tail == 0 || Encoding == null)
return string.Empty; return string.Empty;
return Encoding.GetString(Data, 0, Tail); return Encoding.GetString(Data, 0, Tail);
} }
public override object Clone() public override object Clone()
{ {
return new LineBuffer(_data.ToArray(), Encoding); return new LineBuffer(_data.ToArray(), Encoding);
} }
public static implicit operator LineBuffer(string s) => new LineBuffer(s); public static implicit operator LineBuffer(string s) => new LineBuffer(s);
} }

View File

@ -3,87 +3,87 @@
// add to qrakhen.memory package for clout // add to qrakhen.memory package for clout
public abstract class TailBuffer<T> : ICloneable public abstract class TailBuffer<T> : ICloneable
{ {
protected T[] _data; protected T[] _data;
public T[] Data { public T[] Data {
get => _data; get => _data;
} }
public int Capacity => _data.Length; public int Capacity => _data.Length;
public int Tail { get; private set; } public int Tail { get; private set; }
public TailBuffer(int size = 0x80) public TailBuffer(int size = 0x80)
{ {
_data = new T[size]; _data = new T[size];
} }
public TailBuffer(T[] data) public TailBuffer(T[] data)
{ {
_data = new T[Math.Max(0x10, data.Length * 2)]; _data = new T[Math.Max(0x10, data.Length * 2)];
Array.Copy(data, 0, _data, 0, data.Length); Array.Copy(data, 0, _data, 0, data.Length);
SetTail(data.Length); SetTail(data.Length);
} }
public void Insert(int cursor, T[] data) public void Insert(int cursor, T[] data)
{ {
cursor = SeekTail(cursor); cursor = SeekTail(cursor);
Prepare(cursor, data.Length); Prepare(cursor, data.Length);
int length = data.Length; int length = data.Length;
if (cursor == Tail) { if (cursor == Tail) {
// Just append here. // Just append here.
Array.Copy(data, 0, _data, cursor, length); Array.Copy(data, 0, _data, cursor, length);
SetTail(cursor + length); SetTail(cursor + length);
} else { } else {
// Shift everything to the right of insertion // Shift everything to the right of insertion
Array.Copy(_data, cursor, _data, cursor + length, Tail - cursor); Array.Copy(_data, cursor, _data, cursor + length, Tail - cursor);
Array.Copy(data, 0, _data, cursor, length); Array.Copy(data, 0, _data, cursor, length);
SetTail(Tail + length); SetTail(Tail + length);
} }
} }
public void Delete(Range range) public void Delete(Range range)
=> Delete(range.From, range.To - range.From); => Delete(range.From, range.To - range.From);
public void Delete(int cursor, int count) public void Delete(int cursor, int count)
{ {
if (cursor + count >= Tail) if (cursor + count >= Tail)
SetTail(cursor); // Directly terminate at cursor, nothing is behind tail. SetTail(cursor); // Directly terminate at cursor, nothing is behind tail.
else { else {
// Move everything from end of deletion until tail to start of deletion. // Move everything from end of deletion until tail to start of deletion.
Array.Copy(_data, cursor + count, _data, cursor, Tail - (cursor + count)); Array.Copy(_data, cursor + count, _data, cursor, Tail - (cursor + count));
SetTail(Tail - count); SetTail(Tail - count);
} }
} }
public T[] Slice(int from = 0, int until = -1) public T[] Slice(int from = 0, int until = -1)
{ {
from = SeekTail(from); from = SeekTail(from);
if (from == Tail) if (from == Tail)
return []; return [];
until = until < 0 ? Tail : SeekTail(until); until = until < 0 ? Tail : SeekTail(until);
T[] data = _data[from..until]; T[] data = _data[from..until];
Delete(from, until - from); Delete(from, until - from);
return data; return data;
} }
public abstract object Clone(); public abstract object Clone();
// never write beyond tail (there might be invalid data) // never write beyond tail (there might be invalid data)
private int SeekTail(int cursor) => Math.Max(0, Math.Min(cursor, Tail)); private int SeekTail(int cursor) => Math.Max(0, Math.Min(cursor, Tail));
private void Prepare(int index, int count) private void Prepare(int index, int count)
{ {
ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfNegative(index);
if (count + Tail <= _data.Length) if (count + Tail <= _data.Length)
return; return;
while (count + Tail > _data.Length) { while (count + Tail > _data.Length) {
T[] grow = new T[_data.Length * 2]; T[] grow = new T[_data.Length * 2];
Array.Copy(_data, grow, _data.Length); Array.Copy(_data, grow, _data.Length);
_data = grow; _data = grow;
} }
} }
protected virtual void SetTail(int tail) protected virtual void SetTail(int tail)
{ {
Tail = tail; Tail = tail;
} }
} }

View File

@ -2,9 +2,9 @@ namespace Qrakhen.Qamp.Tests;
public class UnitTest1 public class UnitTest1
{ {
[Fact] [Fact]
public void Test1() public void Test1()
{ {
} }
} }