From d37dfec225a9696cd973eb836409421844680237 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Fri, 2 Aug 2024 22:20:30 +0800 Subject: [PATCH] :sparkles: add string.replace --- makefile | 1 + src/nasal_dbg.cpp | 5 +++- src/nasal_dbg.h | 5 ++-- src/nasal_err.cpp | 15 ++++++++++ src/nasal_err.h | 2 ++ src/natives/builtin.cpp | 65 ++++++++++++++++++++++++++++++++++++----- src/natives/builtin.h | 1 + std/string.nas | 25 ++++++++++++++++ test/replace_test.nas | 22 ++++++++++++++ 9 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 test/replace_test.nas diff --git a/makefile b/makefile index dea3eef..7ffe405 100644 --- a/makefile +++ b/makefile @@ -331,6 +331,7 @@ test:nasal @ ./nasal -e test/qrcode.nas @ ./nasal -t -d test/quick_sort.nas @ ./nasal -t -d test/regex_test.nas + @ ./nasal -t -d test/replace_test.nas @ ./nasal -e test/scalar.nas hello world @ ./nasal test/subprocess_test.nas @ ./nasal -e test/trait.nas diff --git a/src/nasal_dbg.cpp b/src/nasal_dbg.cpp index edc99b2..35a85a4 100644 --- a/src/nasal_dbg.cpp +++ b/src/nasal_dbg.cpp @@ -154,6 +154,7 @@ void dbg::step_info() { src.load(files[bytecode[ctx.pc].fidx]); + std::clog << clear_screen << set_cursor; std::clog << "\nsource code:\n"; for(u64 i = begin; ilocalr; - var delimeter = local[1]; + var separator = local[1]; var str = local[2]; - if (!delimeter.is_str()) { + if (!separator.is_str()) { return nas_err("native::split", "\"separator\" must be string"); } if (!str.is_str()) { return nas_err("native::split", "\"str\" must be string"); } - const auto& deli = delimeter.str(); + const auto& sep = separator.str(); const auto& s = str.str(); // avoid being sweeped auto res = ngc->temp = ngc->alloc(vm_type::vm_vec); auto& vec = res.vec().elems; - if (!deli.length()) { + // empty separator means split every char + if (!sep.length()) { for(auto i : s) { vec.push_back(ngc->newstr(i)); } ngc->temp = nil; return res; } + usize last = 0; - usize pos = s.find(deli, 0); + usize pos = s.find(sep, 0); while(pos!=std::string::npos) { if (pos>last) { vec.push_back(ngc->newstr(s.substr(last, pos-last))); } - last = pos+deli.length(); - pos = s.find(deli, last); + last = pos + sep.length(); + pos = s.find(sep, last); } if (last!=s.length()) { vec.push_back(ngc->newstr(s.substr(last))); @@ -130,6 +132,54 @@ var builtin_split(context* ctx, gc* ngc) { return res; } +var builtin_split_with_empty_substr(context* ctx, gc* ngc) { + auto local = ctx->localr; + var separator = local[1]; + var str = local[2]; + if (!separator.is_str()) { + return nas_err( + "native::split_with_empty_substr", + "\"separator\" must be string" + ); + } + if (!str.is_str()) { + return nas_err( + "native::split_with_empty_substr", + "\"str\" must be string" + ); + } + const auto& sep = separator.str(); + const auto& s = str.str(); + + // avoid being sweeped + auto res = ngc->temp = ngc->alloc(vm_type::vm_vec); + auto& vec = res.vec().elems; + + // empty separator means split every char + if (!sep.length()) { + for(auto i : s) { + vec.push_back(ngc->newstr(i)); + } + ngc->temp = nil; + return res; + } + + usize last = 0; + usize pos = s.find(sep, 0); + while(pos!=std::string::npos) { + if (pos>=last) { + vec.push_back(ngc->newstr(s.substr(last, pos-last))); + } + last = pos + sep.length(); + pos = s.find(sep, last); + } + if (last<=s.length()) { + vec.push_back(ngc->newstr(s.substr(last))); + } + ngc->temp = nil; + return res; +} + var builtin_rand(context* ctx, gc* ngc) { auto val = ctx->localr[1]; if (!val.is_num() && !val.is_nil()) { @@ -746,6 +796,7 @@ nasal_builtin_table builtin[] = { {"__system", builtin_system}, {"__input", builtin_input}, {"__split", builtin_split}, + {"__split_with_empty_substr", builtin_split_with_empty_substr}, {"__rand", builtin_rand}, {"__id", builtin_id}, {"__int", builtin_int}, diff --git a/src/natives/builtin.h b/src/natives/builtin.h index 9b23e0f..b5a16f9 100644 --- a/src/natives/builtin.h +++ b/src/natives/builtin.h @@ -39,6 +39,7 @@ var builtin_setsize(context*, gc*); var builtin_system(context*, gc*); var builtin_input(context*, gc*); var builtin_split(context*, gc*); +var builtin_split_with_empty_substr(context*, gc*); var builtin_rand(context*, gc*); var builtin_id(context*, gc*); var builtin_int(context*, gc*); diff --git a/std/string.nas b/std/string.nas index 10aac2c..b14d2ce 100644 --- a/std/string.nas +++ b/std/string.nas @@ -39,3 +39,28 @@ var to_char = func(number) { var to_num = func(character) { return __temp_contains(__char_to_num, character)? __char_to_num[character]:-1; } + +var __string_split_with_empty_substr = func(separator, str) { + return __split_with_empty_substr(separator, str); +} + +var replace = func(needle, haystack, replacement) { + var needle_size = size(needle); + var haystack_size = size(haystack); + var replacement_size = size(replacement); + + if (needle_size == 0 or + haystack_size == 0 or + replacement_size == 0 or + needle_size > haystack_size + ) { + return haystack; + } + if (needle == haystack) { + return replacement; + } + + var vec = __string_split_with_empty_substr(needle, haystack); + + return join(replacement, vec); +} \ No newline at end of file diff --git a/test/replace_test.nas b/test/replace_test.nas new file mode 100644 index 0000000..c514757 --- /dev/null +++ b/test/replace_test.nas @@ -0,0 +1,22 @@ +use std.string; + +var test_set = [ + ["{}", "{}", "a", "a"], + ["{}", "a{}", "a", "aa"], + ["{}", "{}a", "a", "aa"], + ["{}", "a{}a", "a", "aaa"], + ["{}", "{}a{}", "a", "aaa"], + ["{}", "{{}}", "a", "{a}"], + ["{}", "{}{}{}", "a", "aaa"] +]; + +foreach(var i; test_set) { + if (string.replace(i[0], i[1], i[2]) != i[3]) { + println("Error: string.replace(", + i[0], ", ", + i[1], ", ", + i[2], ") != ", + i[3]); + exit(-1); + } +} \ No newline at end of file