Skip to content

Commit b95d90b

Browse files
committed
Merge pull request adafruit#59 from pfalcon/slice
Implement basic slice object and string slicing
2 parents 9ba9589 + f8b9d3c commit b95d90b

9 files changed

Lines changed: 154 additions & 3 deletions

File tree

py/mpconfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@
2525
#ifndef MICROPY_MEM_STATS
2626
#define MICROPY_MEM_STATS (1)
2727
#endif
28+
29+
// Whether to support slice object and correspondingly
30+
// slice subscript operators
31+
#ifndef MICROPY_ENABLE_SLICE
32+
#define MICROPY_ENABLE_SLICE (1)
33+
#endif

py/obj.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ mp_obj_t mp_obj_new_list(uint n, mp_obj_t *items);
144144
mp_obj_t mp_obj_new_list_reverse(uint n, mp_obj_t *items);
145145
mp_obj_t mp_obj_new_dict(int n_args);
146146
mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items);
147+
mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step);
147148
mp_obj_t mp_obj_new_bound_meth(mp_obj_t self, mp_obj_t meth);
148149
mp_obj_t mp_obj_new_class(struct _mp_map_t *class_locals);
149150
mp_obj_t mp_obj_new_instance(mp_obj_t clas);
@@ -214,6 +215,10 @@ mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
214215
// set
215216
void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item);
216217

218+
// slice
219+
extern const mp_obj_type_t slice_type;
220+
void mp_obj_slice_get(mp_obj_t self_in, machine_int_t *start, machine_int_t *stop, machine_int_t *step);
221+
217222
// functions
218223
typedef struct _mp_obj_fun_native_t { // need this so we can define const objects (to go in ROM)
219224
mp_obj_base_t base;

py/objslice.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <stdlib.h>
2+
#include <stdint.h>
3+
#include <string.h>
4+
#include <assert.h>
5+
6+
#include "nlr.h"
7+
#include "misc.h"
8+
#include "mpconfig.h"
9+
#include "obj.h"
10+
#include "runtime0.h"
11+
12+
#if MICROPY_ENABLE_SLICE
13+
14+
// TODO: This implements only variant of slice with 2 integer args only.
15+
// CPython supports 3rd arg (step), plus args can be arbitrary Python objects.
16+
typedef struct _mp_obj_slice_t {
17+
mp_obj_base_t base;
18+
machine_int_t start;
19+
machine_int_t stop;
20+
} mp_obj_slice_t;
21+
22+
void slice_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in) {
23+
mp_obj_slice_t *o = o_in;
24+
print(env, "slice(" INT_FMT ", " INT_FMT ")", o->start, o->stop);
25+
}
26+
27+
const mp_obj_type_t slice_type = {
28+
{ &mp_const_type },
29+
"slice",
30+
slice_print,
31+
NULL, // call_n
32+
NULL, // unary_op
33+
NULL, // binary_op
34+
NULL, // getiter
35+
NULL, // iternext
36+
{ { NULL, NULL }, }, // method list
37+
};
38+
39+
// TODO: Make sure to handle "empty" values, which are signified by None in CPython
40+
mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) {
41+
assert(ostep == NULL);
42+
machine_int_t start = 0, stop = 0;
43+
if (ostart != mp_const_none) {
44+
start = mp_obj_get_int(ostart);
45+
}
46+
if (ostop != mp_const_none) {
47+
stop = mp_obj_get_int(ostop);
48+
if (stop == 0) {
49+
// [x:0] is a special case - in our slice object, stop = 0 means
50+
// "end of sequence". Fortunately, [x:0] is an empty seqence for
51+
// any x (including negative). [x:x] is also always empty sequence.
52+
// but x also can be 0. But note that b""[x:x] is b"" for any x (i.e.
53+
// no IndexError, at least in Python 3.3.3). So, we just use -1's to
54+
// signify that. -1 is catchy "special" number in case someone will
55+
// try to print [x:0] slice ever.
56+
start = stop = -1;
57+
}
58+
}
59+
mp_obj_slice_t *o = m_new(mp_obj_slice_t, 1);
60+
o->base.type = &slice_type;
61+
o->start = start;
62+
o->stop = stop;
63+
return (mp_obj_t)o;
64+
}
65+
66+
void mp_obj_slice_get(mp_obj_t self_in, machine_int_t *start, machine_int_t *stop, machine_int_t *step) {
67+
assert(MP_OBJ_IS_TYPE(self_in, &slice_type));
68+
mp_obj_slice_t *self = self_in;
69+
*start = self->start;
70+
*stop = self->stop;
71+
*step = 1;
72+
}
73+
74+
#endif

