From 3754c4a04035d18b0bbb780fa0aef273e1c8f033 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 18:20:57 +0200 Subject: [PATCH 01/17] mp_obj_get_qstr(): Handle MP_OBJ_QSTR. --- py/obj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py/obj.c b/py/obj.c index 42f86cf175..dd081ee35e 100644 --- a/py/obj.c +++ b/py/obj.c @@ -222,7 +222,9 @@ void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { #endif qstr mp_obj_get_qstr(mp_obj_t arg) { - if (MP_OBJ_IS_TYPE(arg, &str_type)) { + if (MP_OBJ_IS_QSTR(arg)) { + return MP_OBJ_QSTR_VALUE(arg); + } else if (MP_OBJ_IS_TYPE(arg, &str_type)) { return mp_obj_str_get(arg); } else { assert(0); From dff3f896d7f7ef20762f0be7c8d5aa457c04b71a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 18:37:30 +0200 Subject: [PATCH 02/17] mp_identity(): Add generic identity function. Useful as getiter method for objects which are their own iterators, etc. --- py/obj.c | 6 ++++++ py/obj.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/py/obj.c b/py/obj.c index dd081ee35e..7dd9cc0a24 100644 --- a/py/obj.c +++ b/py/obj.c @@ -288,3 +288,9 @@ mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) { } return MP_OBJ_NEW_SMALL_INT(len); } + +// Return input argument. Useful as .getiter for objects which are +// their own iterators, etc. +mp_obj_t mp_identity(mp_obj_t self) { + return self; +} diff --git a/py/obj.h b/py/obj.h index faf0231474..d7154a743c 100644 --- a/py/obj.h +++ b/py/obj.h @@ -351,6 +351,8 @@ extern const mp_obj_type_t fun_native_type; extern const mp_obj_type_t fun_bc_type; void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte **code); +mp_obj_t mp_identity(mp_obj_t self); + // generator extern const mp_obj_type_t gen_instance_type; From d54bef76921ae719bcbaf4ad7f9915da10f25574 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 18:35:32 +0200 Subject: [PATCH 03/17] stream: Add generic unbuffered iternext method. Uses stream_unbuffered_readline underline. --- py/stream.c | 11 ++++++++++- py/stream.h | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/py/stream.c b/py/stream.c index d3a11affbc..3a87d0444c 100644 --- a/py/stream.c +++ b/py/stream.c @@ -137,9 +137,18 @@ static mp_obj_t stream_unbuffered_readline(uint n_args, const mp_obj_t *args) { // TODO: \0 vstr_add_byte(vstr, 0); vstr_shrink(vstr); - return mp_obj_new_str(qstr_from_str_take(vstr_str(vstr), vstr_len(vstr))); + return MP_OBJ_NEW_QSTR(qstr_from_str_take(vstr_str(vstr), vstr_len(vstr))); } +mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) { + mp_obj_t l_in = stream_unbuffered_readline(1, &self); + const char *l = qstr_str(MP_OBJ_QSTR_VALUE(l_in)); + // TODO: \0 + if (*l != 0) { + return l_in; + } + return mp_const_stop_iteration; +} MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj, 1, 2, stream_read); MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_readall_obj, stream_readall); diff --git a/py/stream.h b/py/stream.h index 58e8072549..a0cc34797b 100644 --- a/py/stream.h +++ b/py/stream.h @@ -2,3 +2,6 @@ extern const mp_obj_fun_native_t mp_stream_read_obj; extern const mp_obj_fun_native_t mp_stream_readall_obj; extern const mp_obj_fun_native_t mp_stream_unbuffered_readline_obj; extern const mp_obj_fun_native_t mp_stream_write_obj; + +// Iterator which uses mp_stream_unbuffered_readline_obj +mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self); From decd597273c39311541bfd45ab71974f90dca9e6 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 18:38:27 +0200 Subject: [PATCH 04/17] unix io.FileIO: Add iteration support. A file cannot be iterated concurrently, so we make io.FileIO its own iterator. --- tests/io/file-iter.py | 3 +++ unix/file.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/io/file-iter.py diff --git a/tests/io/file-iter.py b/tests/io/file-iter.py new file mode 100644 index 0000000000..48e8739966 --- /dev/null +++ b/tests/io/file-iter.py @@ -0,0 +1,3 @@ +f = open("io/data/file1") +for l in f: + print(l) diff --git a/unix/file.c b/unix/file.c index af75944711..bbe84dfb49 100644 --- a/unix/file.c +++ b/unix/file.c @@ -102,8 +102,8 @@ static const mp_obj_type_t rawfile_type = { "io.FileIO", .print = fdfile_print, .make_new = fdfile_make_new, - .getiter = NULL, - .iternext = NULL, + .getiter = mp_identity, + .iternext = mp_stream_unbuffered_iter, .stream_p = { .read = fdfile_read, .write = fdfile_write, From ff3bdea49d10ea129fafb2e8be0cdaed172a036b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 19:40:46 +0200 Subject: [PATCH 05/17] stream_read(): Shrink memory block to actual read size. --- py/stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/py/stream.c b/py/stream.c index 3a87d0444c..34bbea2e0a 100644 --- a/py/stream.c +++ b/py/stream.c @@ -30,6 +30,7 @@ static mp_obj_t stream_read(uint n_args, const mp_obj_t *args) { if (out_sz == -1) { nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", error)); } else { + buf = m_realloc(buf, sz + 1, out_sz + 1); buf[out_sz] = 0; return mp_obj_new_str(qstr_from_str_take(buf, /*out_sz,*/ sz + 1)); } From fc35aa682847e9123064eeaf5955c06275b95a0d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 19:42:39 +0200 Subject: [PATCH 06/17] unix socket: Add send() and recv() methods. CPython _socket actually have only those and doesn't provide stream interface (higher-level CPython "socket" what adds this). +516 bytes x86. --- unix/socket.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/unix/socket.c b/unix/socket.c index 708cf7af52..7da6bb20d0 100644 --- a/unix/socket.c +++ b/unix/socket.c @@ -127,6 +127,42 @@ static mp_obj_t socket_accept(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); +static mp_obj_t socket_recv(uint n_args, const mp_obj_t *args) { + mp_obj_socket_t *self = args[0]; + int sz = MP_OBJ_SMALL_INT_VALUE(args[1]); + int flags = 0; + + if (n_args > 2) { + flags = MP_OBJ_SMALL_INT_VALUE(args[2]); + } + + char *buf = m_new(char, sz + 1); + int out_sz = recv(self->fd, buf, sz, flags); + RAISE_ERRNO(out_sz, errno); + + buf = m_realloc(buf, sz + 1, out_sz + 1); + buf[out_sz] = 0; + return MP_OBJ_NEW_QSTR(qstr_from_str_take(buf, out_sz + 1)); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv); + +static mp_obj_t socket_send(uint n_args, const mp_obj_t *args) { + mp_obj_socket_t *self = args[0]; + int flags = 0; + + if (n_args > 2) { + flags = MP_OBJ_SMALL_INT_VALUE(args[2]); + } + + const char *buf = qstr_str(mp_obj_str_get(args[1])); + int sz = strlen(buf); + int out_sz = send(self->fd, buf, sz, flags); + RAISE_ERRNO(out_sz, errno); + + return MP_OBJ_NEW_SMALL_INT(out_sz); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_send_obj, 2, 3, socket_send); + static mp_obj_t socket_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { int family = AF_INET; int type = SOCK_STREAM; @@ -159,6 +195,8 @@ static const mp_method_t rawsocket_type_methods[] = { { "bind", &socket_bind_obj }, { "listen", &socket_listen_obj }, { "accept", &socket_accept_obj }, + { "recv", &socket_recv_obj }, + { "send", &socket_send_obj }, { "close", &socket_close_obj }, #if MICROPY_SOCKET_EXTRA { "recv", &mp_stream_read_obj }, From f0cfb8cb45cb1fc91c77fae98f5452b7f7865392 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 20:10:37 +0200 Subject: [PATCH 07/17] Don't preimport socket module. --- unix/socket.c | 1 - 1 file changed, 1 deletion(-) diff --git a/unix/socket.c b/unix/socket.c index 7da6bb20d0..e48d8545f7 100644 --- a/unix/socket.c +++ b/unix/socket.c @@ -318,5 +318,4 @@ void rawsocket_init() { STORE_INT_CONST(m, SOCK_STREAM); STORE_INT_CONST(m, SOCK_DGRAM); STORE_INT_CONST(m, SOCK_RAW); - rt_store_name(qstr_from_str_static("rawsocket"), m); } From a80ff04fe7458b8ed8e948619b3700b720832ec2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 20:32:50 +0200 Subject: [PATCH 08/17] Add dummy bytes() constructor. Currently, MicroPython strings are mix between CPython byte and unicode strings. So, conversion is null so far. This dummy implementation is intended for compatibility with CPython (so, same code can run on both). --- py/builtin.c | 12 ++++++++++++ py/builtin.h | 1 + py/mpqstrraw.h | 1 + py/runtime.c | 1 + 4 files changed, 15 insertions(+) diff --git a/py/builtin.c b/py/builtin.c index f102aa5885..13463ada69 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -347,3 +347,15 @@ static mp_obj_t mp_builtin_str(mp_obj_t o_in) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_str_obj, mp_builtin_str); + +// TODO: This should be type, this is just quick CPython compat hack +static mp_obj_t mp_builtin_bytes(uint n_args, const mp_obj_t *args) { + if (!MP_OBJ_IS_QSTR(args[0]) && !MP_OBJ_IS_TYPE(args[0], &str_type)) { + assert(0); + } + // Currently, MicroPython strings are mix between CPython byte and unicode + // strings. So, conversion is null so far. + return args[0]; +} + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_bytes_obj, 1, 3, mp_builtin_bytes); diff --git a/py/builtin.h b/py/builtin.h index 050a2161c1..4257de5bdb 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -5,6 +5,7 @@ MP_DECLARE_CONST_FUN_OBJ(mp_builtin___repl_print___obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_abs_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_all_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_any_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_builtin_bytes_obj); // Temporary hack MP_DECLARE_CONST_FUN_OBJ(mp_builtin_callable_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_chr_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_divmod_obj); diff --git a/py/mpqstrraw.h b/py/mpqstrraw.h index 10b1fc0d39..9bc01c5851 100644 --- a/py/mpqstrraw.h +++ b/py/mpqstrraw.h @@ -40,6 +40,7 @@ Q(any) Q(array) Q(bool) Q(bytearray) +Q(bytes) Q(callable) Q(chr) Q(complex) diff --git a/py/runtime.c b/py/runtime.c index d8fc3ff6e4..72347aff80 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -130,6 +130,7 @@ void rt_init(void) { mp_map_add_qstr(&map_builtins, MP_QSTR_abs, (mp_obj_t)&mp_builtin_abs_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_all, (mp_obj_t)&mp_builtin_all_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_any, (mp_obj_t)&mp_builtin_any_obj); + mp_map_add_qstr(&map_builtins, MP_QSTR_bytes, (mp_obj_t)&mp_builtin_bytes_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_callable, (mp_obj_t)&mp_builtin_callable_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_chr, (mp_obj_t)&mp_builtin_chr_obj); mp_map_add_qstr(&map_builtins, MP_QSTR_divmod, (mp_obj_t)&mp_builtin_divmod_obj); From d991da72636865eb53a13a7c2a66dbde7f7c695d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 20:35:18 +0200 Subject: [PATCH 09/17] socket-client, socket-server examples now run on both Micro- and C-Python. --- examples/unix/sock-client.py | 30 ++++++++++++++++++++---------- examples/unix/sock-server.py | 32 ++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/examples/unix/sock-client.py b/examples/unix/sock-client.py index d5d5021475..08a39b196a 100644 --- a/examples/unix/sock-client.py +++ b/examples/unix/sock-client.py @@ -1,22 +1,32 @@ -mod = rawsocket -s = mod.socket() +try: + import rawsocket as _socket +except: + import _socket + + +s = _socket.socket() if 1: - ai = mod.getaddrinfo("google.com", 80) + ai = _socket.getaddrinfo("google.com", 80) print("Address infos:", ai) addr = ai[0][4] else: - # Deprecated way to construct connection address - addr = mod.sockaddr_in() + # Deprecated ways to construct connection address + addr = _socket.sockaddr_in() addr.sin_family = 2 #addr.sin_addr = (0x0100 << 16) + 0x007f #addr.sin_addr = (0x7f00 << 16) + 0x0001 - #addr.sin_addr = mod.inet_aton("127.0.0.1") - addr.sin_addr = mod.gethostbyname("google.com") - addr.sin_port = mod.htons(80) + #addr.sin_addr = _socket.inet_aton("127.0.0.1") + addr.sin_addr = _socket.gethostbyname("google.com") + addr.sin_port = _socket.htons(80) print("Connect address:", addr) s.connect(addr) -s.write("GET / HTTP/1.0\n\n") -print(s.readall()) +if 0: + # MicroPython rawsocket module supports file interface directly + s.write("GET / HTTP/1.0\n\n") + print(s.readall()) +else: + s.send(b"GET / HTTP/1.0\n\n") + print(s.recv(4096)) diff --git a/examples/unix/sock-server.py b/examples/unix/sock-server.py index c39a0af21c..ed14b7f31e 100644 --- a/examples/unix/sock-server.py +++ b/examples/unix/sock-server.py @@ -1,7 +1,18 @@ -mod = rawsocket -s = mod.socket() +try: + import rawsocket as socket +except: + import socket -ai = mod.getaddrinfo("127.0.0.1", 8080) + +CONTENT = """\ +HTTP/1.0 200 OK + +Hello #{} from MicroPython! +""" + +s = socket.socket() + +ai = socket.getaddrinfo("127.0.0.1", 8080) print("Bind address info:", ai) addr = ai[0][4] @@ -17,12 +28,13 @@ while True: print("Client address:", client_addr) print("Client socket:", client_s) print("Request:") - print(client_s.read(4096)) - #print(client_s.readall()) - client_s.write("""\ -HTTP/1.0 200 OK - -Hello #{} from MicroPython! -""".format(counter)) + if 0: + # MicroPython rawsocket module supports file interface directly + print(client_s.read(4096)) + #print(client_s.readall()) + client_s.write(CONTENT.format(counter)) + else: + print(client_s.recv(4096)) + client_s.send(bytes(CONTENT.format(counter), "ascii")) client_s.close() counter += 1 From e6da0df6d100f726dcaf90e1552fa1628bb385c1 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 23:00:26 +0200 Subject: [PATCH 10/17] mp_obj_get_type_str(): Handle MP_OBJ_QSTR. --- py/obj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/obj.c b/py/obj.c index 7dd9cc0a24..86429d03d4 100644 --- a/py/obj.c +++ b/py/obj.c @@ -28,6 +28,8 @@ mp_obj_t mp_obj_get_type(mp_obj_t o_in) { const char *mp_obj_get_type_str(mp_obj_t o_in) { if (MP_OBJ_IS_SMALL_INT(o_in)) { return "int"; + } else if (MP_OBJ_IS_QSTR(o_in)) { + return "str"; } else { mp_obj_base_t *o = o_in; return o->type->name; From 8965a5eb1e9d7df9018fc9537169f7bb9a523d9c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 23:33:19 +0200 Subject: [PATCH 11/17] objstr: More support for MP_OBJ_QSTR. --- py/objstr.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/py/objstr.c b/py/objstr.c index 5e87097a82..68fe7f0fcc 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -175,16 +175,8 @@ static bool chr_in_str(const char* const str, const size_t str_len, const char c static mp_obj_t str_find(uint n_args, const mp_obj_t *args) { assert(2 <= n_args && n_args <= 4); - assert(MP_OBJ_IS_TYPE(args[0], &str_type)); - if (!MP_OBJ_IS_TYPE(args[1], &str_type)) { - nlr_jump(mp_obj_new_exception_msg_1_arg( - MP_QSTR_TypeError, - "Can't convert '%s' object to str implicitly", - mp_obj_get_type_str(args[1]))); - } - - const char* haystack = qstr_str(((mp_obj_str_t*)args[0])->qstr); - const char* needle = qstr_str(((mp_obj_str_t*)args[1])->qstr); + const char* haystack = qstr_str(mp_obj_str_get(args[0])); + const char* needle = qstr_str(mp_obj_str_get(args[1])); size_t haystack_len = strlen(haystack); size_t needle_len = strlen(needle); @@ -222,14 +214,11 @@ mp_obj_t str_strip(uint n_args, const mp_obj_t *args) { if (n_args == 1) { chars_to_del = whitespace; } else { - assert(MP_OBJ_IS_TYPE(args[1], &str_type)); - mp_obj_str_t *chars_to_del_obj = args[1]; - chars_to_del = qstr_str(chars_to_del_obj->qstr); + chars_to_del = qstr_str(mp_obj_str_get(args[1])); } const size_t chars_to_del_len = strlen(chars_to_del); - mp_obj_str_t *self = args[0]; - const char *orig_str = qstr_str(self->qstr); + const char *orig_str = qstr_str(mp_obj_str_get(args[0])); const size_t orig_str_len = strlen(orig_str); size_t first_good_char_pos = 0; @@ -321,9 +310,15 @@ mp_obj_t mp_obj_new_str(qstr qstr) { } qstr mp_obj_str_get(mp_obj_t self_in) { - assert(MP_OBJ_IS_TYPE(self_in, &str_type)); - mp_obj_str_t *self = self_in; - return self->qstr; + if (MP_OBJ_IS_QSTR(self_in)) { + return MP_OBJ_QSTR_VALUE(self_in); + } + if (MP_OBJ_IS_TYPE(self_in, &str_type)) { + mp_obj_str_t *self = self_in; + return self->qstr; + } + nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "Can't convert '%s' object to str implicitly", + mp_obj_get_type_str(self_in))); } /******************************************************************************/ From 51ee44a718668964f2986096b3cb5ac45fb85439 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 20 Jan 2014 23:49:56 +0200 Subject: [PATCH 12/17] unix file: Refactor and add sys.stdout/stdin/stderr. --- unix/file.c | 27 +++++++++++++++++++++++---- unix/main.c | 3 ++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/unix/file.c b/unix/file.c index bbe84dfb49..a14f5895b8 100644 --- a/unix/file.c +++ b/unix/file.c @@ -8,6 +8,7 @@ #include "mpconfig.h" #include "mpqstr.h" #include "obj.h" +#include "runtime.h" #include "stream.h" typedef struct _mp_obj_fdfile_t { @@ -15,6 +16,8 @@ typedef struct _mp_obj_fdfile_t { int fd; } mp_obj_fdfile_t; +static const mp_obj_type_t rawfile_type; + static void fdfile_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { mp_obj_fdfile_t *self = self_in; print(env, "", self->fd); @@ -45,6 +48,13 @@ static mp_obj_t fdfile_close(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(fdfile_close_obj, fdfile_close); +static mp_obj_fdfile_t *fdfile_new(int fd) { + mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t); + o->base.type = &rawfile_type; + o->fd = fd; + return o; +} + static mp_obj_t fdfile_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { mp_obj_fdfile_t *o = m_new_obj(mp_obj_fdfile_t); o->base.type = type_in; @@ -81,11 +91,11 @@ static mp_obj_t fdfile_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const } } - o->fd = open(fname, mode, 0644); - if (o->fd == -1) { - nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_OSError, "[Errno %d]", (const char *)(machine_int_t)errno)); + int fd = open(fname, mode, 0644); + if (fd == -1) { + nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", errno)); } - return o; + return fdfile_new(fd); } static const mp_method_t rawfile_type_methods[] = { @@ -117,3 +127,12 @@ mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args) { return fdfile_make_new((mp_obj_t)&rawfile_type, n_args, 0, args); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_open_obj, 1, 2, mp_builtin_open); + +void file_init() { + rt_store_name(qstr_from_str_static("open"), (mp_obj_t)&mp_builtin_open_obj); + + mp_obj_t m_sys = mp_obj_new_module(qstr_from_str_static("sys")); + rt_store_attr(m_sys, qstr_from_str_static("stdin"), fdfile_new(STDIN_FILENO)); + rt_store_attr(m_sys, qstr_from_str_static("stdout"), fdfile_new(STDOUT_FILENO)); + rt_store_attr(m_sys, qstr_from_str_static("stderr"), fdfile_new(STDERR_FILENO)); +} diff --git a/unix/main.c b/unix/main.c index 08567c04dc..7f92814aa6 100644 --- a/unix/main.c +++ b/unix/main.c @@ -21,6 +21,7 @@ #endif extern const mp_obj_fun_native_t mp_builtin_open_obj; +void file_init(); void rawsocket_init(); static void execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, bool is_repl) { @@ -221,7 +222,7 @@ int main(int argc, char **argv) { rt_store_attr(m_sys, qstr_from_str_static("argv"), py_argv); rt_store_name(qstr_from_str_static("test"), test_obj_new(42)); - rt_store_name(qstr_from_str_static("open"), (mp_obj_t)&mp_builtin_open_obj); + file_init(); rawsocket_init(); // Here is some example code to create a class and instance of that class. From 439542f70c4546568dca3f2539d503aa7a6ec05b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 21 Jan 2014 00:19:19 +0200 Subject: [PATCH 13/17] sequence.c: Start to refactor sequence operations for reuse among types. --- py/obj.h | 3 +++ py/objlist.c | 9 ++------- py/py.mk | 1 + py/sequence.c | 25 +++++++++++++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 py/sequence.c diff --git a/py/obj.h b/py/obj.h index d7154a743c..c2ce32588a 100644 --- a/py/obj.h +++ b/py/obj.h @@ -376,3 +376,6 @@ typedef struct _mp_obj_classmethod_t { mp_obj_base_t base; mp_obj_t fun; } mp_obj_classmethod_t; + +// sequence helpers +void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest); diff --git a/py/objlist.c b/py/objlist.c index 0ad7b68799..8043842227 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -153,13 +153,8 @@ static mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) { return NULL; } int n = MP_OBJ_SMALL_INT_VALUE(rhs); - int len = o->len; - mp_obj_list_t *s = list_new(len * n); - mp_obj_t *dest = s->items; - for (int i = 0; i < n; i++) { - memcpy(dest, o->items, sizeof(mp_obj_t) * len); - dest += len; - } + mp_obj_list_t *s = list_new(o->len * n); + mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); return s; } case RT_COMPARE_OP_EQUAL: diff --git a/py/py.mk b/py/py.mk index ce5169f777..6ab1ee3057 100644 --- a/py/py.mk +++ b/py/py.mk @@ -97,6 +97,7 @@ PY_O_BASENAME = \ objstr.o \ objtuple.o \ objtype.o \ + sequence.o \ stream.o \ builtin.o \ builtinimport.o \ diff --git a/py/sequence.c b/py/sequence.c new file mode 100644 index 0000000000..af3c61af6e --- /dev/null +++ b/py/sequence.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "mpqstr.h" +#include "obj.h" +#include "map.h" +#include "runtime0.h" +#include "runtime.h" + +// Helpers for sequence types + +// Implements backend of sequence * integer operation. Assumes elements are +// memory-adjacent in sequence. +void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest) { + for (int i = 0; i < times; i++) { + uint copy_sz = item_sz * len; + memcpy(dest, items, copy_sz); + dest = (char*)dest + copy_sz; + } +} From 545591a696bdff73f68573c54a05ca70eb58032d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 21 Jan 2014 00:27:33 +0200 Subject: [PATCH 14/17] Implement string multiplication. --- py/objstr.c | 12 ++++++++++++ tests/basics/string1.py | 2 ++ 2 files changed, 14 insertions(+) diff --git a/py/objstr.c b/py/objstr.c index 68fe7f0fcc..01de5e3674 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -102,6 +102,18 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (strstr(lhs_str, rhs_str) == NULL)); } break; + case RT_BINARY_OP_MULTIPLY: + { + if (!MP_OBJ_IS_SMALL_INT(rhs_in)) { + return NULL; + } + int n = MP_OBJ_SMALL_INT_VALUE(rhs_in); + size_t len = strlen(lhs_str); + char *s = m_new(char, len * n + 1); + s[len * n] = 0; + mp_seq_multiply(lhs_str, sizeof(*lhs_str), len, n, s); + return MP_OBJ_NEW_QSTR(qstr_from_str_take(s, len * n + 1)); + } } return MP_OBJ_NULL; // op not supported diff --git a/tests/basics/string1.py b/tests/basics/string1.py index 3fecf799be..40e766b594 100644 --- a/tests/basics/string1.py +++ b/tests/basics/string1.py @@ -8,6 +8,8 @@ print(x) print('123' + "456") +print('123' * 5) + # iter print(list('str')) From 7380a837802b4630bdcef6e01cd5be32f92750ca Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 21 Jan 2014 02:22:02 +0200 Subject: [PATCH 15/17] str: Implement proper string (instead of byte string) indexing. Also, support negative indexes. --- py/objstr.c | 5 ++--- tests/basics/string1.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/py/objstr.c b/py/objstr.c index 01de5e3674..4adfef6f80 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -44,9 +44,8 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { // TODO: need predicate to check for int-like type (bools are such for example) // ["no", "yes"][1 == 2] is common idiom if (MP_OBJ_IS_SMALL_INT(rhs_in)) { - // TODO: This implements byte string access for single index so far - // TODO: Handle negative indexes. - return mp_obj_new_int(lhs_str[mp_obj_get_int(rhs_in)]); + uint index = mp_get_index(lhs->base.type, strlen(lhs_str), rhs_in); + return mp_obj_new_str(qstr_from_strn_copy(lhs_str + index, 1)); #if MICROPY_ENABLE_SLICE } else if (MP_OBJ_IS_TYPE(rhs_in, &slice_type)) { machine_int_t start, stop, step; diff --git a/tests/basics/string1.py b/tests/basics/string1.py index 40e766b594..074b04ef8f 100644 --- a/tests/basics/string1.py +++ b/tests/basics/string1.py @@ -10,6 +10,17 @@ print('123' + "456") print('123' * 5) +print('abc'[1]) +print('abc'[-1]) +try: + 'abc'[100] +except IndexError: + print('caught') +try: + 'abc'[-4] +except IndexError: + print('caught2') + # iter print(list('str')) From 4c316552c16fa2bcb77e007d330dc32beaf6e652 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 21 Jan 2014 05:00:21 +0200 Subject: [PATCH 16/17] Implement str.split(None). Note that splitting by explicit string is not implemented so far. --- py/objstr.c | 40 ++++++++++++++++++++++++++++++++++++ tests/basics/string_split.py | 7 +++++++ 2 files changed, 47 insertions(+) create mode 100644 tests/basics/string_split.py diff --git a/py/objstr.c b/py/objstr.c index 4adfef6f80..758e8c2938 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -175,6 +175,44 @@ bad_arg: nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "?str.join expecting a list of str's")); } +#define is_ws(c) ((c) == ' ' || (c) == '\t') + +static mp_obj_t str_split(uint n_args, const mp_obj_t *args) { + int splits = -1; + mp_obj_t sep = mp_const_none; + if (n_args > 1) { + sep = args[1]; + if (n_args > 2) { + splits = MP_OBJ_SMALL_INT_VALUE(args[2]); + } + } + assert(sep == mp_const_none); + mp_obj_t res = mp_obj_new_list(0, NULL); + const char *s = qstr_str(mp_obj_str_get(args[0])); + const char *start; + + // Initial whitespace is not counted as split, so we pre-do it + while (is_ws(*s)) s++; + while (*s && splits != 0) { + start = s; + while (*s != 0 && !is_ws(*s)) s++; + rt_list_append(res, MP_OBJ_NEW_QSTR(qstr_from_strn_copy(start, s - start))); + if (*s == 0) { + break; + } + while (is_ws(*s)) s++; + if (splits > 0) { + splits--; + } + } + + if (*s != 0) { + rt_list_append(res, MP_OBJ_NEW_QSTR(qstr_from_strn_copy(s, strlen(s)))); + } + + return res; +} + static bool chr_in_str(const char* const str, const size_t str_len, const char c) { for (size_t i = 0; i < str_len; i++) { if (str[i] == c) { @@ -293,12 +331,14 @@ mp_obj_t str_format(uint n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj, 2, 4, str_find); static MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_split_obj, 1, 3, str_split); static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj, 1, 2, str_strip); static MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, str_format); static const mp_method_t str_type_methods[] = { { "find", &str_find_obj }, { "join", &str_join_obj }, + { "split", &str_split_obj }, { "strip", &str_strip_obj }, { "format", &str_format_obj }, { NULL, NULL }, // end-of-list sentinel diff --git a/tests/basics/string_split.py b/tests/basics/string_split.py new file mode 100644 index 0000000000..f73cb4291e --- /dev/null +++ b/tests/basics/string_split.py @@ -0,0 +1,7 @@ +print("a b".split()) +print(" a b ".split(None)) +print(" a b ".split(None, 1)) +print(" a b ".split(None, 2)) +print(" a b c ".split(None, 1)) +print(" a b c ".split(None, 0)) +print(" a b c ".split(None, -1)) From 7280f790881fa174e4d234266ff42f0fe3d847bc Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 21 Jan 2014 04:58:34 +0200 Subject: [PATCH 17/17] run-tests: Allow to run tests selectively via command line. --- tests/run-tests | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/run-tests b/tests/run-tests index ef1368725f..752138ccc6 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -10,7 +10,14 @@ numpassed=0 numfailed=0 namefailed= -for infile in basics/*.py io/*.py +if [ $# -eq 0 ] +then + tests="basics/*.py io/*.py" +else + tests="$@" +fi + +for infile in $tests do basename=`basename $infile .py` outfile=${basename}.out