Merge pull request #34 from ValKmjolnir/develop

📝 code improvement
This commit is contained in:
ValK 2023-11-05 21:38:34 +08:00 committed by GitHub
commit 97adfc9ea4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1448 additions and 976 deletions

517
README.md
View File

@ -4,7 +4,7 @@
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v11.1-blue?style=flat-square&logo=github) ![in dev](https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE)
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md) > This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
@ -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,13 +428,13 @@ 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
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; 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;
}; };
``` ```
@ -625,13 +619,13 @@ Use `import("filename.nas")` to get the nasal file including your built-in funct
Also there's another way of importing nasal files, the two way of importing have the same function: Also there's another way of importing nasal files, the two way of importing have the same function:
```javascript ```javascript
import.dirname.dirname.filename; use dirname.dirname.filename;
import("./dirname/dirname/filename.nas"); 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.
@ -728,10 +722,10 @@ 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
import.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);
``` ```
@ -745,11 +739,11 @@ 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
import.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;
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

@ -4,7 +4,7 @@
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v11.1-blue?style=flat-square&logo=github) ![in dev](https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE)
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md) > 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
@ -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,13 +409,13 @@ 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;
// 主要部分
// 一些必要的类型检查和输入合法性检测也要在这里写出
// 如果检测到问题用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; 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;
}; };
``` ```
@ -605,7 +598,7 @@ var print=func(elems...){
当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的 当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的
```javascript ```javascript
import.dirname.dirname.filename; use dirname.dirname.filename;
import("./dirname/dirname/filename.nas"); import("./dirname/dirname/filename.nas");
``` ```
@ -702,10 +695,10 @@ Windows(`.dll`):
下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: 下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台:
```javascript ```javascript
import.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);
``` ```
@ -719,11 +712,11 @@ dylib.dlclose(dlhandle.lib);
`dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写: `dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写:
```javascript ```javascript
import.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;
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

@ -1,5 +1,7 @@
# __Development History__ # __Development History__
![buringship](./pic/burningship.png)
## __Contents__ ## __Contents__
* [__Parser__](#parser) * [__Parser__](#parser)
@ -22,6 +24,7 @@
* [__Release Notes__](#release-notes) * [__Release Notes__](#release-notes)
* [v8.0](#version-80-release) * [v8.0](#version-80-release)
* [v11.0](#version-110-release) * [v11.0](#version-110-release)
* [v11.1](#version-111-release)
## __Parser__ ## __Parser__
@ -697,3 +700,11 @@ This bug is fixed in `v9.0`. So we suggest that do not use `v8.0`.
9. Add `CMakeLists.txt` for cmake user(including `Visual Studio`). 9. Add `CMakeLists.txt` for cmake user(including `Visual Studio`).
10. New ghost type register process. 10. New ghost type register process.
### __version 11.1 release__
1. Bug fix: debugger in v11.0 is malfunctional.
2. Bug fix: symbol_finder does not check definition in foreach/forindex loop.
3. Change extension syntax `import.xx.xx` to `use xx.xx`.

View File

@ -1,5 +1,7 @@
# __开发历史记录__ # __开发历史记录__
![buringship](./pic/burningship.png)
## __目录__ ## __目录__
* [__语法分析__](#语法分析) * [__语法分析__](#语法分析)
@ -22,6 +24,7 @@
* [__发行日志__](#发行日志) * [__发行日志__](#发行日志)
* [v8.0](#version-80-release) * [v8.0](#version-80-release)
* [v11.0](#version-110-release) * [v11.0](#version-110-release)
* [v11.1](#version-111-release)
## __语法分析__ ## __语法分析__
@ -630,3 +633,11 @@ in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
9. 添加`CMakeLists.txt` (可在`Visual Studio`中使用)。 9. 添加`CMakeLists.txt` (可在`Visual Studio`中使用)。
10. 全新的自定义类型注册流程。 10. 全新的自定义类型注册流程。
### __version 11.1 release__
1. Bug 修复: 修复 v11.0 的 debugger 无法启动的问题。
2. Bug 修复: symbol_finder 不检查 foreach/forindex 中的迭代变量声明的问题。
3. 扩展语法 `import.xx.xx` 改为 `use xx.xx`

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,10 +33,11 @@
</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>
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v10.0-blue?style=flat-square&logo=github"></img></a> <a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github"></img></a>
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github"><br/></img></a> <a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github"><br/></img></a>
</div> </div>
<h2>&nbsp;Introduction | 介绍</h2> <h2>&nbsp;Introduction | 介绍</h2>
@ -79,13 +80,14 @@
在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。 在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。
</p> </p>
<p> <p>
The figure below is the feigenbaum-figure generated by ppm script written in nasal. The figure below is the feigenbaum-figure and burningship-figure generated by ppm script written in nasal.
</p> </p>
<p> <p>
下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 图形。 下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 和 burningship 图形。
</p> </p>
</text> </text>
<img src="/doc/pic/feigenbaum.png" width="900" height="550" style="margin-left: 15px;"><br /></img> <img src="/doc/pic/feigenbaum.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
<img src="/doc/pic/burningship.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
<h2>&nbsp;Example | 样例代码</h2> <h2>&nbsp;Example | 样例代码</h2>
<form method="get"> <form method="get">
<text style="margin-left: 15px;"> </text> <text style="margin-left: 15px;"> </text>

BIN
doc/pic/burningship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

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

@ -1,4 +1,4 @@
import.std.dylib; use std.dylib;
var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));

View File

@ -1,4 +1,4 @@
import.std.dylib; use std.dylib;
var ( var (
kbhit, kbhit,

View File

@ -1,4 +1,4 @@
import.std.dylib; use std.dylib;
var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so")); var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so"));

View File

@ -1,4 +1,4 @@
import.std.dylib; use std.dylib;
var socket=func(){ var socket=func(){
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so")); var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));

View File

@ -71,7 +71,7 @@ var nas_bind(var* args, usize size, gc* ngc) {
server.sin_port = htons(args[2].num()); server.sin_port = htons(args[2].num());
return var::num(static_cast<double>(bind( return var::num(static_cast<double>(bind(
args[0].num(), args[0].num(),
(sockaddr*)&server, reinterpret_cast<sockaddr*>(&server),
sizeof(server) sizeof(server)
))); )));
} }
@ -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)
))); )));
} }
@ -110,11 +110,19 @@ var nas_accept(var* args, usize size, gc* ngc) {
sockaddr_in client; sockaddr_in client;
int socklen = sizeof(sockaddr_in); int socklen = sizeof(sockaddr_in);
#ifdef _WIN32 #ifdef _WIN32
int client_sd = accept(args[0].num(), (sockaddr*)&client, &socklen); int client_sd = accept(
args[0].num(),
reinterpret_cast<sockaddr*>(&client),
&socklen
);
#else #else
int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen); int client_sd = accept(
args[0].num(),
reinterpret_cast<sockaddr*>(&client),
reinterpret_cast<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));
@ -159,7 +167,7 @@ var nas_sendto(var* args, usize size, gc* ngc) {
args[3].str().c_str(), args[3].str().c_str(),
args[3].str().length(), args[3].str().length(),
args[4].num(), args[4].num(),
(sockaddr*)&addr, reinterpret_cast<sockaddr*>(&addr),
sizeof(sockaddr_in) sizeof(sockaddr_in)
))); )));
} }
@ -205,7 +213,7 @@ var nas_recvfrom(var* args, usize size, gc* ngc) {
buf, buf,
args[1].num(), args[1].num(),
args[2].num(), args[2].num(),
(sockaddr*)&addr, reinterpret_cast<sockaddr*>(&addr),
&socklen &socklen
); );
#else #else
@ -214,8 +222,8 @@ var nas_recvfrom(var* args, usize size, gc* ngc) {
buf, buf,
args[1].num(), args[1].num(),
args[2].num(), args[2].num(),
(sockaddr*)&addr, reinterpret_cast<sockaddr*>(&addr),
(socklen_t*)&socklen reinterpret_cast<socklen_t*>(&socklen)
); );
#endif #endif
hash["size"] = var::num(static_cast<double>(recvsize)); hash["size"] = var::num(static_cast<double>(recvsize));

View File

