qamp/Qrakhen.Qamp.CLI/Program.cs

308 lines
10 KiB
C#

using Qrakhen.Qamp.Core;
using Qrakhen.Qamp.Core.Execution;
using Qrakhen.Qamp.Core.Tokenization;
using System.Text;
char[] Ignored = [ '\0', '\b' ];
Stack<byte[]> History = [];
ConsoleKeyInfo input;
bool useSyntaxHighlighting = false;
(int x, int y) cursor = (0, 0);
ConsoleCode code = ConsoleCode.Error;
Runner runner = new Runner(new Options());
ConsoleInterface cli = new ConsoleInterface(Encoding.Default);
do {
/*Console.ForegroundColor = ConsoleColor.White;
MemoryStream stream = new MemoryStream();
Console.Write(" <: ");
cursor = Console.GetCursorPosition();
do {
if (useSyntaxHighlighting)
new ConsoleRenderer(stream).Render(cursor);
input = Console.ReadKey(true);
if (input.Modifiers == ConsoleModifiers.Control) {
if (input.Key == ConsoleKey.H) {
useSyntaxHighlighting = !useSyntaxHighlighting;
Console.Write($"\n #: {nameof(useSyntaxHighlighting)} = {useSyntaxHighlighting}\n <: ");
cursor = Console.GetCursorPosition();
}
continue;
}
if (input.Key == ConsoleKey.LeftArrow) {
if (stream.Position > 0) {
stream.Position--;
Console.CursorLeft--;
}
continue;
}
if (input.Key == ConsoleKey.RightArrow) {
if (stream.Position < stream.Length) {
stream.Position++;
Console.CursorLeft++;
}
continue;
}
if (input.Key == ConsoleKey.Backspace && stream.Position > 0) {
Console.CursorLeft -= 1;
Console.Write(' ');
if (!useSyntaxHighlighting)
Console.CursorLeft -= 1;
//stream.Position--;
//stream.SetLength(stream.Position);
stream.Remove(stream.Position - 1, 1);
} else if (input.Key == ConsoleKey.Enter && input.Modifiers == ConsoleModifiers.Shift) {
if (!useSyntaxHighlighting)
Console.Write("\n : ");
stream.Insert(Encoding.Default.GetBytes(Environment.NewLine));
} else if (Ignored.IndexOf(input.KeyChar) < 0) {
if (!useSyntaxHighlighting)
Console.Write(input.KeyChar);
stream.Insert(Encoding.Default.GetBytes([input.KeyChar], 0, 1));
}
} while (input.Key != ConsoleKey.Enter || input.Modifiers == ConsoleModifiers.Shift);*/
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);
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);
}
}