📝 update documents

This commit is contained in:
ValKmjolnir 2023-11-04 00:09:59 +08:00
parent 2f58a7c223
commit c946e9debd
8 changed files with 616 additions and 457 deletions

515
README.md
View File

@ -25,7 +25,7 @@
__Contact us if having great ideas to share!__ __Contact us if having great ideas to share!__
* __E-mail__: __lhk101lhk101@qq.com__ * __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762)
## __Introduction__ ## __Introduction__
@ -97,8 +97,9 @@ You could choose which compiler you want to use:
If your system is `Windows` and you want to output unicode, you could write this in nasal code: If your system is `Windows` and you want to output unicode, you could write this in nasal code:
```javascript ```javascript
if(os.platform()=="windows") if (os.platform()=="windows") {
system("chcp 65001"); system("chcp 65001");
}
``` ```
## __Tutorial__ ## __Tutorial__
@ -116,30 +117,30 @@ This type is not created by user program.
__`nil`__ is a null type. Just like `null`. __`nil`__ is a null type. Just like `null`.
```javascript ```javascript
var spc=nil; var spc = nil;
``` ```
__`num`__ has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 `double` to store. __`num`__ has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 `double` to store.
```javascript ```javascript
# this language use '#' to write notes # this language use '#' to write notes
var n=2.71828; # dec var n = 2.71828; # dec
var n=2.147e16; # dec var n = 2.147e16; # dec
var n=1e-10; # dec var n = 1e-10; # dec
var n=0xAA55; # hex var n = 0xAA55; # hex
var n=0o170001; # oct var n = 0o170001; # oct
# caution: true and false also useful in nasal now # caution: true and false also useful in nasal now
var n=true; # in fact n is now 1.0 var n = true; # in fact n is now 1.0
var n=false; # in face n is now 0.0 var n = false; # in face n is now 0.0
``` ```
__`str`__ has 3 formats. The third one is used to declare a character. __`str`__ has 3 formats. The third one is 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`;
# some special characters is allowed in this language: # some special characters is allowed in this language:
'\a'; '\b'; '\e'; '\f'; '\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v'; '\n'; '\r'; '\t'; '\v';
@ -150,19 +151,19 @@ var s=`c`;
__`vec`__ has unlimited length and can store all types of values. __`vec`__ has unlimited length and can store all types of values.
```javascript ```javascript
var vec=[]; var vec = [];
var vec=[0,nil,{},[],func(){return 0}]; var vec = [0, nil, {}, [], func(){return 0}];
append(vec,0,1,2); append(vec, 0, 1, 2);
``` ```
__`hash`__ is a hashmap (or like a `dict` in `python`) that stores values with strings/identifiers as the key. __`hash`__ is a hashmap (or like a `dict` in `python`) that stores values with strings/identifiers as the key.
```javascript ```javascript
var hash={ var hash = {
member1:nil, member1: nil,
member2:"str", member2: "str",
"member3":"member\'s name can also be a string constant", "member3": "member\'s name can also be a string constant",
funct:func(){ funct: func() {
return me.member2~me.member3; return me.member2~me.member3;
} }
}; };
@ -171,27 +172,28 @@ var hash={
__`func`__ is a function type (in fact it is `lambda`). __`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;
} }
# function could be declared without parameters and `(`, `)` # function could be declared without parameters and `(`, `)`
var f=func{ var f = func {
return 114514; return 114514;
} }
var f=func(x,y,z,deft=1){ var f = func(x, y, z, deft = 1) {
return x+y+z+deft; return x+y+z+deft;
} }
var f=func(args...){ var f = func(args...) {
var sum=0; var sum = 0;
foreach(var i;args) foreach(var i; args) {
sum+=i; sum += i;
}
return sum; return sum;
} }
``` ```
__`upval`__ is used to store upvalues, used in __`vm`__ to make sure closure runs correctly. __`upval`__ is used to store upvalues, used in __`vm`__ to make sure closure runs correctly.
__`obj`__ is used to store other complex `C/C++` data types. __`ghost`__ is used to store other complex `C/C++` data types.
This type is created by native-function of nasal. If want to define a new data type, see how to add native-functions by editing code. This type is created by native-function of nasal. If want to define a new data type, see how to add native-functions by editing code.
</details> </details>
@ -237,16 +239,16 @@ Bitwise operators `~` `|` `&` `^` have the same function as C/C++.
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;
a-=1; a -= 1;
a*=1; a *= 1;
a/=1; a /= 1;
a~="string"; a ~= "string";
a^=0xff; a ^= 0xff;
a&=0xca; a &= 0xca;
a|=0xba; a |= 0xba;
``` ```
</details> </details>
@ -256,9 +258,9 @@ a|=0xba;
As follows. As follows.
```javascript ```javascript
var a=1; # define single variable var a = 1; # define single variable
var (a,b,c)=[0,1,2]; # define multiple variables from a vector var (a, b, c) = [0, 1, 2]; # define multiple variables from a vector
var (a,b,c)=(0,1,2); # define multiple variables from a tuple var (a, b, c) = (0, 1, 2); # define multiple variables from a tuple
``` ```
Nasal has many special global symbols: Nasal has many special global symbols:
@ -292,9 +294,9 @@ func() {
The last one is often used to swap two variables. The last one is often used to swap two variables.
```javascript ```javascript
(a,b[0],c.d)=[0,1,2]; (a, b[0], c.d) = [0, 1, 2];
(a,b[1],c.e)=(0,1,2); (a, b[1], c.e) = (0, 1, 2);
(a,b)=(b,a); (a, b) = (b, a);
``` ```
</details> </details>
@ -305,13 +307,13 @@ In nasal there's a new key word `elsif`.
It has the same functions as `else if`. It has the same functions as `else if`.
```javascript ```javascript
if(1){ if (1) {
; ;
}elsif(2){ } elsif (2) {
; ;
}else if(3){ } else if (3) {
; ;
}else{ } else {
; ;
} }
``` ```
@ -323,10 +325,12 @@ if(1){
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;
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:
@ -334,15 +338,17 @@ Nasal has another two kinds of loops that iterates through a vector:
`forindex` will get the index of a vector. Index will be `0` to `size(elem)-1`. `forindex` will get the index of a vector. Index will be `0` to `size(elem)-1`.
```javascript ```javascript
forindex(var i;elem) forindex(var i; elem) {
print(elem[i]); print(elem[i]);
}
``` ```
`foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`. `foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`.
```javascript ```javascript
foreach(var i;elem) foreach(var i; elem) {
print(i); print(i);
}
``` ```
</details> </details>
@ -356,7 +362,7 @@ If you want to get the character, use built-in function `chr()`.
```javascript ```javascript
a[0]; a[0];
a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil]; a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil];
"hello world"[0]; "hello world"[0];
``` ```
@ -370,7 +376,7 @@ because hashmap use string as the key to compare.
But if it really useful, the efficientcy may not be so important... But if it really useful, the efficientcy may not be so important...
```javascript ```javascript
f(x:0,y:nil,z:[]); f(x:0, y:nil, z:[]);
``` ```
</details> </details>
@ -380,10 +386,10 @@ f(x:0,y:nil,z:[]);
Also functions have this kind of use: Also functions have this kind of use:
```javascript ```javascript
func(x,y){ func(x, y) {
return x+y return x+y
}(0,1); }(0, 1);
func(x){ func(x) {
return 1/(1+math.exp(-x)); return 1/(1+math.exp(-x));
}(0.5); }(0.5);
``` ```
@ -392,11 +398,11 @@ 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);
}( }(
func(f){ func(f) {
return func(x){ return func(x) {
if(x<2) return x; if(x<2) return x;
return f(f)(x-1)+f(f)(x-2); return f(f)(x-1)+f(f)(x-2);
} }
@ -412,9 +418,9 @@ Closure means you could get the variable that is not in the local scope of a fun
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;
return func(){return a;}; return func() {return a;};
} }
print(f()()); print(f()());
``` ```
@ -422,14 +428,14 @@ 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);
return { return {
print_info:func() {println(name,' ',age);}, print_info: func() {println(name, ' ', age);},
set_age: func(a){age=a;}, set_age: func(a) {age = a;},
get_age: func() {return age;}, get_age: func() {return age;},
set_name: func(n){name=n;}, set_name: func(n) {name = n;},
get_name: func() {return name;} get_name: func() {return name;}
}; };
} }
``` ```
@ -448,20 +454,20 @@ 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;},
set:func(x){me.val=x;} set: func(x) {me.val = x;}
}; };
var class={ var class = {
new:func(){ new: func() {
return { return {
val:nil, val: nil,
parents:[trait] parents: [trait]
}; };
} }
}; };
var a=class.new(); var a = class.new();
a.set(114514); a.set(114514);
println(a.get()); println(a.get());
``` ```
@ -473,28 +479,28 @@ And `get` has the same process.
And we must remind you that if you do this: And we must remind you that if you do this:
```javascript ```javascript
var trait={ var trait = {
get:func{return me.val;}, get: func {return me.val;},
set:func(x){me.val=x;} set: func(x) {me.val = x;}
}; };
var class={ var class = {
new:func(){ new: func() {
return { return {
val:nil, val: nil,
parents:[trait] parents: [trait]
}; };
} }
}; };
var a=class.new(); var a = class.new();
var b=class.new(); var b = class.new();
a.set(114); a.set(114);
b.set(514); b.set(514);
println(a.get()); println(a.get());
println(b.get()); println(b.get());
var c=a.get; var c = a.get;
var d=b.get; var d = b.get;
println(c()); println(c());
println(c()); println(c());
@ -532,33 +538,19 @@ Definition:
```C++ ```C++
// you could also use a macro to define one. // you could also use a macro to define one.
nas_native(builtin_print); var builtin_print(context*, gc*);
``` ```
Then complete this function using C++: Then complete this function using C++:
```C++ ```C++
var builtin_print(var* local,gc& ngc) var builtin_print(context* ctx, gc* ngc) {
{
// find value with index begin from 1 // find value with index begin from 1
// because local[0] is reserved for value 'me' // because local[0] is reserved for value 'me'
var vec=local[1]; for(auto& i : ctx->localr[1].vec().elems) {
// main process std::cout << i;
// also check number of arguments and type here }
// if get an error,use nas_err std::cout << std::flush;
for(auto& i:vec.vec().elems)
switch(i.type)
{
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<i.str(); break;
case vm_vec: std::cout<<i.vec(); break;
case vm_hash: std::cout<<i.hash(); break;
case vm_func: std::cout<<"func(..){..}";break;
case vm_obj: std::cout<<"<object>"; break;
}
std::cout<<std::flush;
// generate return value, // generate return value,
// use ngc::alloc(type) to make a new value // use ngc::alloc(type) to make a new value
// or use reserved reference nil/one/zero // or use reserved reference nil/one/zero
@ -572,17 +564,24 @@ The value got before will be collected, but stil in use in this builtin function
So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this: So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this:
```C++ ```C++
var builtin_keys(var* local,gc& ngc) var builtin_keys(context* ctx, gc* ngc) {
{ auto hash = ctx->localr[1];
var hash=local[1]; if (hash.type!=vm_hash && hash.type!=vm_map) {
if(hash.type!=vm_hash) return nas_err("keys", "\"hash\" must be hash");
return nas_err("keys","\"hash\" must be hash"); }
// use gc.temp to store the gc-managed-value, to avoid being sweeped // use gc.temp to store the gc-managed-value, to avoid being sweeped
var res=ngc.temp=ngc.alloc(vm_vec); auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec=res.vec().elems; auto& vec = res.vec().elems;
for(auto& iter:hash.hash().elems) if (hash.type==vm_hash) {
vec.push_back(ngc.newstr(iter.first)); for(const auto& iter : hash.hash().elems) {
ngc.temp=nil; vec.push_back(ngc->newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc->newstr(iter.first));
}
}
ngc->temp = nil;
return res; return res;
} }
``` ```
@ -590,21 +589,16 @@ var builtin_keys(var* local,gc& ngc)
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 nasal_builtin_table builtin[] = {
{ {"__print", builtin_print},
const char* name; {nullptr, nullptr}
var (*func)(var*,gc&);
} builtin[]=
{
{"__print",builtin_print},
{nullptr, nullptr }
}; };
``` ```
At last,warp the `__print` in a nasal file: At last,warp the `__print` in a nasal file:
```javascript ```javascript
var print=func(elems...){ var print = func(elems...) {
return __print(elems); return __print(elems);
}; };
``` ```
@ -613,7 +607,7 @@ In fact the arguments that `__print` uses are not necessary.
So writting it like this is also right: So writting it like this is also right:
```javascript ```javascript
var print=func(elems...){ var print = func(elems...) {
return __print; return __print;
}; };
``` ```
@ -631,7 +625,7 @@ import("./dirname/dirname/filename.nas");
</details> </details>
<details><summary> Modules(for lib developers) </summary> <details><summary> Modules (for lib developers) </summary>
If there is only one way to add your own functions into nasal, If there is only one way to add your own functions into nasal,
that is really inconvenient. that is really inconvenient.
@ -731,7 +725,7 @@ Then we write a test nasal file to run this fib function, using `os.platform()`
use std.dylib; use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib; var fib = dlhandle.fib;
for(var i = 1; i<30; i+=1) for(var i = 1; i<30; i += 1)
println(dylib.dlcall(fib, i)); println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
@ -749,7 +743,7 @@ use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib; var fib = dlhandle.fib;
var invoke = dylib.limitcall(1); # this means the called function has only one parameter var invoke = dylib.limitcall(1); # this means the called function has only one parameter
for(var i = 1; i<30; i+=1) for(var i = 1; i<30; i += 1)
println(invoke(fib, i)); println(invoke(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
@ -791,7 +785,7 @@ If get this, Congratulations!
</details> </details>
<details><summary> Ghost Type(for lib developers) </summary> <details><summary> Ghost Type (for lib developers) </summary>
It's quite easy to create a new ghost by yourself now. It's quite easy to create a new ghost by yourself now.
Look at the example below: Look at the example below:
@ -812,19 +806,31 @@ void ghost_for_test_destructor(void* ptr) {
var create_new_ghost(var* args, usize size, gc* ngc) { var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj); var res = ngc->alloc(vm_obj);
// create ghost type // create ghost type
res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32); res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res; return res;
} }
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.object_check(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) { var print_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0]; var res = args[0];
// check ghost type by the type name // check ghost type by the type name
if (!res.objchk(ghost_for_test)) { if (!res.object_check(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n"; std::cout << "print_new_ghost: not ghost for test type.\n";
return nil; return nil;
} }
std::cout << "print_new_ghost: " << res.obj() << " result = " std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.obj().ptr) << "\n"; << *((u32*)res.ghost().pointer) << "\n";
return nil; return nil;
} }
``` ```
@ -841,7 +847,7 @@ We use this function to create a new ghost type:
And we use this function to check if value is the correct ghost type: And we use this function to check if value is the correct ghost type:
`bool var::objchk(const std::string&);` `bool var::object_check(const std::string&);`
The parameter is the name of the ghost type. The parameter is the name of the ghost type.
@ -860,7 +866,7 @@ So do not use variable without using `var` to declare it.
In Andy's interpreter: In Andy's interpreter:
```javascript ```javascript
foreach(i;[0,1,2,3]) foreach(i; [0, 1, 2, 3])
print(i) print(i)
``` ```
@ -877,7 +883,7 @@ If you forget to add the keyword `var`, you will get this:
code: undefined symbol "i" code: undefined symbol "i"
--> test.nas:1:9 --> test.nas:1:9
| |
1 | foreach(i;[0,1,2,3]) 1 | foreach(i; [0, 1, 2, 3])
| ^ undefined symbol "i" | ^ undefined symbol "i"
code: undefined symbol "i" code: undefined symbol "i"
@ -901,8 +907,7 @@ it will print trace back information:
Function `die` is used to throw error and crash immediately. Function `die` is used to throw error and crash immediately.
```javascript ```javascript
func() func() {
{
println("hello"); println("hello");
die("error occurred this line"); die("error occurred this line");
return; return;
@ -912,17 +917,27 @@ func()
```javascript ```javascript
hello hello
[vm] error: error occurred this line [vm] error: error occurred this line
[vm] native function error. [vm] error: error occurred in native function
trace back:
0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131) call trace (main)
0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4) call func@0x557513935710() {entry: 0x850}
0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6)
vm stack (0x7fffcd21bc68 <sp+80>, limit 10, total 12): trace back (main)
0x0000005b | null | 0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150)
... 0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00000057 | str | <0x138ff60> error occurred t... 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
...
0x00000052 | nil | stack (0x5575138e8c40, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x5575138e8c50
0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x5575138d9190> error occurred t...
0x000007 | nil |
0x000006 | func | <0x5575139356f0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
``` ```
</details> </details>
@ -932,28 +947,41 @@ vm stack (0x7fffcd21bc68 <sp+80>, limit 10, total 12):
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);
}( }(
func(f){ func(f) {
f(f); f(f);
} }
)(); )();
``` ```
```javascript ```javascript
[vm] stack overflow [vm] error: stack overflow
trace back:
0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5) call trace (main)
0x000004fb 1349 same call(s) call func@0x564106058620(f) {entry: 0x859}
0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2) --> 583 same call(s)
0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3) call func@0x5641060586c0(f) {entry: 0x851}
vm stack (0x7fffd3781d58 <sp+80>, limit 10, total 8108):
0x00001ffb | func | <0x15f8d90> entry:0x4f9 trace back (main)
0x00001ffa | func | <0x15f8d90> entry:0x4f9 0x000859 45 00 00 01 calll 0x1(a.nas:5)
0x00001ff9 | pc | 0x4fb 0x00085b 4a 00 00 01 callfv 0x1(a.nas:5)
... 0x00085b 582 same call(s)
0x00001ff2 | addr | 0x7fffd37a16e8 0x000853 4a 00 00 01 callfv 0x1(a.nas:2)
0x00085f 4a 00 00 01 callfv 0x1(a.nas:3)
stack (0x56410600be00, limit 10, total 4096)
0x000fff | func | <0x564106058600> entry:0x859
0x000ffe | pc | 0x85b
0x000ffd | addr | 0x56410601bd20
0x000ffc | nil |
0x000ffb | nil |
0x000ffa | func | <0x564106058600> entry:0x859
0x000ff9 | nil |
0x000ff8 | func | <0x564106058600> entry:0x859
0x000ff7 | pc | 0x85b
0x000ff6 | addr | 0x56410601bcb0
``` ```
</details> </details>
@ -963,17 +991,19 @@ vm stack (0x7fffd3781d58 <sp+80>, limit 10, total 8108):
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;
}()[1]; }()[1];
``` ```
```javascript ```javascript
[vm] callv: must call a vector/hash/string [vm] error: must call a vector/hash/string but get number
trace back:
0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3) trace back (main)
vm stack (0x7fffff539c28 <sp+80>, limit 10, total 1): 0x000854 47 00 00 00 callv 0x0(a.nas:3)
0x00000050 | num | 0
stack (0x564993f462b0, limit 10, total 1)
0x000000 | num | 0
``` ```
</details> </details>
@ -985,35 +1015,48 @@ Use command __`-d`__ or __`--detail`__ the trace back info will show more detail
```javascript ```javascript
hello hello
[vm] error: error occurred this line [vm] error: error occurred this line
[vm] error: native function error [vm] error: error occurred in native function
call trace (main)
call func@0x55dcb5b8fbf0() {entry: 0x850}
trace back (main) trace back (main)
0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131) 0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150)
0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4) 0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6) 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
vm stack (0x7fffe0ffed90 <sp+63>, limit 10, total 12)
0x0000004a | null | stack (0x55dcb5b43120, limit 10, total 14)
0x00000049 | pc | 0x553 0x00000d | null |
0x00000048 | addr | 0x7fffe0ffeda0 0x00000c | pc | 0x856
... 0x00000b | addr | 0x55dcb5b43130
0x00000041 | nil | 0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x55dcb5b33670> error occurred t...
0x000007 | nil |
0x000006 | func | <0x55dcb5b8fbd0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
registers (main) registers (main)
[ pc ] | pc | 0xb0 [pc ] | pc | 0x547
[ global ] | addr | 0x7fffe0ffe9a0 [global] | addr | 0x55dcb5b53130
[ localr ] | addr | 0x7fffe0ffedf0 [local ] | addr | 0x55dcb5b43190
[ memr ] | addr | 0x0 [memr ] | addr | 0x0
[ canary ] | addr | 0x7fffe1002990 [canary] | addr | 0x55dcb5b53110
[ top ] | addr | 0x7fffe0ffee40 [top ] | addr | 0x55dcb5b431f0
[ funcr ] | func | <0x677cd0> entry:0xb0 [funcr ] | func | <0x55dcb5b65620> entry:0x547
[ upvalr ] | nil | [upval ] | nil |
global (0x7fffe0ffe9a0 <sp+0>)
0x00000000 | func | <0x65fb00> entry:0x5 global (0x55dcb5b53130)
0x00000001 | func | <0x65fb20> entry:0xd 0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val]
0x000001 | vec | <0x55dcb5b64c20> [0 val]
... ...
0x0000003d | func | <0x66bf00> entry:0x51f 0x00005e | func | <0x55dcb5b8fc70> entry:0x846
0x0000003e | hash | <0x65ffa0> {5 val}
local (0x7fffe0ffedf0 <sp+45>) local (0x55dcb5b43190 <+7>)
0x00000000 | nil | 0x000000 | nil |
0x00000001 | str | <0x6cb630> error occurred t... 0x000001 | str | <0x55dcb5b33670> error occurred t...
0x000002 | nil |
``` ```
</details> </details>
@ -1038,16 +1081,18 @@ source code:
for(var i=0;i<31;i+=1) for(var i=0;i<31;i+=1)
print(fib(i),'\n'); print(fib(i),'\n');
next bytecode: next bytecode:
--> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0) 0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427)
0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6) 0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427)
0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6) 0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423)
0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6) 0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423)
0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6) 0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423)
0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7) --> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1)
0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7) 0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1)
0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6) 0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1)
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 0)
stack (0x55ccd0a1b9d0, limit 10, total 0)
>> >>
``` ```
@ -1070,23 +1115,26 @@ source code:
for(var i=0;i<31;i+=1) for(var i=0;i<31;i+=1)
print(fib(i),'\n'); print(fib(i),'\n');
next bytecode: next bytecode:
0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503) 0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1)
0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498) --> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3)
0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1) 0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3)
0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1) 0x000853 40 00 08 56 jf 0x856(test/fib.nas:3)
0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1) 0x000854 45 00 00 01 calll 0x1(test/fib.nas:3)
0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1) 0x000855 56 00 00 00 ret 0x0(test/fib.nas:3)
--> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3) 0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4)
0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3) 0x000857 45 00 00 01 calll 0x1(test/fib.nas:4)
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 7)
0x00000047 | pc | 0x566 stack (0x55ccd0a1b9d0, limit 10, total 8)
0x00000046 | addr | 0x0 0x000007 | pc | 0x869
0x00000045 | nil | 0x000006 | addr | 0x0
0x00000044 | num | 0 0x000005 | nil |
0x00000043 | nil | 0x000004 | nil |
0x00000042 | nil | 0x000003 | num | 0
0x00000041 | func | <0x88d2f0> entry:0x5 0x000002 | nil |
0x000001 | nil |
0x000000 | func | <0x55ccd0a58fa0> entry:0x487
>> >>
``` ```
@ -1114,3 +1162,22 @@ Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31)
>>> >>>
``` ```
Try import `std/json.nas`~
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>> use std.json;
{stringify:func(..) {..},parse:func(..) {..}}
>>>
```

View File

@ -25,7 +25,7 @@
__如果有好的意见或建议欢迎联系我们!__ __如果有好的意见或建议欢迎联系我们!__
* __E-mail__: __lhk101lhk101@qq.com__ * __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762)
## __简介__ ## __简介__
@ -88,8 +88,9 @@ __注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构
如果你是 `Windows` 用户且想正常输出unicode在nasal代码里写这个来开启unicode代码页: 如果你是 `Windows` 用户且想正常输出unicode在nasal代码里写这个来开启unicode代码页:
```javascript ```javascript
if(os.platform()=="windows") if (os.platform()=="windows") {
system("chcp 65001"); system("chcp 65001");
}
``` ```
## __教程__ ## __教程__
@ -104,30 +105,30 @@ __`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行
__`nil`__ 是空类型。类似于null。 __`nil`__ 是空类型。类似于null。
```javascript ```javascript
var spc=nil; var spc = nil;
``` ```
__`num`__ 有三种形式:十进制十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。 __`num`__ 有三种形式:十进制十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。
```javascript ```javascript
# 该语言用 '#' 来作为注释的开头 # 该语言用 '#' 来作为注释的开头
var n=2.71828; # dec 十进制 var n = 2.71828; # dec 十进制
var n=2.147e16; # dec 十进制 var n = 2.147e16; # dec 十进制
var n=1e-10; # dec 十进制 var n = 1e-10; # dec 十进制
var n=0xAA55; # hex 十六进制 var n = 0xAA55; # hex 十六进制
var n=0o170001; # oct 八进制 var n = 0o170001; # oct 八进制
# 注意: true 和 false 关键字在现在的 nasal 里也是可用的 # 注意: true 和 false 关键字在现在的 nasal 里也是可用的
var n=true; # n 实际上是数字 1.0 var n = true; # n 实际上是数字 1.0
var n=false; # n 实际上是数字 0.0 var n = false; # n 实际上是数字 0.0
``` ```
__`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。 __`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。
```javascript ```javascript
var s='str'; var s = 'str';
var s="another string"; var s = "another string";
var s=`c`; var s = `c`;
# 该语言也支持一些特别的转义字符: # 该语言也支持一些特别的转义字符:
'\a'; '\b'; '\e'; '\f'; '\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v'; '\n'; '\r'; '\t'; '\v';
@ -138,19 +139,19 @@ var s=`c`;
__`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度) __`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度)
```javascript ```javascript
var vec=[]; var vec = [];
var vec=[0,nil,{},[],func(){return 0}]; var vec = [0, nil, {}, [], func(){return 0}];
append(vec,0,1,2); append(vec, 0, 1, 2);
``` ```
__`hash`__ 使用哈希表 (类似于`python`中的`dict`)通过键值对来存储数据。key可以是一个字符串也可以是一个标识符。 __`hash`__ 使用哈希表 (类似于`python`中的`dict`)通过键值对来存储数据。key可以是一个字符串也可以是一个标识符。
```javascript ```javascript
var hash={ var hash = {
member1:nil, member1: nil,
member2:"str", member2: "str",
"member3":"member\'s name can also be a string constant", "member3": "member\'s name can also be a string constant",
funct:func(){ funct: func() {
return me.member2~me.member3; return me.member2~me.member3;
} }
}; };
@ -159,27 +160,28 @@ var hash={
__`func`__ 函数类型。(实际上在这个语言里函数是一种`lambda`表达式) __`func`__ 函数类型。(实际上在这个语言里函数是一种`lambda`表达式)
```javascript ```javascript
var f=func(x,y,z){ var f = func(x, y, z) {
return nil; return nil;
} }
# 函数声明可以没有参数列表以及 `(`, `)` # 函数声明可以没有参数列表以及 `(`, `)`
var f=func{ var f = func {
return 114514; return 114514;
} }
var f=func(x,y,z,deft=1){ var f = func(x, y, z, deft = 1) {
return x+y+z+deft; return x+y+z+deft;
} }
var f=func(args...){ var f = func(args...) {
var sum=0; var sum = 0;
foreach(var i;args) foreach(var i; args) {
sum+=i; sum += i;
}
return sum; return sum;
} }
``` ```
__`upval`__ 是存储闭包数据的特殊类型, 在 __`vm`__ 中使用,以确保闭包功能正常。 __`upval`__ 是存储闭包数据的特殊类型, 在 __`vm`__ 中使用,以确保闭包功能正常。
__`obj`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。 __`ghost`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。
</details> </details>
@ -223,16 +225,16 @@ Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的
赋值运算符`=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=`正如其名,用于进行赋值。 赋值运算符`=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=`正如其名,用于进行赋值。
```javascript ```javascript
a=b=c=d=1; a = b = c = d = 1;
a+=1; a += 1;
a-=1; a -= 1;
a*=1; a *= 1;
a/=1; a /= 1;
a~="string"; a ~= "string";
a^=0xff; a ^= 0xff;
a&=0xca; a &= 0xca;
a|=0xba; a |= 0xba;
``` ```
</details> </details>
@ -242,9 +244,9 @@ a|=0xba;
如下所示。 如下所示。
```javascript ```javascript
var a=1; # 定义单个变量 var a = 1; # 定义单个变量
var (a,b,c)=[0,1,2]; # 从数组中初始化多个变量 var (a, b, c) = [0, 1, 2]; # 从数组中初始化多个变量
var (a,b,c)=(0,1,2); # 从元组中初始化多个变量 var (a, b, c) = (0, 1, 2); # 从元组中初始化多个变量
``` ```
Nasal 有很多特别的全局变量: Nasal 有很多特别的全局变量:
@ -278,9 +280,9 @@ func() {
最后这个语句通常用于交换两个变量的数据类似于Python中的操作。 最后这个语句通常用于交换两个变量的数据类似于Python中的操作。
```javascript ```javascript
(a,b[0],c.d)=[0,1,2]; (a, b[0], c.d) = [0, 1, 2];
(a,b[1],c.e)=(0,1,2); (a, b[1], c.e) = (0, 1, 2);
(a,b)=(b,a); (a, b) = (b, a);
``` ```
</details> </details>
@ -290,13 +292,13 @@ func() {
nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。 nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。
```javascript ```javascript
if(1){ if (1) {
; ;
}elsif(2){ } elsif (2) {
; ;
}else if(3){ } else if (3) {
; ;
}else{ } else {
; ;
} }
``` ```
@ -308,10 +310,12 @@ if(1){
while循环和for循环大体上与C/C++是一致的。 while循环和for循环大体上与C/C++是一致的。
```javascript ```javascript
while(condition) while(condition) {
continue; continue;
for(var i=0;i<10;i+=1) }
for(var i = 0; i<10; i += 1) {
break; break;
}
``` ```
同时nasal还有另外两种直接遍历列表的循环方式: 同时nasal还有另外两种直接遍历列表的循环方式:
@ -319,15 +323,17 @@ for(var i=0;i<10;i+=1)
`forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。 `forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。
```javascript ```javascript
forindex(var i;elem) forindex(var i; elem) {
print(elem[i]); print(elem[i]);
}
``` ```
`foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`. `foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`.
```javascript ```javascript
foreach(var i;elem) foreach(var i; elem) {
print(i); print(i);
}
``` ```
</details> </details>
@ -338,7 +344,7 @@ nasal提供了下面第一句的类似语法来从列表中随机或者按照一
```javascript ```javascript
a[0]; a[0];
a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil]; a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil];
"hello world"[0]; "hello world"[0];
``` ```
@ -351,7 +357,7 @@ a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil];
然而如果它用起来非常舒适,那效率也显得不是非常重要了…… 然而如果它用起来非常舒适,那效率也显得不是非常重要了……
```javascript ```javascript
f(x:0,y:nil,z:[]); f(x:0, y:nil, z:[]);
``` ```
</details> </details>
@ -361,10 +367,10 @@ f(x:0,y:nil,z:[]);
函数有这样一种直接编写函数体并且立即调用的方式: 函数有这样一种直接编写函数体并且立即调用的方式:
```javascript ```javascript
func(x,y){ func(x, y) {
return x+y; return x+y;
}(0,1); }(0, 1);
func(x){ func(x) {
return 1/(1+math.exp(-x)); return 1/(1+math.exp(-x));
}(0.5); }(0.5);
``` ```
@ -372,11 +378,11 @@ func(x){
测试文件中有一个非常有趣的文件`y-combinator.nas`,可以试一试: 测试文件中有一个非常有趣的文件`y-combinator.nas`,可以试一试:
```javascript ```javascript
var fib=func(f){ var fib = func(f) {
return f(f); return f(f);
}( }(
func(f){ func(f) {
return func(x){ return func(x) {
if(x<2) return x; if(x<2) return x;
return f(f)(x-1)+f(f)(x-2); return f(f)(x-1)+f(f)(x-2);
} }
@ -393,9 +399,9 @@ var fib=func(f){
下面这个例子里,结果是`1`: 下面这个例子里,结果是`1`:
```javascript ```javascript
var f=func(){ var f = func() {
var a=1; var a = 1;
return func(){return a;}; return func() {return a;};
} }
print(f()()); print(f()());
``` ```
@ -403,14 +409,14 @@ print(f()());
如果善用闭包,你可以使用它来进行面向对象编程。 如果善用闭包,你可以使用它来进行面向对象编程。
```javascript ```javascript
var student=func(n,a){ var student = func(n, a) {
var (name,age)=(n,a); var (name, age) = (n, a);
return { return {
print_info:func() {println(name,' ',age);}, print_info: func() {println(name, ' ', age);},
set_age: func(a){age=a;}, set_age: func(a) {age = a;},
get_age: func() {return age;}, get_age: func() {return age;},
set_name: func(n){name=n;}, set_name: func(n) {name = n;},
get_name: func() {return name;} get_name: func() {return name;}
}; };
} }
``` ```
@ -429,20 +435,20 @@ var student=func(n,a){
使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`: 使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`:
```javascript ```javascript
var trait={ var trait = {
get:func{return me.val;}, get: func {return me.val;},
set:func(x){me.val=x;} set: func(x) {me.val = x;}
}; };
var class={ var class = {
new:func(){ new: func() {
return { return {
val:nil, val: nil,
parents:[trait] parents: [trait]
}; };
} }
}; };
var a=class.new(); var a = class.new();
a.set(114514); a.set(114514);
println(a.get()); println(a.get());
``` ```
@ -453,28 +459,28 @@ println(a.get());
不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销: 不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销:
```javascript ```javascript
var trait={ var trait = {
get:func{return me.val;}, get: func {return me.val;},
set:func(x){me.val=x;} set: func(x) {me.val = x;}
}; };
var class={ var class = {
new:func(){ new: func() {
return { return {
val:nil, val: nil,
parents:[trait] parents: [trait]
}; };
} }
}; };
var a=class.new(); var a = class.new();
var b=class.new(); var b = class.new();
a.set(114); a.set(114);
b.set(514); b.set(514);
println(a.get()); println(a.get());
println(b.get()); println(b.get());
var c=a.get; var c = a.get;
var d=b.get; var d = b.get;
println(c()); println(c());
println(c()); println(c());
@ -506,42 +512,27 @@ println(d());
__警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个节 __`模块`__ 的内容。 __警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个节 __`模块`__ 的内容。
如果你确实是想修改源码来搞一个自己私人订制的解释器,那么你可以说:“我他妈就是想自己私人订制,你们他妈的管得着吗”, 如果你确实是想修改源码来搞一个自己私人订制的解释器 ———— “我他妈就是想自己私人订制,你们他妈的管得着吗”,
然后看看源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,还有下面的样例: 参考源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,下面是其中一个样例:
定义新的内置函数: 定义新的内置函数:
```C++ ```C++
// 你可以使用这个宏来直接定义一个新的内置函数 // 你可以使用这个宏来直接定义一个新的内置函数
nas_native(builtin_print); var builtin_print(context*, gc*);
``` ```
然后用C++完成这个函数的函数体: 然后用C++完成这个函数的函数体:
```C++ ```C++
var builtin_print(var* local,gc& ngc) var builtin_print(context* ctx, gc* ngc) {
{ // 局部变量的下标其实是从 1 开始的
// 局部变量的下标其实是从1开始的 // 因为 local[0] 是保留给 'me' 的空间
// 因为local[0]是保留给'me'的空间 for(auto& i : ctx->localr[1].vec().elems) {
var vec=local[1]; std::cout << i;
// 主要部分 }
// 一些必要的类型检查和输入合法性检测也要在这里写出 std::cout << std::flush;
// 如果检测到问题用builtin_err函数来返回vm_null // 最后生成返回值,返回值必须是一个内置的类型,
// 并且狠狠地骂那些不好好写代码的混蛋(玩笑)
for(auto& i:vec.vec().elems)
switch(i.type)
{
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<i.str(); break;
case vm_vec: std::cout<<i.vec(); break;
case vm_hash: std::cout<<i.hash(); break;
case vm_func: std::cout<<"func(..){..}";break;
case vm_obj: std::cout<<"<object>"; break;
}
std::cout<<std::flush;
// 最后一定要记得生成返回值,返回值必须是一个内置的类型,
// 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构 // 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
// 或者用我们已经定义好的nil/one/zero这些可以直接使用 // 或者用我们已经定义好的nil/one/zero这些可以直接使用
return nil; return nil;
@ -554,17 +545,24 @@ var builtin_print(var* local,gc& ngc)
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示 可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++ ```C++
var builtin_keys(var* local,gc& ngc) var builtin_keys(context* ctx, gc* ngc) {
{ auto hash = ctx->localr[1];
var hash=local[1]; if (hash.type!=vm_hash && hash.type!=vm_map) {
if(hash.type!=vm_hash) return nas_err("keys", "\"hash\" must be hash");
return nas_err("keys","\"hash\" must be hash"); }
// 使用gc.temp来存储gc管理的变量防止错误的回收 // 使用gc.temp来存储gc管理的变量防止错误的回收
var res=ngc.temp=ngc.alloc(vm_vec); auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec=res.vec().elems; auto& vec = res.vec().elems;
for(auto& iter:hash.hash().elems) if (hash.type==vm_hash) {
vec.push_back(ngc.newstr(iter.first)); for(const auto& iter : hash.hash().elems) {
ngc.temp=nil; vec.push_back(ngc->newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc->newstr(iter.first));
}
}
ngc->temp = nil;
return res; return res;
} }
``` ```
@ -572,21 +570,16 @@ var builtin_keys(var* local,gc& ngc)
这些工作都完成之后在内置函数注册表中填写它在nasal中的别名并且在表中填对这个函数的函数指针: 这些工作都完成之后在内置函数注册表中填写它在nasal中的别名并且在表中填对这个函数的函数指针:
```C++ ```C++
struct func nasal_builtin_table builtin[] = {
{ {"__print", builtin_print},
const char* name; {nullptr, nullptr}
var (*func)(var*,gc&);
} builtin[]=
{
{"__print",builtin_print},
{nullptr, nullptr }
}; };
``` ```
最后将其包装到nasal文件中: 最后将其包装到nasal文件中:
```javascript ```javascript
var print=func(elems...){ var print = func(elems...) {
return __print(elems); return __print(elems);
}; };
``` ```
@ -594,7 +587,7 @@ var print=func(elems...){
事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对: 事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对:
```javascript ```javascript
var print=func(elems...){ var print = func(elems...) {
return __print; return __print;
}; };
``` ```
@ -705,7 +698,7 @@ Windows(`.dll`):
use std.dylib; use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib; var fib = dlhandle.fib;
for(var i = 1; i<30; i+=1) for(var i = 1; i<30; i += 1)
println(dylib.dlcall(fib, i)); println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
@ -723,7 +716,7 @@ use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib; var fib = dlhandle.fib;
var invoke = dylib.limitcall(1); # this means the called function has only one parameter var invoke = dylib.limitcall(1); # this means the called function has only one parameter
for(var i = 1; i<30; i+=1) for(var i = 1; i<30; i += 1)
println(invoke(fib, i)); println(invoke(fib, i));
dylib.dlclose(dlhandle.lib); dylib.dlclose(dlhandle.lib);
``` ```
@ -767,7 +760,7 @@ dylib.dlclose(dlhandle.lib);
<details><summary> 自定义类型(开发者教程) </summary> <details><summary> 自定义类型(开发者教程) </summary>
创建一个自定义类型现在不是很困难。下面是使用示例: 创建一个自定义类型很容易。下面是使用示例:
```c++ ```c++
const auto ghost_for_test = "ghost_for_test"; const auto ghost_for_test = "ghost_for_test";
@ -785,19 +778,31 @@ void ghost_for_test_destructor(void* ptr) {
var create_new_ghost(var* args, usize size, gc* ngc) { var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj); var res = ngc->alloc(vm_obj);
// 创建自定义类型 // 创建自定义类型
res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32); res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res; return res;
} }
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.object_check(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) { var print_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0]; var res = args[0];
// 用自定义类型的名字来检查是否是正确的自定义类型 // 用自定义类型的名字来检查是否是正确的自定义类型
if (!res.objchk(ghost_for_test)) { if (!res.object_check(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n"; std::cout << "print_new_ghost: not ghost for test type.\n";
return nil; return nil;
} }
std::cout << "print_new_ghost: " << res.obj() << " result = " std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.obj().ptr) << "\n"; << *((u32*)res.ghost().pointer) << "\n";
return nil; return nil;
} }
``` ```
@ -814,7 +819,7 @@ var print_new_ghost(var* args, usize size, gc* ngc) {
我们使用下面的这个函数检测是否是正确的自定义类型: 我们使用下面的这个函数检测是否是正确的自定义类型:
`bool var::objchk(const std::string&);` `bool var::object_check(const std::string&);`
参数是自定义类型的类型名。 参数是自定义类型的类型名。
@ -832,7 +837,7 @@ var print_new_ghost(var* args, usize size, gc* ngc) {
在Andy的解释器中: 在Andy的解释器中:
```javascript ```javascript
foreach(i;[0,1,2,3]) foreach(i; [0, 1, 2, 3])
print(i) print(i)
``` ```
@ -844,7 +849,7 @@ foreach(i;[0,1,2,3])
code: undefined symbol "i" code: undefined symbol "i"
--> test.nas:1:9 --> test.nas:1:9
| |
1 | foreach(i;[0,1,2,3]) 1 | foreach(i; [0, 1, 2, 3])
| ^ undefined symbol "i" | ^ undefined symbol "i"
code: undefined symbol "i" code: undefined symbol "i"
@ -866,8 +871,7 @@ code: undefined symbol "i"
`die`函数用于直接抛出错误并终止执行。 `die`函数用于直接抛出错误并终止执行。
```javascript ```javascript
func() func() {
{
println("hello"); println("hello");
die("error occurred this line"); die("error occurred this line");
return; return;
@ -877,17 +881,27 @@ func()
```javascript ```javascript
hello hello
[vm] error: error occurred this line [vm] error: error occurred this line
[vm] native function error. [vm] error: error occurred in native function
trace back:
0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131) call trace (main)
0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4) call func@0x557513935710() {entry: 0x850}
0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6)
vm stack (0x7fffcd21bc68 <sp+80>, limit 10, total 12): trace back (main)
0x0000005b | null | 0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150)
... 0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00000057 | str | <0x138ff60> error occurred t... 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
...
0x00000052 | nil | stack (0x5575138e8c40, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x5575138e8c50
0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x5575138d9190> error occurred t...
0x000007 | nil |
0x000006 | func | <0x5575139356f0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
``` ```
</details> </details>
@ -897,28 +911,41 @@ vm stack (0x7fffcd21bc68 <sp+80>, limit 10, total 12):
这是一个会导致栈溢出的例子: 这是一个会导致栈溢出的例子:
```javascript ```javascript
func(f){ func(f) {
return f(f); return f(f);
}( }(
func(f){ func(f) {
f(f); f(f);
} }
)(); )();
``` ```
```javascript ```javascript
[vm] stack overflow [vm] error: stack overflow
trace back:
0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5) call trace (main)
0x000004fb 1349 same call(s) call func@0x564106058620(f) {entry: 0x859}
0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2) --> 583 same call(s)
0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3) call func@0x5641060586c0(f) {entry: 0x851}
vm stack (0x7fffd3781d58 <sp+80>, limit 10, total 8108):
0x00001ffb | func | <0x15f8d90> entry:0x4f9 trace back (main)
0x00001ffa | func | <0x15f8d90> entry:0x4f9 0x000859 45 00 00 01 calll 0x1(a.nas:5)
0x00001ff9 | pc | 0x4fb 0x00085b 4a 00 00 01 callfv 0x1(a.nas:5)
... 0x00085b 582 same call(s)
0x00001ff2 | addr | 0x7fffd37a16e8 0x000853 4a 00 00 01 callfv 0x1(a.nas:2)
0x00085f 4a 00 00 01 callfv 0x1(a.nas:3)
stack (0x56410600be00, limit 10, total 4096)
0x000fff | func | <0x564106058600> entry:0x859
0x000ffe | pc | 0x85b
0x000ffd | addr | 0x56410601bd20
0x000ffc | nil |
0x000ffb | nil |
0x000ffa | func | <0x564106058600> entry:0x859
0x000ff9 | nil |
0x000ff8 | func | <0x564106058600> entry:0x859
0x000ff7 | pc | 0x85b
0x000ff6 | addr | 0x56410601bcb0
``` ```
</details> </details>
@ -928,17 +955,19 @@ vm stack (0x7fffd3781d58 <sp+80>, limit 10, total 8108):
如果在执行的时候出现错误,程序会直接终止执行: 如果在执行的时候出现错误,程序会直接终止执行:
```javascript ```javascript
func(){ func() {
return 0; return 0;
}()[1]; }()[1];
``` ```
```javascript ```javascript
[vm] callv: must call a vector/hash/string [vm] error: must call a vector/hash/string but get number
trace back:
0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3) trace back (main)
vm stack (0x7fffff539c28 <sp+80>, limit 10, total 1): 0x000854 47 00 00 00 callv 0x0(a.nas:3)
0x00000050 | num | 0
stack (0x564993f462b0, limit 10, total 1)
0x000000 | num | 0
``` ```
</details> </details>
@ -950,35 +979,48 @@ vm stack (0x7fffff539c28 <sp+80>, limit 10, total 1):
```javascript ```javascript
hello hello
[vm] error: error occurred this line [vm] error: error occurred this line
[vm] error: native function error [vm] error: error occurred in native function
call trace (main)
call func@0x55dcb5b8fbf0() {entry: 0x850}
trace back (main) trace back (main)
0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131) 0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150)
0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4) 0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6) 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
vm stack (0x7fffe0ffed90 <sp+63>, limit 10, total 12)
0x0000004a | null | stack (0x55dcb5b43120, limit 10, total 14)
0x00000049 | pc | 0x553 0x00000d | null |
0x00000048 | addr | 0x7fffe0ffeda0 0x00000c | pc | 0x856
... 0x00000b | addr | 0x55dcb5b43130
0x00000041 | nil | 0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x55dcb5b33670> error occurred t...
0x000007 | nil |
0x000006 | func | <0x55dcb5b8fbd0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
registers (main) registers (main)
[ pc ] | pc | 0xb0 [pc ] | pc | 0x547
[ global ] | addr | 0x7fffe0ffe9a0 [global] | addr | 0x55dcb5b53130
[ localr ] | addr | 0x7fffe0ffedf0 [local ] | addr | 0x55dcb5b43190
[ memr ] | addr | 0x0 [memr ] | addr | 0x0
[ canary ] | addr | 0x7fffe1002990 [canary] | addr | 0x55dcb5b53110
[ top ] | addr | 0x7fffe0ffee40 [top ] | addr | 0x55dcb5b431f0
[ funcr ] | func | <0x677cd0> entry:0xb0 [funcr ] | func | <0x55dcb5b65620> entry:0x547
[ upvalr ] | nil | [upval ] | nil |
global (0x7fffe0ffe9a0 <sp+0>)
0x00000000 | func | <0x65fb00> entry:0x5 global (0x55dcb5b53130)
0x00000001 | func | <0x65fb20> entry:0xd 0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val]
0x000001 | vec | <0x55dcb5b64c20> [0 val]
... ...
0x0000003d | func | <0x66bf00> entry:0x51f 0x00005e | func | <0x55dcb5b8fc70> entry:0x846
0x0000003e | hash | <0x65ffa0> {5 val}
local (0x7fffe0ffedf0 <sp+45>) local (0x55dcb5b43190 <+7>)
0x00000000 | nil | 0x000000 | nil |
0x00000001 | str | <0x6cb630> error occurred t... 0x000001 | str | <0x55dcb5b33670> error occurred t...
0x000002 | nil |
``` ```
</details> </details>
@ -1002,16 +1044,18 @@ source code:
for(var i=0;i<31;i+=1) for(var i=0;i<31;i+=1)
print(fib(i),'\n'); print(fib(i),'\n');
next bytecode: next bytecode:
--> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0) 0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427)
0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6) 0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427)
0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6) 0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423)
0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6) 0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423)
0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6) 0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423)
0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7) --> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1)
0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7) 0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1)
0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6) 0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1)
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 0)
stack (0x55ccd0a1b9d0, limit 10, total 0)
>> >>
``` ```
@ -1034,23 +1078,26 @@ source code:
for(var i=0;i<31;i+=1) for(var i=0;i<31;i+=1)
print(fib(i),'\n'); print(fib(i),'\n');
next bytecode: next bytecode:
0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503) 0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1)
0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498) --> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3)
0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1) 0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3)
0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1) 0x000853 40 00 08 56 jf 0x856(test/fib.nas:3)
0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1) 0x000854 45 00 00 01 calll 0x1(test/fib.nas:3)
0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1) 0x000855 56 00 00 00 ret 0x0(test/fib.nas:3)
--> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3) 0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4)
0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3) 0x000857 45 00 00 01 calll 0x1(test/fib.nas:4)
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 7)
0x00000047 | pc | 0x566 stack (0x55ccd0a1b9d0, limit 10, total 8)
0x00000046 | addr | 0x0 0x000007 | pc | 0x869
0x00000045 | nil | 0x000006 | addr | 0x0
0x00000044 | num | 0 0x000005 | nil |
0x00000043 | nil | 0x000004 | nil |
0x00000042 | nil | 0x000003 | num | 0
0x00000041 | func | <0x88d2f0> entry:0x5 0x000002 | nil |
0x000001 | nil |
0x000000 | func | <0x55ccd0a58fa0> entry:0x487
>> >>
``` ```
@ -1077,3 +1124,22 @@ Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31)
>>> >>>
``` ```
试试引入 `std/json.nas` 模块 ~
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>> use std.json;
{stringify:func(..) {..},parse:func(..) {..}}
>>>
```

View File

@ -62,3 +62,27 @@ var example_module = func {
}; };
}(); }();
``` ```
## Import a module
Here is a module named `std/example_module.nas`:
```nasal
var a = 1;
```
Then there's a script file named `test.nas`, import module in this file using this way:
```nasal
use std.example_module;
println(example_module.a); # 1
```
Or this way:
```nasal
import("std/example_module.nas");
println(example_module.a); # 1
```

View File

@ -33,6 +33,7 @@
</head> </head>
<body> <body>
<h1>&nbsp;Nasal | Not another scripting language!</h1> <h1>&nbsp;Nasal | Not another scripting language!</h1>
<img src="/doc/pic/social.png" width="900" height="400" style="margin-left: 15px;"><br /></img>
<div class="badges"> <div class="badges">
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a> <a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a> <a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>

View File

@ -64,7 +64,7 @@ var set_new_ghost(var* args, usize size, gc* ngc) {
return nil; return nil;
} }
f64 num = args[1].num(); f64 num = args[1].num();
*((u32*)res.ghost().pointer) = static_cast<u32>(num); *(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil; return nil;
} }

View File

@ -99,7 +99,7 @@ var nas_connect(var* args, usize size, gc* ngc) {
memcpy(&addr.sin_addr, entry->h_addr, entry->h_length); memcpy(&addr.sin_addr, entry->h_addr, entry->h_length);
return var::num(static_cast<double>(connect( return var::num(static_cast<double>(connect(
args[0].num(), args[0].num(),
(sockaddr*)&addr, reinterpret_cast<sockaddr*>(&addr),
sizeof(sockaddr_in) sizeof(sockaddr_in)
))); )));
} }
@ -114,7 +114,7 @@ var nas_accept(var* args, usize size, gc* ngc) {
#else #else
int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen); int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen);
#endif #endif
var res=ngc->temp = ngc->alloc(vm_hash); var res = ngc->temp = ngc->alloc(vm_hash);
auto& hash = res.hash().elems; auto& hash = res.hash().elems;
hash["sd"] = var::num(static_cast<double>(client_sd)); hash["sd"] = var::num(static_cast<double>(client_sd));
hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr)); hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr));

View File

@ -260,7 +260,7 @@ var builtin_keys(context* ctx, gc* ngc) {
vec.push_back(ngc->newstr(iter.first)); vec.push_back(ngc->newstr(iter.first));
} }
} }
ngc->temp=nil; ngc->temp = nil;
return res; return res;
} }

View File

@ -320,6 +320,7 @@ while(1){
elsif(path=="/license") elsif(path=="/license")
http.send(client,respond.ok(io.readfile("./LICENSE"))); http.send(client,respond.ok(io.readfile("./LICENSE")));
elsif(path=="/doc/pic/nasal.png" or elsif(path=="/doc/pic/nasal.png" or
path=="/doc/pic/social.png" or
path=="/doc/pic/benchmark.png" or path=="/doc/pic/benchmark.png" or
path=="/doc/pic/mandelbrot.png" or path=="/doc/pic/mandelbrot.png" or
path=="/doc/pic/feigenbaum.png" or path=="/doc/pic/feigenbaum.png" or