From 4da38f686fbb2a72cbeda38c4cbcc341fa128d3d Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 8 Jul 2024 22:47:30 +0800 Subject: [PATCH 1/5] :art: improve report format of ghost type --- src/nasal_type.cpp | 5 ++--- src/nasal_vm.cpp | 7 +------ src/nasal_vm.h | 1 - 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/nasal_type.cpp b/src/nasal_type.cpp index 2380ae3..0df7ff3 100644 --- a/src/nasal_type.cpp +++ b/src/nasal_type.cpp @@ -168,9 +168,8 @@ void nas_ghost::clear() { } std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) { - out << "() << std::dec << ">"; + out << "<" << ghost.get_ghost_name(); + out << "@0x" << std::hex << ghost.convert() << std::dec << ">"; return out; } diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index c3bd940..1ff0808 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -110,11 +110,6 @@ void vm::hash_value_info(var& val, const usize max_show_elems) { std::clog << "}"; } -void vm::ghost_type_info(var& val) { - std::clog << "" << std::dec; -} - void vm::coroutine_value_info(var& val) { std::clog << "[ "; switch(val.co().status) { @@ -183,7 +178,7 @@ void vm::value_info(var& val) { case vm_type::vm_upval: upvalue_info(val); break; case vm_type::vm_vec: vector_value_info(val); break; case vm_type::vm_hash: hash_value_info(val, 4); break; - case vm_type::vm_ghost: ghost_type_info(val); break; + case vm_type::vm_ghost: std::clog << val.ghost(); break; case vm_type::vm_co: coroutine_value_info(val); break; case vm_type::vm_map: namespace_value_info(val, 4); break; default: std::clog << "unknown"; break; diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 478f0d9..830b58d 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -71,7 +71,6 @@ protected: void upvalue_info(var&); void vector_value_info(var&); void hash_value_info(var&, const usize); - void ghost_type_info(var&); void coroutine_value_info(var&); void namespace_value_info(var&, const usize); void value_name_form(const var&); From 94114416fe96cbb433dba5d19bcf335e7bc13d47 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 8 Jul 2024 22:56:33 +0800 Subject: [PATCH 2/5] :sparkles: add native function for version info --- src/natives/builtin.cpp | 5 +++++ src/natives/builtin.h | 1 + std/runtime.nas | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/natives/builtin.cpp b/src/natives/builtin.cpp index 203fae8..ff81e14 100644 --- a/src/natives/builtin.cpp +++ b/src/natives/builtin.cpp @@ -468,6 +468,10 @@ var builtin_platform(context* ctx, gc* ngc) { return ngc->newstr(util::get_platform()); } +var builtin_version(context* ctx, gc* ngc) { + return ngc->newstr(__nasver__); +} + var builtin_arch(context* ctx, gc* ngc) { return ngc->newstr(util::get_arch()); } @@ -769,6 +773,7 @@ nasal_builtin_table builtin[] = { {"__sleep", builtin_sleep}, {"__platform", builtin_platform}, {"__arch", builtin_arch}, + {"__version", builtin_version}, {"__md5", builtin_md5}, {"__maketimestamp", builtin_maketimestamp}, {"__time_stamp", builtin_time_stamp}, diff --git a/src/natives/builtin.h b/src/natives/builtin.h index 2239a17..9b23e0f 100644 --- a/src/natives/builtin.h +++ b/src/natives/builtin.h @@ -66,6 +66,7 @@ var builtin_values(context*, gc*); var builtin_sleep(context*, gc*); var builtin_platform(context*, gc*); var builtin_arch(context*, gc*); +var builtin_version(context*, gc*); // md5 related functions std::string tohex(u32); diff --git a/std/runtime.nas b/std/runtime.nas index 2cd57a1..5aa9421 100644 --- a/std/runtime.nas +++ b/std/runtime.nas @@ -33,3 +33,15 @@ var windows = { return __set_utf8_output; } }; + +var version = func() { + return __version; +} + +var major_version = func() { + return split(".", version())[0]; +} + +var minor_version = func() { + return split(".", version())[1]; +} From d37dfec225a9696cd973eb836409421844680237 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Fri, 2 Aug 2024 22:20:30 +0800 Subject: [PATCH 3/5] :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 From 8d3f75242915821611ea9ef0b9f71173d5f8d12b Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Fri, 2 Aug 2024 22:34:10 +0800 Subject: [PATCH 4/5] :sparkles: add native terminal_size --- src/nasal_codegen.h | 2 +- src/natives/builtin.cpp | 23 +++++++++++++++++++++++ src/natives/builtin.h | 1 + std/utils.nas | 4 ++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index 938cdb8..9a9d749 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -46,7 +46,7 @@ private: // under limited mode, unsafe system api will be banned const std::unordered_set unsafe_system_api = { // builtin - "__system", "__input", + "__system", "__input", "__terminal_size", // io "__fout", "__open", "__write", "__stat" // bits diff --git a/src/natives/builtin.cpp b/src/natives/builtin.cpp index 4291442..8a4e151 100644 --- a/src/natives/builtin.cpp +++ b/src/natives/builtin.cpp @@ -5,6 +5,9 @@ #ifdef _WIN32 #include +#else +#include +#include #endif namespace nasal { @@ -786,6 +789,25 @@ var builtin_set_utf8_output(context* ctx, gc* ngc) { return nil; } +var builtin_terminal_size(context* ctx, gc* ngc) { + var res = ngc->alloc(vm_type::vm_hash); +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { + auto rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + auto cols = csbi.srWindow.Right - csbi.srWindow.Left + 1; + res.hash().elems["rows"] = var::num(rows); + res.hash().elems["cols"] = var::num(cols); + } +#else + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + res.hash().elems["rows"] = var::num(w.ws_row); + res.hash().elems["cols"] = var::num(w.ws_col); +#endif + return res; +} + nasal_builtin_table builtin[] = { {"__print", builtin_print}, {"__println", builtin_println}, @@ -835,6 +857,7 @@ nasal_builtin_table builtin[] = { {"__logtime", builtin_logtime}, {"__ghosttype", builtin_ghosttype}, {"__set_utf8_output", builtin_set_utf8_output}, + {"__terminal_size", builtin_terminal_size}, {nullptr, nullptr} }; diff --git a/src/natives/builtin.h b/src/natives/builtin.h index b5a16f9..ce63e08 100644 --- a/src/natives/builtin.h +++ b/src/natives/builtin.h @@ -85,6 +85,7 @@ var builtin_ghosttype(context*, gc*); // only useful on windows platform var builtin_set_utf8_output(context*, gc*); +var builtin_terminal_size(context*, gc*); // register builtin function's name and it's address here in this table below // this table must end with {nullptr, nullptr} diff --git a/std/utils.nas b/std/utils.nas index e309483..182b937 100644 --- a/std/utils.nas +++ b/std/utils.nas @@ -5,4 +5,8 @@ use std.math; # when count can be divided exactly by times, return true var times_trigger = func(times, count) { return math.mod(times, count)==0; +} + +var terminal_size = func { + return __terminal_size; } \ No newline at end of file From 21911f21f06150656330b0a22301d8134e7e4590 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Fri, 2 Aug 2024 22:41:03 +0800 Subject: [PATCH 5/5] :sparkles: fix clear_screen on _WIN32 --- src/nasal_err.cpp | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/nasal_err.cpp b/src/nasal_err.cpp index 35c1988..f9ba2a0 100644 --- a/src/nasal_err.cpp +++ b/src/nasal_err.cpp @@ -15,8 +15,39 @@ static for_reset windows_system_set; #endif std::ostream& clear_screen(std::ostream& s) { - // TODO: winapi clear screen +#ifdef _WIN32 + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) { + return s; + } + + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) { + return s; + } + + auto rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + auto cols = csbi.srWindow.Right - csbi.srWindow.Left + 1; + DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y; + COORD coord = { 0, 0 }; + DWORD dwCharsWritten; + + FillConsoleOutputCharacter(hConsole, ' ', dwConSize, coord, &dwCharsWritten); + + // set raw attribute + FillConsoleOutputAttribute( + hConsole, + csbi.wAttributes, + dwConSize, + coord, + &dwCharsWritten + ); + + // set cursor position + SetConsoleCursorPosition(hConsole, coord); +#else s << "\033c"; +#endif return s; }