643 lines
17 KiB
C
643 lines
17 KiB
C
|
|
#if 0
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include <debug.h>
|
|
|
|
#include <memory/smalloc.h>
|
|
#include <memory/srealloc.h>
|
|
|
|
#include <string/new.h>
|
|
#include <string/free.h>
|
|
|
|
#include <stringtree/new.h>
|
|
#include <stringtree/append.h>
|
|
#include <stringtree/print.h>
|
|
#include <stringtree/free.h>
|
|
|
|
#include <parse/position/new.h>
|
|
#include <parse/position/free.h>
|
|
|
|
#include <parse/tokenizer/new.h>
|
|
#include <parse/tokenizer/next.h>
|
|
#include <parse/tokenizer/free.h>
|
|
|
|
#include <parse/parse.h>
|
|
|
|
#include <statement/prettyprint.h>
|
|
#include <statement/prettyprint_errors.h>
|
|
#include <statement/execute.h>
|
|
#include <statement/free.h>
|
|
|
|
#include <stringtree/println.h>
|
|
#include <stringtree/free.h>
|
|
|
|
#include <wcistream/string/new.h>
|
|
#include <wcistream/read.h>
|
|
#include <wcistream/free.h>
|
|
|
|
#include "handle_interactive.h"
|
|
|
|
#if 0
|
|
static const enum state {
|
|
s_error,
|
|
|
|
s_EOT,
|
|
|
|
s_delete,
|
|
|
|
s_backspace,
|
|
|
|
s_enter,
|
|
|
|
s_insert_lambda,
|
|
|
|
s_insert_letter,
|
|
|
|
s_up,
|
|
s_down,
|
|
s_right,
|
|
s_left,
|
|
|
|
s_home,
|
|
s_end,
|
|
|
|
s_start,
|
|
|
|
s_sync_idle,
|
|
|
|
s_esc,
|
|
s_csi,
|
|
|
|
number_of_states,
|
|
} lookup[number_of_states][256] = {
|
|
[s_start][0x04] = s_EOT,
|
|
|
|
[s_start][0x7F] = s_backspace,
|
|
|
|
[s_start][0x16] = s_sync_idle,
|
|
[s_sync_idle]['\\'] = s_insert_lambda,
|
|
|
|
[s_start]['\n'] = s_enter,
|
|
|
|
[s_start][' '] = s_insert_letter,
|
|
[s_start]['0' ... '9'] = s_insert_letter,
|
|
[s_start]['a' ... 'z'] = s_insert_letter,
|
|
[s_start]['A' ... 'Z'] = s_insert_letter,
|
|
|
|
[s_start][':'] = s_insert_letter,
|
|
[s_start]['+'] = s_insert_letter,
|
|
[s_start]['-'] = s_insert_letter,
|
|
[s_start]['/'] = s_insert_letter,
|
|
[s_start]['.'] = s_insert_letter,
|
|
[s_start]['('] = s_insert_letter,
|
|
[s_start][')'] = s_insert_letter,
|
|
[s_start]['\\'] = s_insert_letter,
|
|
|
|
[s_start][0x1B] = s_esc,
|
|
[s_esc]['['] = s_csi,
|
|
[s_start][0x9B] = s_csi,
|
|
[s_csi]['3'] = s_delete,
|
|
|
|
[s_csi]['A'] = s_up,
|
|
[s_csi]['B'] = s_down,
|
|
[s_csi]['C'] = s_right,
|
|
[s_csi]['D'] = s_left,
|
|
|
|
[s_csi]['H'] = s_home,
|
|
[s_csi]['F'] = s_end,
|
|
};
|
|
#endif
|
|
|
|
void handle_interactive(
|
|
struct environment** environment,
|
|
struct booleans* booleans,
|
|
struct wcostream* wc_stdout
|
|
#ifndef RELEASE_BUILD
|
|
, const char* test_interactive_using_input_file
|
|
#endif
|
|
)
|
|
{
|
|
ENTER;
|
|
|
|
// use repl-main
|
|
|
|
// which seperates out the tokenizer and buffer-manager thing.
|
|
|
|
// also the input string is a seperate array.
|
|
|
|
// and somehow the error statements and expressions
|
|
// write to the buffer
|
|
|
|
TODO;
|
|
#if 0
|
|
int fd;
|
|
|
|
struct termios termios;
|
|
|
|
if (test_interactive_using_input_file)
|
|
{
|
|
if ((fd = open(test_interactive_using_input_file, O_RDONLY)) < 0)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fd = 0;
|
|
|
|
if (tcgetattr(/* fd: */ 1, /* struct termios: */ &termios) < 0)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
|
|
struct termios modified_termios = termios;
|
|
|
|
modified_termios.c_lflag &= ~(unsigned) ICANON; // disable ICANON.
|
|
modified_termios.c_lflag &= ~(unsigned) ECHO; // disable ECHO.
|
|
|
|
if (tcsetattr(/* fd: */ 1, /* when?: */ TCSADRAIN, /* struct termios: */ &modified_termios) < 0)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
size_t width = 50;
|
|
|
|
enum state state = s_start;
|
|
|
|
struct letter {
|
|
wchar_t code;
|
|
|
|
struct {
|
|
uint8_t r, g, b;
|
|
} fg, bg;
|
|
};
|
|
|
|
struct {
|
|
struct letter* data;
|
|
|
|
size_t pos, cap;
|
|
} live = {};
|
|
|
|
{
|
|
live.data = smalloc(sizeof(*live.data) * width);
|
|
|
|
live.pos = 0;
|
|
|
|
live.cap = width;
|
|
|
|
memset(live.data, 0, sizeof(*live.data) * width);
|
|
}
|
|
|
|
struct {
|
|
struct letter* data;
|
|
|
|
size_t pos, n, cap;
|
|
} line = {};
|
|
|
|
void ensure_capacity(
|
|
size_t newcap)
|
|
{
|
|
while (line.cap < newcap)
|
|
{
|
|
line.cap = line.cap << 1 ?: 1;
|
|
|
|
line.data = srealloc(line.data, sizeof(*line.data) * line.cap);
|
|
}
|
|
}
|
|
|
|
void remove_from_line_at_pos(void)
|
|
{
|
|
assert(line.pos <= line.n);
|
|
|
|
memmove(
|
|
/* dest: */ line.data + line.pos,
|
|
/* src: */ line.data + line.pos + 1,
|
|
/* n: */ sizeof(*line.data) * (line.n - line.pos - 1));
|
|
|
|
line.n--;
|
|
}
|
|
|
|
void insert_into_line_at_pos(
|
|
wchar_t code)
|
|
{
|
|
assert(line.pos <= line.n);
|
|
|
|
ensure_capacity(line.n + 1);
|
|
|
|
memmove(
|
|
/* dest: */ line.data + line.pos + 1,
|
|
/* src: */ line.data + line.pos,
|
|
/* n: */ sizeof(*line.data) * (line.n - line.pos));
|
|
|
|
struct letter letter = {
|
|
.code = code,
|
|
.fg = {255, 255, 255},
|
|
.bg = { 0, 0, 0},
|
|
};
|
|
|
|
line.data[line.pos] = letter;
|
|
|
|
line.n++;
|
|
}
|
|
|
|
void redraw(void)
|
|
{
|
|
size_t pos = live.pos;
|
|
|
|
assert(line.n <= width);
|
|
|
|
struct stringtree* tree = new_stringtree();
|
|
|
|
size_t i = 0;
|
|
|
|
for (; i < width; i++)
|
|
{
|
|
struct letter* intended_letter = i < line.n ? &line.data[i] : &(struct letter) {.code = ' '};
|
|
|
|
struct letter* live_letter = &live.data[i];
|
|
|
|
if (intended_letter->code != live_letter->code)
|
|
{
|
|
if (pos != i)
|
|
{
|
|
stringtree_append_formatstr(tree, "\e[%zuG", i + 1); // Move cursor to indicated column in current row.
|
|
|
|
pos = i;
|
|
}
|
|
|
|
struct string* s = new_string(&intended_letter->code, 1);
|
|
|
|
stringtree_append_string(tree, s);
|
|
|
|
live_letter->code = intended_letter->code;
|
|
|
|
pos++;
|
|
|
|
free_string(s);
|
|
}
|
|
}
|
|
|
|
if (pos != line.pos)
|
|
{
|
|
stringtree_append_formatstr(tree, "\e[%zuG", line.pos + 1); // Move cursor to indicated column in current row.
|
|
|
|
live.pos = pos;
|
|
}
|
|
|
|
stringtree_print(tree, wc_stdout);
|
|
|
|
free_stringtree(tree);
|
|
}
|
|
|
|
struct {
|
|
uint8_t* data;
|
|
size_t n, cap;
|
|
} token = {};
|
|
|
|
void append(uint8_t byte)
|
|
{
|
|
if (token.n == token.cap)
|
|
{
|
|
token.cap = token.cap << 1 ?: 1;
|
|
|
|
token.data = srealloc(token.data, sizeof(*token.data) * token.cap);
|
|
}
|
|
|
|
token.data[token.n++] = byte;
|
|
}
|
|
|
|
void prettyprint_token(void)
|
|
{
|
|
puts("");
|
|
puts("prettyprint_token:");
|
|
|
|
for (size_t i = 0; i < token.n; i++)
|
|
{
|
|
switch (token.data[i])
|
|
{
|
|
case '\e':
|
|
printf("[%zu] = '\\e'\n", i);
|
|
break;
|
|
|
|
case '\n':
|
|
printf("[%zu] = '\\n'\n", i);
|
|
break;
|
|
|
|
default:
|
|
printf("[%zu] = '%c' (0x%02hhX)\n", i, token.data[i], token.data[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fflush(stdout);
|
|
}
|
|
|
|
struct string* filename = new_string(L"<cmdln>", (unsigned) -1);
|
|
|
|
struct position* position = new_position(
|
|
/* filename: */ filename,
|
|
/* line: */ 1,
|
|
/* column: */ 1);
|
|
|
|
for (bool keep_going = true, error = false; !error && keep_going; )
|
|
{
|
|
uint8_t buffer[4096];
|
|
|
|
ssize_t retval = read(fd, buffer, sizeof(buffer));
|
|
|
|
if (retval < 0)
|
|
{
|
|
TODO;
|
|
}
|
|
else if (!retval)
|
|
{
|
|
keep_going = false;
|
|
}
|
|
else for (ssize_t i = 0; i < retval; i++)
|
|
{
|
|
state = lookup[state][buffer[i]];
|
|
|
|
append(buffer[i]);
|
|
|
|
if (state < s_start)
|
|
{
|
|
switch (state)
|
|
{
|
|
case s_error:
|
|
{
|
|
prettyprint_token();
|
|
|
|
exit(1);
|
|
|
|
break;
|
|
}
|
|
|
|
case s_EOT:
|
|
{
|
|
keep_going = false;
|
|
|
|
break;
|
|
}
|
|
|
|
case s_backspace:
|
|
{
|
|
if (0 < line.pos)
|
|
{
|
|
line.pos--;
|
|
|
|
remove_from_line_at_pos();
|
|
|
|
redraw();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case s_enter:
|
|
{
|
|
puts("");
|
|
|
|
if (line.n)
|
|
{
|
|
wchar_t* string = smalloc(sizeof(*string) * (line.n + 1));
|
|
|
|
for (size_t j = 0, m = line.n; j < m; j++)
|
|
{
|
|
string[j] = line.data[j].code;
|
|
}
|
|
|
|
string[line.n] = 0;
|
|
|
|
struct wcistream* stream = new_string_wcistream(
|
|
/* input_string: */ string);
|
|
|
|
wcistream_read(stream);
|
|
|
|
struct tokenizer* tokenizer = new_tokenizer(
|
|
/* istream: */ stream,
|
|
/* position: */ position);
|
|
|
|
tokenizer_next(tokenizer);
|
|
|
|
struct statement* statement = parse(
|
|
/* tokenizer: */ tokenizer);
|
|
|
|
struct stringtree* etree = statement_prettyprint_errors(
|
|
/* instance: */ statement);
|
|
|
|
if (etree)
|
|
{
|
|
stringtree_println(etree,
|
|
/* wide-character stdout: */ wc_stdout);
|
|
}
|
|
else
|
|
{
|
|
struct stringtree* stree = statement_prettyprint(
|
|
/* chosen color reference: */ NULL,
|
|
/* instance: */ statement,
|
|
/* print with color? */ true);
|
|
|
|
stringtree_println(stree,
|
|
/* wide-character stdout: */ wc_stdout);
|
|
|
|
statement_execute(
|
|
/* instance: */ statement,
|
|
/* environment: */ environment,
|
|
/* booleans: */ booleans,
|
|
/* wide-character stdout: */ wc_stdout,
|
|
/* print value: */ true,
|
|
/* print with color?: */ true);
|
|
|
|
free_stringtree(stree);
|
|
}
|
|
|
|
line.n = 0;
|
|
line.pos = 0;
|
|
|
|
redraw();
|
|
|
|
free_stringtree(etree);
|
|
|
|
free_statement(statement);
|
|
|
|
free_tokenizer(tokenizer);
|
|
|
|
free_wcistream(stream);
|
|
|
|
free(string);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case s_delete:
|
|
{
|
|
if (line.pos < line.n)
|
|
{
|
|
remove_from_line_at_pos();
|
|
|
|
redraw();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case s_insert_lambda:
|
|
{
|
|
insert_into_line_at_pos(L'λ'), line.pos++;
|
|
|
|
redraw();
|
|
|
|
break;
|
|
}
|
|
|
|
case s_insert_letter:
|
|
{
|
|
insert_into_line_at_pos(buffer[i]), line.pos++;
|
|
|
|
redraw();
|
|
|
|
break;
|
|
}
|
|
|
|
case s_up:
|
|
break;
|
|
|
|
case s_down:
|
|
break;
|
|
|
|
case s_right:
|
|
{
|
|
if (line.pos < line.n)
|
|
{
|
|
line.pos++;
|
|
|
|
redraw();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case s_left:
|
|
{
|
|
if (0 < line.pos)
|
|
{
|
|
line.pos--;
|
|
|
|
redraw();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case s_home:
|
|
{
|
|
line.pos = 0;
|
|
|
|
redraw();
|
|
|
|
break;
|
|
}
|
|
|
|
case s_end:
|
|
{
|
|
line.pos = line.n;
|
|
|
|
redraw();
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
prettyprint_token();
|
|
|
|
exit(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
state = s_start;
|
|
token.n = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (test_interactive_using_input_file)
|
|
{
|
|
close(fd);
|
|
}
|
|
else
|
|
{
|
|
if (tcsetattr(/* fd: */ 1, /* when?: */ TCSADRAIN, /* struct termios: */ &termios) < 0)
|
|
{
|
|
TODO;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
free_position(position);
|
|
|
|
free_string(filename);
|
|
|
|
free(token.data);
|
|
|
|
free(live.data);
|
|
|
|
free(line.data);
|
|
|
|
#endif
|
|
|
|
EXIT;
|
|
}
|
|
|
|
#if 0
|
|
|
|
// maybe this should go into it's own file, because it's complicated \
|
|
beyond parsing and executing the expression.
|
|
// maintain the line as a character array
|
|
// whenever there's a mutation, re-parse
|
|
// if the text is empty:
|
|
// grey the prompt
|
|
// the input will never contain a newline, it might contain a semicolon
|
|
// color the expression \
|
|
which will color the tokens \
|
|
maybe set all tokens to grey first?
|
|
// redraw the input line with colored tokens
|
|
// the parse will come back happy or unhappy:
|
|
// if happy:
|
|
// make the prompt green
|
|
// if unhappy:
|
|
// make prompt red
|
|
// for all found 'syntax error' expressions:
|
|
// print it's message on a new line, offset at the position \
|
|
of the incedent.
|
|
// if the user hits enter:
|
|
// ... on an empty prompt:
|
|
// new line, redraw grey prompt.
|
|
// ... on an unhappy expression:
|
|
// maybe blink the error messages?
|
|
// ... on a happy expression:
|
|
// new line
|
|
// evaluate
|
|
// '${id} = ' print result
|
|
// new line
|
|
// grey the prompt.
|
|
// increment line number
|
|
// save the result into '${id++}'
|
|
// free result
|
|
#endif
|
|
|
|
#endif
|