py/objstr.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,31 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
2727
const char *lhs_str = qstr_str(lhs->qstr);
2828
switch (op) {
2929
case RT_BINARY_OP_SUBSCR:
30-
// string access
31-
// XXX a massive hack!
32-
return mp_obj_new_int(lhs_str[mp_obj_get_int(rhs_in)]);
30+
// TODO: need predicate to check for int-like type (bools are such for example)
31+
// ["no", "yes"][1 == 2] is common idiom
32+
if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
33+
// TODO: This implements byte string access for single index so far
34+
// TODO: Handle negative indexes.
35+
return mp_obj_new_int(lhs_str[mp_obj_get_int(rhs_in)]);
36+
#if MICROPY_ENABLE_SLICE
37+
} else if (MP_OBJ_IS_TYPE(rhs_in, &slice_type)) {
38+
int start, stop, step;
39+
mp_obj_slice_get(rhs_in, &start, &stop, &step);
40+
assert(step == 1);
41+
int len = strlen(lhs_str);
42+
if (start < 0) {
43+
start = len + start;
44+
}
45+
if (stop <= 0) {
46+
stop = len + stop;
47+
}
48+
return mp_obj_new_str(qstr_from_strn_copy(lhs_str + start, stop - start));
49+
#endif
50+
} else {
51+
// Message doesn't match CPython, but we don't have so much bytes as they
52+
// to spend them on verbose wording
53+
nlr_jump(mp_obj_new_exception_msg(rt_q_TypeError, "index must be int"));
54+
}
3355

3456
case RT_BINARY_OP_ADD:
3557
case RT_BINARY_OP_INPLACE_ADD:

py/vm.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,20 @@ bool mp_execute_byte_code_2(const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **
410410
sp++;
411411
break;
412412

413+
#if MICROPY_ENABLE_SLICE
414+
case MP_BC_BUILD_SLICE:
415+
DECODE_UINT;
416+
if (unum == 2) {
417+
obj2 = POP();
418+
obj1 = TOP();
419+
SET_TOP(mp_obj_new_slice(obj1, obj2, NULL));
420+
} else {
421+
printf("3-argument slice is not supported\n");
422+
assert(0);
423+
}
424+
break;
425+
#endif
426+
413427
case MP_BC_UNPACK_SEQUENCE:
414428
DECODE_UINT;
415429
rt_unpack_sequence(sp[0], unum, sp - unum + 1);

stm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ PY_O = \
7878
objnone.o \
7979
objrange.o \
8080
objset.o \
81+
objslice.o \
8182
objstr.o \
8283
objtuple.o \
8384
objtype.o \

tests/basics/tests/slice-bstr1.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
b"123"[0:1]
2+
3+
b"123"[0:2]
4+
5+
b"123"[:1]
6+
7+
b"123"[1:]
8+
9+
# Idiom for copying sequence
10+
b"123"[:]
11+
12+
b"123"[:-1]
13+
14+
# Weird cases
15+
b"123"[0:0]
16+
b"123"[1:0]
17+
b"123"[1:1]
18+
b"123"[-1:-1]
19+
b"123"[-3:]
20+
b"123"[-3:3]
21+
b"123"[0:]
22+
b"123"[:0]
23+
b"123"[:-3]
24+
b"123"[:-4]
25+
# No IndexError!
26+
b""[1:1]
27+
b""[-1:-1]

unix-cpy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ PY_O = \
4343
objnone.o \
4444
objrange.o \
4545
objset.o \
46+
objslice.o \
4647
objstr.o \
4748
objtuple.o \
4849
objtype.o \

unix/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ PY_O = \
5050
objnone.o \
5151
objrange.o \
5252
objset.o \
53+
objslice.o \
5354
objstr.o \
5455
objtuple.o \
5556
objtype.o \

0 commit comments

Comments
 (0)