#if 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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"", (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