Merge pull request #52 from ValKmjolnir/develop
🎨 add several new native functions
This commit is contained in:
commit
efd403c758
1
makefile
1
makefile
|
@ -331,6 +331,7 @@ test:nasal
|
||||||
@ ./nasal -e test/qrcode.nas
|
@ ./nasal -e test/qrcode.nas
|
||||||
@ ./nasal -t -d test/quick_sort.nas
|
@ ./nasal -t -d test/quick_sort.nas
|
||||||
@ ./nasal -t -d test/regex_test.nas
|
@ ./nasal -t -d test/regex_test.nas
|
||||||
|
@ ./nasal -t -d test/replace_test.nas
|
||||||
@ ./nasal -e test/scalar.nas hello world
|
@ ./nasal -e test/scalar.nas hello world
|
||||||
@ ./nasal test/subprocess_test.nas
|
@ ./nasal test/subprocess_test.nas
|
||||||
@ ./nasal -e test/trait.nas
|
@ ./nasal -e test/trait.nas
|
||||||
|
|
|
@ -46,7 +46,7 @@ private:
|
||||||
// under limited mode, unsafe system api will be banned
|
// under limited mode, unsafe system api will be banned
|
||||||
const std::unordered_set<std::string> unsafe_system_api = {
|
const std::unordered_set<std::string> unsafe_system_api = {
|
||||||
// builtin
|
// builtin
|
||||||
"__system", "__input",
|
"__system", "__input", "__terminal_size",
|
||||||
// io
|
// io
|
||||||
"__fout", "__open", "__write", "__stat"
|
"__fout", "__open", "__write", "__stat"
|
||||||
// bits
|
// bits
|
||||||
|
|
|
@ -154,6 +154,7 @@ void dbg::step_info() {
|
||||||
|
|
||||||
src.load(files[bytecode[ctx.pc].fidx]);
|
src.load(files[bytecode[ctx.pc].fidx]);
|
||||||
|
|
||||||
|
std::clog << clear_screen << set_cursor;
|
||||||
std::clog << "\nsource code:\n";
|
std::clog << "\nsource code:\n";
|
||||||
for(u64 i = begin; i<end && i<src.size(); ++i) {
|
for(u64 i = begin; i<end && i<src.size(); ++i) {
|
||||||
std::clog << (i==line? back_white:reset);
|
std::clog << (i==line? back_white:reset);
|
||||||
|
@ -200,7 +201,9 @@ void dbg::interact() {
|
||||||
std::getline(std::cin, cmd);
|
std::getline(std::cin, cmd);
|
||||||
auto res = parse(cmd);
|
auto res = parse(cmd);
|
||||||
if (res.size()==0) {
|
if (res.size()==0) {
|
||||||
step_info();
|
// enter key without input using cmd_next by default
|
||||||
|
next = true;
|
||||||
|
return;
|
||||||
} else if (res.size()==1) {
|
} else if (res.size()==1) {
|
||||||
switch(get_cmd_type(res[0])) {
|
switch(get_cmd_type(res[0])) {
|
||||||
case cmd_kind::cmd_help: help(); break;
|
case cmd_kind::cmd_help: help(); break;
|
||||||
|
|
|
@ -85,8 +85,9 @@ private:
|
||||||
{"exit", cmd_kind::cmd_exit}
|
{"exit", cmd_kind::cmd_exit}
|
||||||
};
|
};
|
||||||
cmd_kind get_cmd_type(const std::string& cmd) const {
|
cmd_kind get_cmd_type(const std::string& cmd) const {
|
||||||
return command_table.count(cmd)?
|
return command_table.count(cmd)
|
||||||
command_table.at(cmd):cmd_kind::cmd_error;
|
? command_table.at(cmd)
|
||||||
|
: cmd_kind::cmd_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -14,6 +14,52 @@ struct for_reset {
|
||||||
static for_reset windows_system_set;
|
static for_reset windows_system_set;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::ostream& clear_screen(std::ostream& s) {
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& set_cursor(std::ostream& s) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0, 0});
|
||||||
|
#else
|
||||||
|
s << "\033[0;0H";
|
||||||
|
#endif
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& back_white(std::ostream& s) {
|
std::ostream& back_white(std::ostream& s) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
||||||
|
|
|
@ -22,6 +22,8 @@ struct span {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& clear_screen(std::ostream&);
|
||||||
|
std::ostream& set_cursor(std::ostream&);
|
||||||
std::ostream& back_white(std::ostream&);
|
std::ostream& back_white(std::ostream&);
|
||||||
std::ostream& red(std::ostream&);
|
std::ostream& red(std::ostream&);
|
||||||
std::ostream& cyan(std::ostream&);
|
std::ostream& cyan(std::ostream&);
|
||||||
|
|
|
@ -168,9 +168,8 @@ void nas_ghost::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
||||||
out << "<object " << ghost.get_ghost_name();
|
out << "<" << ghost.get_ghost_name();
|
||||||
out << " at 0x" << std::hex;
|
out << "@0x" << std::hex << ghost.convert<u64>() << std::dec << ">";
|
||||||
out << ghost.convert<u64>() << std::dec << ">";
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,11 +110,6 @@ void vm::hash_value_info(var& val, const usize max_show_elems) {
|
||||||
std::clog << "}";
|
std::clog << "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm::ghost_type_info(var& val) {
|
|
||||||
std::clog << "<object:" << val.ghost().type_name;
|
|
||||||
std::clog << "@0x" << std::hex << val.ghost().pointer << ">" << std::dec;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vm::coroutine_value_info(var& val) {
|
void vm::coroutine_value_info(var& val) {
|
||||||
std::clog << "[ ";
|
std::clog << "[ ";
|
||||||
switch(val.co().status) {
|
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_upval: upvalue_info(val); break;
|
||||||
case vm_type::vm_vec: vector_value_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_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_co: coroutine_value_info(val); break;
|
||||||
case vm_type::vm_map: namespace_value_info(val, 4); break;
|
case vm_type::vm_map: namespace_value_info(val, 4); break;
|
||||||
default: std::clog << "unknown"; break;
|
default: std::clog << "unknown"; break;
|
||||||
|
|
|
@ -71,7 +71,6 @@ protected:
|
||||||
void upvalue_info(var&);
|
void upvalue_info(var&);
|
||||||
void vector_value_info(var&);
|
void vector_value_info(var&);
|
||||||
void hash_value_info(var&, const usize);
|
void hash_value_info(var&, const usize);
|
||||||
void ghost_type_info(var&);
|
|
||||||
void coroutine_value_info(var&);
|
void coroutine_value_info(var&);
|
||||||
void namespace_value_info(var&, const usize);
|
void namespace_value_info(var&, const usize);
|
||||||
void value_name_form(const var&);
|
void value_name_form(const var&);
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
@ -92,36 +95,38 @@ var builtin_input(context* ctx, gc* ngc) {
|
||||||
|
|
||||||
var builtin_split(context* ctx, gc* ngc) {
|
var builtin_split(context* ctx, gc* ngc) {
|
||||||
auto local = ctx->localr;
|
auto local = ctx->localr;
|
||||||
var delimeter = local[1];
|
var separator = local[1];
|
||||||
var str = local[2];
|
var str = local[2];
|
||||||
if (!delimeter.is_str()) {
|
if (!separator.is_str()) {
|
||||||
return nas_err("native::split", "\"separator\" must be string");
|
return nas_err("native::split", "\"separator\" must be string");
|
||||||
}
|
}
|
||||||
if (!str.is_str()) {
|
if (!str.is_str()) {
|
||||||
return nas_err("native::split", "\"str\" must be string");
|
return nas_err("native::split", "\"str\" must be string");
|
||||||
}
|
}
|
||||||
const auto& deli = delimeter.str();
|
const auto& sep = separator.str();
|
||||||
const auto& s = str.str();
|
const auto& s = str.str();
|
||||||
|
|
||||||
// avoid being sweeped
|
// avoid being sweeped
|
||||||
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
|
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
|
||||||
auto& vec = res.vec().elems;
|
auto& vec = res.vec().elems;
|
||||||
|
|
||||||
if (!deli.length()) {
|
// empty separator means split every char
|
||||||
|
if (!sep.length()) {
|
||||||
for(auto i : s) {
|
for(auto i : s) {
|
||||||
vec.push_back(ngc->newstr(i));
|
vec.push_back(ngc->newstr(i));
|
||||||
}
|
}
|
||||||
ngc->temp = nil;
|
ngc->temp = nil;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize last = 0;
|
usize last = 0;
|
||||||
usize pos = s.find(deli, 0);
|
usize pos = s.find(sep, 0);
|
||||||
while(pos!=std::string::npos) {
|
while(pos!=std::string::npos) {
|
||||||
if (pos>last) {
|
if (pos>last) {
|
||||||
vec.push_back(ngc->newstr(s.substr(last, pos-last)));
|
vec.push_back(ngc->newstr(s.substr(last, pos-last)));
|
||||||
}
|
}
|
||||||
last = pos+deli.length();
|
last = pos + sep.length();
|
||||||
pos = s.find(deli, last);
|
pos = s.find(sep, last);
|
||||||
}
|
}
|
||||||
if (last!=s.length()) {
|
if (last!=s.length()) {
|
||||||
vec.push_back(ngc->newstr(s.substr(last)));
|
vec.push_back(ngc->newstr(s.substr(last)));
|
||||||
|
@ -130,6 +135,54 @@ var builtin_split(context* ctx, gc* ngc) {
|
||||||
return res;
|
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) {
|
var builtin_rand(context* ctx, gc* ngc) {
|
||||||
auto val = ctx->localr[1];
|
auto val = ctx->localr[1];
|
||||||
if (!val.is_num() && !val.is_nil()) {
|
if (!val.is_num() && !val.is_nil()) {
|
||||||
|
@ -468,6 +521,10 @@ var builtin_platform(context* ctx, gc* ngc) {
|
||||||
return ngc->newstr(util::get_platform());
|
return ngc->newstr(util::get_platform());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var builtin_version(context* ctx, gc* ngc) {
|
||||||
|
return ngc->newstr(__nasver__);
|
||||||
|
}
|
||||||
|
|
||||||
var builtin_arch(context* ctx, gc* ngc) {
|
var builtin_arch(context* ctx, gc* ngc) {
|
||||||
return ngc->newstr(util::get_arch());
|
return ngc->newstr(util::get_arch());
|
||||||
}
|
}
|
||||||
|
@ -732,6 +789,25 @@ var builtin_set_utf8_output(context* ctx, gc* ngc) {
|
||||||
return nil;
|
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[] = {
|
nasal_builtin_table builtin[] = {
|
||||||
{"__print", builtin_print},
|
{"__print", builtin_print},
|
||||||
{"__println", builtin_println},
|
{"__println", builtin_println},
|
||||||
|
@ -742,6 +818,7 @@ nasal_builtin_table builtin[] = {
|
||||||
{"__system", builtin_system},
|
{"__system", builtin_system},
|
||||||
{"__input", builtin_input},
|
{"__input", builtin_input},
|
||||||
{"__split", builtin_split},
|
{"__split", builtin_split},
|
||||||
|
{"__split_with_empty_substr", builtin_split_with_empty_substr},
|
||||||
{"__rand", builtin_rand},
|
{"__rand", builtin_rand},
|
||||||
{"__id", builtin_id},
|
{"__id", builtin_id},
|
||||||
{"__int", builtin_int},
|
{"__int", builtin_int},
|
||||||
|
@ -769,6 +846,7 @@ nasal_builtin_table builtin[] = {
|
||||||
{"__sleep", builtin_sleep},
|
{"__sleep", builtin_sleep},
|
||||||
{"__platform", builtin_platform},
|
{"__platform", builtin_platform},
|
||||||
{"__arch", builtin_arch},
|
{"__arch", builtin_arch},
|
||||||
|
{"__version", builtin_version},
|
||||||
{"__md5", builtin_md5},
|
{"__md5", builtin_md5},
|
||||||
{"__maketimestamp", builtin_maketimestamp},
|
{"__maketimestamp", builtin_maketimestamp},
|
||||||
{"__time_stamp", builtin_time_stamp},
|
{"__time_stamp", builtin_time_stamp},
|
||||||
|
@ -779,6 +857,7 @@ nasal_builtin_table builtin[] = {
|
||||||
{"__logtime", builtin_logtime},
|
{"__logtime", builtin_logtime},
|
||||||
{"__ghosttype", builtin_ghosttype},
|
{"__ghosttype", builtin_ghosttype},
|
||||||
{"__set_utf8_output", builtin_set_utf8_output},
|
{"__set_utf8_output", builtin_set_utf8_output},
|
||||||
|
{"__terminal_size", builtin_terminal_size},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ var builtin_setsize(context*, gc*);
|
||||||
var builtin_system(context*, gc*);
|
var builtin_system(context*, gc*);
|
||||||
var builtin_input(context*, gc*);
|
var builtin_input(context*, gc*);
|
||||||
var builtin_split(context*, gc*);
|
var builtin_split(context*, gc*);
|
||||||
|
var builtin_split_with_empty_substr(context*, gc*);
|
||||||
var builtin_rand(context*, gc*);
|
var builtin_rand(context*, gc*);
|
||||||
var builtin_id(context*, gc*);
|
var builtin_id(context*, gc*);
|
||||||
var builtin_int(context*, gc*);
|
var builtin_int(context*, gc*);
|
||||||
|
@ -66,6 +67,7 @@ var builtin_values(context*, gc*);
|
||||||
var builtin_sleep(context*, gc*);
|
var builtin_sleep(context*, gc*);
|
||||||
var builtin_platform(context*, gc*);
|
var builtin_platform(context*, gc*);
|
||||||
var builtin_arch(context*, gc*);
|
var builtin_arch(context*, gc*);
|
||||||
|
var builtin_version(context*, gc*);
|
||||||
|
|
||||||
// md5 related functions
|
// md5 related functions
|
||||||
std::string tohex(u32);
|
std::string tohex(u32);
|
||||||
|
@ -83,6 +85,7 @@ var builtin_ghosttype(context*, gc*);
|
||||||
|
|
||||||
// only useful on windows platform
|
// only useful on windows platform
|
||||||
var builtin_set_utf8_output(context*, gc*);
|
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
|
// register builtin function's name and it's address here in this table below
|
||||||
// this table must end with {nullptr, nullptr}
|
// this table must end with {nullptr, nullptr}
|
||||||
|
|
|
@ -33,3 +33,15 @@ var windows = {
|
||||||
return __set_utf8_output;
|
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];
|
||||||
|
}
|
||||||
|
|
|
@ -39,3 +39,28 @@ var to_char = func(number) {
|
||||||
var to_num = func(character) {
|
var to_num = func(character) {
|
||||||
return __temp_contains(__char_to_num, character)? __char_to_num[character]:-1;
|
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);
|
||||||
|
}
|
|
@ -6,3 +6,7 @@ use std.math;
|
||||||
var times_trigger = func(times, count) {
|
var times_trigger = func(times, count) {
|
||||||
return math.mod(times, count)==0;
|
return math.mod(times, count)==0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var terminal_size = func {
|
||||||
|
return __terminal_size;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue