summaryrefslogtreecommitdiffstatshomepage
path: root/lib/utils/pyexec.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils/pyexec.c')
-rw-r--r--lib/utils/pyexec.c118
1 files changed, 118 insertions, 0 deletions
diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c
index 2b86af3bba..86321ac129 100644
--- a/lib/utils/pyexec.c
+++ b/lib/utils/pyexec.c
@@ -56,6 +56,7 @@ STATIC bool repl_display_debugging_info = 0;
#define EXEC_FLAG_SOURCE_IS_RAW_CODE (8)
#define EXEC_FLAG_SOURCE_IS_VSTR (16)
#define EXEC_FLAG_SOURCE_IS_FILENAME (32)
+#define EXEC_FLAG_SOURCE_IS_READER (64)
// parses, compiles and executes the code in the lexer
// frees the lexer before returning
@@ -91,6 +92,8 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
const vstr_t *vstr = source;
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
+ } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
+ lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source);
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
lex = mp_lexer_new_from_file(source);
} else {
@@ -122,6 +125,12 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
// uncaught exception
mp_hal_set_interrupt_char(-1); // disable interrupt
mp_handle_pending(false); // clear any pending exceptions (and run any callbacks)
+
+ if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
+ const mp_reader_t *reader = source;
+ reader->close(reader->data);
+ }
+
// print EOF after normal output
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
mp_hal_stdout_tx_strn("\x04", 1);
@@ -170,6 +179,99 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
}
#if MICROPY_ENABLE_COMPILER
+
+// This can be configured by a port (and even configured to a function to be
+// computed dynamically) to indicate the maximum number of bytes that can be
+// held in the stdin buffer.
+#ifndef MICROPY_REPL_STDIN_BUFFER_MAX
+#define MICROPY_REPL_STDIN_BUFFER_MAX (256)
+#endif
+
+typedef struct _mp_reader_stdin_t {
+ bool eof;
+ uint16_t window_max;
+ uint16_t window_remain;
+} mp_reader_stdin_t;
+
+STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) {
+ mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data;
+
+ if (reader->eof) {
+ return MP_READER_EOF;
+ }
+
+ int c = mp_hal_stdin_rx_chr();
+
+ if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) {
+ reader->eof = true;
+ mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
+ if (c == CHAR_CTRL_C) {
+ #if MICROPY_KBD_EXCEPTION
+ MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
+ nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
+ #else
+ mp_raise_type(&mp_type_KeyboardInterrupt);
+ #endif
+ } else {
+ return MP_READER_EOF;
+ }
+ }
+
+ if (--reader->window_remain == 0) {
+ mp_hal_stdout_tx_strn("\x01", 1); // indicate window available to host
+ reader->window_remain = reader->window_max;
+ }
+
+ return c;
+}
+
+STATIC void mp_reader_stdin_close(void *data) {
+ mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data;
+ if (!reader->eof) {
+ reader->eof = true;
+ mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
+ for (;;) {
+ int c = mp_hal_stdin_rx_chr();
+ if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) {
+ break;
+ }
+ }
+ }
+}
+
+STATIC void mp_reader_new_stdin(mp_reader_t *reader, mp_reader_stdin_t *reader_stdin, uint16_t buf_max) {
+ // Make flow-control window half the buffer size, and indicate to the host that 2x windows are
+ // free (sending the window size implicitly indicates that a window is free, and then the 0x01
+ // indicates that another window is free).
+ size_t window = buf_max / 2;
+ char reply[3] = { window & 0xff, window >> 8, 0x01 };
+ mp_hal_stdout_tx_strn(reply, sizeof(reply));
+
+ reader_stdin->eof = false;
+ reader_stdin->window_max = window;
+ reader_stdin->window_remain = window;
+ reader->data = reader_stdin;
+ reader->readbyte = mp_reader_stdin_readbyte;
+ reader->close = mp_reader_stdin_close;
+}
+
+STATIC int do_reader_stdin(int c) {
+ if (c != 'A') {
+ // Unsupported command.
+ mp_hal_stdout_tx_strn("R\x00", 2);
+ return 0;
+ }
+
+ // Indicate reception of command.
+ mp_hal_stdout_tx_strn("R\x01", 2);
+
+ mp_reader_t reader;
+ mp_reader_stdin_t reader_stdin;
+ mp_reader_new_stdin(&reader, &reader_stdin, MICROPY_REPL_STDIN_BUFFER_MAX);
+ int exec_flags = EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_READER;
+ return parse_compile_execute(&reader, MP_PARSE_FILE_INPUT, exec_flags);
+}
+
#if MICROPY_REPL_EVENT_DRIVEN
typedef struct _repl_t {
@@ -203,6 +305,13 @@ void pyexec_event_repl_init(void) {
STATIC int pyexec_raw_repl_process_char(int c) {
if (c == CHAR_CTRL_A) {
// reset raw REPL
+ if (vstr_len(MP_STATE_VM(repl_line)) == 2 && vstr_str(MP_STATE_VM(repl_line))[0] == CHAR_CTRL_E) {
+ int ret = do_reader_stdin(vstr_str(MP_STATE_VM(repl_line))[1]);
+ if (ret & PYEXEC_FORCED_EXIT) {
+ return ret;
+ }
+ goto reset;
+ }
mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
goto reset;
} else if (c == CHAR_CTRL_B) {
@@ -388,6 +497,15 @@ raw_repl_reset:
int c = mp_hal_stdin_rx_chr();
if (c == CHAR_CTRL_A) {
// reset raw REPL
+ if (vstr_len(&line) == 2 && vstr_str(&line)[0] == CHAR_CTRL_E) {
+ int ret = do_reader_stdin(vstr_str(&line)[1]);
+ if (ret & PYEXEC_FORCED_EXIT) {
+ return ret;
+ }
+ vstr_reset(&line);
+ mp_hal_stdout_tx_str(">");
+ continue;
+ }
goto raw_repl_reset;
} else if (c == CHAR_CTRL_B) {
// change to friendly REPL