diff --git a/include/pocketpy/common/str.h b/include/pocketpy/common/str.h index c2336680..d39c43af 100644 --- a/include/pocketpy/common/str.h +++ b/include/pocketpy/common/str.h @@ -35,6 +35,9 @@ pkpy_Str pkpy_Str__substr(const pkpy_Str* self, int start); pkpy_Str pkpy_Str__substr2(const pkpy_Str* self, int start, int size); pkpy_Str pkpy_Str__lower(const pkpy_Str* self); pkpy_Str pkpy_Str__upper(const pkpy_Str* self); +pkpy_Str pkpy_Str__escape(const pkpy_Str* self, char quote); +// pkpy_Str pkpy_Str__strip(const pkpy_Str* self, bool left, bool right); +// pkpy_Str pkpy_Str__strip2(const pkpy_Str* self, bool left, bool right, const pkpy_Str* chars); pkpy_Str pkpy_Str__replace(const pkpy_Str* self, char old, char new_); pkpy_Str pkpy_Str__replace2(const pkpy_Str* self, const pkpy_Str* old, const pkpy_Str* new_); pkpy_Str pkpy_Str__u8_getitem(const pkpy_Str* self, int i); diff --git a/include/pocketpy/common/str.hpp b/include/pocketpy/common/str.hpp index 4d31ddc9..f43fb16a 100644 --- a/include/pocketpy/common/str.hpp +++ b/include/pocketpy/common/str.hpp @@ -177,8 +177,10 @@ struct Str: pkpy_Str { return pkpy_Str__replace2(this, &old, &new_); } - Str escape(bool single_quote = true) const; - void escape_(SStream& ss, bool single_quote = true) const; + Str escape(char quote='\'') const{ + return pkpy_Str__escape(this, quote); + } + vector split(const Str& sep) const; vector split(char sep) const; diff --git a/src/common/str.c b/src/common/str.c index d9c619b1..905a9f83 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -118,6 +118,62 @@ pkpy_Str pkpy_Str__upper(const pkpy_Str *self){ return retval; } +pkpy_Str pkpy_Str__escape(const pkpy_Str* self, char quote){ + assert(quote == '"' || quote == '\''); + c11_vector buffer; + c11_vector__ctor(&buffer, sizeof(char)); + c11_vector__reserve(&buffer, self->size); + c11_vector__push(char, &buffer, quote); + const char* data = pkpy_Str__data(self); + for(int i = 0; i < self->size; i++) { + char c = data[i]; + switch(c) { + case '"': case '\'': + if(c == quote) c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, c); + break; + case '\\': + c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, '\\'); + break; + case '\n': + c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, 'n'); + break; + case '\r': + c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, 'r'); + break; + case '\t': + c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, 't'); + break; + case '\b': + c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, 'b'); + break; + default: + if('\x00' <= c && c <= '\x1f') { + c11_vector__push(char, &buffer, '\\'); + c11_vector__push(char, &buffer, 'x'); + c11_vector__push(char, &buffer, PK_HEX_TABLE[c >> 4]); + c11_vector__push(char, &buffer, PK_HEX_TABLE[c & 0xf]); + } else { + c11_vector__push(char, &buffer, c); + } + } + } + c11_vector__push(char, &buffer, quote); + c11_vector__push(char, &buffer, '\0'); + pkpy_Str retval = { + .size = buffer.count - 1, + .is_ascii = self->is_ascii, + .is_sso = false, + ._ptr = (char*)buffer.data, + }; + return retval; +} + pkpy_Str pkpy_Str__replace(const pkpy_Str *self, char old, char new_){ pkpy_Str retval = pkpy_Str__copy(self); char* p = (char*)pkpy_Str__data(&retval); @@ -143,8 +199,9 @@ pkpy_Str pkpy_Str__replace2(const pkpy_Str *self, const pkpy_Str *old, const pkp pkpy_Str tmp = pkpy_Str__substr2(self, start, self->size - start); c11_vector__extend(char, &buffer, pkpy_Str__data(&tmp), tmp.size); pkpy_Str__dtor(&tmp); + c11_vector__push(char, &buffer, '\0'); pkpy_Str retval = { - .size = buffer.count, + .size = buffer.count - 1, .is_ascii = self->is_ascii && old->is_ascii && new_->is_ascii, .is_sso = false, ._ptr = (char*)buffer.data, @@ -189,8 +246,9 @@ pkpy_Str pkpy_Str__u8_slice(const pkpy_Str *self, int start, int stop, int step) pkpy_Str__dtor(&unicode); } } + c11_vector__push(char, &buffer, '\0'); pkpy_Str retval = { - .size = buffer.count, + .size = buffer.count - 1, .is_ascii = self->is_ascii, .is_sso = false, ._ptr = (char*)buffer.data, diff --git a/src/common/str.cpp b/src/common/str.cpp index 564e5efb..6471d7b2 100644 --- a/src/common/str.cpp +++ b/src/common/str.cpp @@ -56,43 +56,6 @@ Str Str::strip(bool left, bool right) const { } } -Str Str::escape(bool single_quote) const { - SStream ss; - escape_(ss, single_quote); - return ss.str(); -} - -void Str::escape_(SStream& ss, bool single_quote) const { - ss << (single_quote ? '\'' : '"'); - for(int i = 0; i < size; i++) { - char c = this->operator[] (i); - switch(c) { - case '"': - if(!single_quote) ss << '\\'; - ss << '"'; - break; - case '\'': - if(single_quote) ss << '\\'; - ss << '\''; - break; - case '\\': ss << '\\' << '\\'; break; - case '\n': ss << "\\n"; break; - case '\r': ss << "\\r"; break; - case '\t': ss << "\\t"; break; - case '\b': ss << "\\b"; break; - default: - if('\x00' <= c && c <= '\x1f') { - ss << "\\x"; // << std::hex << std::setw(2) << std::setfill('0') << (int)c; - ss << PK_HEX_TABLE[c >> 4]; - ss << PK_HEX_TABLE[c & 0xf]; - } else { - ss << c; - } - } - } - ss << (single_quote ? '\'' : '"'); -} - vector Str::split(const Str& sep) const { vector result; std::string_view tmp; diff --git a/src/interpreter/vm.cpp b/src/interpreter/vm.cpp index 3ea17293..392af7e5 100644 --- a/src/interpreter/vm.cpp +++ b/src/interpreter/vm.cpp @@ -38,7 +38,7 @@ struct JsonSerializer { if(!is_type(k, VM::tp_str)) { vm->TypeError(_S("json keys must be string, got ", _type_name(vm, vm->_tp(k)))); } - ss << _CAST(Str&, k).escape(false) << ": "; + ss << _CAST(Str&, k).escape('"') << ": "; write_object(v); }); ss << '}'; @@ -57,7 +57,7 @@ struct JsonSerializer { } else if(obj_t == vm->tp_bool) { ss << (obj == vm->True ? "true" : "false"); } else if(obj_t == vm->tp_str) { - _CAST(Str&, obj).escape_(ss, false); + ss << _CAST(Str&, obj).escape('"'); } else if(obj_t == vm->tp_list) { write_array(_CAST(List&, obj)); } else if(obj_t == vm->tp_tuple) {