diff --git a/include/pocketpy/common/str.h b/include/pocketpy/common/str.h index 41bf3e85..c1fee572 100644 --- a/include/pocketpy/common/str.h +++ b/include/pocketpy/common/str.h @@ -42,7 +42,6 @@ void c11_string__ctor2(c11_string* self, const char* data, int size); c11_string* c11_string__copy(c11_string* self); void c11_string__delete(c11_string* self); c11_sv c11_string__sv(c11_string* self); -c11_string* c11_string__replace(c11_string* self, char old, char new_); int c11_string__u8_length(c11_string* self); c11_sv c11_string__u8_getitem(c11_string* self, int i); @@ -56,6 +55,9 @@ int c11_sv__index(c11_sv self, char c); int c11_sv__index2(c11_sv self, c11_sv sub, int start); int c11_sv__count(c11_sv self, c11_sv sub); +c11_string* c11_sv__replace(c11_sv self, char old, char new_); +c11_string* c11_sv__replace2(c11_sv self, c11_sv old, c11_sv new_); + c11_vector/* T=c11_sv */ c11_sv__split(c11_sv self, char sep); c11_vector/* T=c11_sv */ c11_sv__split2(c11_sv self, c11_sv sep); diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 8eca55ed..23ef009c 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -70,6 +70,9 @@ void pk_VM__dtor(pk_VM* self); void pk_VM__push_frame(pk_VM* self, Frame* frame); void pk_VM__pop_frame(pk_VM* self); +bool pk__parse_int_slice(const py_Ref slice, int length, int* start, int* stop, int* step); +bool pk__normalize_index(int* index, int length); + typedef enum pk_FrameResult { RES_RETURN, RES_CALL, diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index e93d107f..db3ec960 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -296,6 +296,7 @@ bool py_str(py_Ref val); py_GlobalRef py_retval(); #define py_isnil(self) ((self)->type == 0) +#define py_isnone(self) ((self)->type == tp_none_type) /* tuple */ @@ -334,6 +335,11 @@ const char* py_tpname(py_Type type); /// Check if the object is an instance of the given type. bool py_checktype(const py_Ref self, py_Type type); +#define py_checkint(self) py_checktype(self, tp_int) +#define py_checkfloat(self) py_checktype(self, tp_float) +#define py_checkbool(self) py_checktype(self, tp_bool) +#define py_checkstr(self) py_checktype(self, tp_str) + int py_replinput(char* buf); /// Python favored string formatting. diff --git a/src/common/str.c b/src/common/str.c index b205198e..9441112a 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -38,8 +38,8 @@ void c11_string__delete(c11_string* self) { free(self); } c11_sv c11_string__sv(c11_string* self) { return (c11_sv){self->data, self->size}; } -c11_string* c11_string__replace(c11_string* self, char old, char new_) { - c11_string* retval = c11_string__copy(self); +c11_string* c11_sv__replace(c11_sv self, char old, char new_) { + c11_string* retval = c11_string__new2(self.data, self.size); char* p = (char*)retval->data; for(int i = 0; i < retval->size; i++) { if(p[i] == old) p[i] = new_; @@ -47,6 +47,23 @@ c11_string* c11_string__replace(c11_string* self, char old, char new_) { return retval; } +c11_string* c11_sv__replace2(c11_sv self, c11_sv old, c11_sv new_){ + c11_sbuf buf; + c11_sbuf__ctor(&buf); + int start = 0; + while(true) { + int i = c11_sv__index2(self, old, start); + if(i == -1) break; + c11_sv tmp = c11_sv__slice2(self, start, i); + c11_sbuf__write_sv(&buf, tmp); + c11_sbuf__write_sv(&buf, new_); + start = i + old.size; + } + c11_sv tmp = c11_sv__slice2(self, start, self.size); + c11_sbuf__write_sv(&buf, tmp); + return c11_sbuf__submit(&buf); +} + int c11_string__u8_length(c11_string* self) { return c11__byte_index_to_unicode(self->data, self->size); } diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 8470e4c3..4c6d4da0 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -753,5 +753,7 @@ bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) { pk_VM* self = pk_current_vm; PUSH(lhs); PUSH(rhs); - return stack_binaryop(self, op, rop); + bool ok = stack_binaryop(self, op, rop); + STACK_SHRINK(2); + return ok; } \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 394139ac..db7ab432 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -189,6 +189,70 @@ void pk_VM__pop_frame(pk_VM* self) { Frame__delete(frame); } +static void _clip_int(int* value, int min, int max) { + if(*value < min) *value = min; + if(*value > max) *value = max; +} + +bool pk__parse_int_slice(const py_Ref slice, int length, int* start, int* stop, int* step) { + py_Ref s_start = py_getslot(slice, 0); + py_Ref s_stop = py_getslot(slice, 1); + py_Ref s_step = py_getslot(slice, 2); + + if(py_isnone(s_step)) + *step = 1; + else { + if(!py_checkint(s_step)) return false; + *step = py_toint(s_step); + } + if(*step == 0) return ValueError("slice step cannot be zero"); + + if(*step > 0) { + if(py_isnone(s_start)) + *start = 0; + else { + if(!py_checkint(s_start)) return false; + *start = py_toint(s_start); + if(*start < 0) *start += length; + _clip_int(start, 0, length); + } + if(py_isnone(s_stop)) + *stop = length; + else { + if(!py_checkint(s_stop)) return false; + *stop = py_toint(s_stop); + if(*stop < 0) *stop += length; + _clip_int(stop, 0, length); + } + } else { + if(py_isnone(s_start)) + *start = length - 1; + else { + if(!py_checkint(s_start)) return false; + *start = py_toint(s_start); + if(*start < 0) *start += length; + _clip_int(start, -1, length - 1); + } + if(py_isnone(s_stop)) + *stop = -1; + else { + if(!py_checkint(s_stop)) return false; + *stop = py_toint(s_stop); + if(*stop < 0) *stop += length; + _clip_int(stop, -1, length - 1); + } + } + return true; +} + +bool pk__normalize_index(int *index, int length){ + if(*index < 0) *index += length; + if(*index < 0 || *index >= length){ + return IndexError("index out of range"); + } + return true; +} + py_Type pk_VM__new_type(pk_VM* self, const char* name, py_Type base, diff --git a/src/public/py_list.c b/src/public/py_list.c index fab80d4a..1c97f467 100644 --- a/src/public/py_list.c +++ b/src/public/py_list.c @@ -6,7 +6,6 @@ typedef c11_vector List; - void py_newlist(py_Ref out) { pk_VM* vm = pk_current_vm; PyObject* obj = pk_ManagedHeap__gcnew(&vm->heap, tp_list, 0, sizeof(List)); @@ -60,13 +59,48 @@ void py_list__insert(py_Ref self, int i, const py_Ref val) { } //////////////////////////////// -static bool _py_list__len__(int argc, py_Ref argv){ +static bool _py_list__len__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); py_i64 res = py_list__len(py_arg(0)); py_newint(py_retval(), res); return true; } +static bool _py_list__eq__(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + py_Ref _0 = py_arg(0); + py_Ref _1 = py_arg(1); + if(py_istype(_1, tp_list)) { + int length = py_list__len(_0); + if(length != py_list__len(_1)) { + py_newbool(py_retval(), false); + return true; + } + for(int i = 0; i < length; i++) { + py_Ref a = py_list__getitem(_0, i); + py_Ref b = py_list__getitem(_1, i); + int res = py_eq(a, b); + if(res == -1) return false; + if(res == 0) { + py_newbool(py_retval(), false); + return true; + } + } + py_newbool(py_retval(), true); + } else { + py_newnotimplemented(py_retval()); + } + return true; +} + +static bool _py_list__ne__(int argc, py_Ref argv) { + bool ok = _py_list__eq__(argc, argv); + if(!ok) return false; + py_Ref retval = py_retval(); + py_newbool(retval, !py_tobool(retval)); + return true; +} + py_Type pk_list__register() { pk_VM* vm = pk_current_vm; py_Type type = pk_VM__new_type(vm, "list", tp_object, NULL, false); @@ -74,5 +108,7 @@ py_Type pk_list__register() { ti->dtor = (void (*)(void*))c11_vector__dtor; py_bindmagic(type, __len__, _py_list__len__); + py_bindmagic(type, __eq__, _py_list__eq__); + py_bindmagic(type, __ne__, _py_list__ne__); return type; } \ No newline at end of file diff --git a/src/public/py_str.c b/src/public/py_str.c index a5b514cb..5d4cb87e 100644 --- a/src/public/py_str.c +++ b/src/public/py_str.c @@ -165,9 +165,23 @@ static bool _py_str__iter__(int argc, py_Ref argv) { static bool _py_str__getitem__(int argc, py_Ref argv) { PY_CHECK_ARGC(2); c11_string* self = py_touserdata(&argv[0]); - PY_CHECK_ARG_TYPE(1, tp_int); - c11_sv res = c11_string__u8_getitem(self, py_toint(py_arg(1))); - py_newstrn(py_retval(), res.data, res.size); + py_Ref _1 = py_arg(1); + if(_1->type == tp_int) { + int index = py_toint(py_arg(1)); + pk__normalize_index(&index, self->size); + c11_sv res = c11_string__u8_getitem(self, index); + py_newstrn(py_retval(), res.data, res.size); + } else if(_1->type == tp_slice) { + int start, stop, step; + bool ok = pk__parse_int_slice(_1, c11_string__u8_length(self), &start, &stop, &step); + if(!ok) return false; + c11_string* res = c11_string__u8_slice(self, start, stop, step); + py_newstrn(py_retval(), res->data, res->size); + c11_string__delete(res); + return true; + } else { + return TypeError("str indices must be integers"); + } return true; } @@ -257,6 +271,77 @@ static bool _py_str__join(int argc, py_Ref argv) { return false; } +static bool _py_str__replace(int argc, py_Ref argv) { + PY_CHECK_ARGC(3); + c11_string* self = py_touserdata(&argv[0]); + PY_CHECK_ARG_TYPE(1, tp_str); + PY_CHECK_ARG_TYPE(2, tp_str); + c11_string* old = py_touserdata(&argv[1]); + c11_string* new_ = py_touserdata(&argv[2]); + c11_string* res = + c11_sv__replace2(c11_string__sv(self), c11_string__sv(old), c11_string__sv(new_)); + py_newstrn(py_retval(), res->data, res->size); + c11_string__delete(res); + return true; +} + +static bool _py_str__split(int argc, py_Ref argv) { + c11_sv self = c11_string__sv(py_touserdata(&argv[0])); + c11_vector res; + if(argc > 2) return TypeError("split() takes at most 2 arguments"); + if(argc == 1) { + // sep = ' ' + res = c11_sv__split(self, ' '); + } + if(argc == 2) { + // sep = argv[1] + if(!py_checkstr(&argv[1])) return false; + c11_sv sep = c11_string__sv(py_touserdata(&argv[1])); + res = c11_sv__split2(self, sep); + } + py_newlistn(py_retval(), res.count); + for(int i = 0; i < res.count; i++) { + c11_sv item = c11__getitem(c11_sv, &res, i); + py_newstrn(py_list__getitem(py_retval(), i), item.data, item.size); + } + c11_vector__dtor(&res); + return true; +} + +static bool _py_str__count(int argc, py_Ref argv) { + PY_CHECK_ARGC(2); + c11_string* self = py_touserdata(&argv[0]); + PY_CHECK_ARG_TYPE(1, tp_str); + c11_string* sub = py_touserdata(&argv[1]); + int res = c11_sv__count(c11_string__sv(self), c11_string__sv(sub)); + py_newint(py_retval(), res); + return true; +} + +static bool _py_str__strip(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_string* self = py_touserdata(&argv[0]); + c11_sv res = c11_sv__strip(c11_string__sv(self), true, true); + py_newstrn(py_retval(), res.data, res.size); + return true; +} + +static bool _py_str__lstrip(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_string* self = py_touserdata(&argv[0]); + c11_sv res = c11_sv__strip(c11_string__sv(self), true, false); + py_newstrn(py_retval(), res.data, res.size); + return true; +} + +static bool _py_str__rstrip(int argc, py_Ref argv) { + PY_CHECK_ARGC(1); + c11_string* self = py_touserdata(&argv[0]); + c11_sv res = c11_sv__strip(c11_string__sv(self), false, true); + py_newstrn(py_retval(), res.data, res.size); + return true; +} + py_Type pk_str__register() { pk_VM* vm = pk_current_vm; py_Type type = pk_VM__new_type(vm, "str", tp_object, NULL, false); @@ -286,6 +371,12 @@ py_Type pk_str__register() { py_bindmethod(tp_str, "startswith", _py_str__startswith); py_bindmethod(tp_str, "endswith", _py_str__endswith); py_bindmethod(tp_str, "join", _py_str__join); + py_bindmethod(tp_str, "replace", _py_str__replace); + py_bindmethod(tp_str, "split", _py_str__split); + py_bindmethod(tp_str, "count", _py_str__count); + py_bindmethod(tp_str, "strip", _py_str__strip); + py_bindmethod(tp_str, "lstrip", _py_str__lstrip); + py_bindmethod(tp_str, "rstrip", _py_str__rstrip); return type; }