@ -4,6 +4,20 @@
namespace nasal { namespace nasal {
bool ast_dumper::visit_use_stmt(use_stmt* node) {
dump_indent();
std::cout << "use" << format_location(node->get_location());
push_indent();
for(auto i : node->get_path()) {
if (i==node->get_path().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_null_expr(null_expr* node) { bool ast_dumper::visit_null_expr(null_expr* node) {
dump_indent(); dump_indent();
std::cout << "null" << format_location(node->get_location()); std::cout << "null" << format_location(node->get_location());

View File

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <vector>
namespace nasal { namespace nasal {
@ -41,6 +42,7 @@ private:
} }
public: public:
bool visit_use_stmt(use_stmt*) override;
bool visit_null_expr(null_expr*) override; bool visit_null_expr(null_expr*) override;
bool visit_nil_expr(nil_expr*) override; bool visit_nil_expr(nil_expr*) override;
bool visit_number_literal(number_literal*) override; bool visit_number_literal(number_literal*) override;

View File

@ -7,6 +7,13 @@ bool ast_visitor::visit_expr(expr* node) {
return true; return true;
} }
bool ast_visitor::visit_use_stmt(use_stmt* node) {
for(auto i : node->get_path()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_call(call* node) { bool ast_visitor::visit_call(call* node) {
node->accept(this); node->accept(this);
return true; return true;

View File

@ -7,6 +7,7 @@ namespace nasal {
class ast_visitor { class ast_visitor {
public: public:
virtual bool visit_expr(expr*); virtual bool visit_expr(expr*);
virtual bool visit_use_stmt(use_stmt*);
virtual bool visit_call(call*); virtual bool visit_call(call*);
virtual bool visit_null_expr(null_expr*); virtual bool visit_null_expr(null_expr*);
virtual bool visit_nil_expr(nil_expr*); virtual bool visit_nil_expr(nil_expr*);

View File

@ -45,7 +45,7 @@ var builtin_cocreate(context* ctx, gc* ngc) {
coroutine.ctx.top++; coroutine.ctx.top++;
// store old localr on stack // store old localr on stack
coroutine.ctx.top[0] = var::addr((var*)nullptr); coroutine.ctx.top[0] = var::addr(nullptr);
coroutine.ctx.top++; coroutine.ctx.top++;
// store old pc on stack // store old pc on stack

View File

@ -53,9 +53,9 @@ var builtin_dlopen(context* ctx, gc* ngc) {
// get "get" function, to get the register table // get "get" function, to get the register table
#ifdef _WIN32 #ifdef _WIN32
void* register_table_get_function = (void*)GetProcAddress( void* register_table_get_function = reinterpret_cast<void*>(GetProcAddress(
static_cast<HMODULE>(library_object.ghost().pointer), "get" static_cast<HMODULE>(library_object.ghost().pointer), "get"
); ));
#else #else
void* register_table_get_function = dlsym( void* register_table_get_function = dlsym(
library_object.ghost().pointer, "get" library_object.ghost().pointer, "get"

View File

@ -5,9 +5,6 @@ namespace nasal {
const auto file_type_name = "file"; const auto file_type_name = "file";
void filehandle_destructor(void* ptr) { void filehandle_destructor(void* ptr) {
if (static_cast<FILE*>(ptr)==stdin) {
return;
}
fclose(static_cast<FILE*>(ptr)); fclose(static_cast<FILE*>(ptr));
} }
@ -207,6 +204,25 @@ var builtin_eof(context* ctx, gc* ngc) {
)); ));
} }
var builtin_stdin(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_obj);
file_descriptor.ghost().set(file_type_name, nullptr, stdin);
return file_descriptor;
}
var builtin_stdout(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_obj);
file_descriptor.ghost().set(file_type_name, nullptr, stdout);
return file_descriptor;
}
var builtin_stderr(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_obj);
file_descriptor.ghost().set(file_type_name, nullptr, stderr);
return file_descriptor;
}
nasal_builtin_table io_lib_native[] = { nasal_builtin_table io_lib_native[] = {
{"__readfile", builtin_readfile}, {"__readfile", builtin_readfile},
{"__fout", builtin_fout}, {"__fout", builtin_fout},
@ -220,6 +236,9 @@ nasal_builtin_table io_lib_native[] = {
{"__readln", builtin_readln}, {"__readln", builtin_readln},
{"__stat", builtin_stat}, {"__stat", builtin_stat},
{"__eof", builtin_eof}, {"__eof", builtin_eof},
{"__stdin", builtin_stdin},
{"__stdout", builtin_stdout},
{"__stderr", builtin_stderr},
{nullptr, nullptr} {nullptr, nullptr}
}; };

View File

@ -32,6 +32,9 @@ var builtin_tell(context*, gc*);
var builtin_readln(context*, gc*); var builtin_readln(context*, gc*);
var builtin_stat(context*, gc*); var builtin_stat(context*, gc*);
var builtin_eof(context*, gc*); var builtin_eof(context*, gc*);
var builtin_stdin(context*, gc*);
var builtin_stdout(context*, gc*);
var builtin_stderr(context*, gc*);
extern nasal_builtin_table io_lib_native[]; extern nasal_builtin_table io_lib_native[];

View File

@ -7,6 +7,16 @@ void expr::accept(ast_visitor* visitor) {
visitor->visit_expr(this); visitor->visit_expr(this);
} }
use_stmt::~use_stmt() {
for(auto i : path) {
delete i;
}
}
void use_stmt::accept(ast_visitor* visitor) {
visitor->visit_use_stmt(this);
}
void call::accept(ast_visitor* visitor) { void call::accept(ast_visitor* visitor) {
visitor->visit_call(this); visitor->visit_call(this);
} }

View File

@ -10,6 +10,7 @@ namespace nasal {
enum class expr_type:u32 { enum class expr_type:u32 {
ast_null = 0, // null node ast_null = 0, // null node
ast_use, // use statement
ast_block, // code block ast_block, // code block
ast_nil, // nil keyword ast_nil, // nil keyword
ast_num, // number, basic value type ast_num, // number, basic value type
@ -46,6 +47,7 @@ enum class expr_type:u32 {
}; };
class ast_visitor; class ast_visitor;
class identifier;
class hash_pair; class hash_pair;
class parameter; class parameter;
class slice_vector; class slice_vector;
@ -77,6 +79,19 @@ public:
virtual void accept(ast_visitor*); virtual void accept(ast_visitor*);
}; };
class use_stmt: public expr {
private:
std::vector<identifier*> path;
public:
use_stmt(const span& location):
expr(location, expr_type::ast_use) {}
~use_stmt() override;
void accept(ast_visitor*) override;
void add_path(identifier* node) {path.push_back(node);}
const auto& get_path() const {return path;}
};
class call: public expr { class call: public expr {
public: public:
call(const span& location, expr_type node_type): call(const span& location, expr_type node_type):
@ -121,7 +136,7 @@ public:
string_literal(const span& location, const std::string& str): string_literal(const span& location, const std::string& str):
expr(location, expr_type::ast_str), content(str) {} expr(location, expr_type::ast_str), content(str) {}
~string_literal() override = default; ~string_literal() override = default;
const std::string get_content() const {return content;} const std::string& get_content() const {return content;}
void accept(ast_visitor*) override; void accept(ast_visitor*) override;
}; };

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;
} }
@ -309,8 +309,8 @@ var builtin_substr(context* ctx, gc* ngc) {
if (len.type!=vm_num || len.num()<0) { if (len.type!=vm_num || len.num()<0) {
return nas_err("substr", "\"length\" should be number >= 0"); return nas_err("substr", "\"length\" should be number >= 0");
} }
usize begin = (usize)beg.num(); auto begin = static_cast<usize>(beg.num());
usize length = (usize)len.num(); auto length = static_cast<usize>(len.num());
if (begin>=str.str().length()) { if (begin>=str.str().length()) {
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin)); return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
} }
@ -397,7 +397,7 @@ var builtin_chr(context* ctx, gc* ngc) {
}; };
auto num = static_cast<i32>(ctx->localr[1].num()); auto num = static_cast<i32>(ctx->localr[1].num());
if (0<=num && num<128) { if (0<=num && num<128) {
return ngc->newstr((char)num); return ngc->newstr(static_cast<char>(num));
} else if (128<=num && num<256) { } else if (128<=num && num<256) {
return ngc->newstr(extend[num-128]); return ngc->newstr(extend[num-128]);
} }

View File

@ -58,7 +58,7 @@ void codegen::check_id_exist(identifier* node) {
); );
} }
void codegen::regist_num(const f64 num) { void codegen::regist_number(const f64 num) {
if (const_number_map.count(num)) { if (const_number_map.count(num)) {
return; return;
} }
@ -67,7 +67,7 @@ void codegen::regist_num(const f64 num) {
const_number_table.push_back(num); const_number_table.push_back(num);
} }
void codegen::regist_str(const std::string& str) { void codegen::regist_string(const std::string& str) {
if (const_string_map.count(str)) { if (const_string_map.count(str)) {
return; return;
} }
@ -94,11 +94,11 @@ void codegen::find_symbol(code_block* node) {
scope.insert(i.name); scope.insert(i.name);
} }
// add symbol for codegen symbol check // add symbol for codegen symbol check
add_symbol(i.name); regist_symbol(i.name);
} }
} }
void codegen::add_symbol(const std::string& name) { void codegen::regist_symbol(const std::string& name) {
if (local.empty()) { if (local.empty()) {
if (global.count(name)) { if (global.count(name)) {
return; return;
@ -150,25 +150,25 @@ void codegen::emit(u8 operation_code, u32 immediate_num, const span& location) {
}); });
} }
void codegen::num_gen(number_literal* node) { void codegen::number_gen(number_literal* node) {
f64 num = node->get_number(); f64 num = node->get_number();
regist_num(num); regist_number(num);
emit(op_pnum, const_number_map.at(num), node->get_location()); emit(op_pnum, const_number_map.at(num), node->get_location());
} }
void codegen::str_gen(string_literal* node) { void codegen::string_gen(string_literal* node) {
const auto& str = node->get_content(); const auto& str = node->get_content();
regist_str(str); regist_string(str);
emit(op_pstr, const_string_map.at(str), node->get_location()); emit(op_pstr, const_string_map.at(str), node->get_location());
} }
void codegen::bool_gen(bool_literal* node) { void codegen::bool_gen(bool_literal* node) {
f64 num = node->get_flag()? 1:0; f64 num = node->get_flag()? 1:0;
regist_num(num); regist_number(num);
emit(op_pnum, const_number_map.at(num), node->get_location()); emit(op_pnum, const_number_map.at(num), node->get_location());
} }
void codegen::vec_gen(vector_expr* node) { void codegen::vector_gen(vector_expr* node) {
for(auto child : node->get_elements()) { for(auto child : node->get_elements()) {
calc_gen(child); calc_gen(child);
} }
@ -180,7 +180,7 @@ void codegen::hash_gen(hash_expr* node) {
for(auto child : node->get_members()) { for(auto child : node->get_members()) {
calc_gen(child->get_value()); calc_gen(child->get_value());
const auto& field_name = child->get_name(); const auto& field_name = child->get_name();
regist_str(field_name); regist_string(field_name);
emit(op_happ, const_string_map.at(field_name), child->get_location()); emit(op_happ, const_string_map.at(field_name), child->get_location());
} }
} }
@ -243,7 +243,7 @@ void codegen::func_gen(function* node) {
tmp->get_location() tmp->get_location()
); );
} }
regist_str(name); regist_string(name);
switch(tmp->get_parameter_type()) { switch(tmp->get_parameter_type()) {
case parameter::param_type::normal_parameter: case parameter::param_type::normal_parameter:
emit(op_para, const_string_map.at(name), tmp->get_location()); emit(op_para, const_string_map.at(name), tmp->get_location());
@ -256,7 +256,7 @@ void codegen::func_gen(function* node) {
emit(op_dyn, const_string_map.at(name), tmp->get_location()); emit(op_dyn, const_string_map.at(name), tmp->get_location());
break; break;
} }
add_symbol(name); regist_symbol(name);
} }
code[newf].num = code.size()+1; // entry code[newf].num = code.size()+1; // entry
@ -282,7 +282,7 @@ void codegen::func_gen(function* node) {
while(local_symbol_find(arg)>=0) { while(local_symbol_find(arg)>=0) {
arg = "0" + arg; arg = "0" + arg;
} }
add_symbol(arg); regist_symbol(arg);
// generate code block // generate code block
in_foreach_loop_level.push_back(0); in_foreach_loop_level.push_back(0);
@ -307,20 +307,23 @@ void codegen::func_gen(function* node) {
void codegen::call_gen(call_expr* node) { void codegen::call_gen(call_expr* node) {
calc_gen(node->get_first()); calc_gen(node->get_first());
if (code.back().op==op_callb) { if (code.size() && code.back().op==op_callb) {
return; return;
} }
for(auto i : node->get_calls()) { for(auto i : node->get_calls()) {
switch(i->get_type()) { switch(i->get_type()) {
case expr_type::ast_callh: call_hash_gen((call_hash*)i); break; case expr_type::ast_callh:
case expr_type::ast_callv: call_vector_gen((call_vector*)i); break; call_hash_gen(reinterpret_cast<call_hash*>(i)); break;
case expr_type::ast_callf: call_func_gen((call_function*)i); break; case expr_type::ast_callv:
call_vector_gen(reinterpret_cast<call_vector*>(i)); break;
case expr_type::ast_callf:
call_func_gen(reinterpret_cast<call_function*>(i)); break;
default: break; default: break;
} }
} }
} }
void codegen::call_id(identifier* node) { void codegen::call_identifier(identifier* node) {
const auto& name = node->get_name(); const auto& name = node->get_name();
if (native_function_mapper.count(name)) { if (native_function_mapper.count(name)) {
emit(op_callb, emit(op_callb,
@ -349,10 +352,12 @@ void codegen::call_id(identifier* node) {
return; return;
} }
die("undefined symbol \"" + name + "\"", node->get_location()); die("undefined symbol \"" + name + "\"", node->get_location());
// generation failed, put a push nil operand here to fill the space
emit(op_pnil, index, node->get_location());
} }
void codegen::call_hash_gen(call_hash* node) { void codegen::call_hash_gen(call_hash* node) {
regist_str(node->get_field()); regist_string(node->get_field());
emit(op_callh, const_string_map.at(node->get_field()), node->get_location()); emit(op_callh, const_string_map.at(node->get_field()), node->get_location());
} }
@ -383,9 +388,10 @@ void codegen::call_func_gen(call_function* node) {
node->get_argument()[0]->get_type()==expr_type::ast_pair) { node->get_argument()[0]->get_type()==expr_type::ast_pair) {
emit(op_newh, 0, node->get_location()); emit(op_newh, 0, node->get_location());
for(auto child : node->get_argument()) { for(auto child : node->get_argument()) {
calc_gen(((hash_pair*)child)->get_value()); auto pair_node = reinterpret_cast<hash_pair*>(child);
const auto& field_name = ((hash_pair*)child)->get_name(); calc_gen(pair_node->get_value());
regist_str(field_name); const auto& field_name = pair_node->get_name();
regist_string(field_name);
emit(op_happ, const_string_map.at(field_name), child->get_location()); emit(op_happ, const_string_map.at(field_name), child->get_location());
} }
emit(op_callfh, 0, node->get_location()); emit(op_callfh, 0, node->get_location());
@ -413,7 +419,7 @@ void codegen::mcall(expr* node) {
} }
// generate symbol call if node is just ast_id and return // generate symbol call if node is just ast_id and return
if (node->get_type()==expr_type::ast_id) { if (node->get_type()==expr_type::ast_id) {
mcall_id((identifier*)node); mcall_identifier(reinterpret_cast<identifier*>(node));
return; return;
} }
// generate call expression until the last sub-node // generate call expression until the last sub-node
@ -422,17 +428,22 @@ void codegen::mcall(expr* node) {
for(usize i = 0; i<call_node->get_calls().size()-1; ++i) { for(usize i = 0; i<call_node->get_calls().size()-1; ++i) {
auto tmp = call_node->get_calls()[i]; auto tmp = call_node->get_calls()[i];
switch(tmp->get_type()) { switch(tmp->get_type()) {
case expr_type::ast_callh: call_hash_gen((call_hash*)tmp); break; case expr_type::ast_callh:
case expr_type::ast_callv: call_vector_gen((call_vector*)tmp); break; call_hash_gen(reinterpret_cast<call_hash*>(tmp)); break;
case expr_type::ast_callf: call_func_gen((call_function*)tmp); break; case expr_type::ast_callv:
call_vector_gen(reinterpret_cast<call_vector*>(tmp)); break;
case expr_type::ast_callf:
call_func_gen(reinterpret_cast<call_function*>(tmp)); break;
default: break; default: break;
} }
} }
// the last sub-node will be used to generate memory call expression // the last sub-node will be used to generate memory call expression
auto tmp = call_node->get_calls().back(); auto tmp = call_node->get_calls().back();
switch(tmp->get_type()) { switch(tmp->get_type()) {
case expr_type::ast_callh: mcall_hash((call_hash*)tmp); break; case expr_type::ast_callh:
case expr_type::ast_callv: mcall_vec((call_vector*)tmp); break; mcall_hash(reinterpret_cast<call_hash*>(tmp)); break;
case expr_type::ast_callv:
mcall_vec(reinterpret_cast<call_vector*>(tmp)); break;
case expr_type::ast_callf: case expr_type::ast_callf:
die("bad left-value: function call", tmp->get_location()); break; die("bad left-value: function call", tmp->get_location()); break;
default: default:
@ -440,7 +451,7 @@ void codegen::mcall(expr* node) {
} }
} }
void codegen::mcall_id(identifier* node) { void codegen::mcall_identifier(identifier* node) {
const auto& name = node->get_name(); const auto& name = node->get_name();
if (native_function_mapper.count(name)) { if (native_function_mapper.count(name)) {
die("cannot modify native function", node->get_location()); die("cannot modify native function", node->get_location());
@ -478,7 +489,7 @@ void codegen::mcall_vec(call_vector* node) {
} }
void codegen::mcall_hash(call_hash* node) { void codegen::mcall_hash(call_hash* node) {
regist_str(node->get_field()); regist_string(node->get_field());
emit(op_mcallh, const_string_map.at(node->get_field()), node->get_location()); emit(op_mcallh, const_string_map.at(node->get_field()), node->get_location());
} }
@ -502,14 +513,20 @@ void codegen::multi_def(definition_expr* node) {
// (var a,b,c) = (c,b,a); // (var a,b,c) = (c,b,a);
if (node->get_tuple()) { if (node->get_tuple()) {
auto& vals = node->get_tuple()->get_elements(); auto& vals = node->get_tuple()->get_elements();
if (identifiers.size()<vals.size()) { if (identifiers.size()>vals.size()) {
die("lack values in multi-definition", die("lack values in multi-definition, expect " +
std::to_string(identifiers.size()) + " but get " +
std::to_string(vals.size()),
node->get_tuple()->get_location() node->get_tuple()->get_location()
); );
} else if (identifiers.size()>vals.size()) { return;
die("too many values in multi-definition", } else if (identifiers.size()<vals.size()) {
die("too many values in multi-definition, expect " +
std::to_string(identifiers.size()) + " but get " +
std::to_string(vals.size()),
node->get_tuple()->get_location() node->get_tuple()->get_location()
); );
return;
} }
for(usize i = 0; i<size; ++i) { for(usize i = 0; i<size; ++i) {
calc_gen(vals[i]); calc_gen(vals[i]);
@ -532,7 +549,7 @@ void codegen::multi_def(definition_expr* node) {
emit(op_pop, 0, node->get_location()); emit(op_pop, 0, node->get_location());
} }
void codegen::def_gen(definition_expr* node) { void codegen::definition_gen(definition_expr* node) {
if (node->get_variable_name() && node->get_tuple()) { if (node->get_variable_name() && node->get_tuple()) {
die("cannot accept too many values", node->get_value()->get_location()); die("cannot accept too many values", node->get_value()->get_location());
} }
@ -554,8 +571,9 @@ void codegen::assignment_expression(assignment_expr* node) {
if (node->get_right()->get_type()!=expr_type::ast_num) { if (node->get_right()->get_type()!=expr_type::ast_num) {
emit(op_addeq, 0, node->get_location()); emit(op_addeq, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_addeqc, const_number_map[num], node->get_location()); emit(op_addeqc, const_number_map[num], node->get_location());
} }
break; break;
@ -567,8 +585,9 @@ void codegen::assignment_expression(assignment_expr* node) {
if (node->get_right()->get_type()!=expr_type::ast_num) { if (node->get_right()->get_type()!=expr_type::ast_num) {
emit(op_subeq, 0, node->get_location()); emit(op_subeq, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_subeqc, const_number_map[num], node->get_location()); emit(op_subeqc, const_number_map[num], node->get_location());
} }
break; break;
@ -580,8 +599,9 @@ void codegen::assignment_expression(assignment_expr* node) {
if (node->get_right()->get_type()!=expr_type::ast_num) { if (node->get_right()->get_type()!=expr_type::ast_num) {
emit(op_muleq, 0, node->get_location()); emit(op_muleq, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_muleqc, const_number_map[num], node->get_location()); emit(op_muleqc, const_number_map[num], node->get_location());
} }
break; break;
@ -593,8 +613,9 @@ void codegen::assignment_expression(assignment_expr* node) {
if (node->get_right()->get_type()!=expr_type::ast_num) { if (node->get_right()->get_type()!=expr_type::ast_num) {
emit(op_diveq, 0, node->get_location()); emit(op_diveq, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_diveqc, const_number_map[num], node->get_location()); emit(op_diveqc, const_number_map[num], node->get_location());
} }
break; break;
@ -606,8 +627,9 @@ void codegen::assignment_expression(assignment_expr* node) {
if (node->get_right()->get_type()!=expr_type::ast_str) { if (node->get_right()->get_type()!=expr_type::ast_str) {
emit(op_lnkeq, 0, node->get_location()); emit(op_lnkeq, 0, node->get_location());
} else { } else {
const auto& str = ((string_literal*)node->get_right())->get_content(); const auto& str = reinterpret_cast<string_literal*>(
regist_str(str); node->get_right())->get_content();
regist_string(str);
emit(op_lnkeqc, const_string_map[str], node->get_location()); emit(op_lnkeqc, const_string_map[str], node->get_location());
} }
break; break;
@ -647,7 +669,7 @@ void codegen::gen_assignment_equal_statement(assignment_expr* node) {
// generate symbol load // generate symbol load
calc_gen(node->get_right()); calc_gen(node->get_right());
// get memory space of left identifier // get memory space of left identifier
mcall_id((identifier*)node->get_left()); mcall_identifier(reinterpret_cast<identifier*>(node->get_left()));
// check memory get operand type and replace it with load operand // check memory get operand type and replace it with load operand
switch(code.back().op) { switch(code.back().op) {
case op_mcallg: code.back().op = op_loadg; break; case op_mcallg: code.back().op = op_loadg; break;
@ -697,21 +719,40 @@ void codegen::assignment_statement(assignment_expr* node) {
} }
void codegen::multi_assign_gen(multi_assign* node) { void codegen::multi_assign_gen(multi_assign* node) {
if (node->get_value()->get_type()==expr_type::ast_tuple && auto tuple_node = node->get_tuple();
node->get_tuple()->get_elements().size()<((tuple_expr*)node->get_value())->get_elements().size()) { auto value_node = node->get_value();
die("lack values in multi-assignment", node->get_value()->get_location()); if (value_node->get_type()==expr_type::ast_tuple) {
} else if (node->get_value()->get_type()==expr_type::ast_tuple && auto tuple_size = tuple_node->get_elements().size();
node->get_tuple()->get_elements().size()>((tuple_expr*)node->get_value())->get_elements().size()) { auto value_size = reinterpret_cast<tuple_expr*>(value_node)
die("too many values in multi-assignment", node->get_value()->get_location()); ->get_elements().size();
if (tuple_size>value_size) {
die(
"lack value(s) in multi-assignment, expect " +
std::to_string(tuple_size) + " but get " +
std::to_string(value_size),
value_node->get_location()
);
return;
} else if (tuple_size<value_size) {
die(
"too many values in multi-assignment, expect " +
std::to_string(tuple_size) + " but get " +
std::to_string(value_size),
value_node->get_location()
);
return;
}
} }
i32 size = node->get_tuple()->get_elements().size(); i32 size = tuple_node->get_elements().size();
// generate multiple assignment: (a, b, c) = (1, 2, 3); // generate multiple assignment: (a, b, c) = (1, 2, 3);
if (node->get_value()->get_type()==expr_type::ast_tuple) { if (value_node->get_type()==expr_type::ast_tuple) {
const auto& value_tuple = reinterpret_cast<tuple_expr*>(value_node)
->get_elements();
for(i32 i = size-1; i>=0; --i) { for(i32 i = size-1; i>=0; --i) {
calc_gen(((tuple_expr*)node->get_value())->get_elements()[i]); calc_gen(value_tuple[i]);
} }
auto& tuple = node->get_tuple()->get_elements(); auto& tuple = tuple_node->get_elements();
for(i32 i = 0; i<size; ++i) { for(i32 i = 0; i<size; ++i) {
mcall(tuple[i]); mcall(tuple[i]);
// use load operands to avoid meq's pop operand // use load operands to avoid meq's pop operand
@ -722,10 +763,10 @@ void codegen::multi_assign_gen(multi_assign* node) {
} }
// generate multiple assignment: (a, b, c) = [1, 2, 3]; // generate multiple assignment: (a, b, c) = [1, 2, 3];
calc_gen(node->get_value()); calc_gen(value_node);
auto& tuple = node->get_tuple()->get_elements(); auto& tuple = tuple_node->get_elements();
for(i32 i = 0; i<size; ++i) { for(i32 i = 0; i<size; ++i) {
emit(op_callvi, i, node->get_value()->get_location()); emit(op_callvi, i, value_node->get_location());
mcall(tuple[i]); mcall(tuple[i]);
// use load operands to avoid meq's pop operand // use load operands to avoid meq's pop operand
// and this operation changes local and global value directly // and this operation changes local and global value directly
@ -775,9 +816,12 @@ void codegen::loop_gen(expr* node) {
break_ptr.push_front({}); break_ptr.push_front({});
switch(node->get_type()) { switch(node->get_type()) {
case expr_type::ast_while: while_gen((while_expr*)node); break; case expr_type::ast_while:
case expr_type::ast_for: for_gen((for_expr*)node); break; while_gen(reinterpret_cast<while_expr*>(node)); break;
case expr_type::ast_forei: forei_gen((forei_expr*)node); break; case expr_type::ast_for:
for_gen(reinterpret_cast<for_expr*>(node)); break;
case expr_type::ast_forei:
forei_gen(reinterpret_cast<forei_expr*>(node)); break;
default: break; default: break;
} }
} }
@ -809,7 +853,7 @@ void codegen::for_gen(for_expr* node) {
statement_generation(node->get_initial()); statement_generation(node->get_initial());
usize jmp_place = code.size(); usize jmp_place = code.size();
if (node->get_condition()->get_type()==expr_type::ast_null) { if (node->get_condition()->get_type()==expr_type::ast_null) {
regist_num(1); regist_number(1);
emit(op_pnum, const_number_map.at(1), node->get_condition()->get_location()); emit(op_pnum, const_number_map.at(1), node->get_condition()->get_location());
} else { } else {
calc_gen(node->get_condition()); calc_gen(node->get_condition());
@ -873,11 +917,11 @@ void codegen::statement_generation(expr* node) {
switch(node->get_type()) { switch(node->get_type()) {
case expr_type::ast_null: break; case expr_type::ast_null: break;
case expr_type::ast_def: case expr_type::ast_def:
def_gen((definition_expr*)node); break; definition_gen(reinterpret_cast<definition_expr*>(node)); break;
case expr_type::ast_multi_assign: case expr_type::ast_multi_assign:
multi_assign_gen((multi_assign*)node); break; multi_assign_gen(reinterpret_cast<multi_assign*>(node)); break;
case expr_type::ast_assign: case expr_type::ast_assign:
assignment_statement((assignment_expr*)node); break; assignment_statement(reinterpret_cast<assignment_expr*>(node)); break;
case expr_type::ast_nil: case expr_type::ast_nil:
case expr_type::ast_num: case expr_type::ast_num:
case expr_type::ast_str: case expr_type::ast_str:
@ -936,7 +980,7 @@ void codegen::and_gen(binary_operator* node) {
void codegen::unary_gen(unary_operator* node) { void codegen::unary_gen(unary_operator* node) {
// generate optimized result // generate optimized result
if (node->get_optimized_number()) { if (node->get_optimized_number()) {
num_gen(node->get_optimized_number()); number_gen(node->get_optimized_number());
return; return;
} }
@ -954,11 +998,11 @@ void codegen::unary_gen(unary_operator* node) {
void codegen::binary_gen(binary_operator* node) { void codegen::binary_gen(binary_operator* node) {
// generate optimized result // generate optimized result
if (node->get_optimized_number()) { if (node->get_optimized_number()) {
num_gen(node->get_optimized_number()); number_gen(node->get_optimized_number());
return; return;
} }
if (node->get_optimized_string()) { if (node->get_optimized_string()) {
str_gen(node->get_optimized_string()); string_gen(node->get_optimized_string());
return; return;
} }
@ -1002,8 +1046,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_add, 0, node->get_location()); emit(op_add, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_addc, const_number_map.at(num), node->get_location()); emit(op_addc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1013,8 +1058,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_sub, 0, node->get_location()); emit(op_sub, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_subc, const_number_map.at(num), node->get_location()); emit(op_subc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1024,8 +1070,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_mul, 0, node->get_location()); emit(op_mul, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_mulc, const_number_map.at(num), node->get_location()); emit(op_mulc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1035,8 +1082,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_div, 0, node->get_location()); emit(op_div, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_divc, const_number_map.at(num), node->get_location()); emit(op_divc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1046,8 +1094,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_lnk, 0, node->get_location()); emit(op_lnk, 0, node->get_location());
} else { } else {
const auto& str = ((string_literal*)node->get_right())->get_content(); const auto& str = reinterpret_cast<string_literal*>(
regist_str(str); node->get_right())->get_content();
regist_string(str);
emit(op_lnkc, const_string_map.at(str), node->get_location()); emit(op_lnkc, const_string_map.at(str), node->get_location());
} }
break; break;
@ -1057,8 +1106,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_less, 0, node->get_location()); emit(op_less, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_lessc, const_number_map.at(num), node->get_location()); emit(op_lessc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1068,8 +1118,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_leq, 0, node->get_location()); emit(op_leq, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_leqc, const_number_map.at(num), node->get_location()); emit(op_leqc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1079,8 +1130,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_grt, 0, node->get_location()); emit(op_grt, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_grtc, const_number_map.at(num), node->get_location()); emit(op_grtc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1090,8 +1142,9 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_right()); calc_gen(node->get_right());
emit(op_geq, 0, node->get_location()); emit(op_geq, 0, node->get_location());
} else { } else {
auto num = ((number_literal*)node->get_right())->get_number(); auto num = reinterpret_cast<number_literal*>(node->get_right())
regist_num(num); ->get_number();
regist_number(num);
emit(op_geqc, const_number_map.at(num), node->get_location()); emit(op_geqc, const_number_map.at(num), node->get_location());
} }
return; return;
@ -1116,33 +1169,38 @@ void codegen::calc_gen(expr* node) {
case expr_type::ast_nil: case expr_type::ast_nil:
emit(op_pnil, 0, node->get_location()); break; emit(op_pnil, 0, node->get_location()); break;
case expr_type::ast_num: case expr_type::ast_num:
num_gen((number_literal*)node); break; number_gen(reinterpret_cast<number_literal*>(node)); break;
case expr_type::ast_str: case expr_type::ast_str:
str_gen((string_literal*)node); break; string_gen(reinterpret_cast<string_literal*>(node)); break;
case expr_type::ast_id: case expr_type::ast_id:
call_id((identifier*)node); break; call_identifier(reinterpret_cast<identifier*>(node)); break;
case expr_type::ast_bool: case expr_type::ast_bool:
bool_gen((bool_literal*)node); break; bool_gen(reinterpret_cast<bool_literal*>(node)); break;
case expr_type::ast_vec: case expr_type::ast_vec:
vec_gen((vector_expr*)node); break; vector_gen(reinterpret_cast<vector_expr*>(node)); break;
case expr_type::ast_hash: case expr_type::ast_hash:
hash_gen((hash_expr*)node); break; hash_gen(reinterpret_cast<hash_expr*>(node)); break;
case expr_type::ast_func: case expr_type::ast_func:
func_gen((function*)node); break; func_gen(reinterpret_cast<function*>(node)); break;
case expr_type::ast_call: case expr_type::ast_call:
call_gen((call_expr*)node); break; call_gen(reinterpret_cast<call_expr*>(node)); break;
case expr_type::ast_assign: case expr_type::ast_assign:
assignment_expression((assignment_expr*)node); break; assignment_expression(
reinterpret_cast<assignment_expr*>(node)
);
break;
case expr_type::ast_ternary: case expr_type::ast_ternary:
trino_gen((ternary_operator*)node); break; trino_gen(reinterpret_cast<ternary_operator*>(node)); break;
case expr_type::ast_unary: case expr_type::ast_unary:
unary_gen((unary_operator*)node); break; unary_gen(reinterpret_cast<unary_operator*>(node)); break;
case expr_type::ast_binary: case expr_type::ast_binary:
binary_gen((binary_operator*)node); break; binary_gen(reinterpret_cast<binary_operator*>(node)); break;
case expr_type::ast_def: case expr_type::ast_def:
// definition in calculation only should be single def // definition in calculation only should be single def
single_def((definition_expr*)node); single_def(reinterpret_cast<definition_expr*>(node));
call_id(((definition_expr*)node)->get_variable_name()); call_identifier(
(reinterpret_cast<definition_expr*>(node))->get_variable_name()
);
break; break;
default: break; default: break;
} }
@ -1150,11 +1208,16 @@ void codegen::calc_gen(expr* node) {
void codegen::repl_mode_info_output_gen(expr* node) { void codegen::repl_mode_info_output_gen(expr* node) {
switch(node->get_type()) { switch(node->get_type()) {
case expr_type::ast_id: call_id((identifier*)node); break; case expr_type::ast_id:
case expr_type::ast_nil: emit(op_pnil, 0, node->get_location()); break; call_identifier(reinterpret_cast<identifier*>(node)); break;
case expr_type::ast_num: num_gen((number_literal*)node); break; case expr_type::ast_nil:
case expr_type::ast_str: str_gen((string_literal*)node); break; emit(op_pnil, 0, node->get_location()); break;
case expr_type::ast_bool: bool_gen((bool_literal*)node); break; case expr_type::ast_num:
number_gen(reinterpret_cast<number_literal*>(node)); break;
case expr_type::ast_str:
string_gen(reinterpret_cast<string_literal*>(node)); break;
case expr_type::ast_bool:
bool_gen(reinterpret_cast<bool_literal*>(node)); break;
default: return; default: return;
} }
// generate repl output operand // generate repl output operand
@ -1164,26 +1227,41 @@ void codegen::repl_mode_info_output_gen(expr* node) {
} }
void codegen::block_gen(code_block* node) { void codegen::block_gen(code_block* node) {
bool is_use_statement = true;
for(auto tmp : node->get_expressions()) { for(auto tmp : node->get_expressions()) {
if (tmp->get_type()!=expr_type::ast_use) {
is_use_statement = false;
}
switch(tmp->get_type()) { switch(tmp->get_type()) {
case expr_type::ast_use:
if (!local.empty()) {
die("module import is not allowed here.",
tmp->get_location()
);
} else if (!is_use_statement) {
die("module import should be used at the top of the file.",
tmp->get_location()
);
}
break;
case expr_type::ast_null: break; case expr_type::ast_null: break;
case expr_type::ast_id: case expr_type::ast_id:
if (need_repl_output) { if (need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp); repl_mode_info_output_gen(tmp);
} else { } else {
check_id_exist((identifier*)tmp); check_id_exist(reinterpret_cast<identifier*>(tmp));
} }
break; break;
case expr_type::ast_nil: case expr_type::ast_nil:
case expr_type::ast_num: case expr_type::ast_num:
case expr_type::ast_str: case expr_type::ast_str:
case expr_type::ast_bool: case expr_type::ast_bool:
if (need_repl_output) { if (need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp); repl_mode_info_output_gen(tmp);
} }
break; break;
case expr_type::ast_cond: case expr_type::ast_cond:
cond_gen((condition_expr*)tmp); break; cond_gen(reinterpret_cast<condition_expr*>(tmp)); break;
case expr_type::ast_continue: case expr_type::ast_continue:
continue_ptr.front().push_back(code.size()); continue_ptr.front().push_back(code.size());
emit(op_jmp, 0, tmp->get_location()); emit(op_jmp, 0, tmp->get_location());
@ -1208,7 +1286,7 @@ void codegen::block_gen(code_block* node) {
case expr_type::ast_multi_assign: case expr_type::ast_multi_assign:
statement_generation(tmp); break; statement_generation(tmp); break;
case expr_type::ast_ret: case expr_type::ast_ret:
ret_gen((return_expr*)tmp); break; ret_gen(reinterpret_cast<return_expr*>(tmp)); break;
default: break; default: break;
} }
} }
@ -1231,9 +1309,9 @@ const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
in_foreach_loop_level.push_back(0); in_foreach_loop_level.push_back(0);
// add special symbol globals, which is a hash stores all global variables // add special symbol globals, which is a hash stores all global variables
add_symbol("globals"); regist_symbol("globals");
// add special symbol arg here, which is used to store command line args // add special symbol arg here, which is used to store command line args
add_symbol("arg"); regist_symbol("arg");
// search global symbols first // search global symbols first
find_symbol(parse.tree()); find_symbol(parse.tree());

View File

@ -78,34 +78,34 @@ private:
err.err("code", loc, info); err.err("code", loc, info);
} }
void regist_num(const f64); void regist_number(const f64);
void regist_str(const std::string&); void regist_string(const std::string&);
void find_symbol(code_block*); void find_symbol(code_block*);
void add_symbol(const std::string&); void regist_symbol(const std::string&);
i32 local_symbol_find(const std::string&); i32 local_symbol_find(const std::string&);
i32 global_symbol_find(const std::string&); i32 global_symbol_find(const std::string&);
i32 upvalue_symbol_find(const std::string&); i32 upvalue_symbol_find(const std::string&);
void emit(u8, u32, const span&); void emit(u8, u32, const span&);
void num_gen(number_literal*); void number_gen(number_literal*);
void str_gen(string_literal*); void string_gen(string_literal*);
void bool_gen(bool_literal*); void bool_gen(bool_literal*);
void vec_gen(vector_expr*); void vector_gen(vector_expr*);
void hash_gen(hash_expr*); void hash_gen(hash_expr*);
void func_gen(function*); void func_gen(function*);
void call_gen(call_expr*); void call_gen(call_expr*);
void call_id(identifier*); void call_identifier(identifier*);
void call_hash_gen(call_hash*); void call_hash_gen(call_hash*);
void call_vector_gen(call_vector*); void call_vector_gen(call_vector*);
void call_func_gen(call_function*); void call_func_gen(call_function*);
void mcall(expr*); void mcall(expr*);
void mcall_id(identifier*); void mcall_identifier(identifier*);
void mcall_vec(call_vector*); void mcall_vec(call_vector*);
void mcall_hash(call_hash*); void mcall_hash(call_hash*);
void multi_def(definition_expr*); void multi_def(definition_expr*);
void single_def(definition_expr*); void single_def(definition_expr*);
void def_gen(definition_expr*); void definition_gen(definition_expr*);
void assignment_expression(assignment_expr*); void assignment_expression(assignment_expr*);
void gen_assignment_equal_statement(assignment_expr*); void gen_assignment_equal_statement(assignment_expr*);
void replace_left_assignment_with_load(const span&); void replace_left_assignment_with_load(const span&);

View File

@ -2,123 +2,122 @@
#include "symbol_finder.h" #include "symbol_finder.h"
#include <memory> #include <memory>
#include <unordered_set>
namespace nasal { namespace nasal {
linker::linker(): linker::linker(): show_path_flag(false), library_loaded(false), this_file("") {
show_path(false), lib_loaded(false), const auto seperator= is_windows()? ';':':';
this_file(""), lib_path("") { const auto PATH = std::string(getenv("PATH"));
char sep = is_windows()? ';':':'; usize last = 0, position = PATH.find(seperator, 0);
std::string PATH = getenv("PATH"); while(position!=std::string::npos) {
usize last = 0, pos = PATH.find(sep, 0); std::string dirpath = PATH.substr(last, position-last);
while(pos!=std::string::npos) {
std::string dirpath = PATH.substr(last, pos-last);
if (dirpath.length()) { if (dirpath.length()) {
envpath.push_back(dirpath); envpath.push_back(dirpath);
} }
last = pos+1; last = position+1;
pos = PATH.find(sep, last); position = PATH.find(seperator, last);
} }
if (last!=PATH.length()) { if (last!=PATH.length()) {
envpath.push_back(PATH.substr(last)); envpath.push_back(PATH.substr(last));
} }
} }
std::string linker::get_path(call_expr* node) { std::string linker::get_path(expr* node) {
if (node->get_calls()[0]->get_type()==expr_type::ast_callf) { if (node->get_type()==expr_type::ast_use) {
auto tmp = (call_function*)node->get_calls()[0]; auto file_relative_path = std::string("");
return ((string_literal*)tmp->get_argument()[0])->get_content(); const auto& path = reinterpret_cast<use_stmt*>(node)->get_path();
for(auto i : path) {
file_relative_path += i->get_name();
if (i!=path.back()) {
file_relative_path += (is_windows()? "\\":"/");
} }
auto fpath = std::string(".");
for(auto i : node->get_calls()) {
fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field();
} }
return fpath + ".nas"; return file_relative_path + ".nas";
}
auto call_node = reinterpret_cast<call_expr*>(node);
auto arguments = reinterpret_cast<call_function*>(call_node->get_calls()[0]);
auto content = reinterpret_cast<string_literal*>(arguments->get_argument()[0]);
return content->get_content();
} }
std::string linker::find_file( std::string linker::find_real_file_path(
const std::string& filename, const span& location) { const std::string& filename, const span& location) {
// first add file name itself into the file path // first add file name itself into the file path
std::vector<std::string> fpath = {filename}; std::vector<std::string> path_list = {filename};
// generate search path from environ path // generate search path from environ path
for(const auto& p : envpath) { for(const auto& p : envpath) {
fpath.push_back(p + (is_windows()? "\\":"/") + filename); path_list.push_back(p + (is_windows()? "\\":"/") + filename);
} }
// search file // search file
for(const auto& i : fpath) { for(const auto& path : path_list) {
if (access(i.c_str(), F_OK)!=-1) { if (access(path.c_str(), F_OK)!=-1) {
return i; return path;
} }
} }
// we will find lib.nas in nasal std directory // we will find lib.nas in nasal std directory
if (filename=="lib.nas") { if (filename=="lib.nas") {
return is_windows()? return is_windows()?
find_file("std\\lib.nas", location): find_real_file_path("std\\lib.nas", location):
find_file("std/lib.nas", location); find_real_file_path("std/lib.nas", location);
} }
if (!show_path) { if (!show_path_flag) {
err.err("link", err.err("link",
"in <" + location.file + ">: " + "in <" + location.file + ">: " +
"cannot find file <" + filename + ">, " + "cannot find file <" + filename + ">, " +
"use <-d> to get detail search path"); "use <-d> to get detail search path"
);
return ""; return "";
} }
std::string paths = ""; auto path_list_info = std::string("");
for(const auto& i : fpath) { for(const auto& path : path_list) {
paths += " -> " + i + "\n"; path_list_info += " -> " + path + "\n";
} }
err.err("link", err.err("link",
"in <" + location.file + ">: " + "in <" + location.file + ">: " +
"cannot find file <" + filename + "> in these paths:\n" + paths); "cannot find file <" + filename +
"> in these paths:\n" + path_list_info
);
return ""; return "";
} }
bool linker::import_check(expr* node) { bool linker::import_check(expr* node) {
/* if (node->get_type()==expr_type::ast_use) {
call
|_id:import
|_callh:std
|_callh:file
*/
if (node->get_type()!=expr_type::ast_call) {
return false;
}
auto tmp = (call_expr*)node;
if (tmp->get_first()->get_type()!=expr_type::ast_id) {
return false;
}
if (((identifier*)tmp->get_first())->get_name()!="import") {
return false;
}
if (!tmp->get_calls().size()) {
return false;
}
// import.xxx.xxx;
if (tmp->get_calls()[0]->get_type()==expr_type::ast_callh) {
for(auto i : tmp->get_calls()) {
if (i->get_type()!=expr_type::ast_callh) {
return false;
}
}
return true; return true;
} }
// import("xxx");
if (tmp->get_calls().size()!=1) {
return false;
}
/* /*
call call
|_id:import |_id:import
|_call_func |_call_func
|_string:'filename' |_string:'filename'
*/ */
if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) { if (node->get_type()!=expr_type::ast_call) {
return false; return false;
} }
auto func_call = (call_function*)tmp->get_calls()[0]; auto call_node = reinterpret_cast<call_expr*>(node);
auto first_expr = call_node->get_first();
if (first_expr->get_type()!=expr_type::ast_id) {
return false;
}
if (reinterpret_cast<identifier*>(first_expr)->get_name()!="import") {
return false;
}
if (!call_node->get_calls().size()) {
return false;
}
// import("xxx");
if (call_node->get_calls().size()!=1) {
return false;
}
auto maybe_func_call = call_node->get_calls()[0];
if (maybe_func_call->get_type()!=expr_type::ast_callf) {
return false;
}
auto func_call = reinterpret_cast<call_function*>(maybe_func_call);
if (func_call->get_argument().size()!=1) { if (func_call->get_argument().size()!=1) {
return false; return false;
} }
@ -128,31 +127,20 @@ bool linker::import_check(expr* node) {
return true; return true;
} }
bool linker::exist(const std::string& file) { bool linker::check_exist_or_record_file(const std::string& file) {
// avoid importing the same file // avoid importing the same file
for(const auto& fname : files) { for(const auto& name : imported_files) {
if (file==fname) { if (file==name) {
return true; return true;
} }
} }
files.push_back(file); imported_files.push_back(file);
return false; return false;
} }
u16 linker::find(const std::string& file) {
for(usize i = 0; i<files.size(); ++i) {
if (files[i]==file) {
return static_cast<u16>(i);
}
}
std::cerr << "unreachable: using this method incorrectly\n";
std::exit(-1);
return UINT16_MAX;
}
bool linker::check_self_import(const std::string& file) { bool linker::check_self_import(const std::string& file) {
for(const auto& i : module_load_stack) { for(const auto& name : module_load_stack) {
if (file==i) { if (file==name) {
return true; return true;
} }
} }
@ -176,27 +164,19 @@ void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
old_tree_root->get_expressions().clear(); old_tree_root->get_expressions().clear();
} }
code_block* linker::import_regular_file(call_expr* node) { code_block* linker::import_regular_file(
lexer lex; expr* node, std::unordered_set<std::string>& used_modules) {
parse par;
// get filename // get filename
auto filename = get_path(node); auto filename = get_path(node);
// clear this node
for(auto i : node->get_calls()) {
delete i;
}
node->get_calls().clear();
auto location = node->get_first()->get_location();
delete node->get_first();
node->set_first(new nil_expr(location));
// this will make node to call_expr(nil),
// will not be optimized when generating bytecodes
// avoid infinite loading loop // avoid infinite loading loop
filename = find_file(filename, node->get_location()); filename = find_real_file_path(filename, node->get_location());
if (!filename.length()) { // if get empty string(error) or this file is used before, do not parse
if (!filename.length() || used_modules.count(filename)) {
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, filename});
} }
// check self import, avoid infinite loading loop
if (check_self_import(filename)) { if (check_self_import(filename)) {
err.err("link", err.err("link",
"self-referenced module <" + filename + ">:\n" + "self-referenced module <" + filename + ">:\n" +
@ -204,58 +184,61 @@ code_block* linker::import_regular_file(call_expr* node) {
); );
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, filename});
} }
exist(filename); check_exist_or_record_file(filename);
module_load_stack.push_back(filename); module_load_stack.push_back(filename);
// start importing... // start importing...
if (lex.scan(filename).geterr()) { lexer nasal_lexer;
parse nasal_parser;
if (nasal_lexer.scan(filename).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">"); err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, filename});
} }
if (par.compile(lex).geterr()) { if (nasal_parser.compile(nasal_lexer).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">"); err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, filename});
} }
// swap result out
auto parse_result = par.swap(nullptr); auto parse_result = nasal_parser.swap(nullptr);
// check if parse result has 'import' // check if parse result has 'import'
auto result = load(parse_result, find(filename)); auto result = load(parse_result, filename);
module_load_stack.pop_back(); module_load_stack.pop_back();
return result; return result;
} }
code_block* linker::import_nasal_lib() { code_block* linker::import_nasal_lib() {
lexer lex; auto path = find_real_file_path(
parse par; "lib.nas", {0, 0, 0, 0, this_file}
auto filename = find_file("lib.nas", {0, 0, 0, 0, files[0]}); );
if (!filename.length()) { if (!path.length()) {
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, path});
} }
lib_path = filename;
// avoid infinite loading library // avoid infinite loading library
if (exist(filename)) { if (check_exist_or_record_file(path)) {
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, path});
} }
// start importing... // start importing...
if (lex.scan(filename).geterr()) { lexer nasal_lexer;
parse nasal_parser;
if (nasal_lexer.scan(path).geterr()) {
err.err("link", err.err("link",
"error occurred when analysing library <" + filename + ">" "error occurred when analysing library <" + path + ">"
); );
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, path});
} }
if (par.compile(lex).geterr()) { if (nasal_parser.compile(nasal_lexer).geterr()) {
err.err("link", err.err("link",
"error occurred when analysing library <" + filename + ">" "error occurred when analysing library <" + path + ">"
); );
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, path});
} }
// swap result out
auto parse_result = par.swap(nullptr); auto parse_result = nasal_parser.swap(nullptr);
// check if library has 'import' (in fact it should not) // check if library has 'import' (in fact it should not)
return load(parse_result, find(filename)); return load(parse_result, path);
} }
std::string linker::generate_module_name(const std::string& file_path) { std::string linker::generate_module_name(const std::string& file_path) {
@ -301,14 +284,11 @@ std::string linker::generate_module_name(const std::string& file_path) {
"get empty module name from <" + file_path + ">, " + "get empty module name from <" + file_path + ">, " +
"will not be easily accessed." "will not be easily accessed."
); );
return module_name;
} }
if (module_name.length() && '0' <= module_name[0] && module_name[0] <= '9') { if (std::isdigit(module_name[0]) ||
err.warn("link", module_name.find(".")!=std::string::npos ||
"get module <" + module_name + "> from <" + file_path + ">, " + module_name.find("-")!=std::string::npos) {
"will not be easily accessed."
);
}
if (module_name.length() && module_name.find(".")!=std::string::npos) {
err.warn("link", err.warn("link",
"get module <" + module_name + "> from <" + file_path + ">, " + "get module <" + module_name + "> from <" + file_path + ">, " +
"will not be easily accessed." "will not be easily accessed."
@ -353,32 +333,43 @@ definition_expr* linker::generate_module_definition(code_block* block) {
return def; return def;
} }
code_block* linker::load(code_block* program_root, u16 fileindex) { code_block* linker::load(code_block* program_root, const std::string& filename) {
auto tree = new code_block({0, 0, 0, 0, files[fileindex]}); auto tree = new code_block({0, 0, 0, 0, filename});
// load library, this ast will be linked with root directly // load library, this ast will be linked with root directly
// so no extra namespace is generated // so no extra namespace is generated
if (!lib_loaded) { if (!library_loaded) {
auto nasal_lib_code_block = import_nasal_lib(); auto nasal_lib_code_block = import_nasal_lib();
// insert nasal lib code to the back of tree // insert nasal lib code to the back of tree
link(tree, nasal_lib_code_block); link(tree, nasal_lib_code_block);
delete nasal_lib_code_block; delete nasal_lib_code_block;
lib_loaded = true; library_loaded = true;
} }
// load imported modules // load imported modules
for(auto& import_ast_node : program_root->get_expressions()) { std::unordered_set<std::string> used_modules = {};
if (!import_check(import_ast_node)) { for(auto& import_node : program_root->get_expressions()) {
if (!import_check(import_node)) {
break; break;
} }
auto module_code_block = import_regular_file((call_expr*)import_ast_node); // parse file and get ast
auto module_code_block = import_regular_file(import_node, used_modules);
auto replace_node = new null_expr(import_node->get_location());
// after importing the regular file as module, delete this node // after importing the regular file as module, delete this node
const auto loc = import_ast_node->get_location(); delete import_node;
delete import_ast_node;
// and replace the node with null_expr node // and replace the node with null_expr node
import_ast_node = new null_expr(loc); import_node = replace_node;
// avoid repeatedly importing the same module
const auto& module_path = module_code_block->get_location().file;
if (used_modules.count(module_path)) {
delete module_code_block;
continue;
}
// then we generate a function warping the code block, // then we generate a function warping the code block,
// and export the necessary global symbols in this code block // and export the necessary global symbols in this code block
// by generate a return statement, with a hashmap return value // by generate a return statement, with a hashmap return value
used_modules.insert(module_path);
tree->add_expression(generate_module_definition(module_code_block)); tree->add_expression(generate_module_definition(module_code_block));
} }
@ -389,15 +380,17 @@ code_block* linker::load(code_block* program_root, u16 fileindex) {
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; // switch for showing path when errors occur
show_path_flag = spath;
// initializing file map // initializing file map
this_file = self; this_file = self;
files = {self}; imported_files = {self};
module_load_stack = {self}; module_load_stack = {self};
// scan root and import files // scan root and import files
// then generate a new ast and return to import_ast // then generate a new ast and return to import_ast
// the main file's index is 0 auto new_tree_root = load(parse.tree(), self);
auto new_tree_root = load(parse.tree(), 0);
auto old_tree_root = parse.swap(new_tree_root); auto old_tree_root = parse.swap(new_tree_root);
delete old_tree_root; delete old_tree_root;
return err; return err;

View File

@ -18,43 +18,42 @@
#include "nasal_parse.h" #include "nasal_parse.h"
#include "symbol_finder.h" #include "symbol_finder.h"
#include <cstring>
#include <sstream>
#include <vector> #include <vector>
#include <unordered_set>
namespace nasal { namespace nasal {
class linker { class linker {
private: private:
bool show_path; bool show_path_flag;
bool lib_loaded; bool library_loaded;
std::string this_file; std::string this_file;
std::string lib_path;
error err; error err;
std::vector<std::string> files; std::vector<std::string> imported_files;
std::vector<std::string> module_load_stack; std::vector<std::string> module_load_stack;
std::vector<std::string> envpath; std::vector<std::string> envpath;
private: private:
bool import_check(expr*); bool import_check(expr*);
bool exist(const std::string&); bool check_exist_or_record_file(const std::string&);
u16 find(const std::string&);
bool check_self_import(const std::string&); bool check_self_import(const std::string&);
std::string generate_self_import_path(const std::string&); std::string generate_self_import_path(const std::string&);
void link(code_block*, code_block*); void link(code_block*, code_block*);
std::string get_path(call_expr*); std::string get_path(expr*);
std::string find_file(const std::string&, const span&); std::string find_real_file_path(const std::string&, const span&);
code_block* import_regular_file(call_expr*); code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
code_block* import_nasal_lib(); code_block* import_nasal_lib();
std::string generate_module_name(const std::string&); std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*); return_expr* generate_module_return(code_block*);
definition_expr* generate_module_definition(code_block*); definition_expr* generate_module_definition(code_block*);
code_block* load(code_block*, u16); code_block* load(code_block*, const std::string&);
public: public:
linker(); linker();
const error& link(parse&, const std::string&, bool); const error& link(parse&, const std::string&, bool);
const auto& get_file_list() const {return files;} const auto& get_file_list() const {return imported_files;}
const auto& get_this_file() const {return this_file;}
const auto& get_lib_path() const {return lib_path;}
}; };
} }

View File

@ -28,6 +28,7 @@ enum class tok:u32 {
id, // identifier id, // identifier
tktrue, // keyword true tktrue, // keyword true
tkfalse, // keyword false tkfalse, // keyword false
use, // keyword use
rfor, // loop keyword for rfor, // loop keyword for
forindex, // loop keyword forindex forindex, // loop keyword forindex
foreach, // loop keyword foreach foreach, // loop keyword foreach
@ -103,6 +104,7 @@ private:
std::vector<token> toks; std::vector<token> toks;
const std::unordered_map<std::string, tok> typetbl { const std::unordered_map<std::string, tok> typetbl {
{"use" ,tok::use },
{"true" ,tok::tktrue }, {"true" ,tok::tktrue },
{"false" ,tok::tkfalse }, {"false" ,tok::tkfalse },
{"for" ,tok::rfor }, {"for" ,tok::rfor },

View File

@ -132,9 +132,13 @@ bool parse::check_func_end(expr* node) {
if (type==expr_type::ast_func) { if (type==expr_type::ast_func) {
return true; return true;
} else if (type==expr_type::ast_def) { } else if (type==expr_type::ast_def) {
return check_func_end(((definition_expr*)node)->get_value()); return check_func_end(
reinterpret_cast<definition_expr*>(node)->get_value()
);
} else if (type==expr_type::ast_assign) { } else if (type==expr_type::ast_assign) {
return check_func_end(((assignment_expr*)node)->get_right()); return check_func_end(
reinterpret_cast<assignment_expr*>(node)->get_right()
);
} }
return false; return false;
} }
@ -197,6 +201,18 @@ void parse::update_location(expr* node) {
node->update_location(toks[ptr-1].loc); node->update_location(toks[ptr-1].loc);
} }
use_stmt* parse::use_stmt_gen() {
auto node = new use_stmt(toks[ptr].loc);
match(tok::use);
node->add_path(id());
while(lookahead(tok::dot)) {
match(tok::dot);
node->add_path(id());
}
update_location(node);
return node;
}
null_expr* parse::null() { null_expr* parse::null() {
return new null_expr(toks[ptr].loc); return new null_expr(toks[ptr].loc);
} }
@ -355,6 +371,7 @@ expr* parse::expression() {
die(thisspan, "must use return in functions"); die(thisspan, "must use return in functions");
} }
switch(type) { switch(type) {
case tok::use: return use_stmt_gen();
case tok::tknil: case tok::tknil:
case tok::num: case tok::num:
case tok::str: case tok::str:

View File

@ -24,6 +24,7 @@ private:
private: private:
const std::unordered_map<tok, std::string> tokname { const std::unordered_map<tok, std::string> tokname {
{tok::use ,"use" },
{tok::rfor ,"for" }, {tok::rfor ,"for" },
{tok::forindex,"forindex"}, {tok::forindex,"forindex"},
{tok::foreach ,"foreach" }, {tok::foreach ,"foreach" },
@ -92,6 +93,7 @@ private:
void update_location(expr*); void update_location(expr*);
private: private:
use_stmt* use_stmt_gen();
null_expr* null(); null_expr* null();
nil_expr* nil(); nil_expr* nil();
number_literal* num(); number_literal* num();

View File

@ -12,7 +12,8 @@ void optimizer::const_string(
const auto& left = left_node->get_content(); const auto& left = left_node->get_content();
const auto& right = right_node->get_content(); const auto& right = right_node->get_content();
node->set_optimized_string( node->set_optimized_string(
new string_literal(node->get_location(), left+right)); new string_literal(node->get_location(), left+right)
);
} }
void optimizer::const_number( void optimizer::const_number(
@ -43,7 +44,8 @@ void optimizer::const_number(
return; return;
} }
node->set_optimized_number( node->set_optimized_number(
new number_literal(node->get_location(), res)); new number_literal(node->get_location(), res)
);
} }
void optimizer::const_number( void optimizer::const_number(
@ -62,46 +64,56 @@ void optimizer::const_number(
return; return;
} }
node->set_optimized_number( node->set_optimized_number(
new number_literal(node->get_location(), res)); new number_literal(node->get_location(), res)
);
} }
bool optimizer::visit_binary_operator(binary_operator* node) { bool optimizer::visit_binary_operator(binary_operator* node) {
node->get_left()->accept(this); auto left_node = node->get_left();
node->get_right()->accept(this); auto right_node = node->get_right();
left_node->accept(this);
right_node->accept(this);
number_literal* left_num_node = nullptr; number_literal* left_num_node = nullptr;
number_literal* right_num_node = nullptr; number_literal* right_num_node = nullptr;
string_literal* left_str_node = nullptr; string_literal* left_str_node = nullptr;
string_literal* right_str_node = nullptr; string_literal* right_str_node = nullptr;
if (node->get_left()->get_type()==expr_type::ast_num) { if (left_node->get_type()==expr_type::ast_num) {
left_num_node = (number_literal*)node->get_left(); left_num_node = reinterpret_cast<number_literal*>(left_node);
} else if (node->get_left()->get_type()==expr_type::ast_binary && } else if (left_node->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_left())->get_optimized_number()) { reinterpret_cast<binary_operator*>(left_node)->get_optimized_number()) {
left_num_node = ((binary_operator*)node->get_left())->get_optimized_number(); auto optimized = reinterpret_cast<binary_operator*>(left_node);
} else if (node->get_left()->get_type()==expr_type::ast_unary && left_num_node = optimized->get_optimized_number();
((unary_operator*)node->get_left())->get_optimized_number()) { } else if (left_node->get_type()==expr_type::ast_unary &&
left_num_node = ((unary_operator*)node->get_left())->get_optimized_number(); reinterpret_cast<unary_operator*>(left_node)->get_optimized_number()) {
auto optimized = reinterpret_cast<unary_operator*>(left_node);
left_num_node = optimized->get_optimized_number();
} }
if (node->get_right()->get_type()==expr_type::ast_num) { if (right_node->get_type()==expr_type::ast_num) {
right_num_node = (number_literal*)node->get_right(); right_num_node = reinterpret_cast<number_literal*>(right_node);
} else if (node->get_right()->get_type()==expr_type::ast_binary && } else if (right_node->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_right())->get_optimized_number()) { reinterpret_cast<binary_operator*>(right_node)->get_optimized_number()) {
right_num_node = ((binary_operator*)node->get_right())->get_optimized_number(); auto optimized = reinterpret_cast<binary_operator*>(right_node);
} else if (node->get_right()->get_type()==expr_type::ast_unary && right_num_node = optimized->get_optimized_number();
((unary_operator*)node->get_right())->get_optimized_number()) { } else if (right_node->get_type()==expr_type::ast_unary &&
right_num_node = ((unary_operator*)node->get_right())->get_optimized_number(); reinterpret_cast<unary_operator*>(right_node)->get_optimized_number()) {
auto optimized = reinterpret_cast<unary_operator*>(right_node);
right_num_node = optimized->get_optimized_number();
} }
if (node->get_left()->get_type()==expr_type::ast_str) { if (left_node->get_type()==expr_type::ast_str) {
left_str_node = (string_literal*)node->get_left(); left_str_node = reinterpret_cast<string_literal*>(left_node);
} else if (node->get_left()->get_type()==expr_type::ast_binary && } else if (left_node->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_left())->get_optimized_string()) { reinterpret_cast<binary_operator*>(left_node)->get_optimized_string()) {
left_str_node = ((binary_operator*)node->get_left())->get_optimized_string(); auto optimized = reinterpret_cast<binary_operator*>(left_node);
left_str_node = optimized->get_optimized_string();
} }
if (node->get_right()->get_type()==expr_type::ast_str) { if (right_node->get_type()==expr_type::ast_str) {
right_str_node = (string_literal*)node->get_right(); right_str_node = reinterpret_cast<string_literal*>(right_node);
} else if (node->get_right()->get_type()==expr_type::ast_binary && } else if (right_node->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_right())->get_optimized_string()) { reinterpret_cast<binary_operator*>(right_node)->get_optimized_string()) {
right_str_node = ((binary_operator*)node->get_right())->get_optimized_string(); auto optimized = reinterpret_cast<binary_operator*>(right_node);
right_str_node = optimized->get_optimized_string();
} }
if (left_num_node && right_num_node) { if (left_num_node && right_num_node) {
const_number(node, left_num_node, right_num_node); const_number(node, left_num_node, right_num_node);
@ -115,19 +127,23 @@ bool optimizer::visit_binary_operator(binary_operator* node) {
} }
bool optimizer::visit_unary_operator(unary_operator* node) { bool optimizer::visit_unary_operator(unary_operator* node) {
node->get_value()->accept(this); auto value = node->get_value();
number_literal* value_node = nullptr; value->accept(this);
if (node->get_value()->get_type()==expr_type::ast_num) {
value_node = (number_literal*)node->get_value(); number_literal* num_node = nullptr;
} else if (node->get_value()->get_type()==expr_type::ast_binary && if (value->get_type()==expr_type::ast_num) {
((binary_operator*)node->get_value())->get_optimized_number()) { num_node = reinterpret_cast<number_literal*>(value);
value_node = ((binary_operator*)node->get_value())->get_optimized_number(); } else if (value->get_type()==expr_type::ast_binary &&
} else if (node->get_value()->get_type()==expr_type::ast_unary && reinterpret_cast<binary_operator*>(value)->get_optimized_number()) {
((unary_operator*)node->get_value())->get_optimized_number()) { auto optimized = reinterpret_cast<binary_operator*>(value);
value_node = ((unary_operator*)node->get_value())->get_optimized_number(); num_node = optimized->get_optimized_number();
} else if (value->get_type()==expr_type::ast_unary &&
reinterpret_cast<unary_operator*>(value)->get_optimized_number()) {
auto optimized = reinterpret_cast<unary_operator*>(value);
num_node = optimized->get_optimized_number();
} }
if (value_node) { if (num_node) {
const_number(node, value_node); const_number(node, num_node);
} }
return true; return true;
} }

View File

@ -1,6 +1,6 @@
# flightgear developer environments simulator (beta) # flightgear developer environments simulator (beta)
# ValKmjolnir 2022 # ValKmjolnir 2022
import.std.runtime; use std.runtime;
println("-------------------------------------------------------------"); println("-------------------------------------------------------------");
println(" FlightGear simulated-env for developers project, since 2019"); println(" FlightGear simulated-env for developers project, since 2019");

View File

@ -66,3 +66,9 @@ var stat = func(filename) {
var eof = func(filehandle) { var eof = func(filehandle) {
return __eof(filehandle); return __eof(filehandle);
} }
var stdin = func() { return __stdin; }();
var stdout = func() { return __stdout;}();
var stderr = func() { return __stderr; }();

View File

@ -1,13 +1,13 @@
# lib.nas # lib.nas
# 2019 ValKmjolnir # 2019 ValKmjolnir
import.std.coroutine; use std.coroutine;
import.std.math; use std.math;
import.std.string; use std.string;
import.std.io; use std.io;
import.std.os; use std.os;
import.std.bits; use std.bits;
import.std.unix; use std.unix;
# print is used to print all things in nasal, try and see how it works. # print is used to print all things in nasal, try and see how it works.
# this function uses std::cout to output logs. # this function uses std::cout to output logs.

View File

@ -379,7 +379,7 @@ var dump = func {
# Don't recurse into aliases, lest we get stuck in a loop # Don't recurse into aliases, lest we get stuck in a loop
if(type != "ALIAS") { if(type != "ALIAS") {
var children = node.getChildren(); var children = node.getChildren();
foreach(c; children) { dump(name ~ "/", c); } foreach(var c; children) { dump(name ~ "/", c); }
} }
} }
@ -498,7 +498,7 @@ var createNodeObjectsFromHash = func (property_list, namespace = nil) {
logprint(LOG_WARN, "createNodeObjectsFromHash: Error, property_list argument is not a hash."); logprint(LOG_WARN, "createNodeObjectsFromHash: Error, property_list argument is not a hash.");
return nil; return nil;
} }
foreach (key; keys(property_list)) { foreach (var key; keys(property_list)) {
namespace[key] = props.getNode(property_list[key],1); namespace[key] = props.getNode(property_list[key],1);
} }
} }
@ -743,7 +743,7 @@ var UpdateManager =
obj.needs_update = 0; obj.needs_update = 0;
obj.property = {}; obj.property = {};
obj.is_numeric = {}; obj.is_numeric = {};
foreach (hashkey; obj.hashkeylist) { foreach (var hashkey; obj.hashkeylist) {
obj.property[hashkey] = props.globals.getNode(hashkey); obj.property[hashkey] = props.globals.getNode(hashkey);
obj.lastval[hashkey] = nil; obj.lastval[hashkey] = nil;
# var ty = obj.property[hashkey].getType(); # var ty = obj.property[hashkey].getType();
@ -833,7 +833,7 @@ var UpdateManager =
me.needs_update = 0; me.needs_update = 0;
if (obj != nil or me.lastval == nil) { if (obj != nil or me.lastval == nil) {
foreach (hashkey; me.hashkeylist) { foreach (var hashkey; me.hashkeylist) {
if (me.isnum) { if (me.isnum) {
if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) { if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) {
me.needs_update = 1; me.needs_update = 1;
@ -847,7 +847,7 @@ var UpdateManager =
} }
if (me.needs_update) { if (me.needs_update) {
me.changed(obj); me.changed(obj);
foreach (hashkey; me.hashkeylist) { foreach (var hashkey; me.hashkeylist) {
me.lastval[hashkey] = obj[hashkey]; me.lastval[hashkey] = obj[hashkey];
} }
} }

View File

@ -1,22 +1,23 @@
# stack.nas # stack.nas
# valkmjolnir 2021/3/31 # valkmjolnir 2021/3/31
var stack=func(){ var stack = func() {
var vec=[]; var vec = [];
return{ return {
push:func(elem){ push: func(elem) {
append(vec,elem); append(vec, elem);
}, },
pop:func(){ pop: func() {
return pop(vec); return pop(vec);
}, },
top:func(){ top: func() {
if(size(vec)!=0) if (size(vec)!=0) {
return vec[-1]; return vec[-1];
}
}, },
clear:func(){ clear: func() {
vec=[]; vec = [];
}, },
empty:func(){ empty: func() {
return size(vec)==0; return size(vec)==0;
} }
}; };

View File

@ -1,5 +1,5 @@
import.std.padding; use std.padding;
import.std.process_bar; use std.process_bar;
var char_ttf=[ var char_ttf=[
[" "," "," "," "," "," "], [" "," "," "," "," "," "],

View File

@ -1,5 +1,5 @@
# Road check and auto pilot by ValKmjolnir # Road check and auto pilot by ValKmjolnir
import.std.fg_env; use std.fg_env;
var props = fg_env.props; var props = fg_env.props;
var geodinfo = fg_env.geodinfo; var geodinfo = fg_env.geodinfo;

View File

@ -1,4 +1,4 @@
import.std.queue; use std.queue;
rand(time(0)); rand(time(0));
var pixel=[' ','#','.','*']; var pixel=[' ','#','.','*'];

View File

@ -1,4 +1,4 @@
import.std.mat; use std.mat;
rand(time(0)); rand(time(0));

45
test/burningship.nas Normal file
View File

@ -0,0 +1,45 @@
use std.process_bar;
var ppm = func(filename, width, height, RGB) {
# P3 use ASCII number
# P6 use binary character
var fd = io.open(filename, "wb");
io.write(fd, "P6\n"~width~" "~height~"\n255\n");
for(var i = 0; i<height; i += 1) {
for(var j = 0; j<width; j += 1) {
io.write(fd,RGB(i,j));
}
}
io.close(fd);
}
var width = 1920;
var height = 1080;
var bar = (os.platform()=="windows")?
process_bar.bar(front:"sharp", back:"point", sep:"line", length:50):
process_bar.high_resolution_bar(50);
var f = func(i, j) {
var (yMin, yMax, xMin, xMax) = (-2, 1.1, -3.2, 2);
var (yDel, xDel) = (yMax-yMin, xMax-xMin);
var (y, x) = ((i/height)*yDel+yMin, (j/width)*xDel+xMin);
var (x0, y0) = (x, y);
for(var iter = 0; iter<64; iter += 1) {
(x0, y0) = (math.abs(x0), math.abs(y0));
var (x1, y1) = ((x0*x0)-(y0*y0)+x, 2*x0*y0+y);
(x0, y0) = (x1, y1);
if ((x0*x0)+(y0*y0)>8) {
break;
}
}
var progress = (i*width+j+1)/(width*height);
if (progress*100-int(progress*100)==0) {
print(bar.bar(progress), " ", progress*100, "% \r");
}
iter = iter>=25? 255:int(iter/25*255);
var c = char(iter);
return c~c~c;
}
ppm("burningship.ppm", width, height, f);
println();

View File

@ -1,5 +1,5 @@
import.std.padding; use std.padding;
import.std.file; use std.file;
var source=file.find_all_files_with_extension("./src","cpp","h"); var source=file.find_all_files_with_extension("./src","cpp","h");
sort(source,func(a,b){return cmp(a,b)<0}); sort(source,func(a,b){return cmp(a,b)<0});

View File

@ -21,8 +21,8 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
import.module.libmat; use module.libmat;
import.std.runtime; use std.runtime;
func(){ func(){
# allocate more spaces # allocate more spaces

View File

@ -1,7 +1,7 @@
# coroutine.nas by ValKmjolnir # coroutine.nas by ValKmjolnir
# 2022/5/19 # 2022/5/19
import.std.process_bar; use std.process_bar;
import.std.padding; use std.padding;
if(os.platform()=="windows"){ if(os.platform()=="windows"){
system("chcp 65001"); system("chcp 65001");

View File

@ -1,5 +1,5 @@
import.std.padding; use std.padding;
import.std.process_bar; use std.process_bar;
var mess=func(vec) { var mess=func(vec) {
srand(); srand();

View File

@ -1,4 +1,4 @@
import.std.runtime; use std.runtime;
var mod = math.mod; var mod = math.mod;

View File

@ -1,6 +1,6 @@
import.std.process_bar; use std.process_bar;
var ppm=func(filename, width, height, RGB){ var ppm = func(filename, width, height, RGB) {
# P3 use ASCII number # P3 use ASCII number
# P6 use binary character # P6 use binary character
var fd = io.open(filename, "wb"); var fd = io.open(filename, "wb");
@ -15,8 +15,8 @@ var ppm=func(filename, width, height, RGB){
var width = 1600; var width = 1600;
var height = 900; var height = 900;
var bar=(os.platform()=="windows")? var bar = (os.platform()=="windows")?
process_bar.bar(front:"sharp",back:"point",sep:"line",length:50): process_bar.bar(front:"sharp", back:"point", sep:"line", length:50):
process_bar.high_resolution_bar(50); process_bar.high_resolution_bar(50);
var RGB = func(h, w) { var RGB = func(h, w) {

View File

@ -1,4 +1,4 @@
import.module.libkey; use module.libkey;
srand(); srand();

View File

@ -1,4 +1,4 @@
import.std.runtime; use std.runtime;
var test_func = func(test_processes...) { var test_func = func(test_processes...) {
var test_process_total = maketimestamp(); var test_process_total = maketimestamp();

View File

@ -1,7 +1,7 @@
# hexdump.nas by ValKmjolnir # hexdump.nas by ValKmjolnir
# 2021/8/13 # 2021/8/13
import.std.file; use std.file;
import.std.runtime; use std.runtime;
# init # init
var hex=func(){ var hex=func(){

View File

@ -1,4 +1,4 @@
import.module.libsock; use module.libsock;
var socket = libsock.socket; var socket = libsock.socket;
@ -320,9 +320,11 @@ 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") path=="/doc/pic/feigenbaum.png" or
path=="/doc/pic/burningship.png")
http.send(client,respond.ok(io.readfile("."~path))); http.send(client,respond.ok(io.readfile("."~path)));
else{ else{
var filename=substr(path,1,size(path)-1); var filename=substr(path,1,size(path)-1);

View File

@ -1,5 +1,5 @@
import.std.json; use std.json;
import.std.process_bar; use std.process_bar;
var ss = json.stringify({ var ss = json.stringify({
vec:[0,1,2], vec:[0,1,2],

View File

@ -1,6 +1,6 @@
import.module.libsock; use module.libsock;
import.std.json; use std.json;
import.std.runtime; use std.runtime;
var socket = libsock.socket; var socket = libsock.socket;

View File

@ -1,5 +1,5 @@
import.std.process_bar; use std.process_bar;
import.std.runtime; use std.runtime;
var new_map=func(width,height){ var new_map=func(width,height){
var tmp=[]; var tmp=[];

View File

@ -1,98 +1,105 @@
import.test.md5_self; use test.md5_self;
import.std.process_bar; use std.process_bar;
import.std.file; use std.file;
srand(); srand();
var compare=func() { var compare = func() {
var ch=[ var ch = [
"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+", "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+",
"_","*","/","\'","\"",".",",",";",":","<",">","!","@","#","$","%", "_","*","/","\'","\"",".",",",";",":","<",">","!","@","#","$","%",
"^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?" "^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?"
]; ];
return func(begin,end) { return func(begin, end) {
var byte=0; var byte = 0;
var total=end-begin; var total = end-begin;
var timestamp=maketimestamp(); var timestamp = maketimestamp();
timestamp.stamp(); timestamp.stamp();
var bar=process_bar.high_resolution_bar(40); var bar = process_bar.high_resolution_bar(40);
for(var i=begin;i<end;i+=1) { for(var i = begin; i<end; i += 1) {
var s=""; var s = "";
for(var j=0;j<i;j+=1) { for(var j = 0; j<i; j += 1) {
s~=ch[rand()*size(ch)]; s ~= ch[rand()*size(ch)];
} }
byte+=size(s); byte += size(s);
var res=md5(s); var res = md5(s);
if(cmp(res, md5_self.md5(s))) { if(cmp(res, md5_self.md5(s))) {
die("error: "~str(i)); die("error: "~str(i));
} }
if (i-begin-int((i-begin)/4)*4==0) { if (i-begin-int((i-begin)/4)*4==0) {
print( print(
"\e[1000D ",bar.bar((i-begin+1)/total), "\e[1000D ", bar.bar((i-begin+1)/total),
" (",i-begin+1,"/",total,")\t", " (", i-begin+1, "/", total, ")\t",
res," byte: ",int(byte/1024),"k" res, " byte: ", int(byte/1024), "k"
); );
} }
} }
print( print(
"\e[1000D ",bar.bar((i-begin)/total), "\e[1000D ", bar.bar((i-begin)/total),
" (",i-begin,"/",total,")\t", " (", i-begin, "/", total, ")\t",
res," byte: ",int(byte/1024),"k", res, " byte: ", int(byte/1024), "k",
" time: ",timestamp.elapsedMSec() " time: ", timestamp.elapsedMSec()
); );
print("\n"); print("\n");
}; };
}(); }();
var filechecksum=func(){ var filechecksum = func() {
var files=[]; var files = [];
foreach(var p;file.find_all_files_with_extension("./test","nas")) { foreach(var p; file.find_all_files_with_extension("./test","nas")) {
append(files,"./test/"~p); append(files, "./test/"~p);
} }
foreach(var p;file.find_all_files_with_extension("./std","nas")) { foreach(var p; file.find_all_files_with_extension("./std","nas")) {
append(files,"./std/"~p); append(files, "./std/"~p);
} }
foreach(var p;file.find_all_files_with_extension("./module","nas","cpp")) { foreach(var p; file.find_all_files_with_extension("./module","nas","cpp")) {
append(files,"./module/"~p); append(files, "./module/"~p);
} }
foreach(var p;file.find_all_files_with_extension(".","md")) { foreach(var p; file.find_all_files_with_extension(".","md")) {
append(files,"./"~p); append(files, "./"~p);
} }
foreach(var p;file.find_all_files_with_extension("./src","cpp","h")) { foreach(var p; file.find_all_files_with_extension("./src","cpp","h")) {
append(files,"./src/"~p); append(files, "./src/"~p);
} }
foreach(var p;file.find_all_files_with_extension("./doc","md")) { foreach(var p; file.find_all_files_with_extension("./doc","md")) {
append(files,"./doc/"~p); append(files, "./doc/"~p);
} }
var byte=0; var source = [];
var total=size(files); foreach(var f; files) {
var timestamp=maketimestamp(); append(source, io.readfile(f));
}
var byte = 0;
var total = size(files);
var timestamp = maketimestamp();
timestamp.stamp(); timestamp.stamp();
var bar=process_bar.high_resolution_bar(40); var bar = process_bar.high_resolution_bar(40);
forindex(var i;files){ forindex(var i; files) {
var f=io.readfile(files[i]); var f = source[i];
var res=md5(f); var res = md5(f);
byte+=size(f); byte += size(f);
if(cmp(res, md5_self.md5(f))){ if(cmp(res, md5_self.md5(f))){
die("error: "~files[i]); die("error: "~files[i]);
} }
print( print(
"\e[1000D ",bar.bar((i+1)/total), "\e[1000D ", bar.bar((i+1)/total),
" (",i+1,"/",total,")\t",res, " (", i+1, "/", total, ")\t", res,
" byte: ",int(byte/1024),"k", " byte: ", int(byte/1024), "k",
" time: ",timestamp.elapsedMSec() " time: ", timestamp.elapsedMSec()
); );
} }
print("\n"); print("\n");
} }
var randomchecksum=func(){ var randomchecksum = func() {
for(var i=0;i<2048;i+=256) for(var i = 0; i<2048; i += 256) {
compare(i,i+256); compare(i, i+256);
}
} }
if(os.platform()=="windows") if(os.platform()=="windows") {
system("chcp 65001"); system("chcp 65001");
}
filechecksum(); filechecksum();
randomchecksum(); randomchecksum();

View File

@ -1,5 +1,5 @@
import.std.dylib; use std.dylib;
import.module.libfib; use module.libfib;
println(keys(libfib)); println(keys(libfib));
libfib.test_ghost(); libfib.test_ghost();

View File

@ -1,6 +1,6 @@
import.std.process_bar; use std.process_bar;
import.module.libkey; use module.libkey;
import.std.runtime; use std.runtime;
var is_windows_platform=os.platform()=="windows"; var is_windows_platform=os.platform()=="windows";
var is_macos_platform=os.platform()=="macOS"; var is_macos_platform=os.platform()=="macOS";

View File

@ -1,41 +1,43 @@
import.std.process_bar; use std.process_bar;
var ppm=func(filename,width,height,RGB){ var ppm = func(filename, width, height, RGB) {
# P3 use ASCII number # P3 use ASCII number
# P6 use binary character # P6 use binary character
var fd=io.open(filename,"wb"); var fd = io.open(filename, "wb");
io.write(fd,"P3\n"~width~" "~height~"\n255\n"); io.write(fd, "P6\n"~width~" "~height~"\n255\n");
for(var i=0;i<height;i+=1){ for(var i = 0; i<height; i += 1) {
for(var j=0;j<width;j+=1) for(var j = 0; j<width; j += 1) {
io.write(fd,RGB(i,j)); io.write(fd,RGB(i,j));
io.write(fd,"\n"); }
} }
io.close(fd); io.close(fd);
} }
var width=1920; var width = 1920;
var height=1080; var height = 1080;
var bar=(os.platform()=="windows")? var bar = (os.platform()=="windows")?
process_bar.bar(front:"sharp",back:"point",sep:"line",length:50): process_bar.bar(front:"sharp", back:"point", sep:"line", length:50):
process_bar.high_resolution_bar(50); process_bar.high_resolution_bar(50);
var f=func(i,j){ var f = func(i, j) {
var (yMin,yMax,xMin,xMax)=(-1.35,1.35,-3.3,1.5); var (yMin, yMax, xMin, xMax) = (-1.35, 1.35, -3.3, 1.5);
var (yDel,xDel)=(yMax-yMin,xMax-xMin); var (yDel, xDel) = (yMax-yMin, xMax-xMin);
var (y,x)=((i/height)*yDel+yMin,(j/width)*xDel+xMin); var (y, x) = ((i/height)*yDel+yMin, (j/width)*xDel+xMin);
var (x0,y0)=(x,y); var (x0, y0) = (x, y);
for(var iter=0;iter<64;iter+=1){ for(var iter = 0; iter<64; iter += 1) {
var (x1,y1)=((x0*x0)-(y0*y0)+x,2*x0*y0+y); var (x1, y1) = ((x0*x0)-(y0*y0)+x, 2*x0*y0+y);
(x0,y0)=(x1,y1); (x0, y0) = (x1, y1);
if((x0*x0)+(y0*y0)>4){ if ((x0*x0)+(y0*y0)>4) {
break; break;
} }
} }
var progress=(i*width+j+1)/(width*height); var progress = (i*width+j+1)/(width*height);
if(progress*100-int(progress*100)==0){ if (progress*100-int(progress*100)==0) {
print(bar.bar(progress)," ",progress*100,"% \r"); print(bar.bar(progress), " ", progress*100, "% \r");
} }
iter=iter==25?255:int(iter/25*255); iter = iter>=25? 255:int(iter/25*255);
return iter~" "~iter~" "~iter~" "; var c = char(iter);
return c~c~c;
} }
ppm("a.ppm",width,height,f);
ppm("mandelbrotset.ppm", width, height, f);
println(); println();

View File

@ -1,4 +1,4 @@
import.std.runtime; use std.runtime;
# basic type # basic type
nil; nil;

View File

@ -1,5 +1,5 @@
# this will cause error # this will cause error
# import.b; # use b;
println("init a"); println("init a");
var a = "hello"; var a = "hello";

View File

@ -1,3 +1,3 @@
import.a; use a;
println("init b"); println("init b");

View File

@ -1,3 +1,3 @@
import.b; use b;
println("init c"); println("init c");

View File

@ -1,6 +1,6 @@
import.c; use c;
import.a; use a;
import.b; use b;
println(a); println(a);
println(b); println(b);

View File

@ -1,6 +1,6 @@
import.module.libkey; use module.libkey;
import.std.list; use std.list;
import.std.runtime; use std.runtime;
var game=func(x,y){ var game=func(x,y){
rand(time(0)); rand(time(0));

View File

@ -1,5 +1,5 @@
import.module.libkey; use module.libkey;
import.std.runtime; use std.runtime;
var color=[ var color=[
"\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m", "\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m",

View File

@ -1,4 +1,4 @@
import.std.runtime; use std.runtime;
var os_time=func(){ var os_time=func(){
return "[\e[33;1m"~os.time()~"\e[0m] "; return "[\e[33;1m"~os.time()~"\e[0m] ";

View File

@ -1,4 +1,4 @@
import.std.runtime; use std.runtime;
var to_lower=func(s){ var to_lower=func(s){
var tmp=""; var tmp="";

View File

@ -1,7 +1,9 @@
println("[",os.time(),"] auto push, please wait..."); println("[",os.time(),"] (=.=) auto push, please wait...");
while(system("git push")!=0) { while(system("git push")!=0) {
println("[",os.time(),"] failed to push, retrying..."); println("[",os.time(),"] (ToT) failed to push, retrying...");
unix.sleep(0.5); unix.sleep(0.5);
} }
println("[",os.time(),"] (^o^) auto push complete.");

View File

@ -1,7 +1,19 @@
import.std.file; use std.file;
use std.padding;
use std.process_bar;
if (size(arg)!=1) { var tips = func() {
println("usage:");
println(" nasal search_file.nas [key]");
}
if (size(arg)<1) {
println("need a key string to search files."); println("need a key string to search files.");
tips();
exit(-1);
} else if (size(arg)>1) {
println("too many arguments.");
tips();
exit(-1); exit(-1);
} }
@ -10,9 +22,9 @@ var needle = arg[0];
var do_flat = func(vec) { var do_flat = func(vec) {
var flat = []; var flat = [];
var bfs = [vec]; var bfs = [vec];
while(size(bfs)) { while(size(bfs)!=0) {
var d = pop(bfs); var d = pop(bfs);
foreach(var f;d.files) { foreach(var f; d.files) {
if (ishash(f)) { if (ishash(f)) {
append(bfs,f); append(bfs,f);
continue; continue;
@ -20,20 +32,48 @@ var do_flat = func(vec) {
append(flat, d.dir~"/"~f); append(flat, d.dir~"/"~f);
} }
} }
sort(flat, func(a,b){return cmp(a,b)<0}); sort(flat, func(a, b) {return cmp(a, b)<0});
return flat; return flat;
} }
var count = 0; var result = [];
foreach(var f;do_flat(file.recursive_find_files("."))) { var all_files = file.recursive_find_files(".");
foreach(var f; do_flat(all_files)) {
var pos = find(needle, f); var pos = find(needle, f);
if (pos == -1) { if (pos == -1) {
continue; continue;
} }
count += 1;
var begin = substr(f, 0, pos); var begin = substr(f, 0, pos);
var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f)); var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f));
println(begin, "\e[95;1m", needle, "\e[0m", end); var file_size = fstat(f).st_size;
var unit = " b";
if (file_size>1024) {
file_size/=1024;
unit = "kb";
}
if (file_size>1024) {
file_size/=1024;
unit = "mb";
}
if (file_size>1024) {
file_size/=1024;
unit = "gb";
}
file_size = int(file_size);
append(result, {
info: begin~"\e[95;1m"~needle~"\e[0m"~end,
size: file_size,
unit: unit
});
} }
println("\n", count, " result(s)."); var max_len = 0;
foreach(var elem; result) {
var temp = size(str(elem.size)~" "~elem.unit);
max_len = math.max(max_len, temp);
}
foreach(var elem; result) {
var temp = padding.leftpad(str(elem.size)~" "~elem.unit, max_len);
println(temp, " | ", elem.info);
}
println("\n", size(result), " result(s).");