add unix.opendir unix.readdir unix.closedir

This commit is contained in:
ValKmjolnir 2021-10-31 23:11:04 +08:00
parent e4ea34db51
commit f8e2918561
7 changed files with 101 additions and 11 deletions

3
.gitignore vendored
View File

@ -32,4 +32,5 @@
*.app *.app
nasal nasal
.vscode .vscode
dump

View File

@ -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. So i tried to write a new interpreter to help them checking syntax error and even, runtime error.
I wrote the lexer, I wrote the lexer,
parser and parser and
bytecode virtual machine(there was an ast-interpreter, bytecode virtual machine(there was an ast-interpreter,
but i deleted it after version4.0) to help checking errors. but i deleted it after version4.0) to help checking errors.
We found it much easier to check syntax and runtime 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! 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 > [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. 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. `vm_nil` is a null type. It means nothing.
```javascript ```javascript
var spc=nil; var spc=nil;
``` ```
`vm_num` has 3 formats. Dec, hex and oct. Using IEEE754 double to store. `vm_num` has 3 formats. Dec, hex and oct. Using IEEE754 double to store.
```javascript ```javascript
var n=1; var n=1;
var n=2.71828; var n=2.71828;
@ -605,13 +608,17 @@ var n=0x7fffffff;
var n=0xAA55; var n=0xAA55;
var n=0o170001; var n=0o170001;
``` ```
`vm_str` has 3 formats. But the third one is often used to declare a character. `vm_str` has 3 formats. But the third one is often used to declare a character.
```javascript ```javascript
var s='str'; var s='str';
var s="another string"; var s="another string";
var s=`c`; var s=`c`;
``` ```
`vm_vec` has unlimited length and can store all types of values. `vm_vec` has unlimited length and can store all types of values.
```javascript ```javascript
var vec=[]; var vec=[];
var vec=[ var vec=[
@ -623,7 +630,9 @@ var vec=[
]; ];
append(vec,0,1,2); append(vec,0,1,2);
``` ```
`vm_hash` is a hashmap that stores values with strings/identifiers as the key. `vm_hash` is a hashmap that stores values with strings/identifiers as the key.
```javascript ```javascript
var hash={ var hash={
member1:nil, member1:nil,
@ -636,7 +645,9 @@ var hash={
} }
}; };
``` ```
`vm_func` is a function type (in fact it is lambda). `vm_func` is a function type (in fact it is lambda).
```javascript ```javascript
var f=func(x,y,z){ var f=func(x,y,z){
return nil; return nil;
@ -654,11 +665,13 @@ var f=func(args...){
return sum; return sum;
} }
``` ```
`vm_obj` is a special type that stores user data. `vm_obj` is a special type that stores user data.
This means you could use other complex C/C++ data types in nasal. 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, 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. 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. You could see how to write your own native-functions below.
```javascript ```javascript
var my_new_obj=func(){ var my_new_obj=func(){
return __builtin_my_obj(); return __builtin_my_obj();
@ -675,20 +688,26 @@ Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that l
'str1'~'str2'; 'str1'~'str2';
(1+2)*(3+4) (1+2)*(3+4)
``` ```
For conditional expressions, operators `==` `!=` `<` `>` `<=` `>=` are used to compare two values. For conditional expressions, operators `==` `!=` `<` `>` `<=` `>=` are used to compare two values.
`and` `or` have the same function as C/C++ `&&` `||`, link comparations together. `and` `or` have the same function as C/C++ `&&` `||`, link comparations together.
```javascript ```javascript
1+1 and 0; 1+1 and 0;
1<0 or 1>0; 1<0 or 1>0;
1<=0 and 1>=0; 1<=0 and 1>=0;
1==0 or 1!=0; 1==0 or 1!=0;
``` ```
Unary operators `-` `!` have the same function as C/C++. Unary operators `-` `!` have the same function as C/C++.
```javascript ```javascript
-1; -1;
!0; !0;
``` ```
Operators `=` `+=` `-=` `*=` `/=` `~=` are used in assignment expressions. Operators `=` `+=` `-=` `*=` `/=` `~=` are used in assignment expressions.
```javascript ```javascript
a=b=c=d=1; a=b=c=d=1;
a+=1; a+=1;
@ -736,6 +755,7 @@ if(1){
### __Loop__ ### __Loop__
While loop and for loop is simalar to C/C++. While loop and for loop is simalar to C/C++.
```javascript ```javascript
while(condition) while(condition)
continue; continue;
@ -743,14 +763,18 @@ while(condition)
for(var i=0;i<10;i+=1) for(var i=0;i<10;i+=1)
break; break;
``` ```
Nasal has another two kinds of loops that iterates through a vector: Nasal has another two kinds of loops that iterates through a vector:
`forindex` will get the index of a vector. `forindex` will get the index of a vector.
```javascript ```javascript
forindex(var i;elem) forindex(var i;elem)
print(elem[i]); print(elem[i]);
``` ```
`foreach` will get the element of a vector. `foreach` will get the element of a vector.
```javascript ```javascript
foreach(var i;elem) foreach(var i;elem)
print(i); 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', There's an interesting test file 'y-combinator.nas',
try it for fun: try it for fun:
```javascript ```javascript
var fib=func(f){ var fib=func(f){
return f(f); return f(f);
@ -800,8 +825,10 @@ var fib=func(f){
``` ```
### __Closure__ ### __Closure__
Closure means you could get the variable that is not in the local scope of a function that you called. 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`: Here is an example, result is `1`:
```javascript ```javascript
var f=func(){ var f=func(){
var a=1; var a=1;
@ -809,7 +836,9 @@ var f=func(){
} }
print(f()()); print(f()());
``` ```
Using closure makes it easier to OOP. Using closure makes it easier to OOP.
```javascript ```javascript
var student=func(n,a){ var student=func(n,a){
var (name,age)=(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. 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`: Using this mechanism, we could OOP like this, the result is `114514`:
```javascript ```javascript
var trait={ var trait={
get:func{return me.val;}, 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`. 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`. variable `me` points to hash `a`, so we change the `a.val`.
And `get` has the same process. And `get` has the same process.
```javascript ```javascript
var a=class.new(); var a=class.new();
a.set(114514); a.set(114514);
@ -874,7 +906,9 @@ Definition:
```C++ ```C++
nasal_ref builtin_chr(std::vector<nasal_ref>&,nasal_gc&); nasal_ref builtin_chr(std::vector<nasal_ref>&,nasal_gc&);
``` ```
Then complete this function using C++: Then complete this function using C++:
```C++ ```C++
nasal_ref builtin_print(std::vector<nasal_ref>& local,nasal_gc& gc) nasal_ref builtin_print(std::vector<nasal_ref>& local,nasal_gc& gc)
{ {
@ -902,7 +936,9 @@ nasal_ref builtin_print(std::vector<nasal_ref>& local,nasal_gc& gc)
return gc.nil; return gc.nil;
} }
``` ```
After that, register the built-in function's name(in nasal) and the function's pointer in this table: After that, register the built-in function's name(in nasal) and the function's pointer in this table:
```C++ ```C++
struct func struct func
{ {
@ -922,6 +958,7 @@ var print=func(elems...){
return __builtin_print(elems); return __builtin_print(elems);
}; };
``` ```
In fact the arguments that `__builtin_print` uses is not necessary. In fact the arguments that `__builtin_print` uses is not necessary.
So writting it like this is also right: So writting it like this is also right:
@ -1080,6 +1117,7 @@ vm stack(limit 10):
``` ```
Here is an example of stack overflow: Here is an example of stack overflow:
```javascript ```javascript
func(f){ func(f){
return f(f); return f(f);
@ -1091,6 +1129,7 @@ func(f){
``` ```
And the trace back info: And the trace back info:
```javascript ```javascript
[vm] stack overflow [vm] stack overflow
trace back: trace back:
@ -1104,6 +1143,7 @@ vm stack(limit 10):
``` ```
Error will be thrown if there's a fatal error when executing: Error will be thrown if there's a fatal error when executing:
```javascript ```javascript
func(){ func(){
return 0; return 0;
@ -1111,6 +1151,7 @@ func(){
``` ```
And the trace back info: And the trace back info:
```javascript ```javascript
[vm] error at 0x00000008: callv: must call a vector/hash/string [vm] error at 0x00000008: callv: must call a vector/hash/string
trace back: trace back:
@ -1120,6 +1161,7 @@ vm stack(limit 10):
``` ```
Use command `-d` or `--detail` the trace back info will be this: Use command `-d` or `--detail` the trace back info will be this:
```javascript ```javascript
hello world hello world
[vm] error: exception test [vm] error: exception test
@ -1188,4 +1230,4 @@ global:
local: local:
[0] nil | [0] nil |
[1] str | <0x249abd0> exception test [1] str | <0x249abd0> exception test
``` ```

View File

@ -100,9 +100,9 @@ var unix=
dup2: func(fd0,fd1){die("not supported yet");}, dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");}, exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){die("not supported yet");}, waitpid: func(pid,nohang=0){die("not supported yet");},
opendir: func(path){die("not supported yet");}, opendir: func(path){return __builtin_opendir;},
readdir: func(handle){die("not supported yet");}, readdir: func(handle){return __builtin_readdir;},
closedir: func(handle){die("not supported yet");}, closedir: func(handle){return __builtin_closedir;},
time: func(){return time(0);}, time: func(){return time(0);},
sleep: func(secs){return __builtin_sleep(secs);}, sleep: func(secs){return __builtin_sleep(secs);},
chdir: func(path){return __builtin_chdir(path);}, chdir: func(path){return __builtin_chdir(path);},

View File

@ -63,6 +63,7 @@ void err()
void execute(const std::string& file,const uint32_t cmd) void execute(const std::string& file,const uint32_t cmd)
{ {
// 33kb space on stack
nasal_lexer lexer; nasal_lexer lexer;
nasal_parse parse; nasal_parse parse;
nasal_import linker; nasal_import linker;
@ -107,6 +108,11 @@ void execute(const std::string& file,const uint32_t cmd)
int main(int argc,const char* argv[]) int main(int argc,const char* argv[])
{ {
if(argc==1)
{
help();
return 0;
}
if(argc==2) if(argc==2)
{ {
std::string s(argv[1]); std::string s(argv[1]);

View File

@ -21,6 +21,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h>
inline double hex_to_double(const char* str) inline double hex_to_double(const char* str)
{ {

View File

@ -9,7 +9,8 @@
*/ */
enum obj_type enum obj_type
{ {
obj_file, obj_file=1,
obj_dir,
}; };
// declaration of builtin functions // declaration of builtin functions
// to add new builtin function, declare it here and write the definition below // 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_setfld);
nas_native(builtin_buf); nas_native(builtin_buf);
nas_native(builtin_sleep); nas_native(builtin_sleep);
nas_native(builtin_opendir);
nas_native(builtin_readdir);
nas_native(builtin_closedir);
nas_native(builtin_chdir); nas_native(builtin_chdir);
nas_native(builtin_getcwd); nas_native(builtin_getcwd);
nas_native(builtin_getenv); nas_native(builtin_getenv);
@ -142,6 +146,9 @@ struct
{"__builtin_setfld", builtin_setfld }, {"__builtin_setfld", builtin_setfld },
{"__builtin_buf", builtin_buf }, {"__builtin_buf", builtin_buf },
{"__builtin_sleep", builtin_sleep }, {"__builtin_sleep", builtin_sleep },
{"__builtin_opendir", builtin_opendir },
{"__builtin_readdir", builtin_readdir },
{"__builtin_closedir",builtin_closedir},
{"__builtin_chdir", builtin_chdir }, {"__builtin_chdir", builtin_chdir },
{"__builtin_getcwd", builtin_getcwd }, {"__builtin_getcwd", builtin_getcwd },
{"__builtin_getenv", builtin_getenv }, {"__builtin_getenv", builtin_getenv },
@ -895,6 +902,39 @@ nasal_ref builtin_sleep(std::vector<nasal_ref>& local,nasal_gc& gc)
usleep((useconds_t)(val.num()*1e6)); usleep((useconds_t)(val.num()*1e6));
return gc.nil; return gc.nil;
} }
nasal_ref builtin_opendir(std::vector<nasal_ref>& 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<nasal_ref>& 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<nasal_ref>& 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<nasal_ref>& local,nasal_gc& gc) nasal_ref builtin_chdir(std::vector<nasal_ref>& local,nasal_gc& gc)
{ {
nasal_ref path=local[1]; nasal_ref path=local[1];

View File

@ -100,9 +100,9 @@ var unix=
dup2: func(fd0,fd1){die("not supported yet");}, dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");}, exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){die("not supported yet");}, waitpid: func(pid,nohang=0){die("not supported yet");},
opendir: func(path){die("not supported yet");}, opendir: func(path){return __builtin_opendir;},
readdir: func(handle){die("not supported yet");}, readdir: func(handle){return __builtin_readdir;},
closedir: func(handle){die("not supported yet");}, closedir: func(handle){return __builtin_closedir;},
time: func(){return time(0);}, time: func(){return time(0);},
sleep: func(secs){return __builtin_sleep(secs);}, sleep: func(secs){return __builtin_sleep(secs);},
chdir: func(path){return __builtin_chdir(path);}, chdir: func(path){return __builtin_chdir(path);},