fix a few issues, split CLI classes, fix member calls
This commit is contained in:
parent
1c161bfda4
commit
0e11be061b
|
|
@ -0,0 +1,6 @@
|
||||||
|
public enum ConsoleCode
|
||||||
|
{
|
||||||
|
Error = -1,
|
||||||
|
OK = 0,
|
||||||
|
Exit = 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
public static class ConsoleExtensions
|
||||||
|
{
|
||||||
|
public static bool Check(this ConsoleKeyInfo keyInfo, ConsoleKey key, ConsoleModifiers modifier = default, bool exactModifier = false)
|
||||||
|
{
|
||||||
|
return ((exactModifier ?
|
||||||
|
keyInfo.Modifiers == modifier :
|
||||||
|
(keyInfo.Modifiers & modifier) == modifier)
|
||||||
|
&& keyInfo.Key == key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
|
||||||
|
using Qrakhen.Qamp.Core;
|
||||||
|
using Qrakhen.Qamp.Core.Tokenization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
public class ConsoleRenderer
|
||||||
|
{
|
||||||
|
private readonly IReader<Token> _reader;
|
||||||
|
private readonly Stream _stream;
|
||||||
|
|
||||||
|
public ConsoleRenderer(Stream stream)
|
||||||
|
{
|
||||||
|
_stream = new MemoryStream();
|
||||||
|
stream.Position = 0;
|
||||||
|
stream.CopyTo(_stream);
|
||||||
|
_reader = new Reader(_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render((int x, int y) position)
|
||||||
|
{
|
||||||
|
Console.CursorVisible = false;
|
||||||
|
Console.SetCursorPosition(position.x, position.y);
|
||||||
|
List<Token> tokens = [];
|
||||||
|
while (!_reader.Done) {
|
||||||
|
tokens.Add(_reader.NextToken(true));
|
||||||
|
}
|
||||||
|
_stream.Position = 0;
|
||||||
|
foreach (var token in tokens) {
|
||||||
|
ConsoleColor color = ConsoleColor.Gray;
|
||||||
|
if (token.Type.IsBoolean())
|
||||||
|
color = ConsoleColor.Blue;
|
||||||
|
if (token.Type.IsString())
|
||||||
|
color = ConsoleColor.Magenta;
|
||||||
|
if (token.Type.IsNumber())
|
||||||
|
color = ConsoleColor.DarkCyan;
|
||||||
|
if (token.Type.IsOperator())
|
||||||
|
color = ConsoleColor.DarkGreen;
|
||||||
|
if (token.Type.IsBracket())
|
||||||
|
color = ConsoleColor.Red;
|
||||||
|
if (token.Type.IsIdentifier())
|
||||||
|
color = ConsoleColor.White;
|
||||||
|
if (token.Type.IsControl())
|
||||||
|
color = ConsoleColor.DarkYellow;
|
||||||
|
if (token.Type == TokenType.Error)
|
||||||
|
color = ConsoleColor.DarkRed;
|
||||||
|
Console.ForegroundColor = color;
|
||||||
|
byte[] buffer = new byte[token.Span.Length];
|
||||||
|
_stream.ReadExactly(buffer, 0, (int)token.Span.Length);
|
||||||
|
string str = Encoding.Default.GetString(buffer);
|
||||||
|
Console.Write(str);
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
if (token.Type == TokenType.NewLine)
|
||||||
|
Console.Write(" : ");
|
||||||
|
}
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
Console.CursorVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
public class ConsoleWriter
|
||||||
|
{
|
||||||
|
private const string SYMBOL_INPUT = " <: ";
|
||||||
|
private const string SYMBOL_INPUT_CONTINUE = " : ";
|
||||||
|
|
||||||
|
private readonly List<char> _buffer = [];
|
||||||
|
private readonly Stack<char[]> _history = [];
|
||||||
|
private readonly Encoding _encoding;
|
||||||
|
|
||||||
|
private int _position;
|
||||||
|
private (int x, int y) _origin;
|
||||||
|
private (int x, int y) _cursorPosition => Console.GetCursorPosition();
|
||||||
|
|
||||||
|
private int _cursorOffset => SYMBOL_INPUT.Length;
|
||||||
|
private int _cursorLeft { get => Console.CursorLeft; set => Console.CursorLeft = value; }
|
||||||
|
private int _cursorTop { get => Console.CursorTop; set => Console.CursorTop = value; }
|
||||||
|
|
||||||
|
public bool UseSyntaxHighlighting { get; set; } = false;
|
||||||
|
|
||||||
|
public ConsoleWriter(Encoding encoding)
|
||||||
|
{
|
||||||
|
_encoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Write(char c) => Console.Write(c);
|
||||||
|
private void Write(string str) => Console.Write(str);
|
||||||
|
private void SetCursor((int x, int y) position) => Console.SetCursorPosition(position.x, position.y);
|
||||||
|
|
||||||
|
private void SetColor(ConsoleColor color, ConsoleColor background = ConsoleColor.Black)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = color;
|
||||||
|
Console.BackgroundColor = background;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Move(int x, int y = 0)
|
||||||
|
{
|
||||||
|
_position = Math.Min(_buffer.Count, Math.Max(0, _position + x));
|
||||||
|
_cursorLeft -= 1; //????
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearLine()
|
||||||
|
{
|
||||||
|
int left = _cursorLeft;
|
||||||
|
while (_cursorLeft < Console.BufferWidth - 1)
|
||||||
|
Write(' ');
|
||||||
|
_cursorLeft = left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Print((int x, int y) origin, (int x, int y)? remain = null)
|
||||||
|
{
|
||||||
|
SetColor(ConsoleColor.White);
|
||||||
|
SetCursor(origin);
|
||||||
|
Write(" <: ");
|
||||||
|
foreach (var c in _buffer) {
|
||||||
|
if (c == '\n') {
|
||||||
|
ClearLine();
|
||||||
|
Write("\n : ");
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (input.Key == ConsoleKey.LeftArrow) {
|
||||||
|
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.Key == ConsoleKey.LeftArrow) {
|
||||||
|
if (_position > 0) {
|
||||||
|
_position--;
|
||||||
|
_cursorLeft--;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
|
|
||||||
using Qrakhen.Qamp.Core;
|
using Qrakhen.Qamp.Core;
|
||||||
using Qrakhen.Qamp.Core.Execution;
|
using Qrakhen.Qamp.Core.Execution;
|
||||||
using Qrakhen.Qamp.Core.Tokenization;
|
using Qrakhen.Qamp.Core.Logging;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
char[] Ignored = [ '\0', '\b' ];
|
char[] Ignored = [ '\0', '\b' ];
|
||||||
|
LoggerService.Default = LogLevel.Error;
|
||||||
Stack<byte[]> History = [];
|
Stack<byte[]> History = [];
|
||||||
ConsoleKeyInfo input;
|
ConsoleKeyInfo input;
|
||||||
bool useSyntaxHighlighting = false;
|
bool useSyntaxHighlighting = false;
|
||||||
(int x, int y) cursor = (0, 0);
|
(int x, int y) cursor = (0, 0);
|
||||||
ConsoleCode code = ConsoleCode.Error;
|
ConsoleCode code = ConsoleCode.Error;
|
||||||
Runner runner = new Runner(new Options());
|
Runner runner = new Runner(new Options());
|
||||||
ConsoleInterface cli = new ConsoleInterface(Encoding.Default);
|
ConsoleWriter cli = new ConsoleWriter(Encoding.Default);
|
||||||
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();
|
||||||
|
|
@ -28,6 +29,11 @@ do {
|
||||||
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) {
|
||||||
|
LoggerService.Default = LoggerService.Default < LogLevel.All ? LogLevel.All : LogLevel.Info;
|
||||||
|
Console.Write($"\n #: LogLevel = {LoggerService.Default}\n <: ");
|
||||||
|
cursor = Console.GetCursorPosition();
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,17 +70,10 @@ do {
|
||||||
Console.Write(input.KeyChar);
|
Console.Write(input.KeyChar);
|
||||||
stream.Insert(Encoding.Default.GetBytes([input.KeyChar], 0, 1));
|
stream.Insert(Encoding.Default.GetBytes([input.KeyChar], 0, 1));
|
||||||
}
|
}
|
||||||
} while (input.Key != ConsoleKey.Enter || input.Modifiers == ConsoleModifiers.Shift);*/
|
} while (input.Key != ConsoleKey.Enter || input.Modifiers == ConsoleModifiers.Shift);
|
||||||
try {
|
try {
|
||||||
code = cli.Read(out Stream? stream);
|
Console.WriteLine();
|
||||||
if (code == ConsoleCode.OK) {
|
runner.Run(stream);
|
||||||
if (stream == null)
|
|
||||||
throw new QampException($"Stream from CLI was null {code}");
|
|
||||||
Console.WriteLine();
|
|
||||||
runner.Run(stream);
|
|
||||||
}
|
|
||||||
if (code == ConsoleCode.Error)
|
|
||||||
throw new QampException($"CLI returned {code}");
|
|
||||||
}
|
}
|
||||||
catch (QampException e) {
|
catch (QampException e) {
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
|
@ -93,216 +92,3 @@ do {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while(code != ConsoleCode.Exit);
|
} while(code != ConsoleCode.Exit);
|
||||||
|
|
||||||
class ConsoleInterface
|
|
||||||
{
|
|
||||||
private const string SYMBOL_INPUT = " <: ";
|
|
||||||
private const string SYMBOL_INPUT_CONTINUE = " : ";
|
|
||||||
|
|
||||||
private readonly List<char> _buffer = [];
|
|
||||||
private readonly Stack<char[]> _history = [];
|
|
||||||
private readonly Encoding _encoding;
|
|
||||||
|
|
||||||
private int _position;
|
|
||||||
private (int x, int y) _origin;
|
|
||||||
private (int x, int y) _cursorPosition => Console.GetCursorPosition();
|
|
||||||
|
|
||||||
private int _cursorOffset => SYMBOL_INPUT.Length;
|
|
||||||
private int _cursorLeft { get => Console.CursorLeft; set => Console.CursorLeft = value; }
|
|
||||||
private int _cursorTop { get => Console.CursorTop; set => Console.CursorTop = value; }
|
|
||||||
|
|
||||||
public bool UseSyntaxHighlighting { get; set; } = false;
|
|
||||||
|
|
||||||
public ConsoleInterface(Encoding encoding)
|
|
||||||
{
|
|
||||||
_encoding = encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Write(char c) => Console.Write(c);
|
|
||||||
private void Write(string str) => Console.Write(str);
|
|
||||||
private void SetCursor((int x, int y) position) => Console.SetCursorPosition(position.x, position.y);
|
|
||||||
|
|
||||||
private void SetColor(ConsoleColor color, ConsoleColor background = ConsoleColor.Black)
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = color;
|
|
||||||
Console.BackgroundColor = background;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Move(int x, int y = 0)
|
|
||||||
{
|
|
||||||
_position = Math.Min(_buffer.Count, Math.Max(0, _position + x));
|
|
||||||
_cursorLeft -= 1; //????
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearLine()
|
|
||||||
{
|
|
||||||
int left = _cursorLeft;
|
|
||||||
while (_cursorLeft < Console.BufferWidth - 1)
|
|
||||||
Write(' ');
|
|
||||||
_cursorLeft = left;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Print((int x, int y) origin, (int x, int y)? remain = null)
|
|
||||||
{
|
|
||||||
SetColor(ConsoleColor.White);
|
|
||||||
SetCursor(origin);
|
|
||||||
Write(" <: ");
|
|
||||||
foreach (var c in _buffer) {
|
|
||||||
if (c == '\n') {
|
|
||||||
ClearLine();
|
|
||||||
Write("\n : ");
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (input.Key == ConsoleKey.LeftArrow) {
|
|
||||||
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.Key == ConsoleKey.LeftArrow) {
|
|
||||||
if (_position > 0) {
|
|
||||||
_position--;
|
|
||||||
_cursorLeft--;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ConsoleCode
|
|
||||||
{
|
|
||||||
Error = -1,
|
|
||||||
OK = 0,
|
|
||||||
Exit = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConsoleRenderer
|
|
||||||
{
|
|
||||||
private readonly IReader<Token> _reader;
|
|
||||||
private readonly Stream _stream;
|
|
||||||
|
|
||||||
public ConsoleRenderer(Stream stream)
|
|
||||||
{
|
|
||||||
_stream = new MemoryStream();
|
|
||||||
stream.Position = 0;
|
|
||||||
stream.CopyTo(_stream);
|
|
||||||
_reader = new Reader(_stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render((int x, int y) position)
|
|
||||||
{
|
|
||||||
Console.CursorVisible = false;
|
|
||||||
Console.SetCursorPosition(position.x, position.y);
|
|
||||||
List<Token> tokens = [];
|
|
||||||
while (!_reader.Done) {
|
|
||||||
tokens.Add(_reader.NextToken(true));
|
|
||||||
}
|
|
||||||
_stream.Position = 0;
|
|
||||||
foreach (var token in tokens) {
|
|
||||||
ConsoleColor color = ConsoleColor.Gray;
|
|
||||||
if (token.Type.IsBoolean())
|
|
||||||
color = ConsoleColor.Blue;
|
|
||||||
if (token.Type.IsString())
|
|
||||||
color = ConsoleColor.Red;
|
|
||||||
if (token.Type.IsNumber())
|
|
||||||
color = ConsoleColor.DarkCyan;
|
|
||||||
if (token.Type.IsOperator())
|
|
||||||
color = ConsoleColor.DarkGreen;
|
|
||||||
if (token.Type.IsBracket())
|
|
||||||
color = ConsoleColor.Yellow;
|
|
||||||
if (token.Type.IsIdentifier())
|
|
||||||
color = ConsoleColor.White;
|
|
||||||
if (token.Type.IsControl())
|
|
||||||
color = ConsoleColor.DarkYellow;
|
|
||||||
if (token.Type == TokenType.Error)
|
|
||||||
color = ConsoleColor.DarkRed;
|
|
||||||
Console.ForegroundColor = color;
|
|
||||||
byte[] buffer = new byte[token.Span.Length];
|
|
||||||
_stream.ReadExactly(buffer, 0, (int)token.Span.Length);
|
|
||||||
string str = Encoding.Default.GetString(buffer);
|
|
||||||
Console.Write(str);
|
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
|
||||||
if (token.Type == TokenType.NewLine)
|
|
||||||
Console.Write(" : ");
|
|
||||||
}
|
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
|
||||||
Console.CursorVisible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConsoleExtensions
|
|
||||||
{
|
|
||||||
public static bool Check(this ConsoleKeyInfo keyInfo, ConsoleKey key, ConsoleModifiers modifier = default, bool exactModifier = false)
|
|
||||||
{
|
|
||||||
return ((exactModifier ?
|
|
||||||
keyInfo.Modifiers == modifier :
|
|
||||||
(keyInfo.Modifiers & modifier) == modifier)
|
|
||||||
&& keyInfo.Key == key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
using Qrakhen.Qamp.Core;
|
||||||
|
using Qrakhen.Qamp.Core.Execution;
|
||||||
|
using Qrakhen.Qamp.Core.Tokenization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
class C
|
||||||
|
{
|
||||||
|
void X()
|
||||||
|
{
|
||||||
|
ConsoleCode code = ConsoleCode.Exit;
|
||||||
|
Runner runner = new Runner(new Options());
|
||||||
|
ConsoleWriter cli = new ConsoleWriter(Encoding.Default);
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
code = cli.Read(out Stream? stream);
|
||||||
|
if (code == ConsoleCode.OK) {
|
||||||
|
if (stream == null)
|
||||||
|
throw new QampException($"Stream from CLI was null {code}");
|
||||||
|
Console.WriteLine();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
public class Pointer<T>
|
public class Pointer<T>
|
||||||
{
|
{
|
||||||
protected IGetSet<long, T> Target;
|
protected IGetSet<long, T> Target;
|
||||||
public long Ptr;
|
public long Cursor;
|
||||||
|
|
||||||
public T Current {
|
public T Current {
|
||||||
get => Get();
|
get => Get();
|
||||||
|
|
@ -16,41 +16,41 @@ public class Pointer<T>
|
||||||
public Pointer(IGetSet<long, T> target, long pointer = 0)
|
public Pointer(IGetSet<long, T> target, long pointer = 0)
|
||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
Ptr = pointer;
|
Cursor = pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pointer<T> Branch(long delta = 0)
|
public Pointer<T> Branch(long delta = 0)
|
||||||
{
|
{
|
||||||
return new Pointer<T>(Target, Ptr + 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(Ptr++);
|
public T Next() => Target.Get(Cursor++);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the value of <see cref="Target"/> at <paramref name="position"/>.
|
/// Sets the value of <see cref="Target"/> RELATIVE from the current position (<paramref name="delta"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Set(long position, T value) => Target.Set(position, 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="Ptr"/> location.
|
/// Sets the value of <see cref="Target"/> at the current <see cref="Cursor"/> location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Set(T value) => Set(Ptr, value);
|
public void Set(T value) => Set(Cursor, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value of <see cref="Target"/> at <paramref name="position"/>.
|
/// Gets the value of <see cref="Target"/> RELATIVE from the current position (<paramref name="delta"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Get(long position) => Target.Get(position);
|
public T Get(long delta) => Target.Get(Cursor + delta);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value of <see cref="Target"/> at the current <see cref="Ptr"/> location.
|
/// Gets the value of <see cref="Target"/> at the current <see cref="Cursor"/> location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Get() => Get(Ptr);
|
public T Get() => Get(Cursor);
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"&{Ptr:X4}={Current}";
|
return $"&{Cursor:X4}={Current}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,11 @@ public static class ExpressionParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void TypeOf(Digester digester, bool canAssign)
|
||||||
|
{
|
||||||
|
digester.TypeOf();
|
||||||
|
}
|
||||||
|
|
||||||
static ExpressionParser()
|
static ExpressionParser()
|
||||||
{
|
{
|
||||||
_rules[TokenType.GroupOpen] = new Rule(Group, Call, Weight.Call);
|
_rules[TokenType.GroupOpen] = new Rule(Group, Call, Weight.Call);
|
||||||
|
|
@ -290,11 +295,11 @@ public static class ExpressionParser
|
||||||
_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(null, 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);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ public class InstructionPtr : Pointer<Instruction>
|
||||||
{
|
{
|
||||||
public Segment Segment;
|
public Segment Segment;
|
||||||
|
|
||||||
public Instruction Instruction => Segment.Instructions[Ptr];
|
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)
|
||||||
{
|
{
|
||||||
|
|
@ -17,8 +17,8 @@ public class InstructionPtr : Pointer<Instruction>
|
||||||
|
|
||||||
public long NextLong()
|
public long NextLong()
|
||||||
{
|
{
|
||||||
long value = Segment.ReadLong(Ptr);
|
long value = Segment.ReadLong(Cursor);
|
||||||
Ptr += sizeof(long);
|
Cursor += sizeof(long);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,8 +37,8 @@ public class InstructionPtr : Pointer<Instruction>
|
||||||
|
|
||||||
public long NextDynamic()
|
public long NextDynamic()
|
||||||
{
|
{
|
||||||
var result = Segment.ReadDynamic(Ptr, out int read);
|
var result = Segment.ReadDynamic(Cursor, out int read);
|
||||||
Ptr += read;
|
Cursor += read;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,20 +46,20 @@ public class InstructionPtr : Pointer<Instruction>
|
||||||
=> 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.Ptr + 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.Ptr - value];
|
=> ptr.Segment.Instructions[ptr.Cursor - value];
|
||||||
|
|
||||||
public static InstructionPtr operator ++(InstructionPtr ptr)
|
public static InstructionPtr operator ++(InstructionPtr ptr)
|
||||||
{
|
{
|
||||||
ptr.Ptr++;
|
ptr.Cursor++;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstructionPtr operator --(InstructionPtr ptr)
|
public static InstructionPtr operator --(InstructionPtr ptr)
|
||||||
{
|
{
|
||||||
ptr.Ptr--;
|
ptr.Cursor--;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,10 @@ public class Runner : IDisposable
|
||||||
public ExecutionResult Run(Stream stream)
|
public ExecutionResult Run(Stream stream)
|
||||||
{
|
{
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
|
if (Stack.Position < 0) {
|
||||||
|
_logger.Warn($"Something went wrong, stack cursor is at {Stack.Position}. Resetting Stack.");
|
||||||
|
Stack.Decimate(0);
|
||||||
|
}
|
||||||
using Reader reader = new Reader(stream);
|
using Reader reader = new Reader(stream);
|
||||||
Compilation.Digester digester = new(reader);
|
Compilation.Digester digester = new(reader);
|
||||||
Function function = digester.Digest();
|
Function function = digester.Digest();
|
||||||
|
|
@ -95,7 +99,7 @@ public class Runner : IDisposable
|
||||||
case Op.GetGlobal: {
|
case Op.GetGlobal: {
|
||||||
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
||||||
if (string.IsNullOrEmpty(name))
|
if (string.IsNullOrEmpty(name))
|
||||||
throw new RuntimeException($"tried to set global variable with empty name");
|
return Error($"tried to set global variable with empty name");
|
||||||
if (!Globals.Has(name))
|
if (!Globals.Has(name))
|
||||||
Push(Value.Void);
|
Push(Value.Void);
|
||||||
else
|
else
|
||||||
|
|
@ -107,9 +111,9 @@ public class Runner : IDisposable
|
||||||
case Op.SetGlobal: {
|
case Op.SetGlobal: {
|
||||||
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
||||||
if (string.IsNullOrEmpty(name))
|
if (string.IsNullOrEmpty(name))
|
||||||
throw new RuntimeException($"tried to set global variable with empty name");
|
return Error($"tried to set global variable with empty name");
|
||||||
if (!Globals.Has(name))
|
if (!Globals.Has(name))
|
||||||
throw new RuntimeException($"tried to set a value of non-existing global variable");
|
return Error($"tried to set a value of non-existing global variable");
|
||||||
Globals[name] = Pop();
|
Globals[name] = Pop();
|
||||||
_logger.Verbose($"set global {name} = {Globals[name]}");
|
_logger.Verbose($"set global {name} = {Globals[name]}");
|
||||||
break;
|
break;
|
||||||
|
|
@ -118,7 +122,7 @@ public class Runner : IDisposable
|
||||||
case Op.DefineGlobal: {
|
case Op.DefineGlobal: {
|
||||||
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
string? name = call.Instruction.GetStringConstant(call.Instruction.NextDynamic())?.Value;
|
||||||
if (string.IsNullOrEmpty(name))
|
if (string.IsNullOrEmpty(name))
|
||||||
throw new RuntimeException($"tried to define global variable with empty name");
|
return Error($"tried to define global variable with empty name");
|
||||||
Globals[name] = Pop();
|
Globals[name] = Pop();
|
||||||
_logger.Verbose($"defined global {name} as {Globals[name]}");
|
_logger.Verbose($"defined global {name} as {Globals[name]}");
|
||||||
break;
|
break;
|
||||||
|
|
@ -189,20 +193,20 @@ public class Runner : IDisposable
|
||||||
|
|
||||||
case Op.Jump: {
|
case Op.Jump: {
|
||||||
long delta = call.Instruction.NextLong();
|
long delta = call.Instruction.NextLong();
|
||||||
call.Instruction.Ptr += delta;
|
call.Instruction.Cursor += delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Op.JumpIfFalse: {
|
case Op.JumpIfFalse: {
|
||||||
long delta = call.Instruction.NextLong();
|
long delta = call.Instruction.NextLong();
|
||||||
if (Peek().IsFalsy)
|
if (Peek().IsFalsy)
|
||||||
call.Instruction.Ptr += delta;
|
call.Instruction.Cursor += delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Op.Loop: {
|
case Op.Loop: {
|
||||||
long delta = call.Instruction.NextLong();
|
long delta = call.Instruction.NextLong();
|
||||||
call.Instruction.Ptr -= delta;
|
call.Instruction.Cursor -= delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +267,7 @@ public class Runner : IDisposable
|
||||||
Pop();
|
Pop();
|
||||||
return ExecutionResult.OK;
|
return ExecutionResult.OK;
|
||||||
}
|
}
|
||||||
Stack.Decimate(call.StackPtr.Ptr);
|
Stack.Decimate(call.StackPtr.Cursor);
|
||||||
Push(result);
|
Push(result);
|
||||||
call = Calls.Peek();
|
call = Calls.Peek();
|
||||||
break;
|
break;
|
||||||
|
|
@ -293,7 +297,18 @@ public class Runner : IDisposable
|
||||||
|
|
||||||
case Op.Typeof: {
|
case Op.Typeof: {
|
||||||
Value value = Pop();
|
Value value = Pop();
|
||||||
Push(String.Make(value.Type.ToString()));
|
string type;
|
||||||
|
if (value.Is(T.Class))
|
||||||
|
type = value.Ptr.As<Class>()!.Name;
|
||||||
|
else if (value.Is(T.Instance))
|
||||||
|
type = $"instance of {value.Ptr.As<Instance>()!.Class.Name}";
|
||||||
|
else if (value.Is(T.Function))
|
||||||
|
type = $"function {value.Ptr.As<Function>()!.Name}";
|
||||||
|
else if (value.Is(T.Method))
|
||||||
|
type = $"method {value.Ptr.As<Method>()!.Function.Name}";
|
||||||
|
else
|
||||||
|
type = value.Type.ToString();
|
||||||
|
Push(String.Make(type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,7 +336,7 @@ public class Runner : IDisposable
|
||||||
return Error($"Unexpected or not yet supported OpCode {opCode}!");
|
return Error($"Unexpected or not yet supported OpCode {opCode}!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (call.Instruction.Segment.Instructions.Length > call.Instruction.Ptr);
|
} while (call.Instruction.Segment.Instructions.Length > call.Instruction.Cursor);
|
||||||
|
|
||||||
return ExecutionResult.OK;
|
return ExecutionResult.OK;
|
||||||
}
|
}
|
||||||
|
|
@ -331,11 +346,11 @@ public class Runner : IDisposable
|
||||||
Outer? outer = null;
|
Outer? outer = null;
|
||||||
for (int i = Outers.Count; i-->0;) {
|
for (int i = Outers.Count; i-->0;) {
|
||||||
outer = Outers[i];
|
outer = Outers[i];
|
||||||
if (outer.Target.Ptr <= target.Ptr)
|
if (outer.Target.Cursor <= target.Cursor)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outer != null && outer.Target.Ptr == target.Ptr)
|
if (outer != null && outer.Target.Cursor == target.Cursor)
|
||||||
return outer;
|
return outer;
|
||||||
|
|
||||||
return new Outer(target);
|
return new Outer(target);
|
||||||
|
|
@ -347,7 +362,7 @@ public class Runner : IDisposable
|
||||||
Outer outer = Outers[i];
|
Outer outer = Outers[i];
|
||||||
if (outer.IsClosed)
|
if (outer.IsClosed)
|
||||||
continue;
|
continue;
|
||||||
if (outer.Target.Ptr < last.Ptr)
|
if (outer.Target.Cursor < last.Cursor)
|
||||||
return;
|
return;
|
||||||
outer.Close();
|
outer.Close();
|
||||||
}
|
}
|
||||||
|
|
@ -356,11 +371,15 @@ public class Runner : IDisposable
|
||||||
private bool Invoke(Context closure, int argumentCount)
|
private bool Invoke(Context closure, int argumentCount)
|
||||||
{
|
{
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
if (argumentCount != closure.Function.ArgumentCount)
|
if (argumentCount != closure.Function.ArgumentCount) {
|
||||||
throw new Exception($"Expected {closure.Function.ArgumentCount} arguments but got {argumentCount}");
|
Error($"Expected {closure.Function.ArgumentCount} arguments but got {argumentCount}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (Calls.Count > Options.MaxCalls)
|
if (Calls.Count > Options.MaxCalls) {
|
||||||
throw new StackOverflowException($"Stack overflow {Calls.Count}");
|
Error($"Stack overflow {Calls.Count}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Call call = new(closure, Stack, Cursor - argumentCount - 1);
|
Call call = new(closure, Stack, Cursor - argumentCount - 1);
|
||||||
Calls.Push(call);
|
Calls.Push(call);
|
||||||
|
|
@ -369,7 +388,7 @@ public class Runner : IDisposable
|
||||||
|
|
||||||
private bool Invoke(string methodName, int argumentCount)
|
private bool Invoke(string methodName, int argumentCount)
|
||||||
{
|
{
|
||||||
Value value = Peek(-argumentCount);
|
Value value = Peek(-argumentCount - 1);
|
||||||
if (!value.Is(T.Instance)) {
|
if (!value.Is(T.Instance)) {
|
||||||
Error($"Can not call {methodName} on value of {value}");
|
Error($"Can not call {methodName} on value of {value}");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -430,7 +449,7 @@ public class Runner : IDisposable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Method method = new Method(value.Ptr.As<Function>()!, Peek());
|
Method method = new Method(value.Ptr.As<Context>()!.Function, Peek());
|
||||||
Pop(); // remove instance from stack
|
Pop(); // remove instance from stack
|
||||||
Push(Obj.Create(method));
|
Push(Obj.Create(method));
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -483,11 +502,18 @@ public class Runner : IDisposable
|
||||||
{
|
{
|
||||||
_logger.Method(message);
|
_logger.Method(message);
|
||||||
_logger.Error(message, context);
|
_logger.Error(message, context);
|
||||||
|
Panic();
|
||||||
if (@throw)
|
if (@throw)
|
||||||
throw new QampException(message, context);
|
throw new QampException(message, context);
|
||||||
return ExecutionResult.Execution;
|
return ExecutionResult.Execution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Panic()
|
||||||
|
{
|
||||||
|
_logger.Method();
|
||||||
|
Stack.Decimate(0);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_logger.Method();
|
_logger.Method();
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@ public class LoggerService
|
||||||
public class Logger : ILogger, ILogFormatter
|
public class Logger : ILogger, ILogFormatter
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public LogLevel Level { get; set; }
|
private LogLevel _level;
|
||||||
|
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, (" !? ", " !? ") },
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,9 @@ 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()
|
||||||
|
{
|
||||||
|
return $"{Name} (Class)";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,16 @@ 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()
|
||||||
|
{
|
||||||
|
string str = $"{Class.Name} {{\n";
|
||||||
|
foreach (var member in Class.Members) {
|
||||||
|
str += $" {member.Key}: {member.Value},\n";
|
||||||
|
}
|
||||||
|
foreach (var value in Values) {
|
||||||
|
str += $" {value.Key}: {value.Value},\n";
|
||||||
|
}
|
||||||
|
return $"{str}}}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue