diff --git a/README.md b/README.md index a091ade..2a00439 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ * [__Difference__](#difference-between-andys-and-this-interpreter) * [__Trace Back Info__](#trace-back-info) * [__Debugger__](#debugger) +* [__REPL__](#repl) __Contact us if having great ideas to share!__ @@ -613,19 +614,24 @@ that is really inconvenient. Luckily, we have developed some useful native-functions to help you add modules that created by you. -After 2021/12/3, there are some new functions added to `lib.nas`: +Functions used to load dynamic libraries are added to `std/dylib.nas`: ```javascript -var dylib={ - dlopen: func(libname){ - ... - }, - dlclose: func(lib){return __dlclose; }, - dlcall: func(ptr,args...){return __dlcallv}, - limitcall: func(arg_size=0){ - ... - } -}; +var dlopen = func(libname) { + ... +} + +var dlclose = func(lib) { + ... +} + +var dlcall = func(ptr, args...) { + ... +} + +var limitcall = func(arg_size = 0) { + ... +} ``` As you could see, these functions are used to load dynamic libraries into the nasal runtime and execute. @@ -636,21 +642,26 @@ First, write a cpp file that you want to generate the dynamic lib, take the `fib ```C++ // add header file nasal.h to get api #include "nasal.h" -double fibonaci(double x){ - if(x<=2) +double fibonaci(double x) { + if (x<=2) { return x; + } return fibonaci(x-1)+fibonaci(x-2); } // module functions' parameter list example -var fib(var* args,usize size,gc* ngc){ +var fib(var* args, usize size, gc* ngc) { + if (!size) { + return nas_err("fib", "lack arguments"); + } // the arguments are generated into a vm_vec: args // get values from the vector that must be used here - var num=args[0]; + var num = args[0]; // if you want your function safer, try this // nas_err will print the error info on screen // and return vm_null for runtime to interrupt - if(num.type!=vm_num) - return nas_err("extern_fib","\"num\" must be number"); + if(num.type!=vm_num) { + return nas_err("extern_fib", "\"num\" must be number"); + } // ok, you must know that vm_num now is not managed by gc // if want to return a gc object, use ngc->alloc(type) // usage of gc is the same as adding a native function @@ -659,9 +670,9 @@ var fib(var* args,usize size,gc* ngc){ // then put function name and address into this table // make sure the end of the table is {nullptr,nullptr} -mod_func func_tbl[]={ - {"fib",fib}, - {nullptr,nullptr} +module_func_info func_tbl[] = { + {"fib", fib}, + {nullptr, nullptr} }; // must write this function, this will help nasal to @@ -669,7 +680,7 @@ mod_func func_tbl[]={ // the reason why using this way to get function pointer // is because `var` has constructors, which is not compatiable in C // so "extern "C" var fib" may get compilation warnings -extern "C" mod_func get(){ +extern "C" module_func_info* get() { return func_tbl; } ``` @@ -693,10 +704,11 @@ Windows(`.dll`): Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program: ```javascript -var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); -var fib=dlhandle.fib; -for(var i=1;i<30;i+=1) - println(dylib.dlcall(fib,i)); +import.std.dylib; +var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var fib = dlhandle.fib; +for(var i = 1; i<30; i+=1) + println(dylib.dlcall(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -709,11 +721,12 @@ dylib.dlclose(dlhandle.lib); `dylib.limitcall` is used to get `dlcall` function that has limited parameter size, this function will prove the performance of your code because it does not use `vm_vec` to store the arguments, instead it uses local scope to store them, so this could avoid frequently garbage collecting. And the code above could also be written like this: ```javascript -var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); -var fib=dlhandle.fib; -var invoke=dylib.limitcall(1); # this means the called function has only one parameter -for(var i=1;i<30;i+=1) - println(invoke(fib,i)); +import.std.dylib; +var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var fib = dlhandle.fib; +var invoke = dylib.limitcall(1); # this means the called function has only one parameter +for(var i = 1; i<30; i+=1) + println(invoke(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -998,3 +1011,10 @@ vm stack (0x7fffd0259138 , limit 10, total 7) ``` + +## REPL + +We added experimental repl interpreter in v11.0. +Use this command to use the repl interpreter: + +> nasal -r diff --git a/doc/README_zh.md b/doc/README_zh.md index 90ee530..a803ce1 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -21,6 +21,7 @@ * [__特殊之处__](#与andy解释器的不同之处) * [__堆栈追踪信息__](#堆栈追踪信息) * [__调试器__](#调试器) +* [__交互解释器__](#交互解释器) __如果有好的意见或建议,欢迎联系我们!__ @@ -591,19 +592,24 @@ import("./dirname/dirname/filename.nas"); 如果只有上文中那种方式来添加你自定义的函数到nasal中,这肯定是非常麻烦的。 因此,我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。 -在2021/12/3更新后,我们给`lib.nas`添加了下面的这一批函数: +用于加载动态库的函数在`std/dylib.nas`中: ```javascript -var dylib={ - dlopen: func(libname){ - ... - }, - dlclose: func(lib){return __dlclose; }, - dlcall: func(ptr,args...){return __dlcallv}, - limitcall: func(arg_size=0){ - ... - } -}; +var dlopen = func(libname) { + ... +} + +var dlclose = func(lib) { + ... +} + +var dlcall = func(ptr, args...) { + ... +} + +var limitcall = func(arg_size = 0) { + ... +} ``` 这些函数是用来加载动态库的,这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。 @@ -613,36 +619,41 @@ var dylib={ ```C++ // 这个头文件得加上,因为我们需要拿到nasal的api #include "nasal.h" -double fibonaci(double x){ - if(x<=2) +double fibonaci(double x) { + if (x<=2) { return x; + } return fibonaci(x-1)+fibonaci(x-2); } // 模块函数的参数列表一律以这个为准 -var fib(var* args,usize size,gc* ngc){ +var fib(var* args, usize size, gc* ngc) { + if (!size) { + return nas_err("fib", "lack arguments"); + } // 传参会给予一个var指针,指向一个vm_vec的data() - var num=args[0]; + var num = args[0]; // 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查 // nas_err会输出错误信息并返回错误类型让虚拟机终止执行 - if(num.type!=vm_num) - return nas_err("extern_fib","\"num\" must be number"); + if(num.type!=vm_num) { + return nas_err("extern_fib", "\"num\" must be number"); + } // vm_num作为普通的数字类型,不是内存管理的对象,所以无需申请 // 如果需要返回内存管理的对象,请使用ngc->alloc(type) return var::num(fibonaci(num.tonum())); } // 然后将函数名字和函数地址放到一个表里,一定要记住表尾是{nullptr,nullptr} -mod_func func_tbl[]={ - {"fib",fib}, - {nullptr,nullptr} +module_func_info func_tbl[] = { + {"fib", fib}, + {nullptr, nullptr} }; // 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针 // 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的 // 有构造函数的类型作为返回值, 和C是不兼容的, 这导致 // 类似 "extern "C" var fib" 的写法会得到编译错误 -extern "C" mod_func get(){ +extern "C" module_func_info* get() { return func_tbl; } ``` @@ -667,10 +678,11 @@ Windows(`.dll`): 下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: ```javascript -var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); -var fib=dlhandle.fib; -for(var i=1;i<30;i+=1) - println(dylib.dlcall(fib,i)); +import.std.dylib; +var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var fib = dlhandle.fib; +for(var i = 1; i<30; i+=1) + println(dylib.dlcall(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -683,15 +695,16 @@ dylib.dlclose(dlhandle.lib); `dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写: ```javascript -var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); -var fib=dlhandle.fib; -var invoke=dylib.limitcall(1); # this means the called function has only one parameter -for(var i=1;i<30;i+=1) - println(invoke(fib,i)); +import.std.dylib; +var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); +var fib = dlhandle.fib; +var invoke = dylib.limitcall(1); # this means the called function has only one parameter +for(var i = 1; i<30; i+=1) + println(invoke(fib, i)); dylib.dlclose(dlhandle.lib); ``` -如果接下来你看到了这个运行结果,恭喜你! +如果得到如下运行结果,恭喜你! ```bash ./nasal a.nas @@ -963,3 +976,9 @@ vm stack (0x7fffd0259138 , limit 10, total 7) ``` + +## 交互解释器 + +v11.0 版本新增了交互式解释器 (REPL),使用如下命令开启: + +> nasal -r diff --git a/module/fib.cpp b/module/fib.cpp index 8c0c213..1e81a99 100644 --- a/module/fib.cpp +++ b/module/fib.cpp @@ -15,7 +15,7 @@ double fibonaci(double x) { var fib(var* args, usize size, gc* ngc) { if (!size) { - return nas_err("fib","lack arguments"); + return nas_err("fib", "lack arguments"); } var num = args[0]; return var::num(fibonaci(num.tonum())); diff --git a/src/nasal_ast.h b/src/nasal_ast.h index 76ffd6a..73142c1 100644 --- a/src/nasal_ast.h +++ b/src/nasal_ast.h @@ -357,7 +357,7 @@ public: void set_first(expr* node) {first = node;} void add_call(call* node) {calls.push_back(node);} expr* get_first() {return first;} - std::vector& get_calls() {return calls;} + auto& get_calls() {return calls;} void accept(ast_visitor*) override; }; diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index f7390ae..54f6f5d 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -297,7 +297,8 @@ definition_expr* linker::generate_module_definition(code_block* block) { auto def = new definition_expr(block->get_location()); def->set_identifier(new identifier( block->get_location(), - generate_module_name(block->get_location().file))); + generate_module_name(block->get_location().file) + )); auto call = new call_expr(block->get_location()); auto func = new function(block->get_location()); @@ -310,33 +311,44 @@ definition_expr* linker::generate_module_definition(code_block* block) { return def; } -code_block* linker::load(code_block* root, u16 fileindex) { +code_block* linker::load(code_block* program_root, u16 fileindex) { auto tree = new code_block({0, 0, 0, 0, files[fileindex]}); // load library, this ast will be linked with root directly - // so no namespace is generated + // so no extra namespace is generated if (!lib_loaded) { - auto tmp = import_nasal_lib(); - link(tree, tmp); - delete tmp; + auto nasal_lib_code_block = import_nasal_lib(); + // insert nasal lib code to the back of tree + link(tree, nasal_lib_code_block); + delete nasal_lib_code_block; lib_loaded = true; } + // load imported modules - for(auto i : root->get_expressions()) { - if (!import_check(i)) { + for(auto& import_ast_node : program_root->get_expressions()) { + if (!import_check(import_ast_node)) { break; } - auto tmp = import_regular_file((call_expr*)i); - tree->add_expression(generate_module_definition(tmp)); + auto module_code_block = import_regular_file((call_expr*)import_ast_node); + // after importing the regular file as module, delete this node + const auto loc = import_ast_node->get_location(); + delete import_ast_node; + // and replace the node with null_expr node + import_ast_node = new null_expr(loc); + // then we generate a function warping the code block, + // and export the necessary global symbols in this code block + // by generate a return statement, with a hashmap return value + tree->add_expression(generate_module_definition(module_code_block)); } - // add root to the back of tree - link(tree, root); + + // insert program root to the back of tree + link(tree, program_root); return tree; } const error& linker::link( parse& parse, const std::string& self, bool spath = false) { show_path = spath; - // initializing + // initializing file map this_file = self; files = {self}; module_load_stack = {self}; diff --git a/src/nasal_misc.cpp b/src/nasal_misc.cpp index f529db8..374e190 100644 --- a/src/nasal_misc.cpp +++ b/src/nasal_misc.cpp @@ -159,7 +159,7 @@ f64 dec2f(const char* str) { if (*str) { return nan(""); } - return ret*std::pow(10,negative*num_pow); + return ret*std::pow(10, negative*num_pow); } f64 str2num(const char* str) { @@ -226,7 +226,7 @@ std::string rawstr(const std::string& str, const usize maxlen) { } } if (maxlen && ret.length()>maxlen) { - ret = ret.substr(0,maxlen)+"..."; + ret = ret.substr(0, maxlen)+"..."; } return ret; } diff --git a/src/repl.cpp b/src/repl.cpp index d749c07..2917361 100644 --- a/src/repl.cpp +++ b/src/repl.cpp @@ -116,7 +116,9 @@ void repl::execute() { runtime.set_allow_repl_output_flag(true); std::cout << "[nasal-repl] Initialization complete.\n\n"; - std::cout << "Nasal REPL interpreter(experimental).\n"; + // finish initialization, output version info + std::cout << "Nasal REPL interpreter version " << __nasver; + std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n"; help(); while(true) { diff --git a/test/scalar.nas b/test/scalar.nas index eb0b0ea..4d853f7 100644 --- a/test/scalar.nas +++ b/test/scalar.nas @@ -233,4 +233,5 @@ for(var a=0;a<16;a+=1) { } } -print([0, 1, 2]~[3, 4, 5], "\n"); \ No newline at end of file +print([0, 1, 2]~[3, 4, 5], "\n"); +print(num("4.94065645841246544176568792868e-324"), "\n"); \ No newline at end of file