📝 update README

This commit is contained in:
ValKmjolnir 2023-10-04 11:52:09 +08:00
parent d69dd0b03f
commit 63b0112b9d
8 changed files with 134 additions and 80 deletions

View File

@ -21,6 +21,7 @@
* [__Difference__](#difference-between-andys-and-this-interpreter) * [__Difference__](#difference-between-andys-and-this-interpreter)
* [__Trace Back Info__](#trace-back-info) * [__Trace Back Info__](#trace-back-info)
* [__Debugger__](#debugger) * [__Debugger__](#debugger)
* [__REPL__](#repl)
__Contact us if having great ideas to share!__ __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. 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 ```javascript
var dylib={ var dlopen = func(libname) {
dlopen: func(libname){ ...
... }
},
dlclose: func(lib){return __dlclose; }, var dlclose = func(lib) {
dlcall: func(ptr,args...){return __dlcallv}, ...
limitcall: func(arg_size=0){ }
...
} 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. 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++ ```C++
// add header file nasal.h to get api // add header file nasal.h to get api
#include "nasal.h" #include "nasal.h"
double fibonaci(double x){ double fibonaci(double x) {
if(x<=2) if (x<=2) {
return x; return x;
}
return fibonaci(x-1)+fibonaci(x-2); return fibonaci(x-1)+fibonaci(x-2);
} }
// module functions' parameter list example // 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 // the arguments are generated into a vm_vec: args
// get values from the vector that must be used here // 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 // if you want your function safer, try this
// nas_err will print the error info on screen // nas_err will print the error info on screen
// and return vm_null for runtime to interrupt // and return vm_null for runtime to interrupt
if(num.type!=vm_num) if(num.type!=vm_num) {
return nas_err("extern_fib","\"num\" must be number"); return nas_err("extern_fib", "\"num\" must be number");
}
// ok, you must know that vm_num now is not managed by gc // ok, you must know that vm_num now is not managed by gc
// if want to return a gc object, use ngc->alloc(type) // if want to return a gc object, use ngc->alloc(type)
// usage of gc is the same as adding a native function // 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 // then put function name and address into this table
// make sure the end of the table is {nullptr,nullptr} // make sure the end of the table is {nullptr,nullptr}
mod_func func_tbl[]={ module_func_info func_tbl[] = {
{"fib",fib}, {"fib", fib},
{nullptr,nullptr} {nullptr, nullptr}
}; };
// must write this function, this will help nasal to // 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 // the reason why using this way to get function pointer
// is because `var` has constructors, which is not compatiable in C // is because `var` has constructors, which is not compatiable in C
// so "extern "C" var fib" may get compilation warnings // so "extern "C" var fib" may get compilation warnings
extern "C" mod_func get(){ extern "C" module_func_info* get() {
return func_tbl; 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: Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program:
```javascript ```javascript
var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); import.std.dylib;
var fib=dlhandle.fib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
for(var i=1;i<30;i+=1) var fib = dlhandle.fib;
println(dylib.dlcall(fib,i)); for(var i = 1; i<30; i+=1)
println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib); 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: `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 ```javascript
var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); import.std.dylib;
var fib=dlhandle.fib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var invoke=dylib.limitcall(1); # this means the called function has only one parameter var fib = dlhandle.fib;
for(var i=1;i<30;i+=1) var invoke = dylib.limitcall(1); # this means the called function has only one parameter
println(invoke(fib,i)); for(var i = 1; i<30; i+=1)
println(invoke(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
@ -998,3 +1011,10 @@ vm stack (0x7fffd0259138 <sp+65>, limit 10, total 7)
``` ```
</details> </details>
## REPL
We added experimental repl interpreter in v11.0.
Use this command to use the repl interpreter:
> nasal -r

View File

@ -21,6 +21,7 @@
* [__特殊之处__](#与andy解释器的不同之处) * [__特殊之处__](#与andy解释器的不同之处)
* [__堆栈追踪信息__](#堆栈追踪信息) * [__堆栈追踪信息__](#堆栈追踪信息)
* [__调试器__](#调试器) * [__调试器__](#调试器)
* [__交互解释器__](#交互解释器)
__如果有好的意见或建议欢迎联系我们!__ __如果有好的意见或建议欢迎联系我们!__
@ -591,19 +592,24 @@ import("./dirname/dirname/filename.nas");
如果只有上文中那种方式来添加你自定义的函数到nasal中这肯定是非常麻烦的。 如果只有上文中那种方式来添加你自定义的函数到nasal中这肯定是非常麻烦的。
因此,我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。 因此,我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。
在2021/12/3更新后我们给`lib.nas`添加了下面的这一批函数: 用于加载动态库的函数在`std/dylib.nas`中:
```javascript ```javascript
var dylib={ var dlopen = func(libname) {
dlopen: func(libname){ ...
... }
},
dlclose: func(lib){return __dlclose; }, var dlclose = func(lib) {
dlcall: func(ptr,args...){return __dlcallv}, ...
limitcall: func(arg_size=0){ }
...
} var dlcall = func(ptr, args...) {
}; ...
}
var limitcall = func(arg_size = 0) {
...
}
``` ```
这些函数是用来加载动态库的这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。 这些函数是用来加载动态库的这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。
@ -613,36 +619,41 @@ var dylib={
```C++ ```C++
// 这个头文件得加上因为我们需要拿到nasal的api // 这个头文件得加上因为我们需要拿到nasal的api
#include "nasal.h" #include "nasal.h"
double fibonaci(double x){ double fibonaci(double x) {
if(x<=2) if (x<=2) {
return x; return x;
}
return fibonaci(x-1)+fibonaci(x-2); 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指针指向一个vm_vec的data()
var num=args[0]; var num = args[0];
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查 // 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
// nas_err会输出错误信息并返回错误类型让虚拟机终止执行 // nas_err会输出错误信息并返回错误类型让虚拟机终止执行
if(num.type!=vm_num) if(num.type!=vm_num) {
return nas_err("extern_fib","\"num\" must be number"); return nas_err("extern_fib", "\"num\" must be number");
}
// vm_num作为普通的数字类型不是内存管理的对象所以无需申请 // vm_num作为普通的数字类型不是内存管理的对象所以无需申请
// 如果需要返回内存管理的对象请使用ngc->alloc(type) // 如果需要返回内存管理的对象请使用ngc->alloc(type)
return var::num(fibonaci(num.tonum())); return var::num(fibonaci(num.tonum()));
} }
// 然后将函数名字和函数地址放到一个表里,一定要记住表尾是{nullptr,nullptr} // 然后将函数名字和函数地址放到一个表里,一定要记住表尾是{nullptr,nullptr}
mod_func func_tbl[]={ module_func_info func_tbl[] = {
{"fib",fib}, {"fib", fib},
{nullptr,nullptr} {nullptr, nullptr}
}; };
// 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针 // 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针
// 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的 // 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的
// 有构造函数的类型作为返回值, 和C是不兼容的, 这导致 // 有构造函数的类型作为返回值, 和C是不兼容的, 这导致
// 类似 "extern "C" var fib" 的写法会得到编译错误 // 类似 "extern "C" var fib" 的写法会得到编译错误
extern "C" mod_func get(){ extern "C" module_func_info* get() {
return func_tbl; return func_tbl;
} }
``` ```
@ -667,10 +678,11 @@ Windows(`.dll`):
下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: 下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台:
```javascript ```javascript
var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); import.std.dylib;
var fib=dlhandle.fib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
for(var i=1;i<30;i+=1) var fib = dlhandle.fib;
println(dylib.dlcall(fib,i)); for(var i = 1; i<30; i+=1)
println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
@ -683,15 +695,16 @@ dylib.dlclose(dlhandle.lib);
`dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写: `dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写:
```javascript ```javascript
var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); import.std.dylib;
var fib=dlhandle.fib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var invoke=dylib.limitcall(1); # this means the called function has only one parameter var fib = dlhandle.fib;
for(var i=1;i<30;i+=1) var invoke = dylib.limitcall(1); # this means the called function has only one parameter
println(invoke(fib,i)); for(var i = 1; i<30; i+=1)
println(invoke(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
如果接下来你看到了这个运行结果,恭喜你! 如果得到如下运行结果,恭喜你!
```bash ```bash
./nasal a.nas ./nasal a.nas
@ -963,3 +976,9 @@ vm stack (0x7fffd0259138 <sp+65>, limit 10, total 7)
``` ```
</details> </details>
## 交互解释器
v11.0 版本新增了交互式解释器 (REPL),使用如下命令开启:
> nasal -r

View File

@ -15,7 +15,7 @@ double fibonaci(double x) {
var fib(var* args, usize size, gc* ngc) { var fib(var* args, usize size, gc* ngc) {
if (!size) { if (!size) {
return nas_err("fib","lack arguments"); return nas_err("fib", "lack arguments");
} }
var num = args[0]; var num = args[0];
return var::num(fibonaci(num.tonum())); return var::num(fibonaci(num.tonum()));

View File

@ -357,7 +357,7 @@ public:
void set_first(expr* node) {first = node;} void set_first(expr* node) {first = node;}
void add_call(call* node) {calls.push_back(node);} void add_call(call* node) {calls.push_back(node);}
expr* get_first() {return first;} expr* get_first() {return first;}
std::vector<call*>& get_calls() {return calls;} auto& get_calls() {return calls;}
void accept(ast_visitor*) override; void accept(ast_visitor*) override;
}; };

View File

@ -297,7 +297,8 @@ definition_expr* linker::generate_module_definition(code_block* block) {
auto def = new definition_expr(block->get_location()); auto def = new definition_expr(block->get_location());
def->set_identifier(new identifier( def->set_identifier(new identifier(
block->get_location(), 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 call = new call_expr(block->get_location());
auto func = new function(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; 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]}); auto tree = new code_block({0, 0, 0, 0, files[fileindex]});
// load library, this ast will be linked with root directly // load library, this ast will be linked with root directly
// so no namespace is generated // so no extra namespace is generated
if (!lib_loaded) { if (!lib_loaded) {
auto tmp = import_nasal_lib(); auto nasal_lib_code_block = import_nasal_lib();
link(tree, tmp); // insert nasal lib code to the back of tree
delete tmp; link(tree, nasal_lib_code_block);
delete nasal_lib_code_block;
lib_loaded = true; lib_loaded = true;
} }
// load imported modules // load imported modules
for(auto i : root->get_expressions()) { for(auto& import_ast_node : program_root->get_expressions()) {
if (!import_check(i)) { if (!import_check(import_ast_node)) {
break; break;
} }
auto tmp = import_regular_file((call_expr*)i); auto module_code_block = import_regular_file((call_expr*)import_ast_node);
tree->add_expression(generate_module_definition(tmp)); // 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; return tree;
} }
const error& linker::link( const error& linker::link(
parse& parse, const std::string& self, bool spath = false) { parse& parse, const std::string& self, bool spath = false) {
show_path = spath; show_path = spath;
// initializing // initializing file map
this_file = self; this_file = self;
files = {self}; files = {self};
module_load_stack = {self}; module_load_stack = {self};

View File

@ -159,7 +159,7 @@ f64 dec2f(const char* str) {
if (*str) { if (*str) {
return nan(""); return nan("");
} }
return ret*std::pow(10,negative*num_pow); return ret*std::pow(10, negative*num_pow);
} }
f64 str2num(const char* str) { f64 str2num(const char* str) {
@ -226,7 +226,7 @@ std::string rawstr(const std::string& str, const usize maxlen) {
} }
} }
if (maxlen && ret.length()>maxlen) { if (maxlen && ret.length()>maxlen) {
ret = ret.substr(0,maxlen)+"..."; ret = ret.substr(0, maxlen)+"...";
} }
return ret; return ret;
} }

View File

@ -116,7 +116,9 @@ void repl::execute() {
runtime.set_allow_repl_output_flag(true); runtime.set_allow_repl_output_flag(true);
std::cout << "[nasal-repl] Initialization complete.\n\n"; 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(); help();
while(true) { while(true) {

View File

@ -234,3 +234,4 @@ for(var a=0;a<16;a+=1) {
} }
print([0, 1, 2]~[3, 4, 5], "\n"); print([0, 1, 2]~[3, 4, 5], "\n");
print(num("4.94065645841246544176568792868e-324"), "\n");