mp_obj_t str_format(uint n_args, const mp_obj_t *args) { assert(MP_OBJ_IS_STR(args[0])); GET_STR_DATA_LEN(args[0], str, len); int arg_i = 1; vstr_t *vstr = vstr_new(); for (const byte *top = str + len; str < top; str++) { if (*str == '{') { str++; if (str < top && *str == '{') { vstr_add_char(vstr, '{'); } else { while (str < top && *str != '}') str++; if (arg_i >= n_args) { nlr_jump(mp_obj_new_exception_msg(MP_QSTR_IndexError, "tuple index out of range")); } // TODO: may be PRINT_REPR depending on formatting code mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, vstr, args[arg_i], PRINT_STR); arg_i++; } } else { vstr_add_char(vstr, *str); } } mp_obj_t s = mp_obj_new_str((byte*)vstr->buf, vstr->len, false); vstr_free(vstr); return s; }
STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *dest) { #if MICROPY_PY_SYS // extract the list of paths mp_uint_t path_num; mp_obj_t *path_items; mp_obj_list_get(mp_sys_path, &path_num, &path_items); if (path_num == 0) { #endif // mp_sys_path is empty, so just use the given file name vstr_add_strn(dest, file_str, file_len); return stat_dir_or_file(dest); #if MICROPY_PY_SYS } else { // go through each path looking for a directory or file for (mp_uint_t i = 0; i < path_num; i++) { vstr_reset(dest); mp_uint_t p_len; const char *p = mp_obj_str_get_data(path_items[i], &p_len); if (p_len > 0) { vstr_add_strn(dest, p, p_len); vstr_add_char(dest, PATH_SEP_CHAR); } vstr_add_strn(dest, file_str, file_len); mp_import_stat_t stat = stat_dir_or_file(dest); if (stat != MP_IMPORT_STAT_NO_EXIST) { return stat; } } // could not find a directory or file return MP_IMPORT_STAT_NO_EXIST; } #endif }
int pyexec_raw_repl(void) { vstr_t line; vstr_init(&line, 32); raw_repl_reset: mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); for (;;) { vstr_reset(&line); mp_hal_stdout_tx_str(">"); for (;;) { int c = mp_hal_stdin_rx_chr(); if (c == CHAR_CTRL_A) { // reset raw REPL goto raw_repl_reset; } else if (c == CHAR_CTRL_B) { // change to friendly REPL mp_hal_stdout_tx_str("\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); } } // indicate reception of command mp_hal_stdout_tx_str("OK"); if (line.len == 0) { // exit for a soft reset mp_hal_stdout_tx_str("\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; } } } }
int pyexec_raw_repl(void) { vstr_t line; vstr_init(&line, 32); raw_repl_reset: stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); for (;;) { vstr_reset(&line); stdout_tx_str(">"); for (;;) { char c = stdin_rx_chr(); if (c == VCP_CHAR_CTRL_A) { // reset raw REPL goto raw_repl_reset; } else if (c == VCP_CHAR_CTRL_B) { // change to friendly REPL stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; return 0; } else if (c == VCP_CHAR_CTRL_C) { // clear line vstr_reset(&line); } else if (c == VCP_CHAR_CTRL_D) { // input finished break; } else if (c <= 127) { // let through any other ASCII character vstr_add_char(&line, c); } } // indicate reception of command stdout_tx_str("OK"); if (line.len == 0) { // exit for a soft reset stdout_tx_str("\r\n"); vstr_clear(&line); return 1; } mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, 0); if (lex == NULL) { printf("MemoryError\n"); } else { parse_compile_execute(lex, MP_PARSE_FILE_INPUT, false); } // indicate end of output with EOF character stdout_tx_str("\004"); } }
mp_obj_t mp_builtin___import__(mp_uint_t n_args, const mp_obj_t *args) { #if DEBUG_PRINT DEBUG_printf("__import__:\n"); for (mp_uint_t i = 0; i < n_args; i++) { DEBUG_printf(" "); mp_obj_print(args[i], PRINT_REPR); DEBUG_printf("\n"); } #endif mp_obj_t module_name = args[0]; mp_obj_t fromtuple = mp_const_none; mp_int_t level = 0; if (n_args >= 4) { fromtuple = args[3]; if (n_args >= 5) { level = MP_OBJ_SMALL_INT_VALUE(args[4]); } } mp_uint_t mod_len; const char *mod_str = mp_obj_str_get_data(module_name, &mod_len); if (level != 0) { // What we want to do here is to take name of current module, // chop <level> trailing components, and concatenate with passed-in // module name, thus resolving relative import name into absolue. // This even appears to be correct per // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "Relative imports use a module's __name__ attribute to determine that // module's position in the package hierarchy." level--; mp_obj_t this_name_q = mp_obj_dict_get(mp_globals_get(), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(this_name_q != MP_OBJ_NULL); #if MICROPY_CPYTHON_COMPAT if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) { // This is a module run by -m command-line switch, get its real name from backup attribute this_name_q = mp_obj_dict_get(mp_globals_get(), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif mp_map_t *globals_map = mp_obj_dict_get_map(mp_globals_get()); mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); bool is_pkg = (elem != NULL); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); mp_obj_print(this_name_q, PRINT_REPR); DEBUG_printf(", is_package: %d", is_pkg); DEBUG_printf("\n"); #endif mp_uint_t this_name_l; const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l); const char *p = this_name + this_name_l; if (!is_pkg) { // We have module, but relative imports are anchored at package, so // go there. chop_component(this_name, &p); } uint dots_seen = 0; while (level--) { chop_component(this_name, &p); dots_seen++; } if (dots_seen == 0 && level >= 1) { // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "If the module's name does not contain any package information // (e.g. it is set to '__main__') then relative imports are // resolved as if the module were a top level module, regardless // of where the module is actually located on the file system." // Supposedly this if catches this condition and resolve it properly // TODO: But nobody knows for sure. This condition happens when // package's __init__.py does something like "import .submod". So, // maybe we should check for package here? But quote above doesn't // talk about packages, it talks about dot-less module names. DEBUG_printf("Warning: no dots in current module name and level>0\n"); p = this_name + this_name_l; } else if (level != -1) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "Invalid relative import")); } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); char *new_mod = alloca(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len); } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); if (new_mod_q == MP_QSTR_) { // CPython raises SystemError nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "cannot perform relative import")); } module_name = MP_OBJ_NEW_QSTR(new_mod_q); mod_str = new_mod; mod_len = new_mod_l; } // check if module already exists qstr module_name_qstr = mp_obj_str_get_qstr(module_name); mp_obj_t module_obj = mp_module_get(module_name_qstr); if (module_obj != MP_OBJ_NULL) { DEBUG_printf("Module already loaded\n"); // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } DEBUG_printf("Module not yet loaded\n"); #if MICROPY_MODULE_FROZEN mp_lexer_t *lex = mp_find_frozen_module(mod_str, mod_len); if (lex != NULL) { module_obj = mp_obj_new_module(module_name_qstr); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). // TODO: Duplicated below too. if (fromtuple == mp_const_false) { mp_obj_module_t *o = module_obj; mp_obj_dict_store(o->globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } do_load_from_lexer(module_obj, lex, mod_str); return module_obj; } #endif uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path)); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { #if MICROPY_MODULE_WEAK_LINKS // check if there is a weak link to this module if (i == mod_len) { mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); if (el == NULL) { goto no_exist; } // found weak linked module module_obj = el->value; } else { no_exist: #else { #endif // couldn't find the file, so fail if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "module not found")); } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "no module named '%q'", mod_name)); } } } else { // found the file, so get the module module_obj = mp_module_get(mod_name); } if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). if (i == mod_len && fromtuple == mp_const_false) { mp_obj_module_t *o = module_obj; mp_obj_dict_store(o->globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); #if MICROPY_CPYTHON_COMPAT // Store real name in "__main__" attribute. Choosen semi-randonly, to reuse existing qstr's. mp_obj_dict_store(o->globals, MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(mod_name)); #endif } if (stat == MP_IMPORT_STAT_DIR) { DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false)); vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (mp_import_stat(vstr_null_terminated_str(&path)) != MP_IMPORT_STAT_FILE) { vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { do_load(module_obj, &path); vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py } } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // TODO: We cannot just break here, at the very least, we must execute // trailer code below. But otherwise if there're remaining components, // that would be (??) object path within module, not modules path within FS. // break; } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } }
STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, mp_token_t *tok, bool first_token) { // skip white space and comments bool had_physical_newline = false; while (!is_end(lex)) { if (is_physical_newline(lex)) { had_physical_newline = true; next_char(lex); } else if (is_whitespace(lex)) { next_char(lex); } else if (is_char(lex, '#')) { next_char(lex); while (!is_end(lex) && !is_physical_newline(lex)) { next_char(lex); } // had_physical_newline will be set on next loop } else if (is_char(lex, '\\')) { // backslash (outside string literals) must appear just before a physical newline next_char(lex); if (!is_physical_newline(lex)) { // SyntaxError: unexpected character after line continuation character tok->src_line = lex->line; tok->src_column = lex->column; tok->kind = MP_TOKEN_BAD_LINE_CONTINUATION; vstr_reset(&lex->vstr); tok->str = vstr_str(&lex->vstr); tok->len = 0; return; } else { next_char(lex); } } else { break; } } // set token source information tok->src_line = lex->line; tok->src_column = lex->column; // start new token text vstr_reset(&lex->vstr); if (first_token && lex->line == 1 && lex->column != 1) { // check that the first token is in the first column // if first token is not on first line, we get a physical newline and // this check is done as part of normal indent/dedent checking below // (done to get equivalence with CPython) tok->kind = MP_TOKEN_INDENT; } else if (lex->emit_dent < 0) { tok->kind = MP_TOKEN_DEDENT; lex->emit_dent += 1; } else if (lex->emit_dent > 0) { tok->kind = MP_TOKEN_INDENT; lex->emit_dent -= 1; } else if (had_physical_newline && lex->nested_bracket_level == 0) { tok->kind = MP_TOKEN_NEWLINE; uint num_spaces = lex->column - 1; lex->emit_dent = 0; if (num_spaces == indent_top(lex)) { } else if (num_spaces > indent_top(lex)) { indent_push(lex, num_spaces); lex->emit_dent += 1; } else { while (num_spaces < indent_top(lex)) { indent_pop(lex); lex->emit_dent -= 1; } if (num_spaces != indent_top(lex)) { tok->kind = MP_TOKEN_DEDENT_MISMATCH; } } } else if (is_end(lex)) { if (indent_top(lex) > 0) { tok->kind = MP_TOKEN_NEWLINE; lex->emit_dent = 0; while (indent_top(lex) > 0) { indent_pop(lex); lex->emit_dent -= 1; } } else { tok->kind = MP_TOKEN_END; } } else if (is_char_or(lex, '\'', '\"') || (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"')) || ((is_char_and(lex, 'r', 'b') || is_char_and(lex, 'b', 'r')) && is_char_following_following_or(lex, '\'', '\"'))) { // a string or bytes literal // parse type codes bool is_raw = false; bool is_bytes = false; if (is_char(lex, 'u')) { next_char(lex); } else if (is_char(lex, 'b')) { is_bytes = true; next_char(lex); if (is_char(lex, 'r')) { is_raw = true; next_char(lex); } } else if (is_char(lex, 'r')) { is_raw = true; next_char(lex); if (is_char(lex, 'b')) { is_bytes = true; next_char(lex); } } // set token kind if (is_bytes) { tok->kind = MP_TOKEN_BYTES; } else { tok->kind = MP_TOKEN_STRING; } // get first quoting character char quote_char = '\''; if (is_char(lex, '\"')) { quote_char = '\"'; } next_char(lex); // work out if it's a single or triple quoted literal int num_quotes; if (is_char_and(lex, quote_char, quote_char)) { // triple quotes next_char(lex); next_char(lex); num_quotes = 3; } else { // single quotes num_quotes = 1; } // parse the literal int n_closing = 0; while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) { if (is_char(lex, quote_char)) { n_closing += 1; vstr_add_char(&lex->vstr, CUR_CHAR(lex)); } else { n_closing = 0; if (is_char(lex, '\\')) { next_char(lex); unichar c = CUR_CHAR(lex); if (is_raw) { // raw strings allow escaping of quotes, but the backslash is also emitted vstr_add_char(&lex->vstr, '\\'); } else { switch (c) { case MP_LEXER_CHAR_EOF: break; // TODO a proper error message? case '\n': c = MP_LEXER_CHAR_EOF; break; // TODO check this works correctly (we are supposed to ignore it case '\\': break; case '\'': break; case '"': break; case 'a': c = 0x07; break; case 'b': c = 0x08; break; case 't': c = 0x09; break; case 'n': c = 0x0a; break; case 'v': c = 0x0b; break; case 'f': c = 0x0c; break; case 'r': c = 0x0d; break; case 'u': case 'U': if (is_bytes) { // b'\u1234' == b'\\u1234' vstr_add_char(&lex->vstr, '\\'); break; } // Otherwise fall through. case 'x': { uint num = 0; if (!get_hex(lex, (c == 'x' ? 2 : c == 'u' ? 4 : 8), &num)) { // TODO error message assert(0); } c = num; break; } case 'N': // Supporting '\N{LATIN SMALL LETTER A}' == 'a' would require keeping the // entire Unicode name table in the core. As of Unicode 6.3.0, that's nearly // 3MB of text; even gzip-compressed and with minimal structure, it'll take // roughly half a meg of storage. This form of Unicode escape may be added // later on, but it's definitely not a priority right now. -- CJA 20140607 assert(!"Unicode name escapes not supported"); break; default: if (c >= '0' && c <= '7') { // Octal sequence, 1-3 chars int digits = 3; int num = c - '0'; while (is_following_odigit(lex) && --digits != 0) { next_char(lex); num = num * 8 + (CUR_CHAR(lex) - '0'); } c = num; } else { // unrecognised escape character; CPython lets this through verbatim as '\' and then the character vstr_add_char(&lex->vstr, '\\'); } break; } } if (c != MP_LEXER_CHAR_EOF) { if (c < 0x110000 && !is_bytes) { vstr_add_char(&lex->vstr, c); } else if (c < 0x100 && is_bytes) { vstr_add_byte(&lex->vstr, c); } else { assert(!"TODO: Throw an error, invalid escape code probably"); } } } else { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); } } next_char(lex); } // check we got the required end quotes if (n_closing < num_quotes) { tok->kind = MP_TOKEN_LONELY_STRING_OPEN; } // cut off the end quotes from the token text vstr_cut_tail_bytes(&lex->vstr, n_closing); } else if (is_head_of_identifier(lex)) { tok->kind = MP_TOKEN_NAME; // get first char vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); // get tail chars while (!is_end(lex) && is_tail_of_identifier(lex)) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } } else if (is_digit(lex) || (is_char(lex, '.') && is_following_digit(lex))) { tok->kind = MP_TOKEN_NUMBER; // get first char vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); // get tail chars while (!is_end(lex)) { if (is_char_or(lex, 'e', 'E')) { vstr_add_char(&lex->vstr, 'e'); next_char(lex); if (is_char(lex, '+') || is_char(lex, '-')) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } } else if (is_letter(lex) || is_digit(lex) || is_char_or(lex, '_', '.')) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } else { break; } } } else if (is_char(lex, '.')) { // special handling for . and ... operators, because .. is not a valid operator // get first char vstr_add_char(&lex->vstr, '.'); next_char(lex); if (is_char_and(lex, '.', '.')) { vstr_add_char(&lex->vstr, '.'); vstr_add_char(&lex->vstr, '.'); next_char(lex); next_char(lex); tok->kind = MP_TOKEN_ELLIPSIS; } else { tok->kind = MP_TOKEN_DEL_PERIOD; } } else { // search for encoded delimiter or operator const char *t = tok_enc; uint tok_enc_index = 0; for (; *t != 0 && !is_char(lex, *t); t += 1) { if (*t == 'e' || *t == 'c') { t += 1; } else if (*t == 'E') { tok_enc_index -= 1; t += 1; } tok_enc_index += 1; } next_char(lex); if (*t == 0) { // didn't match any delimiter or operator characters tok->kind = MP_TOKEN_INVALID; } else { // matched a delimiter or operator character // get the maximum characters for a valid token t += 1; uint t_index = tok_enc_index; for (;;) { for (; *t == 'e'; t += 1) { t += 1; t_index += 1; if (is_char(lex, *t)) { next_char(lex); tok_enc_index = t_index; break; } } if (*t == 'E') { t += 1; if (is_char(lex, *t)) { next_char(lex); tok_enc_index = t_index; } else { tok->kind = MP_TOKEN_INVALID; goto tok_enc_no_match; } break; } if (*t == 'c') { t += 1; t_index += 1; if (is_char(lex, *t)) { next_char(lex); tok_enc_index = t_index; t += 1; } else { break; } } else { break; } } // set token kind tok->kind = tok_enc_kind[tok_enc_index]; tok_enc_no_match: // compute bracket level for implicit line joining if (tok->kind == MP_TOKEN_DEL_PAREN_OPEN || tok->kind == MP_TOKEN_DEL_BRACKET_OPEN || tok->kind == MP_TOKEN_DEL_BRACE_OPEN) { lex->nested_bracket_level += 1; } else if (tok->kind == MP_TOKEN_DEL_PAREN_CLOSE || tok->kind == MP_TOKEN_DEL_BRACKET_CLOSE || tok->kind == MP_TOKEN_DEL_BRACE_CLOSE) { lex->nested_bracket_level -= 1; } } } // point token text to vstr buffer tok->str = vstr_str(&lex->vstr); tok->len = vstr_len(&lex->vstr); // check for keywords if (tok->kind == MP_TOKEN_NAME) { // We check for __debug__ here and convert it to its value. This is so // the parser gives a syntax error on, eg, x.__debug__. Otherwise, we // need to check for this special token in many places in the compiler. // TODO improve speed of these string comparisons //for (int i = 0; tok_kw[i] != NULL; i++) { for (int i = 0; i < MP_ARRAY_SIZE(tok_kw); i++) { if (str_strn_equal(tok_kw[i], tok->str, tok->len)) { if (i == MP_ARRAY_SIZE(tok_kw) - 1) { // tok_kw[MP_ARRAY_SIZE(tok_kw) - 1] == "__debug__" tok->kind = (mp_optimise_value == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE); } else { tok->kind = MP_TOKEN_KW_FALSE + i; } break; } } } }
int pyexec_friendly_repl(void) { vstr_t line; vstr_init(&line, 32); #if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD // in host mode, we enable the LCD for the repl mp_obj_t lcd_o = mp_call_function_0(mp_load_name(qstr_from_str("LCD"))); mp_call_function_1(mp_load_attr(lcd_o, qstr_from_str("light")), mp_const_true); #endif friendly_repl_reset: stdout_tx_str("Micro Python " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n"); stdout_tx_str("Type \"help()\" for more information.\r\n"); // to test ctrl-C /* { uint32_t x[4] = {0x424242, 0xdeaddead, 0x242424, 0xdeadbeef}; for (;;) { nlr_buf_t nlr; printf("pyexec_repl: %p\n", x); mp_hal_set_interrupt_char(CHAR_CTRL_C); if (nlr_push(&nlr) == 0) { for (;;) { } } else { printf("break\n"); } } } */ for (;;) { input_restart: vstr_reset(&line); int ret = readline(&line, ">>> "); if (ret == CHAR_CTRL_A) { // change to raw REPL stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; return 0; } else if (ret == CHAR_CTRL_B) { // reset friendly REPL stdout_tx_str("\r\n"); goto friendly_repl_reset; } else if (ret == CHAR_CTRL_C) { // break stdout_tx_str("\r\n"); continue; } else if (ret == CHAR_CTRL_D) { // exit for a soft reset stdout_tx_str("\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 = readline(&line, "... "); if (ret == CHAR_CTRL_C) { // cancel everything stdout_tx_str("\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; } } } }
mp_obj_t mp_builtin___import__(uint n_args, mp_obj_t *args) { #if DEBUG_PRINT printf("__import__:\n"); for (int i = 0; i < n_args; i++) { printf(" "); mp_obj_print(args[i], PRINT_REPR); printf("\n"); } #endif mp_obj_t module_name = args[0]; mp_obj_t fromtuple = mp_const_none; int level = 0; if (n_args >= 4) { fromtuple = args[3]; if (n_args >= 5) { level = MP_OBJ_SMALL_INT_VALUE(args[4]); } } uint mod_len; const char *mod_str = (const char*)mp_obj_str_get_data(module_name, &mod_len); if (level != 0) { // What we want to do here is to take name of current module, // chop <level> trailing components, and concatenate with passed-in // module name, thus resolving relative import name into absolue. // This even appears to be correct per // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "Relative imports use a module's __name__ attribute to determine that // module's position in the package hierarchy." mp_obj_t this_name_q = mp_obj_dict_get(mp_globals_get(), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(this_name_q != MP_OBJ_NULL); #if DEBUG_PRINT printf("Current module: "); mp_obj_print(this_name_q, PRINT_REPR); printf("\n"); #endif uint this_name_l; const char *this_name = (const char*)mp_obj_str_get_data(this_name_q, &this_name_l); uint dots_seen = 0; const char *p = this_name + this_name_l - 1; while (p > this_name) { if (*p == '.') { dots_seen++; if (--level == 0) { break; } } p--; } if (dots_seen == 0 && level == 1) { // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "If the module's name does not contain any package information // (e.g. it is set to '__main__') then relative imports are // resolved as if the module were a top level module, regardless // of where the module is actually located on the file system." // Supposedly this if catches this condition and resolve it properly // TODO: But nobody knows for sure. This condition happens when // package's __init__.py does something like "import .submod". So, // maybe we should check for package here? But quote above doesn't // talk about packages, it talks about dot-less module names. p = this_name + this_name_l; } else if (level != 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "Invalid relative import")); } uint new_mod_l = (mod_len == 0 ? p - this_name : p - this_name + 1 + mod_len); char *new_mod = alloca(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len); } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); DEBUG_printf("Resolved relative name: %s\n", qstr_str(new_mod_q)); module_name = MP_OBJ_NEW_QSTR(new_mod_q); mod_str = new_mod; mod_len = new_mod_l; } // check if module already exists mp_obj_t module_obj = mp_module_get(mp_obj_str_get_qstr(module_name)); if (module_obj != MP_OBJ_NULL) { DEBUG_printf("Module already loaded\n"); // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } DEBUG_printf("Module not yet loaded\n"); uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); DEBUG_printf("Previous path: %s\n", vstr_str(&path)); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } DEBUG_printf("Current path: %s\n", vstr_str(&path)); // fail if we couldn't find the file if (stat == MP_IMPORT_STAT_NO_EXIST) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "No module named '%s'", qstr_str(mod_name))); } module_obj = mp_module_get(mod_name); if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); if (stat == MP_IMPORT_STAT_DIR) { DEBUG_printf("%s is dir\n", vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false)); vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (mp_import_stat(vstr_str(&path)) != MP_IMPORT_STAT_FILE) { vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py printf("Notice: %s is imported as namespace package\n", vstr_str(&path)); } else { do_load(module_obj, &path); vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py } } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // TODO: We cannot just break here, at the very least, we must execute // trailer code below. But otherwise if there're remaining components, // that would be (??) object path within module, not modules path within FS. // break; } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } } if (i < mod_len) { // we loaded a package, now need to load objects from within that package // TODO assert(0); } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package return top_module_obj; }
int main(void) { // TODO disable JTAG // update the SystemCoreClock variable SystemCoreClockUpdate(); // set interrupt priority config to use all 4 bits for pre-empting NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // enable the CCM RAM and the GPIO's RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN | RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN; #if MICROPY_HW_HAS_SDCARD { // configure SDIO pins to be high to start with (apparently makes it more robust) // FIXME this is not making them high, it just makes them outputs... GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOC, &GPIO_InitStructure); // Configure PD.02 CMD line GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOD, &GPIO_InitStructure); } #endif #if defined(NETDUINO_PLUS_2) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; #if MICROPY_HW_HAS_SDCARD // Turn on the power enable for the sdcard (PB1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET); #endif // Turn on the power for the 5V on the expansion header (PB2) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_SET); } #endif // basic sub-system init sys_tick_init(); pendsv_init(); led_init(); #if MICROPY_HW_ENABLE_RTC rtc_init(); #endif // turn on LED to indicate bootup led_state(PYB_LED_G1, 1); // more sub-system init #if MICROPY_HW_HAS_SDCARD sdcard_init(); #endif storage_init(); // uncomment these 2 lines if you want REPL on USART_6 (or another usart) as well as on USB VCP //pyb_usart_global_debug = PYB_USART_YA; //usart_init(pyb_usart_global_debug, 115200); int first_soft_reset = true; soft_reset: // GC init gc_init(&_heap_start, &_heap_end); // Micro Python init qstr_init(); mp_init(); mp_obj_list_init(mp_sys_path, 0); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_)); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_lib)); mp_obj_list_init(mp_sys_argv, 0); exti_init(); #if MICROPY_HW_HAS_SWITCH switch_init(); #endif #if MICROPY_HW_HAS_LCD // LCD init (just creates class, init hardware by calling LCD()) lcd_init(); #endif #if MICROPY_HW_ENABLE_SERVO // servo servo_init(); #endif #if MICROPY_HW_ENABLE_TIMER // timer timer_init(); #endif #if MICROPY_HW_ENABLE_RNG // RNG RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE); RNG_Cmd(ENABLE); #endif pin_map_init(); // add some functions to the builtin Python namespace mp_store_name(MP_QSTR_help, mp_make_function_n(0, pyb_help)); mp_store_name(MP_QSTR_open, mp_make_function_n(2, pyb_io_open)); // load the pyb module mp_module_register(MP_QSTR_pyb, (mp_obj_t)&pyb_module); // check if user switch held (initiates reset of filesystem) bool reset_filesystem = false; #if MICROPY_HW_HAS_SWITCH if (switch_get()) { reset_filesystem = true; for (int i = 0; i < 50; i++) { if (!switch_get()) { reset_filesystem = false; break; } sys_tick_delay_ms(10); } } #endif // local filesystem init { // try to mount the flash FRESULT res = f_mount(&fatfs0, "0:", 1); if (!reset_filesystem && res == FR_OK) { // mount sucessful } else if (reset_filesystem || res == FR_NO_FILESYSTEM) { // no filesystem, so create a fresh one // TODO doesn't seem to work correctly when reset_filesystem is true... // LED on to indicate creation of LFS led_state(PYB_LED_R2, 1); uint32_t stc = sys_tick_counter; res = f_mkfs("0:", 0, 0); if (res == FR_OK) { // success creating fresh LFS } else { __fatal_error("could not create LFS"); } // create src directory res = f_mkdir("0:/src"); // ignore result from mkdir // create empty main.py FIL fp; f_open(&fp, "0:/src/main.py", FA_WRITE | FA_CREATE_ALWAYS); UINT n; f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n); // TODO check we could write n bytes f_close(&fp); // keep LED on for at least 200ms sys_tick_wait_at_least(stc, 200); led_state(PYB_LED_R2, 0); } else { __fatal_error("could not access LFS"); } } // make sure we have a /boot.py { FILINFO fno; FRESULT res = f_stat("0:/boot.py", &fno); if (res == FR_OK) { if (fno.fattrib & AM_DIR) { // exists as a directory // TODO handle this case // see http://elm-chan.org/fsw/ff/img/app2.c for a "rm -rf" implementation } else { // exists as a file, good! } } else { // doesn't exist, create fresh file // LED on to indicate creation of boot.py led_state(PYB_LED_R2, 1); uint32_t stc = sys_tick_counter; FIL fp; f_open(&fp, "0:/boot.py", FA_WRITE | FA_CREATE_ALWAYS); UINT n; f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n); // TODO check we could write n bytes f_close(&fp); // keep LED on for at least 200ms sys_tick_wait_at_least(stc, 200); led_state(PYB_LED_R2, 0); } } // run /boot.py if (!pyexec_file("0:/boot.py")) { flash_error(4); } if (first_soft_reset) { #if MICROPY_HW_HAS_MMA7660 // MMA accel: init and reset address to zero accel_init(); #endif } // turn boot-up LED off led_state(PYB_LED_G1, 0); #if MICROPY_HW_HAS_SDCARD // if an SD card is present then mount it on 1:/ if (sdcard_is_present()) { FRESULT res = f_mount(&fatfs1, "1:", 1); if (res != FR_OK) { printf("[SD] could not mount SD card\n"); } else { if (first_soft_reset) { // use SD card as medium for the USB MSD usbd_storage_select_medium(USBD_STORAGE_MEDIUM_SDCARD); } } } #endif #ifdef USE_HOST_MODE // USB host pyb_usb_host_init(); #elif defined(USE_DEVICE_MODE) // USB device pyb_usb_dev_init(PYB_USB_DEV_VCP_MSC); #endif // run main script { vstr_t *vstr = vstr_new(); vstr_add_str(vstr, "0:/"); if (pyb_config_source_dir == MP_OBJ_NULL) { vstr_add_str(vstr, "src"); } else { vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_source_dir)); } vstr_add_char(vstr, '/'); if (pyb_config_main == MP_OBJ_NULL) { vstr_add_str(vstr, "main.py"); } else { vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_main)); } if (!pyexec_file(vstr_str(vstr))) { flash_error(3); } vstr_free(vstr); } #if MICROPY_HW_HAS_MMA7660 // HID example if (0) { uint8_t data[4]; data[0] = 0; data[1] = 1; data[2] = -2; data[3] = 0; for (;;) { #if MICROPY_HW_HAS_SWITCH if (switch_get()) { data[0] = 0x01; // 0x04 is middle, 0x02 is right } else { data[0] = 0x00; } #else data[0] = 0x00; #endif accel_start(0x4c /* ACCEL_ADDR */, 1); accel_send_byte(0); accel_restart(0x4c /* ACCEL_ADDR */, 0); for (int i = 0; i <= 1; i++) { int v = accel_read_ack() & 0x3f; if (v & 0x20) { v |= ~0x1f; } data[1 + i] = v; } accel_read_nack(); usb_hid_send_report(data); sys_tick_delay_ms(15); } } #endif #if MICROPY_HW_HAS_WLAN // wifi pyb_wlan_init(); pyb_wlan_start(); #endif pyexec_repl(); printf("PYB: sync filesystems\n"); storage_flush(); printf("PYB: soft reboot\n"); first_soft_reset = false; goto soft_reset; }
static void mp_lexer_next_token_into(mp_lexer_t *lex, mp_token_t *tok, bool first_token) { // skip white space and comments bool had_physical_newline = false; while (!is_end(lex)) { if (is_physical_newline(lex)) { had_physical_newline = true; next_char(lex); } else if (is_whitespace(lex)) { next_char(lex); } else if (is_char(lex, '#')) { next_char(lex); while (!is_end(lex) && !is_physical_newline(lex)) { next_char(lex); } // had_physical_newline will be set on next loop } else if (is_char(lex, '\\')) { // backslash (outside string literals) must appear just before a physical newline next_char(lex); if (!is_physical_newline(lex)) { // TODO SyntaxError assert(0); } else { next_char(lex); } } else { break; } } // set token source information tok->src_name = lex->name; tok->src_line = lex->line; tok->src_column = lex->column; // start new token text vstr_reset(&lex->vstr); if (first_token && lex->line == 1 && lex->column != 1) { // check that the first token is in the first column // if first token is not on first line, we get a physical newline and // this check is done as part of normal indent/dedent checking below // (done to get equivalence with CPython) tok->kind = MP_TOKEN_INDENT; } else if (lex->emit_dent < 0) { tok->kind = MP_TOKEN_DEDENT; lex->emit_dent += 1; } else if (lex->emit_dent > 0) { tok->kind = MP_TOKEN_INDENT; lex->emit_dent -= 1; } else if (had_physical_newline && lex->nested_bracket_level == 0) { tok->kind = MP_TOKEN_NEWLINE; uint num_spaces = lex->column - 1; lex->emit_dent = 0; if (num_spaces == indent_top(lex)) { } else if (num_spaces > indent_top(lex)) { indent_push(lex, num_spaces); lex->emit_dent += 1; } else { while (num_spaces < indent_top(lex)) { indent_pop(lex); lex->emit_dent -= 1; } if (num_spaces != indent_top(lex)) { tok->kind = MP_TOKEN_DEDENT_MISMATCH; } } } else if (is_end(lex)) { if (indent_top(lex) > 0) { tok->kind = MP_TOKEN_NEWLINE; lex->emit_dent = 0; while (indent_top(lex) > 0) { indent_pop(lex); lex->emit_dent -= 1; } } else { tok->kind = MP_TOKEN_END; } } else if (is_char_or(lex, '\'', '\"') || (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"')) || ((is_char_and(lex, 'r', 'b') || is_char_and(lex, 'b', 'r')) && is_char_following_following_or(lex, '\'', '\"'))) { // a string or bytes literal // parse type codes bool is_raw = false; bool is_bytes = false; if (is_char(lex, 'u')) { next_char(lex); } else if (is_char(lex, 'b')) { is_bytes = true; next_char(lex); if (is_char(lex, 'r')) { is_raw = true; next_char(lex); } } else if (is_char(lex, 'r')) { is_raw = true; next_char(lex); if (is_char(lex, 'b')) { is_bytes = true; next_char(lex); } } // set token kind if (is_bytes) { tok->kind = MP_TOKEN_BYTES; } else { tok->kind = MP_TOKEN_STRING; } // get first quoting character char quote_char = '\''; if (is_char(lex, '\"')) { quote_char = '\"'; } next_char(lex); // work out if it's a single or triple quoted literal int num_quotes; if (is_char_and(lex, quote_char, quote_char)) { // triple quotes next_char(lex); next_char(lex); num_quotes = 3; } else { // single quotes num_quotes = 1; } // parse the literal int n_closing = 0; while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) { if (is_char(lex, quote_char)) { n_closing += 1; vstr_add_char(&lex->vstr, CUR_CHAR(lex)); } else { n_closing = 0; if (!is_raw && is_char(lex, '\\')) { next_char(lex); unichar c = CUR_CHAR(lex); switch (c) { case MP_LEXER_CHAR_EOF: break; // TODO a proper error message? case '\n': c = MP_LEXER_CHAR_EOF; break; // TODO check this works correctly (we are supposed to ignore it case '\\': break; case '\'': break; case '"': break; case 'a': c = 0x07; break; case 'b': c = 0x08; break; case 't': c = 0x09; break; case 'n': c = 0x0a; break; case 'v': c = 0x0b; break; case 'f': c = 0x0c; break; case 'r': c = 0x0d; break; // TODO \ooo octal case 'x': // TODO \xhh case 'N': // TODO \N{name} only in strings case 'u': // TODO \uxxxx only in strings case 'U': // TODO \Uxxxxxxxx only in strings default: break; // TODO error message } if (c != MP_LEXER_CHAR_EOF) { vstr_add_char(&lex->vstr, c); } } else { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); } } next_char(lex); } // check we got the required end quotes if (n_closing < num_quotes) { tok->kind = MP_TOKEN_LONELY_STRING_OPEN; } // cut off the end quotes from the token text vstr_cut_tail(&lex->vstr, n_closing); } else if (is_head_of_identifier(lex)) { tok->kind = MP_TOKEN_NAME; // get first char vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); // get tail chars while (!is_end(lex) && is_tail_of_identifier(lex)) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } } else if (is_digit(lex) || (is_char(lex, '.') && is_following_digit(lex))) { tok->kind = MP_TOKEN_NUMBER; // get first char vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); // get tail chars while (!is_end(lex)) { if (is_char_or(lex, 'e', 'E')) { vstr_add_char(&lex->vstr, 'e'); next_char(lex); if (is_char(lex, '+') || is_char(lex, '-')) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } } else if (is_letter(lex) || is_digit(lex) || is_char_or(lex, '_', '.')) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } else { break; } } } else { // search for encoded delimiter or operator const char *t = tok_enc; uint tok_enc_index = 0; for (; *t != 0 && !is_char(lex, *t); t += 1) { if (*t == 'e' || *t == 'c') { t += 1; } else if (*t == 'E') { tok_enc_index -= 1; t += 1; } tok_enc_index += 1; } next_char(lex); if (*t == 0) { // didn't match any delimiter or operator characters tok->kind = MP_TOKEN_INVALID; } else { // matched a delimiter or operator character // get the maximum characters for a valid token t += 1; uint t_index = tok_enc_index; for (;;) { for (; *t == 'e'; t += 1) { t += 1; t_index += 1; if (is_char(lex, *t)) { next_char(lex); tok_enc_index = t_index; break; } } if (*t == 'E') { t += 1; if (is_char(lex, *t)) { next_char(lex); tok_enc_index = t_index; } else { tok->kind = MP_TOKEN_INVALID; } break; } if (*t == 'c') { t += 1; t_index += 1; if (is_char(lex, *t)) { next_char(lex); tok_enc_index = t_index; t += 1; } else { break; } } else { break; } } // set token kind tok->kind = tok_enc_kind[tok_enc_index]; // compute bracket level for implicit line joining if (tok->kind == MP_TOKEN_DEL_PAREN_OPEN || tok->kind == MP_TOKEN_DEL_BRACKET_OPEN || tok->kind == MP_TOKEN_DEL_BRACE_OPEN) { lex->nested_bracket_level += 1; } else if (tok->kind == MP_TOKEN_DEL_PAREN_CLOSE || tok->kind == MP_TOKEN_DEL_BRACKET_CLOSE || tok->kind == MP_TOKEN_DEL_BRACE_CLOSE) { lex->nested_bracket_level -= 1; } } } // point token text to vstr buffer tok->str = vstr_str(&lex->vstr); tok->len = vstr_len(&lex->vstr); // check for keywords if (tok->kind == MP_TOKEN_NAME) { for (int i = 0; tok_kw[i] != NULL; i++) { if (str_strn_equal(tok_kw[i], tok->str, tok->len)) { tok->kind = MP_TOKEN_KW_FALSE + i; break; } } } }
STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ); ujson_stream_t s = {stream_obj, stream_p->read, 0, 0}; vstr_t vstr; vstr_init(&vstr, 8); mp_obj_list_t stack; // we use a list as a simple stack for nested JSON stack.len = 0; stack.items = NULL; mp_obj_t stack_top = MP_OBJ_NULL; mp_obj_type_t *stack_top_type = NULL; mp_obj_t stack_key = MP_OBJ_NULL; S_NEXT(s); for (;;) { cont: if (S_END(s)) { break; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; byte cur = S_CUR(s); S_NEXT(s); switch (cur) { case ',': case ':': case ' ': case '\t': case '\n': case '\r': goto cont; case 'n': if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') { S_NEXT(s); next = mp_const_none; } else { goto fail; } break; case 'f': if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') { S_NEXT(s); next = mp_const_false; } else { goto fail; } break; case 't': if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') { S_NEXT(s); next = mp_const_true; } else { goto fail; } break; case '"': vstr_reset(&vstr); for (; !S_END(s) && S_CUR(s) != '"';) { byte c = S_CUR(s); if (c == '\\') { c = S_NEXT(s); switch (c) { case 'b': c = 0x08; break; case 'f': c = 0x0c; break; case 'n': c = 0x0a; break; case 'r': c = 0x0d; break; case 't': c = 0x09; break; case 'u': { mp_uint_t num = 0; for (int i = 0; i < 4; i++) { c = (S_NEXT(s) | 0x20) - '0'; if (c > 9) { c -= ('a' - ('9' + 1)); } num = (num << 4) | c; } vstr_add_char(&vstr, num); goto str_cont; } } } vstr_add_byte(&vstr, c); str_cont: S_NEXT(s); } if (S_END(s)) { goto fail; } S_NEXT(s); next = mp_obj_new_str(vstr.buf, vstr.len, false); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool flt = false; vstr_reset(&vstr); for (;;) { vstr_add_byte(&vstr, cur); cur = S_CUR(s); if (cur == '.' || cur == 'E' || cur == 'e') { flt = true; } else if (cur == '-' || unichar_isdigit(cur)) { // pass } else { break; } S_NEXT(s); } if (flt) { next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL); } else { next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } break; } case '[': next = mp_obj_new_list(0, NULL); enter = true; break; case '{': next = mp_obj_new_dict(0); enter = true; break; case '}': case ']': { if (stack_top == MP_OBJ_NULL) { // no object at all goto fail; } if (stack.len == 0) { // finished; compound object goto success; } stack.len -= 1; stack_top = stack.items[stack.len]; stack_top_type = mp_obj_get_type(stack_top); goto cont; } default: goto fail; } if (stack_top == MP_OBJ_NULL) { stack_top = next; stack_top_type = mp_obj_get_type(stack_top); if (!enter) { // finished; single primitive only goto success; } } else { // append to list or dict if (stack_top_type == &mp_type_list) { mp_obj_list_append(stack_top, next); } else { if (stack_key == MP_OBJ_NULL) { stack_key = next; if (enter) { goto fail; } } else { mp_obj_dict_store(stack_top, stack_key, next); stack_key = MP_OBJ_NULL; } } if (enter) { if (stack.items == NULL) { mp_obj_list_init(&stack, 1); stack.items[0] = stack_top; } else { mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top); } stack_top = next; stack_top_type = mp_obj_get_type(stack_top); } } } success: // eat trailing whitespace while (unichar_isspace(S_CUR(s))) { S_NEXT(s); } if (!S_END(s)) { // unexpected chars goto fail; } if (stack_top == MP_OBJ_NULL || stack.len != 0) { // not exactly 1 object goto fail; } vstr_clear(&vstr); return stack_top; fail: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "syntax error in JSON")); }
// This function implements a simple non-recursive JSON parser. // // The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt // The parser here will parse any valid JSON and return the correct // corresponding Python object. It allows through a superset of JSON, since // it treats commas and colons as "whitespace", and doesn't care if // brackets/braces are correctly paired. It will raise a ValueError if the // input is outside it's specs. // // Most of the work is parsing the primitives (null, false, true, numbers, // strings). It does 1 pass over the input string and so is easily extended to // being able to parse from a non-seekable stream. It tries to be fast and // small in code size, while not using more RAM than necessary. STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) { mp_uint_t len; const char *s = mp_obj_str_get_data(obj, &len); const char *top = s + len; vstr_t vstr; vstr_init(&vstr, 8); mp_obj_list_t stack; // we use a list as a simple stack for nested JSON stack.len = 0; stack.items = NULL; mp_obj_t stack_top = MP_OBJ_NULL; mp_obj_type_t *stack_top_type = NULL; mp_obj_t stack_key = MP_OBJ_NULL; for (;;) { cont: if (s == top) { break; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; switch (*s) { case ',': case ':': case ' ': case '\t': case '\n': case '\r': s += 1; goto cont; case 'n': if (s + 3 < top && s[1] == 'u' && s[2] == 'l' && s[3] == 'l') { s += 4; next = mp_const_none; } else { goto fail; } break; case 'f': if (s + 4 < top && s[1] == 'a' && s[2] == 'l' && s[3] == 's' && s[4] == 'e') { s += 5; next = mp_const_false; } else { goto fail; } break; case 't': if (s + 3 < top && s[1] == 'r' && s[2] == 'u' && s[3] == 'e') { s += 4; next = mp_const_true; } else { goto fail; } break; case '"': vstr_reset(&vstr); for (s++; s < top && *s != '"';) { byte c = *s; if (c == '\\') { s++; c = *s; switch (c) { case 'b': c = 0x08; break; case 'f': c = 0x0c; break; case 'n': c = 0x0a; break; case 'r': c = 0x0d; break; case 't': c = 0x09; break; case 'u': { if (s + 4 >= top) { goto fail; } mp_uint_t num = 0; for (int i = 0; i < 4; i++) { c = (*++s | 0x20) - '0'; if (c > 9) { c -= ('a' - ('9' + 1)); } num = (num << 4) | c; } vstr_add_char(&vstr, num); goto str_cont; } } } vstr_add_byte(&vstr, c); str_cont: s++; } if (s == top) { goto fail; } s++; next = mp_obj_new_str(vstr.buf, vstr.len, false); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool flt = false; vstr_reset(&vstr); for (; s < top; s++) { if (*s == '.' || *s == 'E' || *s == 'e') { flt = true; } else if (*s == '-' || unichar_isdigit(*s)) { // pass } else { break; } vstr_add_byte(&vstr, *s); } if (flt) { next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL); } else { next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } break; } case '[': next = mp_obj_new_list(0, NULL); enter = true; s += 1; break; case '{': next = mp_obj_new_dict(0); enter = true; s += 1; break; case '}': case ']': { s += 1; if (stack_top == MP_OBJ_NULL) { // no object at all goto fail; } if (stack.len == 0) { // finished; compound object goto success; } stack.len -= 1; stack_top = stack.items[stack.len]; stack_top_type = mp_obj_get_type(stack_top); goto cont; } default: goto fail; } if (stack_top == MP_OBJ_NULL) { stack_top = next; stack_top_type = mp_obj_get_type(stack_top); if (!enter) { // finished; single primitive only goto success; } } else { // append to list or dict if (stack_top_type == &mp_type_list) { mp_obj_list_append(stack_top, next); } else { if (stack_key == MP_OBJ_NULL) { stack_key = next; if (enter) { goto fail; } } else { mp_obj_dict_store(stack_top, stack_key, next); stack_key = MP_OBJ_NULL; } } if (enter) { if (stack.items == NULL) { mp_obj_list_init(&stack, 1); stack.items[0] = stack_top; } else { mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top); } stack_top = next; stack_top_type = mp_obj_get_type(stack_top); } } } success: // eat trailing whitespace while (s < top && unichar_isspace(*s)) { s++; } if (s < top) { // unexpected chars goto fail; } if (stack_top == MP_OBJ_NULL || stack.len != 0) { // not exactly 1 object goto fail; } vstr_clear(&vstr); return stack_top; fail: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "syntax error in JSON")); }
mp_obj_t mp_builtin___import__(uint n_args, mp_obj_t *args) { /* printf("import:\n"); for (int i = 0; i < n_args; i++) { printf(" "); mp_obj_print(args[i], PRINT_REPR); printf("\n"); } */ mp_obj_t fromtuple = mp_const_none; int level = 0; if (n_args >= 4) { fromtuple = args[3]; if (n_args >= 5) { level = MP_OBJ_SMALL_INT_VALUE(args[4]); } } if (level != 0) { nlr_jump(mp_obj_new_exception_msg(&mp_type_NotImplementedError, "Relative import is not implemented")); } uint mod_len; const char *mod_str = (const char*)mp_obj_str_get_data(args[0], &mod_len); // check if module already exists mp_obj_t module_obj = mp_module_get(mp_obj_str_get_qstr(args[0])); if (module_obj != MP_OBJ_NULL) { // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } uint last = 0; VSTR_FIXED(path, MICROPY_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } // fail if we couldn't find the file if (stat == MP_IMPORT_STAT_NO_EXIST) { nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "ImportError: No module named '%s'", qstr_str(mod_name))); } module_obj = mp_module_get(mod_name); if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); if (stat == MP_IMPORT_STAT_DIR) { vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (mp_import_stat(vstr_str(&path)) != MP_IMPORT_STAT_FILE) { vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "Per PEP-420 a dir without __init__.py (%s) is a namespace package; " "namespace packages are not supported", vstr_str(&path))); } do_load(module_obj, &path); vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // TODO: We cannot just break here, at the very least, we must execute // trailer code below. But otherwise if there're remaining components, // that would be (??) object path within module, not modules path within FS. // break; } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } } if (i < mod_len) { // we loaded a package, now need to load objects from within that package // TODO assert(0); } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package return top_module_obj; }
void mp_lexer_to_next(mp_lexer_t *lex) { // start new token text vstr_reset(&lex->vstr); // skip white space and comments bool had_physical_newline = skip_whitespace(lex, false); // set token source information lex->tok_line = lex->line; lex->tok_column = lex->column; if (lex->emit_dent < 0) { lex->tok_kind = MP_TOKEN_DEDENT; lex->emit_dent += 1; } else if (lex->emit_dent > 0) { lex->tok_kind = MP_TOKEN_INDENT; lex->emit_dent -= 1; } else if (had_physical_newline && lex->nested_bracket_level == 0) { lex->tok_kind = MP_TOKEN_NEWLINE; size_t num_spaces = lex->column - 1; if (num_spaces == indent_top(lex)) { } else if (num_spaces > indent_top(lex)) { indent_push(lex, num_spaces); lex->emit_dent += 1; } else { while (num_spaces < indent_top(lex)) { indent_pop(lex); lex->emit_dent -= 1; } if (num_spaces != indent_top(lex)) { lex->tok_kind = MP_TOKEN_DEDENT_MISMATCH; } } } else if (is_end(lex)) { lex->tok_kind = MP_TOKEN_END; } else if (is_string_or_bytes(lex)) { // a string or bytes literal // Python requires adjacent string/bytes literals to be automatically // concatenated. We do it here in the tokeniser to make efficient use of RAM, // because then the lexer's vstr can be used to accumulate the string literal, // in contrast to creating a parse tree of strings and then joining them later // in the compiler. It's also more compact in code size to do it here. // MP_TOKEN_END is used to indicate that this is the first string token lex->tok_kind = MP_TOKEN_END; // Loop to accumulate string/bytes literals do { // parse type codes bool is_raw = false; mp_token_kind_t kind = MP_TOKEN_STRING; int n_char = 0; if (is_char(lex, 'u')) { n_char = 1; } else if (is_char(lex, 'b')) { kind = MP_TOKEN_BYTES; n_char = 1; if (is_char_following(lex, 'r')) { is_raw = true; n_char = 2; } } else if (is_char(lex, 'r')) { is_raw = true; n_char = 1; if (is_char_following(lex, 'b')) { kind = MP_TOKEN_BYTES; n_char = 2; } } // Set or check token kind if (lex->tok_kind == MP_TOKEN_END) { lex->tok_kind = kind; } else if (lex->tok_kind != kind) { // Can't concatenate string with bytes break; } // Skip any type code characters if (n_char != 0) { next_char(lex); if (n_char == 2) { next_char(lex); } } // Parse the literal parse_string_literal(lex, is_raw); // Skip whitespace so we can check if there's another string following skip_whitespace(lex, true); } while (is_string_or_bytes(lex)); } else if (is_head_of_identifier(lex)) { lex->tok_kind = MP_TOKEN_NAME; // get first char (add as byte to remain 8-bit clean and support utf-8) vstr_add_byte(&lex->vstr, CUR_CHAR(lex)); next_char(lex); // get tail chars while (!is_end(lex) && is_tail_of_identifier(lex)) { vstr_add_byte(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } // Check if the name is a keyword. // We also check for __debug__ here and convert it to its value. This is // so the parser gives a syntax error on, eg, x.__debug__. Otherwise, we // need to check for this special token in many places in the compiler. const char *s = vstr_null_terminated_str(&lex->vstr); for (size_t i = 0; i < MP_ARRAY_SIZE(tok_kw); i++) { int cmp = strcmp(s, tok_kw[i]); if (cmp == 0) { lex->tok_kind = MP_TOKEN_KW_FALSE + i; if (lex->tok_kind == MP_TOKEN_KW___DEBUG__) { lex->tok_kind = (MP_STATE_VM(mp_optimise_value) == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE); } break; } else if (cmp < 0) { // Table is sorted and comparison was less-than, so stop searching break; } } } else if (is_digit(lex) || (is_char(lex, '.') && is_following_digit(lex))) { bool forced_integer = false; if (is_char(lex, '.')) { lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG; } else { lex->tok_kind = MP_TOKEN_INTEGER; if (is_char(lex, '0') && is_following_base_char(lex)) { forced_integer = true; } } // get first char vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); // get tail chars while (!is_end(lex)) { if (!forced_integer && is_char_or(lex, 'e', 'E')) { lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG; vstr_add_char(&lex->vstr, 'e'); next_char(lex); if (is_char(lex, '+') || is_char(lex, '-')) { vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } } else if (is_letter(lex) || is_digit(lex) || is_char(lex, '.')) { if (is_char_or3(lex, '.', 'j', 'J')) { lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG; } vstr_add_char(&lex->vstr, CUR_CHAR(lex)); next_char(lex); } else { break; } } } else { // search for encoded delimiter or operator const char *t = tok_enc; size_t tok_enc_index = 0; for (; *t != 0 && !is_char(lex, *t); t += 1) { if (*t == 'e' || *t == 'c') { t += 1; } tok_enc_index += 1; } next_char(lex); if (*t == 0) { // didn't match any delimiter or operator characters lex->tok_kind = MP_TOKEN_INVALID; } else if (*t == '!') { // "!=" is a special case because "!" is not a valid operator if (is_char(lex, '=')) { next_char(lex); lex->tok_kind = MP_TOKEN_OP_NOT_EQUAL; } else { lex->tok_kind = MP_TOKEN_INVALID; } } else if (*t == '.') { // "." and "..." are special cases because ".." is not a valid operator if (is_char_and(lex, '.', '.')) { next_char(lex); next_char(lex); lex->tok_kind = MP_TOKEN_ELLIPSIS; } else { lex->tok_kind = MP_TOKEN_DEL_PERIOD; } } else { // matched a delimiter or operator character // get the maximum characters for a valid token t += 1; size_t t_index = tok_enc_index; while (*t == 'c' || *t == 'e') { t_index += 1; if (is_char(lex, t[1])) { next_char(lex); tok_enc_index = t_index; if (*t == 'e') { break; } } else if (*t == 'c') { break; } t += 2; } // set token kind lex->tok_kind = tok_enc_kind[tok_enc_index]; // compute bracket level for implicit line joining if (lex->tok_kind == MP_TOKEN_DEL_PAREN_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACKET_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACE_OPEN) { lex->nested_bracket_level += 1; } else if (lex->tok_kind == MP_TOKEN_DEL_PAREN_CLOSE || lex->tok_kind == MP_TOKEN_DEL_BRACKET_CLOSE || lex->tok_kind == MP_TOKEN_DEL_BRACE_CLOSE) { lex->nested_bracket_level -= 1; } } } }
STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) { // get first quoting character char quote_char = '\''; if (is_char(lex, '\"')) { quote_char = '\"'; } next_char(lex); // work out if it's a single or triple quoted literal size_t num_quotes; if (is_char_and(lex, quote_char, quote_char)) { // triple quotes next_char(lex); next_char(lex); num_quotes = 3; } else { // single quotes num_quotes = 1; } size_t n_closing = 0; while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) { if (is_char(lex, quote_char)) { n_closing += 1; vstr_add_char(&lex->vstr, CUR_CHAR(lex)); } else { n_closing = 0; if (is_char(lex, '\\')) { next_char(lex); unichar c = CUR_CHAR(lex); if (is_raw) { // raw strings allow escaping of quotes, but the backslash is also emitted vstr_add_char(&lex->vstr, '\\'); } else { switch (c) { // note: "c" can never be MP_LEXER_EOF because next_char // always inserts a newline at the end of the input stream case '\n': c = MP_LEXER_EOF; break; // backslash escape the newline, just ignore it case '\\': break; case '\'': break; case '"': break; case 'a': c = 0x07; break; case 'b': c = 0x08; break; case 't': c = 0x09; break; case 'n': c = 0x0a; break; case 'v': c = 0x0b; break; case 'f': c = 0x0c; break; case 'r': c = 0x0d; break; case 'u': case 'U': if (lex->tok_kind == MP_TOKEN_BYTES) { // b'\u1234' == b'\\u1234' vstr_add_char(&lex->vstr, '\\'); break; } // Otherwise fall through. case 'x': { mp_uint_t num = 0; if (!get_hex(lex, (c == 'x' ? 2 : c == 'u' ? 4 : 8), &num)) { // not enough hex chars for escape sequence lex->tok_kind = MP_TOKEN_INVALID; } c = num; break; } case 'N': // Supporting '\N{LATIN SMALL LETTER A}' == 'a' would require keeping the // entire Unicode name table in the core. As of Unicode 6.3.0, that's nearly // 3MB of text; even gzip-compressed and with minimal structure, it'll take // roughly half a meg of storage. This form of Unicode escape may be added // later on, but it's definitely not a priority right now. -- CJA 20140607 mp_not_implemented("unicode name escapes"); break; default: if (c >= '0' && c <= '7') { // Octal sequence, 1-3 chars size_t digits = 3; mp_uint_t num = c - '0'; while (is_following_odigit(lex) && --digits != 0) { next_char(lex); num = num * 8 + (CUR_CHAR(lex) - '0'); } c = num; } else { // unrecognised escape character; CPython lets this through verbatim as '\' and then the character vstr_add_char(&lex->vstr, '\\'); } break; } } if (c != MP_LEXER_EOF) { if (MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) { if (c < 0x110000 && lex->tok_kind == MP_TOKEN_STRING) { vstr_add_char(&lex->vstr, c); } else if (c < 0x100 && lex->tok_kind == MP_TOKEN_BYTES) { vstr_add_byte(&lex->vstr, c); } else { // unicode character out of range // this raises a generic SyntaxError; could provide more info lex->tok_kind = MP_TOKEN_INVALID; } } else { // without unicode everything is just added as an 8-bit byte if (c < 0x100) { vstr_add_byte(&lex->vstr, c); } else { // 8-bit character out of range // this raises a generic SyntaxError; could provide more info lex->tok_kind = MP_TOKEN_INVALID; } } } } else { // Add the "character" as a byte so that we remain 8-bit clean. // This way, strings are parsed correctly whether or not they contain utf-8 chars. vstr_add_byte(&lex->vstr, CUR_CHAR(lex)); } } next_char(lex); } // check we got the required end quotes if (n_closing < num_quotes) { lex->tok_kind = MP_TOKEN_LONELY_STRING_OPEN; } // cut off the end quotes from the token text vstr_cut_tail_bytes(&lex->vstr, n_closing); }
mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #if DEBUG_PRINT DEBUG_printf("__import__:\n"); for (size_t i = 0; i < n_args; i++) { DEBUG_printf(" "); mp_obj_print(args[i], PRINT_REPR); DEBUG_printf("\n"); } #endif mp_obj_t module_name = args[0]; mp_obj_t fromtuple = mp_const_none; mp_int_t level = 0; if (n_args >= 4) { fromtuple = args[3]; if (n_args >= 5) { level = MP_OBJ_SMALL_INT_VALUE(args[4]); if (level < 0) { mp_raise_ValueError(NULL); } } } size_t mod_len; const char *mod_str = mp_obj_str_get_data(module_name, &mod_len); if (level != 0) { // What we want to do here is to take name of current module, // chop <level> trailing components, and concatenate with passed-in // module name, thus resolving relative import name into absolute. // This even appears to be correct per // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "Relative imports use a module's __name__ attribute to determine that // module's position in the package hierarchy." level--; mp_obj_t this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(this_name_q != MP_OBJ_NULL); #if MICROPY_CPYTHON_COMPAT if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) { // This is a module run by -m command-line switch, get its real name from backup attribute this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif mp_map_t *globals_map = &mp_globals_get()->map; mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); bool is_pkg = (elem != NULL); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); mp_obj_print(this_name_q, PRINT_REPR); DEBUG_printf(", is_package: %d", is_pkg); DEBUG_printf("\n"); #endif size_t this_name_l; const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l); const char *p = this_name + this_name_l; if (!is_pkg) { // We have module, but relative imports are anchored at package, so // go there. chop_component(this_name, &p); } while (level--) { chop_component(this_name, &p); } // We must have some component left over to import from if (p == this_name) { mp_raise_ValueError("cannot perform relative import"); } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); char *new_mod = mp_local_alloc(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len); } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); mp_local_free(new_mod); DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); module_name = MP_OBJ_NEW_QSTR(new_mod_q); mod_str = qstr_str(new_mod_q); mod_len = new_mod_l; } // check if module already exists qstr module_name_qstr = mp_obj_str_get_qstr(module_name); mp_obj_t module_obj = mp_module_get(module_name_qstr); if (module_obj != MP_OBJ_NULL) { DEBUG_printf("Module already loaded\n"); // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } DEBUG_printf("Module not yet loaded\n"); uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path)); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { #if MICROPY_MODULE_WEAK_LINKS // check if there is a weak link to this module if (i == mod_len) { mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); if (el == NULL) { goto no_exist; } // found weak linked module module_obj = el->value; mp_module_call_init(mod_name, module_obj); } else { no_exist: #else { #endif // couldn't find the file, so fail if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_msg(&mp_type_ImportError, "module not found"); } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "no module named '%q'", mod_name)); } } } else { // found the file, so get the module module_obj = mp_module_get(mod_name); } if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). Do this only // for *modules* however - packages never have their names // replaced, instead they're -m'ed using a special __main__ // submodule in them. (This all apparently is done to not // touch package name itself, which is important for future // imports). if (i == mod_len && fromtuple == mp_const_false && stat != MP_IMPORT_STAT_DIR) { mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj); mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); #if MICROPY_CPYTHON_COMPAT // Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules). mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj); // Store real name in "__main__" attribute. Chosen semi-randonly, to reuse existing qstr's. mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(mod_name)); #endif } if (stat == MP_IMPORT_STAT_DIR) { DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path))); size_t orig_path_len = path.len; vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) { //mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { do_load(module_obj, &path); } path.len = orig_path_len; } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // This should be the last component in the import path. If there are // remaining components then it's an ImportError because the current path // (the module that was just loaded) is not a package. This will be caught // on the next iteration because the file will not exist. } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } }
int main(void) { // TODO disable JTAG /* STM32F4xx HAL library initialization: - Configure the Flash prefetch, instruction and Data caches - Configure the Systick to generate an interrupt each 1 msec - Set NVIC Group Priority to 4 - Global MSP (MCU Support Package) initialization */ HAL_Init(); // set the system clock to be HSE SystemClock_Config(); // enable GPIO clocks __GPIOA_CLK_ENABLE(); __GPIOB_CLK_ENABLE(); __GPIOC_CLK_ENABLE(); __GPIOD_CLK_ENABLE(); // enable the CCM RAM __CCMDATARAMEN_CLK_ENABLE(); #if 0 #if defined(NETDUINO_PLUS_2) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; #if MICROPY_HW_HAS_SDCARD // Turn on the power enable for the sdcard (PB1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET); #endif // Turn on the power for the 5V on the expansion header (PB2) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_SET); } #endif #endif // basic sub-system init pendsv_init(); led_init(); switch_init0(); int first_soft_reset = true; uint reset_mode; soft_reset: // check if user switch held to select the reset mode reset_mode = 1; led_state(1, 0); led_state(2, 1); led_state(3, 0); led_state(4, 0); #if MICROPY_HW_HAS_SWITCH if (switch_get()) { for (uint i = 0; i < 3000; i++) { if (!switch_get()) { break; } HAL_Delay(20); if (i % 30 == 29) { reset_mode = (reset_mode + 1) & 7; led_state(2, reset_mode & 1); led_state(3, reset_mode & 2); led_state(4, reset_mode & 4); } } // flash the selected reset mode for (uint i = 0; i < 6; i++) { led_state(2, 0); led_state(3, 0); led_state(4, 0); HAL_Delay(50); led_state(2, reset_mode & 1); led_state(3, reset_mode & 2); led_state(4, reset_mode & 4); HAL_Delay(50); } HAL_Delay(400); } #endif #if MICROPY_HW_ENABLE_RTC if (first_soft_reset) { rtc_init(); } #endif // more sub-system init #if MICROPY_HW_HAS_SDCARD if (first_soft_reset) { sdcard_init(); } #endif if (first_soft_reset) { storage_init(); } // GC init gc_init(&_heap_start, &_heap_end); // Change #if 0 to #if 1 if you want REPL on USART_6 (or another usart) // as well as on USB VCP #if 0 pyb_usart_global_debug = pyb_Usart(MP_OBJ_NEW_SMALL_INT(PYB_USART_YA), MP_OBJ_NEW_SMALL_INT(115200)); #else pyb_usart_global_debug = NULL; #endif // Micro Python init qstr_init(); mp_init(); mp_obj_t def_path[3]; def_path[0] = MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_); def_path[1] = MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_src); def_path[2] = MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_lib); mp_sys_path = mp_obj_new_list(3, def_path); readline_init(); exti_init(); #if MICROPY_HW_HAS_SWITCH // must come after exti_init switch_init(); #endif #if MICROPY_HW_HAS_LCD // LCD init (just creates class, init hardware by calling LCD()) lcd_init(); #endif pin_map_init(); // local filesystem init { // try to mount the flash FRESULT res = f_mount(&fatfs0, "0:", 1); if (reset_mode == 3 || res == FR_NO_FILESYSTEM) { // no filesystem, or asked to reset it, so create a fresh one // LED on to indicate creation of LFS led_state(PYB_LED_R2, 1); uint32_t start_tick = HAL_GetTick(); res = f_mkfs("0:", 0, 0); if (res == FR_OK) { // success creating fresh LFS } else { __fatal_error("could not create LFS"); } // create src directory res = f_mkdir("0:/src"); // ignore result from mkdir // create empty main.py FIL fp; f_open(&fp, "0:/src/main.py", FA_WRITE | FA_CREATE_ALWAYS); UINT n; f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n); // TODO check we could write n bytes f_close(&fp); // keep LED on for at least 200ms sys_tick_wait_at_least(start_tick, 200); led_state(PYB_LED_R2, 0); } else if (res == FR_OK) { // mount sucessful } else { __fatal_error("could not access LFS"); } } // make sure we have a /boot.py { FILINFO fno; #if _USE_LFN fno.lfname = NULL; fno.lfsize = 0; #endif FRESULT res = f_stat("0:/boot.py", &fno); if (res == FR_OK) { if (fno.fattrib & AM_DIR) { // exists as a directory // TODO handle this case // see http://elm-chan.org/fsw/ff/img/app2.c for a "rm -rf" implementation } else { // exists as a file, good! } } else { // doesn't exist, create fresh file // LED on to indicate creation of boot.py led_state(PYB_LED_R2, 1); uint32_t start_tick = HAL_GetTick(); FIL fp; f_open(&fp, "0:/boot.py", FA_WRITE | FA_CREATE_ALWAYS); UINT n; f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n); // TODO check we could write n bytes f_close(&fp); // keep LED on for at least 200ms sys_tick_wait_at_least(start_tick, 200); led_state(PYB_LED_R2, 0); } } // run /boot.py if (reset_mode == 1) { if (!pyexec_file("0:/boot.py")) { flash_error(4); } } // turn boot-up LEDs off led_state(2, 0); led_state(3, 0); led_state(4, 0); #if defined(USE_DEVICE_MODE) usb_storage_medium_t usb_medium = USB_STORAGE_MEDIUM_FLASH; #endif #if MICROPY_HW_HAS_SDCARD // if an SD card is present then mount it on 1:/ if (reset_mode == 1 && sdcard_is_present()) { FRESULT res = f_mount(&fatfs1, "1:", 1); if (res != FR_OK) { printf("[SD] could not mount SD card\n"); } else { if (first_soft_reset) { // use SD card as medium for the USB MSD #if defined(USE_DEVICE_MODE) usb_medium = USB_STORAGE_MEDIUM_SDCARD; #endif } } } #else // Get rid of compiler warning if no SDCARD is configured. (void)first_soft_reset; #endif #if defined(USE_HOST_MODE) // USB host pyb_usb_host_init(); #elif defined(USE_DEVICE_MODE) // USB device if (reset_mode == 1) { usb_device_mode_t usb_mode = USB_DEVICE_MODE_CDC_MSC; if (pyb_config_usb_mode != MP_OBJ_NULL) { if (strcmp(mp_obj_str_get_str(pyb_config_usb_mode), "CDC+HID") == 0) { usb_mode = USB_DEVICE_MODE_CDC_HID; } } pyb_usb_dev_init(usb_mode, usb_medium); } else { pyb_usb_dev_init(USB_DEVICE_MODE_CDC_MSC, usb_medium); } #endif #if MICROPY_HW_ENABLE_RNG // RNG rng_init(); #endif // I2C i2c_init(); #if MICROPY_HW_HAS_MMA7660 // MMA accel: init and reset accel_init(); #endif #if MICROPY_HW_ENABLE_SERVO // servo servo_init(); #endif #if 0 #if MICROPY_HW_ENABLE_TIMER // timer timer_init(); #endif #endif #if MICROPY_HW_ENABLE_DAC // DAC dac_init(); #endif // run main script if (reset_mode == 1) { vstr_t *vstr = vstr_new(); vstr_add_str(vstr, "0:/"); if (pyb_config_source_dir == MP_OBJ_NULL) { vstr_add_str(vstr, "src"); } else { vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_source_dir)); } vstr_add_char(vstr, '/'); if (pyb_config_main == MP_OBJ_NULL) { vstr_add_str(vstr, "main.py"); } else { vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_main)); } if (!pyexec_file(vstr_str(vstr))) { flash_error(3); } vstr_free(vstr); } #if 0 #if MICROPY_HW_HAS_WLAN // wifi pyb_wlan_init(); pyb_wlan_start(); #endif #endif // enter REPL // REPL mode can change, or it can request a soft reset for (;;) { if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { if (pyexec_raw_repl() != 0) { break; } } else { if (pyexec_friendly_repl() != 0) { break; } } } printf("PYB: sync filesystems\n"); storage_flush(); printf("PYB: soft reboot\n"); first_soft_reset = false; goto soft_reset; }