diff options
Diffstat (limited to 'apps/interpreters/micropython/pyexec.c')
-rw-r--r-- | apps/interpreters/micropython/pyexec.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/apps/interpreters/micropython/pyexec.c b/apps/interpreters/micropython/pyexec.c new file mode 100644 index 000000000..b8d95643b --- /dev/null +++ b/apps/interpreters/micropython/pyexec.c @@ -0,0 +1,404 @@ +/**************************************************************************** + * interpreters/micropython/py_readline.c + * + * This file was part of the Micro Python project, http://micropython.org/ + * and has been integrated into Nuttx by Dave Marples (dave@marples.net) + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <time.h> + +#include "py/nlr.h" +#include "py/parsehelper.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/pfenv.h" + +#include "py_readline.h" +#include "pyexec.h" +#include "genhdr/py-version.h" + +#define EXEC_FLAG_PRINT_EOF (1) +#define EXEC_FLAG_ALLOW_DEBUGGING (2) +#define EXEC_FLAG_IS_REPL (4) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; +STATIC bool repl_display_debugging_info = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* parses, compiles and executes the code in the lexer + * frees the lexer before returning + * EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after + * exception output + * EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after + * executing the code + * EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) + */ + +STATIC int parse_compile_execute(mp_lexer_t * lex, + mp_parse_input_kind_t input_kind, + int exec_flags) +{ + int ret = 0; + + mp_parse_error_kind_t parse_error_kind; + mp_parse_node_t pn = mp_parse(lex, input_kind, &parse_error_kind); + qstr source_name = lex->source_name; + + /* check for parse error */ + + if (pn == MP_PARSE_NODE_NULL) + { + if (exec_flags & EXEC_FLAG_PRINT_EOF) + { + fprintf(stdout, "\x04"); + } + + mp_parse_show_exception(lex, parse_error_kind); + mp_lexer_free(lex); + goto finish; + } + + mp_lexer_free(lex); + + mp_obj_t module_fun = + mp_compile(pn, source_name, MP_EMIT_OPT_NONE, + exec_flags & EXEC_FLAG_IS_REPL); + + /* check for compile error */ + + if (mp_obj_is_exception_instance(module_fun)) + { + if (exec_flags & EXEC_FLAG_PRINT_EOF) + { + fprintf(stdout, "\x04"); + } + + mp_obj_print_exception(printf_wrapper, NULL, module_fun); + goto finish; + } + + /* execute code */ + + nlr_buf_t nlr; + + struct timespec start; + clock_gettime(CLOCK_REALTIME, &start); + + if (nlr_push(&nlr) == 0) + { + // mp_hal_set_interrupt_char(CHAR_CTRL_C); /* allow ctrl-C to interrupt us */ + mp_call_function_0(module_fun); + // mp_hal_set_interrupt_char(-1); /* disable interrupt */ + + nlr_pop(); + ret = 1; + if (exec_flags & EXEC_FLAG_PRINT_EOF) + { + fprintf(stdout, "\x04"); + } + } + else + { + /* uncaught exception */ + + // mp_hal_set_interrupt_char(-1); /* disable interrupt */ + + /* print EOF after normal output */ + + if (exec_flags & EXEC_FLAG_PRINT_EOF) + { + fprintf(stdout, "\x04"); + } + + /* check for SystemExit */ + + if (mp_obj_is_subclass_fast + (mp_obj_get_type((mp_obj_t) nlr.ret_val), &mp_type_SystemExit)) + { + /* at the moment, the value of SystemExit is unused */ + + ret = PYEXEC_FORCED_EXIT; + } + else + { + mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t) nlr.ret_val); + ret = 0; + } + } + + /* display debugging info if wanted */ + + if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) + { + struct timespec endTime; + clock_gettime(CLOCK_REALTIME, &endTime); + + mp_uint_t ticks = ((endTime.tv_sec - start.tv_sec) * 1000) + + ((((endTime.tv_nsec / 1000000) + 1000) - + (start.tv_nsec / 1000000)) % 1000); + + printf("took " UINT_FMT " ms\n", ticks); + + /* qstr info */ + + { + mp_uint_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; + qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); + printf("qstr:\n n_pool=" UINT_FMT "\n n_qstr=" UINT_FMT + "\n n_str_data_bytes=" UINT_FMT "\n n_total_bytes=" UINT_FMT + "\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes); + } + } + +finish: + if (exec_flags & EXEC_FLAG_PRINT_EOF) + { + fprintf(stdout, "\x04"); + } + + return ret; +} + +int pyexec_raw_repl(void) +{ + vstr_t line; + vstr_init(&line, 32); + +raw_repl_reset: + fprintf(stdout, "raw REPL; CTRL-B to exit\r\n"); + + for (;;) + { + vstr_reset(&line); + fputc('>', stdout); + fflush(stdout); + + for (;;) + { + char c = getc(stdin); + if (c == CHAR_CTRL_A) + { + /* reset raw REPL */ + + goto raw_repl_reset; + } + else if (c == CHAR_CTRL_B) + { + /* change to friendly REPL */ + + fprintf(stdout, "\r\n"); + vstr_clear(&line); + pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; + return 0; + } + else if (c == CHAR_CTRL_C) + { + /* clear line */ + + vstr_reset(&line); + } + else if (c == CHAR_CTRL_D) + { + /* input finished */ + + break; + } + else if (c <= 127) + { + /* let through any other ASCII character */ + + vstr_add_char(&line, c); + fputc(c, stdout); + } + fflush(stdout); + } + + /* indicate reception of command */ + + fprintf(stdout, "OK"); + + if (line.len == 0) + { + /* exit for a soft reset */ + + fprintf(stdout, "\r\n"); + vstr_clear(&line); + return PYEXEC_FORCED_EXIT; + } + + mp_lexer_t *lex = + mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0); + if (lex == NULL) + { + printf("\x04MemoryError\n\x04"); + } + else + { + int ret = + parse_compile_execute(lex, MP_PARSE_FILE_INPUT, + EXEC_FLAG_PRINT_EOF); + if (ret & PYEXEC_FORCED_EXIT) + { + return ret; + } + } + fflush(stdout); + } +} + +int pyexec_friendly_repl(void) +{ + vstr_t line; + vstr_init(&line, 32); + +friendly_repl_reset: + fprintf(stdout, + "Micro Python " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE + "; NuttX with " CONFIG_ARCH_FAMILY " " CONFIG_ARCH_CHIP "\r\n"); + fprintf(stdout, "Type \"help()\" for more information.\r\n"); + + for (;;) + { + input_restart: + vstr_reset(&line); + int ret = py_readline(&line, ">>> "); + + if (ret == CHAR_CTRL_A) + { + /* change to raw REPL */ + + fprintf(stdout, "\r\n"); + vstr_clear(&line); + pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; + return 0; + } + else if (ret == CHAR_CTRL_B) + { + /* reset friendly REPL */ + + fprintf(stdout, "\r\n"); + goto friendly_repl_reset; + } + else if (ret == CHAR_CTRL_C) + { + /* break */ + + fprintf(stdout, "\r\n"); + continue; + } + else if (ret == CHAR_CTRL_D) + { + /* exit for a soft reset */ + + fprintf(stdout, "\r\n"); + vstr_clear(&line); + return PYEXEC_FORCED_EXIT; + } + else if (vstr_len(&line) == 0) + { + continue; + } + + while (mp_repl_continue_with_input(vstr_str(&line))) + { + vstr_add_char(&line, '\n'); + int ret = py_readline(&line, "... "); + if (ret == CHAR_CTRL_C) + { + /* cancel everything */ + + fprintf(stdout, "\r\n"); + goto input_restart; + } + else if (ret == CHAR_CTRL_D) + { + /* stop entering compound statement */ + + break; + } + } + + mp_lexer_t *lex = + mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr_str(&line), + vstr_len(&line), 0); + if (lex == NULL) + { + printf("MemoryError\n"); + } + else + { + int ret = + parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, + EXEC_FLAG_ALLOW_DEBUGGING | + EXEC_FLAG_IS_REPL); + if (ret & PYEXEC_FORCED_EXIT) + { + return ret; + } + } + } +} + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +int pyexec_file(const char *filename) +{ + mp_lexer_t *lex = mp_lexer_new_from_file(filename); + + if (lex == NULL) + { + printf("could not open file '%s' for reading\n", filename); + return false; + } + + return parse_compile_execute(lex, MP_PARSE_FILE_INPUT, 0); +} + +mp_obj_t pyb_set_repl_info(mp_obj_t o_value) +{ + repl_display_debugging_info = mp_obj_get_int(o_value); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj, pyb_set_repl_info); |