fix formatting everywhere and make operation resolving faster
This commit is contained in:
parent
fad93a7973
commit
a39096014c
|
|
@ -1,6 +1,6 @@
|
||||||
public enum ConsoleCode
|
public enum ConsoleCode
|
||||||
{
|
{
|
||||||
Error = -1,
|
Error = -1,
|
||||||
OK = 0,
|
OK = 0,
|
||||||
Exit = 1
|
Exit = 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
public class UnitTest1
|
public class UnitTest1
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Test1()
|
public void Test1()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
public interface IPeekable<out T>
|
public interface IPeekable<out T>
|
||||||
{
|
{
|
||||||
T Peek(int delta = -1);
|
T Peek(int delta = -1);
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
public interface ISteppable<out T>
|
public interface ISteppable<out T>
|
||||||
{
|
{
|
||||||
bool Done { get; }
|
bool Done { get; }
|
||||||
T Next();
|
T Next();
|
||||||
}
|
}
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
public interface IToArray<out T>
|
public interface IToArray<out T>
|
||||||
{
|
{
|
||||||
T[] ToArray();
|
T[] ToArray();
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
public interface IBuilder<TOut>
|
public interface IBuilder<TOut>
|
||||||
{
|
{
|
||||||
public TOut Build();
|
public TOut Build();
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
|
@ -8,5 +8,5 @@ namespace Qrakhen.Qamp.Core.Compilation.Digesters;
|
||||||
|
|
||||||
internal interface IDigester
|
internal interface IDigester
|
||||||
{
|
{
|
||||||
void Digest(CompilerState state);
|
void Digest(CompilerState state);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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}>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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}" ]);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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, "!>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}]";
|
||||||
}
|
}
|
||||||
|
|
@ -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)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}(){{}}";
|
||||||
}
|
}
|
||||||
|
|
@ -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)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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}}}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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}]";
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>";
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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}}}";
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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}";
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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', '"', '²');
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ namespace Qrakhen.Qamp.Editor;
|
||||||
|
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ namespace Qrakhen.Qamp.Tests;
|
||||||
|
|
||||||
public class UnitTest1
|
public class UnitTest1
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Test1()
|
public void Test1()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue