From f8e2918561ac81b85789199c3594534059d429cb Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 31 Oct 2021 23:11:04 +0800 Subject: [PATCH] add unix.opendir unix.readdir unix.closedir --- .gitignore | 3 ++- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- lib.nas | 6 +++--- main.cpp | 6 ++++++ nasal.h | 1 + nasal_builtin.h | 42 +++++++++++++++++++++++++++++++++++++++++- stl/lib.nas | 6 +++--- 7 files changed, 101 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index af58d4b..ee49fd1 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ *.app nasal -.vscode \ No newline at end of file +.vscode +dump \ No newline at end of file diff --git a/README.md b/README.md index 09b91d1..c41e3e4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ especially when checking syntax errors. So i tried to write a new interpreter to help them checking syntax error and even, runtime error. I wrote the lexer, -parser and +parser and bytecode virtual machine(there was an ast-interpreter, but i deleted it after version4.0) to help checking errors. We found it much easier to check syntax and runtime @@ -43,7 +43,7 @@ It's quite easy to build this interpreter. MUST USE `-O2/-O3` if want to optimize the interpreter! -Also remember to use g++ or clang++. +Also remember to use g++ or clang++.(mingw-w64 in Windows) > [cpp compiler] -std=c++11 -O3 main.cpp -o nasal.exe -fno-exceptions @@ -592,10 +592,13 @@ You could totally use it after reading this simple tutorial: This type is used to interrupt the execution of virtual machine and will not be created by user program. `vm_nil` is a null type. It means nothing. + ```javascript var spc=nil; ``` + `vm_num` has 3 formats. Dec, hex and oct. Using IEEE754 double to store. + ```javascript var n=1; var n=2.71828; @@ -605,13 +608,17 @@ var n=0x7fffffff; var n=0xAA55; var n=0o170001; ``` + `vm_str` has 3 formats. But the third one is often used to declare a character. + ```javascript var s='str'; var s="another string"; var s=`c`; ``` + `vm_vec` has unlimited length and can store all types of values. + ```javascript var vec=[]; var vec=[ @@ -623,7 +630,9 @@ var vec=[ ]; append(vec,0,1,2); ``` + `vm_hash` is a hashmap that stores values with strings/identifiers as the key. + ```javascript var hash={ member1:nil, @@ -636,7 +645,9 @@ var hash={ } }; ``` + `vm_func` is a function type (in fact it is lambda). + ```javascript var f=func(x,y,z){ return nil; @@ -654,11 +665,13 @@ var f=func(args...){ return sum; } ``` + `vm_obj` is a special type that stores user data. This means you could use other complex C/C++ data types in nasal. This type is used when you are trying to add a new data structure into nasal, so this type is often created by native-function that programmed in C/C++ by library developers. You could see how to write your own native-functions below. + ```javascript var my_new_obj=func(){ return __builtin_my_obj(); @@ -675,20 +688,26 @@ Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that l 'str1'~'str2'; (1+2)*(3+4) ``` + For conditional expressions, operators `==` `!=` `<` `>` `<=` `>=` are used to compare two values. `and` `or` have the same function as C/C++ `&&` `||`, link comparations together. + ```javascript 1+1 and 0; 1<0 or 1>0; 1<=0 and 1>=0; 1==0 or 1!=0; ``` + Unary operators `-` `!` have the same function as C/C++. + ```javascript -1; !0; ``` + Operators `=` `+=` `-=` `*=` `/=` `~=` are used in assignment expressions. + ```javascript a=b=c=d=1; a+=1; @@ -736,6 +755,7 @@ if(1){ ### __Loop__ While loop and for loop is simalar to C/C++. + ```javascript while(condition) continue; @@ -743,14 +763,18 @@ while(condition) for(var i=0;i<10;i+=1) break; ``` + Nasal has another two kinds of loops that iterates through a vector: `forindex` will get the index of a vector. + ```javascript forindex(var i;elem) print(elem[i]); ``` + `foreach` will get the element of a vector. + ```javascript foreach(var i;elem) print(i); @@ -786,6 +810,7 @@ func(x){return 1/(1+math.exp(-x));}(0.5); There's an interesting test file 'y-combinator.nas', try it for fun: + ```javascript var fib=func(f){ return f(f); @@ -800,8 +825,10 @@ var fib=func(f){ ``` ### __Closure__ + Closure means you could get the variable that is not in the local scope of a function that you called. Here is an example, result is `1`: + ```javascript var f=func(){ var a=1; @@ -809,7 +836,9 @@ var f=func(){ } print(f()()); ``` + Using closure makes it easier to OOP. + ```javascript var student=func(n,a){ var (name,age)=(n,a); @@ -833,6 +862,7 @@ virtual machine will search the member is parents. If there is a hash that has the member, you will get the member's value. Using this mechanism, we could OOP like this, the result is `114514`: + ```javascript var trait={ get:func{return me.val;}, @@ -848,9 +878,11 @@ var class={ } }; ``` + First virtual machine cannot find member `set` in hash `a`, but in `a.parents` there's a hash `trait` has the member `set`, so we get the `set`. variable `me` points to hash `a`, so we change the `a.val`. And `get` has the same process. + ```javascript var a=class.new(); a.set(114514); @@ -874,7 +906,9 @@ Definition: ```C++ nasal_ref builtin_chr(std::vector&,nasal_gc&); ``` + Then complete this function using C++: + ```C++ nasal_ref builtin_print(std::vector& local,nasal_gc& gc) { @@ -902,7 +936,9 @@ nasal_ref builtin_print(std::vector& local,nasal_gc& gc) return gc.nil; } ``` + After that, register the built-in function's name(in nasal) and the function's pointer in this table: + ```C++ struct func { @@ -922,6 +958,7 @@ var print=func(elems...){ return __builtin_print(elems); }; ``` + In fact the arguments that `__builtin_print` uses is not necessary. So writting it like this is also right: @@ -1080,6 +1117,7 @@ vm stack(limit 10): ``` Here is an example of stack overflow: + ```javascript func(f){ return f(f); @@ -1091,6 +1129,7 @@ func(f){ ``` And the trace back info: + ```javascript [vm] stack overflow trace back: @@ -1104,6 +1143,7 @@ vm stack(limit 10): ``` Error will be thrown if there's a fatal error when executing: + ```javascript func(){ return 0; @@ -1111,6 +1151,7 @@ func(){ ``` And the trace back info: + ```javascript [vm] error at 0x00000008: callv: must call a vector/hash/string trace back: @@ -1120,6 +1161,7 @@ vm stack(limit 10): ``` Use command `-d` or `--detail` the trace back info will be this: + ```javascript hello world [vm] error: exception test @@ -1188,4 +1230,4 @@ global: local: [0] nil | [1] str | <0x249abd0> exception test -``` \ No newline at end of file +``` diff --git a/lib.nas b/lib.nas index e1df102..8822adc 100644 --- a/lib.nas +++ b/lib.nas @@ -100,9 +100,9 @@ var unix= dup2: func(fd0,fd1){die("not supported yet");}, exec: func(filename,argv,envp){die("not supported yet");}, waitpid: func(pid,nohang=0){die("not supported yet");}, - opendir: func(path){die("not supported yet");}, - readdir: func(handle){die("not supported yet");}, - closedir: func(handle){die("not supported yet");}, + opendir: func(path){return __builtin_opendir;}, + readdir: func(handle){return __builtin_readdir;}, + closedir: func(handle){return __builtin_closedir;}, time: func(){return time(0);}, sleep: func(secs){return __builtin_sleep(secs);}, chdir: func(path){return __builtin_chdir(path);}, diff --git a/main.cpp b/main.cpp index ca274c4..5869920 100644 --- a/main.cpp +++ b/main.cpp @@ -63,6 +63,7 @@ void err() void execute(const std::string& file,const uint32_t cmd) { + // 33kb space on stack nasal_lexer lexer; nasal_parse parse; nasal_import linker; @@ -107,6 +108,11 @@ void execute(const std::string& file,const uint32_t cmd) int main(int argc,const char* argv[]) { + if(argc==1) + { + help(); + return 0; + } if(argc==2) { std::string s(argv[1]); diff --git a/nasal.h b/nasal.h index 8b20911..5e6b002 100644 --- a/nasal.h +++ b/nasal.h @@ -21,6 +21,7 @@ #include #include +#include inline double hex_to_double(const char* str) { diff --git a/nasal_builtin.h b/nasal_builtin.h index 3eefa41..b5b3ce4 100644 --- a/nasal_builtin.h +++ b/nasal_builtin.h @@ -9,7 +9,8 @@ */ enum obj_type { - obj_file, + obj_file=1, + obj_dir, }; // declaration of builtin functions // to add new builtin function, declare it here and write the definition below @@ -69,6 +70,9 @@ nas_native(builtin_sfld); nas_native(builtin_setfld); nas_native(builtin_buf); nas_native(builtin_sleep); +nas_native(builtin_opendir); +nas_native(builtin_readdir); +nas_native(builtin_closedir); nas_native(builtin_chdir); nas_native(builtin_getcwd); nas_native(builtin_getenv); @@ -142,6 +146,9 @@ struct {"__builtin_setfld", builtin_setfld }, {"__builtin_buf", builtin_buf }, {"__builtin_sleep", builtin_sleep }, + {"__builtin_opendir", builtin_opendir }, + {"__builtin_readdir", builtin_readdir }, + {"__builtin_closedir",builtin_closedir}, {"__builtin_chdir", builtin_chdir }, {"__builtin_getcwd", builtin_getcwd }, {"__builtin_getenv", builtin_getenv }, @@ -895,6 +902,39 @@ nasal_ref builtin_sleep(std::vector& local,nasal_gc& gc) usleep((useconds_t)(val.num()*1e6)); return gc.nil; } +nasal_ref builtin_opendir(std::vector& local,nasal_gc& gc) +{ + nasal_ref path=local[1]; + if(path.type!=vm_str) + return builtin_err("opendir","\"path\" must be string"); + DIR* p=opendir(path.str()->c_str()); + if(!p) + return builtin_err("opendir","cannot open dir <"+*path.str()+">"); + nasal_ref ret=gc.alloc(vm_obj); + ret.obj()->type=obj_dir; + ret.obj()->ptr=(void*)p; + return ret; +} +nasal_ref builtin_readdir(std::vector& local,nasal_gc& gc) +{ + nasal_ref handle=local[1]; + if(handle.type!=vm_obj || handle.obj()->type!=obj_dir) + return builtin_err("readdir","not a correct dir handle"); + dirent* p=readdir((DIR*)handle.obj()->ptr); + if(!p) + return gc.nil; + nasal_ref ret=gc.alloc(vm_str); + *ret.str()=p->d_name; + return ret; +} +nasal_ref builtin_closedir(std::vector& local,nasal_gc& gc) +{ + nasal_ref handle=local[1]; + if(handle.type!=vm_obj || handle.obj()->type!=obj_dir) + return builtin_err("closedir","not a correct dir handle"); + closedir((DIR*)handle.obj()->ptr); + return gc.nil; +} nasal_ref builtin_chdir(std::vector& local,nasal_gc& gc) { nasal_ref path=local[1]; diff --git a/stl/lib.nas b/stl/lib.nas index e1df102..8822adc 100644 --- a/stl/lib.nas +++ b/stl/lib.nas @@ -100,9 +100,9 @@ var unix= dup2: func(fd0,fd1){die("not supported yet");}, exec: func(filename,argv,envp){die("not supported yet");}, waitpid: func(pid,nohang=0){die("not supported yet");}, - opendir: func(path){die("not supported yet");}, - readdir: func(handle){die("not supported yet");}, - closedir: func(handle){die("not supported yet");}, + opendir: func(path){return __builtin_opendir;}, + readdir: func(handle){return __builtin_readdir;}, + closedir: func(handle){return __builtin_closedir;}, time: func(){return time(0);}, sleep: func(secs){return __builtin_sleep(secs);}, chdir: func(path){return __builtin_chdir(path);},