blob: a1d139e83aed2fb71bae3be9f108374ac2ead709 [file] [log] [blame]
Damiend99b0522013-12-21 18:17:45 +00001#include <stdlib.h>
2#include <stdint.h>
3#include <stdarg.h>
4#include <string.h>
5#include <assert.h>
6
7#include "nlr.h"
8#include "misc.h"
9#include "mpconfig.h"
Damien Georgeeb7bfcb2014-01-04 15:57:35 +000010#include "mpqstr.h"
Damiend99b0522013-12-21 18:17:45 +000011#include "obj.h"
12#include "runtime0.h"
13#include "runtime.h"
14
15typedef struct _mp_obj_str_t {
16 mp_obj_base_t base;
17 qstr qstr;
18} mp_obj_str_t;
19
xyb8cfc9f02014-01-05 18:47:51 +080020static mp_obj_t mp_obj_new_str_iterator(mp_obj_str_t *str, int cur);
21
22/******************************************************************************/
23/* str */
24
Damiend99b0522013-12-21 18:17:45 +000025void str_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
26 mp_obj_str_t *self = self_in;
27 // TODO need to escape chars etc
28 print(env, "'%s'", qstr_str(self->qstr));
29}
30
31mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
32 mp_obj_str_t *lhs = lhs_in;
33 const char *lhs_str = qstr_str(lhs->qstr);
34 switch (op) {
35 case RT_BINARY_OP_SUBSCR:
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020036 // TODO: need predicate to check for int-like type (bools are such for example)
37 // ["no", "yes"][1 == 2] is common idiom
38 if (MP_OBJ_IS_SMALL_INT(rhs_in)) {
39 // TODO: This implements byte string access for single index so far
Paul Sokolovskyf8b9d3c2014-01-04 01:38:26 +020040 // TODO: Handle negative indexes.
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020041 return mp_obj_new_int(lhs_str[mp_obj_get_int(rhs_in)]);
Paul Sokolovskye606cb62014-01-04 01:34:23 +020042#if MICROPY_ENABLE_SLICE
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020043 } else if (MP_OBJ_IS_TYPE(rhs_in, &slice_type)) {
Damien Georgec8d13842014-01-04 01:06:10 +000044 machine_int_t start, stop, step;
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020045 mp_obj_slice_get(rhs_in, &start, &stop, &step);
46 assert(step == 1);
Paul Sokolovskydecad082014-01-03 23:36:56 +020047 int len = strlen(lhs_str);
48 if (start < 0) {
49 start = len + start;
Paul Sokolovsky6ee1e382014-01-04 03:47:34 +020050 if (start < 0) {
51 start = 0;
52 }
53 } else if (start > len) {
54 start = len;
Paul Sokolovskydecad082014-01-03 23:36:56 +020055 }
56 if (stop <= 0) {
57 stop = len + stop;
Paul Sokolovsky6ee1e382014-01-04 03:47:34 +020058 // CPython returns empty string in such case
59 if (stop < 0) {
60 stop = start;
61 }
62 } else if (stop > len) {
63 stop = len;
Paul Sokolovskydecad082014-01-03 23:36:56 +020064 }
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020065 return mp_obj_new_str(qstr_from_strn_copy(lhs_str + start, stop - start));
Paul Sokolovskye606cb62014-01-04 01:34:23 +020066#endif
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020067 } else {
Paul Sokolovskyf8b9d3c2014-01-04 01:38:26 +020068 // Message doesn't match CPython, but we don't have so much bytes as they
69 // to spend them on verbose wording
Damien Georgeeb7bfcb2014-01-04 15:57:35 +000070 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "index must be int"));
Paul Sokolovsky31ba60f2014-01-03 02:51:16 +020071 }
Damiend99b0522013-12-21 18:17:45 +000072
73 case RT_BINARY_OP_ADD:
74 case RT_BINARY_OP_INPLACE_ADD:
75 if (MP_OBJ_IS_TYPE(rhs_in, &str_type)) {
76 // add 2 strings
77 const char *rhs_str = qstr_str(((mp_obj_str_t*)rhs_in)->qstr);
Damien Georgefe8fb912014-01-02 16:36:09 +000078 size_t lhs_len = strlen(lhs_str);
79 size_t rhs_len = strlen(rhs_str);
80 int alloc_len = lhs_len + rhs_len + 1;
Damien732407f2013-12-29 19:33:23 +000081 char *val = m_new(char, alloc_len);
Damien Georgefe8fb912014-01-02 16:36:09 +000082 memcpy(val, lhs_str, lhs_len);
83 memcpy(val + lhs_len, rhs_str, rhs_len);
84 val[lhs_len + rhs_len] = '\0';
Damien732407f2013-12-29 19:33:23 +000085 return mp_obj_new_str(qstr_from_str_take(val, alloc_len));
Damiend99b0522013-12-21 18:17:45 +000086 }
87 break;
88 }
89
90 return MP_OBJ_NULL; // op not supported
91}
92
xyb8cfc9f02014-01-05 18:47:51 +080093static mp_obj_t str_getiter(mp_obj_t o_in) {
94 return mp_obj_new_str_iterator(o_in, 0);
95}
96
Damiend99b0522013-12-21 18:17:45 +000097mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) {
98 assert(MP_OBJ_IS_TYPE(self_in, &str_type));
99 mp_obj_str_t *self = self_in;
Damiend99b0522013-12-21 18:17:45 +0000100
Damien Georgefe8fb912014-01-02 16:36:09 +0000101 // get separation string
102 const char *sep_str = qstr_str(self->qstr);
103 size_t sep_len = strlen(sep_str);
104
105 // process args
Damiend99b0522013-12-21 18:17:45 +0000106 uint seq_len;
107 mp_obj_t *seq_items;
108 if (MP_OBJ_IS_TYPE(arg, &tuple_type)) {
109 mp_obj_tuple_get(arg, &seq_len, &seq_items);
110 } else if (MP_OBJ_IS_TYPE(arg, &list_type)) {
111 mp_obj_list_get(arg, &seq_len, &seq_items);
112 } else {
113 goto bad_arg;
114 }
Damien Georgefe8fb912014-01-02 16:36:09 +0000115
116 // count required length
117 int required_len = 0;
Damiend99b0522013-12-21 18:17:45 +0000118 for (int i = 0; i < seq_len; i++) {
119 if (!MP_OBJ_IS_TYPE(seq_items[i], &str_type)) {
120 goto bad_arg;
121 }
Damien Georgefe8fb912014-01-02 16:36:09 +0000122 if (i > 0) {
123 required_len += sep_len;
124 }
Damiend99b0522013-12-21 18:17:45 +0000125 required_len += strlen(qstr_str(mp_obj_str_get(seq_items[i])));
126 }
127
128 // make joined string
129 char *joined_str = m_new(char, required_len + 1);
Damien Georgefe8fb912014-01-02 16:36:09 +0000130 char *s_dest = joined_str;
Damiend99b0522013-12-21 18:17:45 +0000131 for (int i = 0; i < seq_len; i++) {
Damiend99b0522013-12-21 18:17:45 +0000132 if (i > 0) {
Damien Georgefe8fb912014-01-02 16:36:09 +0000133 memcpy(s_dest, sep_str, sep_len);
134 s_dest += sep_len;
Damiend99b0522013-12-21 18:17:45 +0000135 }
Damien Georgefe8fb912014-01-02 16:36:09 +0000136 const char *s2 = qstr_str(mp_obj_str_get(seq_items[i]));
137 size_t s2_len = strlen(s2);
138 memcpy(s_dest, s2, s2_len);
139 s_dest += s2_len;
Damiend99b0522013-12-21 18:17:45 +0000140 }
Damien Georgefe8fb912014-01-02 16:36:09 +0000141 *s_dest = '\0';
142
143 // return joined string
Damien732407f2013-12-29 19:33:23 +0000144 return mp_obj_new_str(qstr_from_str_take(joined_str, required_len + 1));
Damiend99b0522013-12-21 18:17:45 +0000145
146bad_arg:
Damien Georgeeb7bfcb2014-01-04 15:57:35 +0000147 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "?str.join expecting a list of str's"));
Damiend99b0522013-12-21 18:17:45 +0000148}
149
150void vstr_printf_wrapper(void *env, const char *fmt, ...) {
151 va_list args;
152 va_start(args, fmt);
153 vstr_vprintf(env, fmt, args);
154 va_end(args);
155}
156
157mp_obj_t str_format(int n_args, const mp_obj_t *args) {
158 assert(MP_OBJ_IS_TYPE(args[0], &str_type));
159 mp_obj_str_t *self = args[0];
160
161 const char *str = qstr_str(self->qstr);
162 int arg_i = 1;
163 vstr_t *vstr = vstr_new();
164 for (; *str; str++) {
165 if (*str == '{') {
166 str++;
167 if (*str == '{') {
168 vstr_add_char(vstr, '{');
169 } else if (*str == '}') {
170 if (arg_i >= n_args) {
Damien Georgeeb7bfcb2014-01-04 15:57:35 +0000171 nlr_jump(mp_obj_new_exception_msg(MP_QSTR_IndexError, "tuple index out of range"));
Damiend99b0522013-12-21 18:17:45 +0000172 }
173 mp_obj_print_helper(vstr_printf_wrapper, vstr, args[arg_i]);
174 arg_i++;
175 }
176 } else {
177 vstr_add_char(vstr, *str);
178 }
179 }
180
Damien732407f2013-12-29 19:33:23 +0000181 return mp_obj_new_str(qstr_from_str_take(vstr->buf, vstr->alloc));
Damiend99b0522013-12-21 18:17:45 +0000182}
183
184static MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join);
185static MP_DEFINE_CONST_FUN_OBJ_VAR(str_format_obj, 1, str_format);
186
187const mp_obj_type_t str_type = {
188 { &mp_const_type },
189 "str",
190 str_print, // print
Damien George71c51812014-01-04 20:21:15 +0000191 NULL, // make_new
Damiend99b0522013-12-21 18:17:45 +0000192 NULL, // call_n
193 NULL, // unary_op
194 str_binary_op, // binary_op
xyb8cfc9f02014-01-05 18:47:51 +0800195 str_getiter, // getiter
Damiend99b0522013-12-21 18:17:45 +0000196 NULL, // iternext
197 { // method list
198 { "join", &str_join_obj },
199 { "format", &str_format_obj },
200 { NULL, NULL }, // end-of-list sentinel
201 },
202};
203
204mp_obj_t mp_obj_new_str(qstr qstr) {
205 mp_obj_str_t *o = m_new_obj(mp_obj_str_t);
206 o->base.type = &str_type;
207 o->qstr = qstr;
208 return o;
209}
210
211qstr mp_obj_str_get(mp_obj_t self_in) {
212 assert(MP_OBJ_IS_TYPE(self_in, &str_type));
213 mp_obj_str_t *self = self_in;
214 return self->qstr;
215}
xyb8cfc9f02014-01-05 18:47:51 +0800216
217/******************************************************************************/
218/* str iterator */
219
220typedef struct _mp_obj_str_it_t {
221 mp_obj_base_t base;
222 mp_obj_str_t *str;
223 machine_uint_t cur;
224} mp_obj_str_it_t;
225
226mp_obj_t str_it_iternext(mp_obj_t self_in) {
227 mp_obj_str_it_t *self = self_in;
228 const char *str = qstr_str(self->str->qstr);
229 if (self->cur < strlen(str)) {
230 mp_obj_t o_out = mp_obj_new_str(qstr_from_strn_copy(str + self->cur, 1));
231 self->cur += 1;
232 return o_out;
233 } else {
234 return mp_const_stop_iteration;
235 }
236}
237
238static const mp_obj_type_t str_it_type = {
239 { &mp_const_type },
240 "str_iterator",
241 NULL, // print
242 NULL, // make_new
243 NULL, // call_n
244 NULL, // unary_op
245 NULL, // binary_op
246 NULL, // getiter
247 str_it_iternext, // iternext
248 { { NULL, NULL }, }, // method str
249};
250
251mp_obj_t mp_obj_new_str_iterator(mp_obj_str_t *str, int cur) {
252 mp_obj_str_it_t *o = m_new_obj(mp_obj_str_it_t);
253 o->base.type = &str_it_type;
254 o->str = str;
255 o->cur = cur;
256 return o;
257}