diff --git a/README.md b/README.md index 9dd33091..5918eec6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![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) -![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) > 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!__ -* __E-mail__: __lhk101lhk101@qq.com__ +* __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762) ## __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: ```javascript -if(os.platform()=="windows") +if (os.platform()=="windows") { system("chcp 65001"); +} ``` ## __Tutorial__ @@ -116,30 +117,30 @@ This type is not created by user program. __`nil`__ is a null type. Just like `null`. ```javascript -var spc=nil; +var spc = nil; ``` __`num`__ has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 `double` to store. ```javascript # this language use '#' to write notes -var n=2.71828; # dec -var n=2.147e16; # dec -var n=1e-10; # dec -var n=0xAA55; # hex -var n=0o170001; # oct +var n = 2.71828; # dec +var n = 2.147e16; # dec +var n = 1e-10; # dec +var n = 0xAA55; # hex +var n = 0o170001; # oct # caution: true and false also useful in nasal now -var n=true; # in fact n is now 1.0 -var n=false; # in face n is now 0.0 +var n = true; # in fact n is now 1.0 +var n = false; # in face n is now 0.0 ``` __`str`__ has 3 formats. The third one is used to declare a character. ```javascript -var s='str'; -var s="another string"; -var s=`c`; +var s = 'str'; +var s = "another string"; +var s = `c`; # some special characters is allowed in this language: '\a'; '\b'; '\e'; '\f'; '\n'; '\r'; '\t'; '\v'; @@ -150,19 +151,19 @@ var s=`c`; __`vec`__ has unlimited length and can store all types of values. ```javascript -var vec=[]; -var vec=[0,nil,{},[],func(){return 0}]; -append(vec,0,1,2); +var vec = []; +var vec = [0, nil, {}, [], func(){return 0}]; +append(vec, 0, 1, 2); ``` __`hash`__ is a hashmap (or like a `dict` in `python`) that stores values with strings/identifiers as the key. ```javascript -var hash={ - member1:nil, - member2:"str", - "member3":"member\'s name can also be a string constant", - funct:func(){ +var hash = { + member1: nil, + member2: "str", + "member3": "member\'s name can also be a string constant", + funct: func() { return me.member2~me.member3; } }; @@ -171,27 +172,28 @@ var hash={ __`func`__ is a function type (in fact it is `lambda`). ```javascript -var f=func(x,y,z){ +var f = func(x, y, z) { return nil; } # function could be declared without parameters and `(`, `)` -var f=func{ +var f = func { return 114514; } -var f=func(x,y,z,deft=1){ +var f = func(x, y, z, deft = 1) { return x+y+z+deft; } -var f=func(args...){ - var sum=0; - foreach(var i;args) - sum+=i; +var f = func(args...) { + var sum = 0; + foreach(var i; args) { + sum += i; + } return sum; } ``` __`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. @@ -237,16 +239,16 @@ Bitwise operators `~` `|` `&` `^` have the same function as C/C++. Operators `=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=` are used in assignment expressions. ```javascript -a=b=c=d=1; -a+=1; -a-=1; -a*=1; -a/=1; -a~="string"; - -a^=0xff; -a&=0xca; -a|=0xba; +a = b = c = d = 1; +a += 1; +a -= 1; +a *= 1; +a /= 1; +a ~= "string"; + +a ^= 0xff; +a &= 0xca; +a |= 0xba; ``` @@ -256,9 +258,9 @@ a|=0xba; As follows. ```javascript -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 tuple +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 tuple ``` Nasal has many special global symbols: @@ -292,9 +294,9 @@ func() { The last one is often used to swap two variables. ```javascript -(a,b[0],c.d)=[0,1,2]; -(a,b[1],c.e)=(0,1,2); -(a,b)=(b,a); +(a, b[0], c.d) = [0, 1, 2]; +(a, b[1], c.e) = (0, 1, 2); +(a, b) = (b, a); ``` @@ -305,13 +307,13 @@ In nasal there's a new key word `elsif`. It has the same functions as `else if`. ```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++. ```javascript -while(condition) +while(condition) { continue; -for(var i=0;i<10;i+=1) +} +for(var i = 0; i<10; i += 1) { break; +} ``` 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`. ```javascript -forindex(var i;elem) +forindex(var i; elem) { print(elem[i]); +} ``` `foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`. ```javascript -foreach(var i;elem) +foreach(var i; elem) { print(i); +} ``` @@ -356,7 +362,7 @@ If you want to get the character, use built-in function `chr()`. ```javascript 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]; ``` @@ -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... ```javascript -f(x:0,y:nil,z:[]); +f(x:0, y:nil, z:[]); ``` @@ -380,10 +386,10 @@ f(x:0,y:nil,z:[]); Also functions have this kind of use: ```javascript -func(x,y){ +func(x, y) { return x+y -}(0,1); -func(x){ +}(0, 1); +func(x) { return 1/(1+math.exp(-x)); }(0.5); ``` @@ -392,11 +398,11 @@ There's an interesting test file `y-combinator.nas`, try it for fun: ```javascript -var fib=func(f){ +var fib = func(f) { return f(f); }( - func(f){ - return func(x){ + func(f) { + return func(x) { if(x<2) return x; 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`: ```javascript -var f=func(){ - var a=1; - return func(){return a;}; +var f = func() { + var a = 1; + return func() {return a;}; } print(f()()); ``` @@ -422,14 +428,14 @@ print(f()()); Using closure makes it easier to OOP. ```javascript -var student=func(n,a){ - var (name,age)=(n,a); +var student = func(n, a) { + var (name, age) = (n, a); return { - print_info:func() {println(name,' ',age);}, - set_age: func(a){age=a;}, - get_age: func() {return age;}, - set_name: func(n){name=n;}, - get_name: func() {return name;} + print_info: func() {println(name, ' ', age);}, + set_age: func(a) {age = a;}, + get_age: func() {return age;}, + set_name: func(n) {name = n;}, + 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`: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); +var a = class.new(); a.set(114514); println(a.get()); ``` @@ -473,28 +479,28 @@ And `get` has the same process. And we must remind you that if you do this: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); -var b=class.new(); +var a = class.new(); +var b = class.new(); a.set(114); b.set(514); println(a.get()); println(b.get()); -var c=a.get; -var d=b.get; +var c = a.get; +var d = b.get; println(c()); println(c()); @@ -532,33 +538,19 @@ Definition: ```C++ // you could also use a macro to define one. -nas_native(builtin_print); +var builtin_print(context*, gc*); ``` Then complete this function using C++: ```C++ -var builtin_print(var* local,gc& ngc) -{ +var builtin_print(context* ctx, gc* ngc) { // find value with index begin from 1 // because local[0] is reserved for value 'me' - var vec=local[1]; - // main process - // 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<"; break; - } - std::cout<localr[1].vec().elems) { + std::cout << i; + } + std::cout << std::flush; // generate return value, // use ngc::alloc(type) to make a new value // 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: ```C++ -var builtin_keys(var* local,gc& ngc) -{ - var hash=local[1]; - if(hash.type!=vm_hash) - return nas_err("keys","\"hash\" must be hash"); +var builtin_keys(context* ctx, gc* ngc) { + auto hash = ctx->localr[1]; + if (hash.type!=vm_hash && hash.type!=vm_map) { + return nas_err("keys", "\"hash\" must be hash"); + } // use gc.temp to store the gc-managed-value, to avoid being sweeped - var res=ngc.temp=ngc.alloc(vm_vec); - auto& vec=res.vec().elems; - for(auto& iter:hash.hash().elems) - vec.push_back(ngc.newstr(iter.first)); - ngc.temp=nil; + auto res = ngc->temp = ngc->alloc(vm_vec); + auto& vec = res.vec().elems; + if (hash.type==vm_hash) { + for(const auto& iter : hash.hash().elems) { + 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; } ``` @@ -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: ```C++ -struct func -{ - const char* name; - var (*func)(var*,gc&); -} builtin[]= -{ - {"__print",builtin_print}, - {nullptr, nullptr } +nasal_builtin_table builtin[] = { + {"__print", builtin_print}, + {nullptr, nullptr} }; ``` At last,warp the `__print` in a nasal file: ```javascript -var print=func(elems...){ +var print = func(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: ```javascript -var print=func(elems...){ +var print = func(elems...) { 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: ```javascript -import.dirname.dirname.filename; +use dirname.dirname.filename; import("./dirname/dirname/filename.nas"); ``` -
Modules(for lib developers) +
Modules (for lib developers) If there is only one way to add your own functions into nasal, 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: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); 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)); 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: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; 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)); dylib.dlclose(dlhandle.lib); ``` @@ -791,7 +785,7 @@ If get this, Congratulations!
-
Ghost Type(for lib developers) +
Ghost Type (for lib developers) It's quite easy to create a new ghost by yourself now. 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 res = ngc->alloc(vm_obj); // 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; } +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(res.ghost().pointer)) = static_cast(num); + std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; + return nil; +} + var print_new_ghost(var* args, usize size, gc* ngc) { var res = args[0]; // 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"; return nil; } - std::cout << "print_new_ghost: " << res.obj() << " result = " - << *((u32*)res.obj().ptr) << "\n"; + std::cout << "print_new_ghost: " << res.ghost() << " result = " + << *((u32*)res.ghost().pointer) << "\n"; 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: -`bool var::objchk(const std::string&);` +`bool var::object_check(const std::string&);` 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: ```javascript -foreach(i;[0,1,2,3]) +foreach(i; [0, 1, 2, 3]) print(i) ``` @@ -877,7 +883,7 @@ If you forget to add the keyword `var`, you will get this: code: undefined symbol "i" --> test.nas:1:9 | -1 | foreach(i;[0,1,2,3]) +1 | foreach(i; [0, 1, 2, 3]) | ^ 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. ```javascript -func() -{ +func() { println("hello"); die("error occurred this line"); return; @@ -912,17 +917,27 @@ func() ```javascript hello [vm] error: error occurred this line -[vm] native function error. -trace back: - 0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131) - 0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4) - 0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6) -vm stack (0x7fffcd21bc68 , limit 10, total 12): - 0x0000005b | null | - ... - 0x00000057 | str | <0x138ff60> error occurred t... - ... - 0x00000052 | nil | +[vm] error: error occurred in native function + +call trace (main) + call func@0x557513935710() {entry: 0x850} + +trace back (main) + 0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +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 ```
@@ -932,28 +947,41 @@ vm stack (0x7fffcd21bc68 , limit 10, total 12): Here is an example of stack overflow: ```javascript -func(f){ +func(f) { return f(f); }( - func(f){ + func(f) { f(f); } )(); ``` ```javascript -[vm] stack overflow -trace back: - 0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5) - 0x000004fb 1349 same call(s) - 0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2) - 0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3) -vm stack (0x7fffd3781d58 , limit 10, total 8108): - 0x00001ffb | func | <0x15f8d90> entry:0x4f9 - 0x00001ffa | func | <0x15f8d90> entry:0x4f9 - 0x00001ff9 | pc | 0x4fb - ... - 0x00001ff2 | addr | 0x7fffd37a16e8 +[vm] error: stack overflow + +call trace (main) + call func@0x564106058620(f) {entry: 0x859} + --> 583 same call(s) + call func@0x5641060586c0(f) {entry: 0x851} + +trace back (main) + 0x000859 45 00 00 01 calll 0x1(a.nas:5) + 0x00085b 4a 00 00 01 callfv 0x1(a.nas:5) + 0x00085b 582 same call(s) + 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 ```
@@ -963,17 +991,19 @@ vm stack (0x7fffd3781d58 , limit 10, total 8108): Error will be thrown if there's a fatal error when executing: ```javascript -func(){ +func() { return 0; }()[1]; ``` ```javascript -[vm] callv: must call a vector/hash/string -trace back: - 0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3) -vm stack (0x7fffff539c28 , limit 10, total 1): - 0x00000050 | num | 0 +[vm] error: must call a vector/hash/string but get number + +trace back (main) + 0x000854 47 00 00 00 callv 0x0(a.nas:3) + +stack (0x564993f462b0, limit 10, total 1) + 0x000000 | num | 0 ```
@@ -985,35 +1015,48 @@ Use command __`-d`__ or __`--detail`__ the trace back info will show more detail ```javascript hello [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) - 0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131) - 0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4) - 0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6) -vm stack (0x7fffe0ffed90 , limit 10, total 12) - 0x0000004a | null | - 0x00000049 | pc | 0x553 - 0x00000048 | addr | 0x7fffe0ffeda0 - ... - 0x00000041 | nil | + 0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +stack (0x55dcb5b43120, limit 10, total 14) + 0x00000d | null | + 0x00000c | pc | 0x856 + 0x00000b | addr | 0x55dcb5b43130 + 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) - [ pc ] | pc | 0xb0 - [ global ] | addr | 0x7fffe0ffe9a0 - [ localr ] | addr | 0x7fffe0ffedf0 - [ memr ] | addr | 0x0 - [ canary ] | addr | 0x7fffe1002990 - [ top ] | addr | 0x7fffe0ffee40 - [ funcr ] | func | <0x677cd0> entry:0xb0 - [ upvalr ] | nil | -global (0x7fffe0ffe9a0 ) - 0x00000000 | func | <0x65fb00> entry:0x5 - 0x00000001 | func | <0x65fb20> entry:0xd + [pc ] | pc | 0x547 + [global] | addr | 0x55dcb5b53130 + [local ] | addr | 0x55dcb5b43190 + [memr ] | addr | 0x0 + [canary] | addr | 0x55dcb5b53110 + [top ] | addr | 0x55dcb5b431f0 + [funcr ] | func | <0x55dcb5b65620> entry:0x547 + [upval ] | nil | + +global (0x55dcb5b53130) + 0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val] + 0x000001 | vec | <0x55dcb5b64c20> [0 val] ... - 0x0000003d | func | <0x66bf00> entry:0x51f - 0x0000003e | hash | <0x65ffa0> {5 val} -local (0x7fffe0ffedf0 ) - 0x00000000 | nil | - 0x00000001 | str | <0x6cb630> error occurred t... + 0x00005e | func | <0x55dcb5b8fc70> entry:0x846 + +local (0x55dcb5b43190 <+7>) + 0x000000 | nil | + 0x000001 | str | <0x55dcb5b33670> error occurred t... + 0x000002 | nil | ``` @@ -1038,16 +1081,18 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: ---> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0) - 0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6) - 0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6) - 0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6) - 0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6) - 0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7) - 0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7) - 0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6) -vm stack (0x7fffd0259138 , limit 10, total 0) + 0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427) + 0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427) + 0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423) + 0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423) + 0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423) +--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1) + 0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1) + 0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1) + +stack (0x55ccd0a1b9d0, limit 10, total 0) >> ``` @@ -1070,23 +1115,26 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: - 0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503) - 0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498) - 0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1) - 0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1) - 0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1) - 0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1) ---> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3) - 0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3) -vm stack (0x7fffd0259138 , limit 10, total 7) - 0x00000047 | pc | 0x566 - 0x00000046 | addr | 0x0 - 0x00000045 | nil | - 0x00000044 | num | 0 - 0x00000043 | nil | - 0x00000042 | nil | - 0x00000041 | func | <0x88d2f0> entry:0x5 + 0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1) +--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3) + 0x000853 40 00 08 56 jf 0x856(test/fib.nas:3) + 0x000854 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000855 56 00 00 00 ret 0x0(test/fib.nas:3) + 0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4) + 0x000857 45 00 00 01 calll 0x1(test/fib.nas:4) + +stack (0x55ccd0a1b9d0, limit 10, total 8) + 0x000007 | pc | 0x869 + 0x000006 | addr | 0x0 + 0x000005 | nil | + 0x000004 | nil | + 0x000003 | num | 0 + 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(..) {..}} + +>>> +``` diff --git a/doc/README_zh.md b/doc/README_zh.md index c1fb67f1..9ad0b061 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -4,7 +4,7 @@ ![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) -![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) > 这篇文档包含多语言版本: [__中文__](../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代码页: ```javascript -if(os.platform()=="windows") +if (os.platform()=="windows") { system("chcp 65001"); +} ``` ## __教程__ @@ -104,30 +105,30 @@ __`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行 __`nil`__ 是空类型。类似于null。 ```javascript -var spc=nil; +var spc = nil; ``` __`num`__ 有三种形式:十进制,十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。 ```javascript # 该语言用 '#' 来作为注释的开头 -var n=2.71828; # dec 十进制 -var n=2.147e16; # dec 十进制 -var n=1e-10; # dec 十进制 -var n=0xAA55; # hex 十六进制 -var n=0o170001; # oct 八进制 +var n = 2.71828; # dec 十进制 +var n = 2.147e16; # dec 十进制 +var n = 1e-10; # dec 十进制 +var n = 0xAA55; # hex 十六进制 +var n = 0o170001; # oct 八进制 # 注意: true 和 false 关键字在现在的 nasal 里也是可用的 -var n=true; # n 实际上是数字 1.0 -var n=false; # n 实际上是数字 0.0 +var n = true; # n 实际上是数字 1.0 +var n = false; # n 实际上是数字 0.0 ``` __`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。 ```javascript -var s='str'; -var s="another string"; -var s=`c`; +var s = 'str'; +var s = "another string"; +var s = `c`; # 该语言也支持一些特别的转义字符: '\a'; '\b'; '\e'; '\f'; '\n'; '\r'; '\t'; '\v'; @@ -138,19 +139,19 @@ var s=`c`; __`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度) ```javascript -var vec=[]; -var vec=[0,nil,{},[],func(){return 0}]; -append(vec,0,1,2); +var vec = []; +var vec = [0, nil, {}, [], func(){return 0}]; +append(vec, 0, 1, 2); ``` __`hash`__ 使用哈希表 (类似于`python`中的`dict`),通过键值对来存储数据。key可以是一个字符串,也可以是一个标识符。 ```javascript -var hash={ - member1:nil, - member2:"str", - "member3":"member\'s name can also be a string constant", - funct:func(){ +var hash = { + member1: nil, + member2: "str", + "member3": "member\'s name can also be a string constant", + funct: func() { return me.member2~me.member3; } }; @@ -159,27 +160,28 @@ var hash={ __`func`__ 函数类型。(实际上在这个语言里函数是一种`lambda`表达式) ```javascript -var f=func(x,y,z){ +var f = func(x, y, z) { return nil; } # 函数声明可以没有参数列表以及 `(`, `)` -var f=func{ +var f = func { return 114514; } -var f=func(x,y,z,deft=1){ +var f = func(x, y, z, deft = 1) { return x+y+z+deft; } -var f=func(args...){ - var sum=0; - foreach(var i;args) - sum+=i; +var f = func(args...) { + var sum = 0; + foreach(var i; args) { + sum += i; + } return sum; } ``` __`upval`__ 是存储闭包数据的特殊类型, 在 __`vm`__ 中使用,以确保闭包功能正常。 -__`obj`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。 +__`ghost`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。 @@ -223,16 +225,16 @@ Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的 赋值运算符`=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=`正如其名,用于进行赋值。 ```javascript -a=b=c=d=1; -a+=1; -a-=1; -a*=1; -a/=1; -a~="string"; - -a^=0xff; -a&=0xca; -a|=0xba; +a = b = c = d = 1; +a += 1; +a -= 1; +a *= 1; +a /= 1; +a ~= "string"; + +a ^= 0xff; +a &= 0xca; +a |= 0xba; ``` @@ -242,9 +244,9 @@ a|=0xba; 如下所示。 ```javascript -var a=1; # 定义单个变量 -var (a,b,c)=[0,1,2]; # 从数组中初始化多个变量 -var (a,b,c)=(0,1,2); # 从元组中初始化多个变量 +var a = 1; # 定义单个变量 +var (a, b, c) = [0, 1, 2]; # 从数组中初始化多个变量 +var (a, b, c) = (0, 1, 2); # 从元组中初始化多个变量 ``` Nasal 有很多特别的全局变量: @@ -278,9 +280,9 @@ func() { 最后这个语句通常用于交换两个变量的数据,类似于Python中的操作。 ```javascript -(a,b[0],c.d)=[0,1,2]; -(a,b[1],c.e)=(0,1,2); -(a,b)=(b,a); +(a, b[0], c.d) = [0, 1, 2]; +(a, b[1], c.e) = (0, 1, 2); +(a, b) = (b, a); ``` @@ -290,13 +292,13 @@ func() { nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。 ```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++是一致的。 ```javascript -while(condition) +while(condition) { continue; -for(var i=0;i<10;i+=1) +} +for(var i = 0; i<10; i += 1) { break; +} ``` 同时,nasal还有另外两种直接遍历列表的循环方式: @@ -319,15 +323,17 @@ for(var i=0;i<10;i+=1) `forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。 ```javascript -forindex(var i;elem) +forindex(var i; elem) { print(elem[i]); +} ``` `foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`. ```javascript -foreach(var i;elem) +foreach(var i; elem) { print(i); +} ``` @@ -338,7 +344,7 @@ nasal提供了下面第一句的类似语法来从列表中随机或者按照一 ```javascript 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]; ``` @@ -351,7 +357,7 @@ a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil]; 然而如果它用起来非常舒适,那效率也显得不是非常重要了…… ```javascript -f(x:0,y:nil,z:[]); +f(x:0, y:nil, z:[]); ``` @@ -361,10 +367,10 @@ f(x:0,y:nil,z:[]); 函数有这样一种直接编写函数体并且立即调用的方式: ```javascript -func(x,y){ +func(x, y) { return x+y; -}(0,1); -func(x){ +}(0, 1); +func(x) { return 1/(1+math.exp(-x)); }(0.5); ``` @@ -372,11 +378,11 @@ func(x){ 测试文件中有一个非常有趣的文件`y-combinator.nas`,可以试一试: ```javascript -var fib=func(f){ +var fib = func(f) { return f(f); }( - func(f){ - return func(x){ + func(f) { + return func(x) { if(x<2) return x; return f(f)(x-1)+f(f)(x-2); } @@ -393,9 +399,9 @@ var fib=func(f){ 下面这个例子里,结果是`1`: ```javascript -var f=func(){ - var a=1; - return func(){return a;}; +var f = func() { + var a = 1; + return func() {return a;}; } print(f()()); ``` @@ -403,14 +409,14 @@ print(f()()); 如果善用闭包,你可以使用它来进行面向对象编程。 ```javascript -var student=func(n,a){ - var (name,age)=(n,a); +var student = func(n, a) { + var (name, age) = (n, a); return { - print_info:func() {println(name,' ',age);}, - set_age: func(a){age=a;}, - get_age: func() {return age;}, - set_name: func(n){name=n;}, - get_name: func() {return name;} + print_info: func() {println(name, ' ', age);}, + set_age: func(a) {age = a;}, + get_age: func() {return age;}, + set_name: func(n) {name = n;}, + get_name: func() {return name;} }; } ``` @@ -429,20 +435,20 @@ var student=func(n,a){ 使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); +var a = class.new(); a.set(114514); println(a.get()); ``` @@ -453,28 +459,28 @@ println(a.get()); 不过我们必须提醒你一点,如果你在这个地方使用该优化来减少hash的搜索开销: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); -var b=class.new(); +var a = class.new(); +var b = class.new(); a.set(114); b.set(514); println(a.get()); println(b.get()); -var c=a.get; -var d=b.get; +var c = a.get; +var d = b.get; println(c()); println(c()); @@ -506,42 +512,27 @@ println(d()); __警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个节 __`模块`__ 的内容。 -如果你确实是想修改源码来搞一个自己私人订制的解释器,那么你可以说:“我他妈就是想自己私人订制,你们他妈的管得着吗”, -然后看看源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,还有下面的样例: +如果你确实是想修改源码来搞一个自己私人订制的解释器 ———— “我他妈就是想自己私人订制,你们他妈的管得着吗?”, +参考源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,下面是其中一个样例: 定义新的内置函数: ```C++ // 你可以使用这个宏来直接定义一个新的内置函数 -nas_native(builtin_print); +var builtin_print(context*, gc*); ``` 然后用C++完成这个函数的函数体: ```C++ -var builtin_print(var* local,gc& ngc) -{ - // 局部变量的下标其实是从1开始的 - // 因为local[0]是保留给'me'的空间 - var vec=local[1]; - // 主要部分 - // 一些必要的类型检查和输入合法性检测也要在这里写出 - // 如果检测到问题,用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<"; break; - } - std::cout<localr[1].vec().elems) { + std::cout << i; + } + std::cout << std::flush; + // 最后生成返回值,返回值必须是一个内置的类型, // 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构 // 或者用我们已经定义好的nil/one/zero,这些可以直接使用 return nil; @@ -554,17 +545,24 @@ var builtin_print(var* local,gc& ngc) 可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量,这样可以防止内部所有的申请错误触发垃圾回收。如下所示: ```C++ -var builtin_keys(var* local,gc& ngc) -{ - var hash=local[1]; - if(hash.type!=vm_hash) - return nas_err("keys","\"hash\" must be hash"); +var builtin_keys(context* ctx, gc* ngc) { + auto hash = ctx->localr[1]; + if (hash.type!=vm_hash && hash.type!=vm_map) { + return nas_err("keys", "\"hash\" must be hash"); + } // 使用gc.temp来存储gc管理的变量,防止错误的回收 - var res=ngc.temp=ngc.alloc(vm_vec); - auto& vec=res.vec().elems; - for(auto& iter:hash.hash().elems) - vec.push_back(ngc.newstr(iter.first)); - ngc.temp=nil; + auto res = ngc->temp = ngc->alloc(vm_vec); + auto& vec = res.vec().elems; + if (hash.type==vm_hash) { + for(const auto& iter : hash.hash().elems) { + 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; } ``` @@ -572,21 +570,16 @@ var builtin_keys(var* local,gc& ngc) 这些工作都完成之后,在内置函数注册表中填写它在nasal中的别名,并且在表中填对这个函数的函数指针: ```C++ -struct func -{ - const char* name; - var (*func)(var*,gc&); -} builtin[]= -{ - {"__print",builtin_print}, - {nullptr, nullptr } +nasal_builtin_table builtin[] = { + {"__print", builtin_print}, + {nullptr, nullptr} }; ``` 最后,将其包装到nasal文件中: ```javascript -var print=func(elems...){ +var print = func(elems...) { return __print(elems); }; ``` @@ -594,7 +587,7 @@ var print=func(elems...){ 事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对: ```javascript -var print=func(elems...){ +var print = func(elems...) { return __print; }; ``` @@ -605,7 +598,7 @@ var print=func(elems...){ 当然也有另外一种办法来导入这些nasal文件,下面两种导入方式的效果是一样的: ```javascript -import.dirname.dirname.filename; +use dirname.dirname.filename; import("./dirname/dirname/filename.nas"); ``` @@ -702,10 +695,10 @@ Windows(`.dll`): 下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); 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)); dylib.dlclose(dlhandle.lib); ``` @@ -719,11 +712,11 @@ dylib.dlclose(dlhandle.lib); `dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; 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)); dylib.dlclose(dlhandle.lib); ``` @@ -767,7 +760,7 @@ dylib.dlclose(dlhandle.lib);
自定义类型(开发者教程) -创建一个自定义类型现在不是很困难。下面是使用示例: +创建一个自定义类型很容易。下面是使用示例: ```c++ 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 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; } +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(res.ghost().pointer)) = static_cast(num); + std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; + return nil; +} + var print_new_ghost(var* args, usize size, gc* ngc) { 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"; return nil; } - std::cout << "print_new_ghost: " << res.obj() << " result = " - << *((u32*)res.obj().ptr) << "\n"; + std::cout << "print_new_ghost: " << res.ghost() << " result = " + << *((u32*)res.ghost().pointer) << "\n"; 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的解释器中: ```javascript -foreach(i;[0,1,2,3]) +foreach(i; [0, 1, 2, 3]) print(i) ``` @@ -844,7 +849,7 @@ foreach(i;[0,1,2,3]) code: undefined symbol "i" --> test.nas:1:9 | -1 | foreach(i;[0,1,2,3]) +1 | foreach(i; [0, 1, 2, 3]) | ^ undefined symbol "i" code: undefined symbol "i" @@ -866,8 +871,7 @@ code: undefined symbol "i" `die`函数用于直接抛出错误并终止执行。 ```javascript -func() -{ +func() { println("hello"); die("error occurred this line"); return; @@ -877,17 +881,27 @@ func() ```javascript hello [vm] error: error occurred this line -[vm] native function error. -trace back: - 0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131) - 0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4) - 0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6) -vm stack (0x7fffcd21bc68 , limit 10, total 12): - 0x0000005b | null | - ... - 0x00000057 | str | <0x138ff60> error occurred t... - ... - 0x00000052 | nil | +[vm] error: error occurred in native function + +call trace (main) + call func@0x557513935710() {entry: 0x850} + +trace back (main) + 0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +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 ```
@@ -897,28 +911,41 @@ vm stack (0x7fffcd21bc68 , limit 10, total 12): 这是一个会导致栈溢出的例子: ```javascript -func(f){ +func(f) { return f(f); }( - func(f){ + func(f) { f(f); } )(); ``` ```javascript -[vm] stack overflow -trace back: - 0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5) - 0x000004fb 1349 same call(s) - 0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2) - 0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3) -vm stack (0x7fffd3781d58 , limit 10, total 8108): - 0x00001ffb | func | <0x15f8d90> entry:0x4f9 - 0x00001ffa | func | <0x15f8d90> entry:0x4f9 - 0x00001ff9 | pc | 0x4fb - ... - 0x00001ff2 | addr | 0x7fffd37a16e8 +[vm] error: stack overflow + +call trace (main) + call func@0x564106058620(f) {entry: 0x859} + --> 583 same call(s) + call func@0x5641060586c0(f) {entry: 0x851} + +trace back (main) + 0x000859 45 00 00 01 calll 0x1(a.nas:5) + 0x00085b 4a 00 00 01 callfv 0x1(a.nas:5) + 0x00085b 582 same call(s) + 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 ``` @@ -928,17 +955,19 @@ vm stack (0x7fffd3781d58 , limit 10, total 8108): 如果在执行的时候出现错误,程序会直接终止执行: ```javascript -func(){ +func() { return 0; }()[1]; ``` ```javascript -[vm] callv: must call a vector/hash/string -trace back: - 0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3) -vm stack (0x7fffff539c28 , limit 10, total 1): - 0x00000050 | num | 0 +[vm] error: must call a vector/hash/string but get number + +trace back (main) + 0x000854 47 00 00 00 callv 0x0(a.nas:3) + +stack (0x564993f462b0, limit 10, total 1) + 0x000000 | num | 0 ``` @@ -950,35 +979,48 @@ vm stack (0x7fffff539c28 , limit 10, total 1): ```javascript hello [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) - 0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131) - 0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4) - 0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6) -vm stack (0x7fffe0ffed90 , limit 10, total 12) - 0x0000004a | null | - 0x00000049 | pc | 0x553 - 0x00000048 | addr | 0x7fffe0ffeda0 - ... - 0x00000041 | nil | + 0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +stack (0x55dcb5b43120, limit 10, total 14) + 0x00000d | null | + 0x00000c | pc | 0x856 + 0x00000b | addr | 0x55dcb5b43130 + 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) - [ pc ] | pc | 0xb0 - [ global ] | addr | 0x7fffe0ffe9a0 - [ localr ] | addr | 0x7fffe0ffedf0 - [ memr ] | addr | 0x0 - [ canary ] | addr | 0x7fffe1002990 - [ top ] | addr | 0x7fffe0ffee40 - [ funcr ] | func | <0x677cd0> entry:0xb0 - [ upvalr ] | nil | -global (0x7fffe0ffe9a0 ) - 0x00000000 | func | <0x65fb00> entry:0x5 - 0x00000001 | func | <0x65fb20> entry:0xd + [pc ] | pc | 0x547 + [global] | addr | 0x55dcb5b53130 + [local ] | addr | 0x55dcb5b43190 + [memr ] | addr | 0x0 + [canary] | addr | 0x55dcb5b53110 + [top ] | addr | 0x55dcb5b431f0 + [funcr ] | func | <0x55dcb5b65620> entry:0x547 + [upval ] | nil | + +global (0x55dcb5b53130) + 0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val] + 0x000001 | vec | <0x55dcb5b64c20> [0 val] ... - 0x0000003d | func | <0x66bf00> entry:0x51f - 0x0000003e | hash | <0x65ffa0> {5 val} -local (0x7fffe0ffedf0 ) - 0x00000000 | nil | - 0x00000001 | str | <0x6cb630> error occurred t... + 0x00005e | func | <0x55dcb5b8fc70> entry:0x846 + +local (0x55dcb5b43190 <+7>) + 0x000000 | nil | + 0x000001 | str | <0x55dcb5b33670> error occurred t... + 0x000002 | nil | ``` @@ -1002,16 +1044,18 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: ---> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0) - 0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6) - 0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6) - 0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6) - 0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6) - 0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7) - 0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7) - 0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6) -vm stack (0x7fffd0259138 , limit 10, total 0) + 0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427) + 0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427) + 0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423) + 0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423) + 0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423) +--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1) + 0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1) + 0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1) + +stack (0x55ccd0a1b9d0, limit 10, total 0) >> ``` @@ -1034,23 +1078,26 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: - 0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503) - 0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498) - 0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1) - 0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1) - 0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1) - 0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1) ---> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3) - 0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3) -vm stack (0x7fffd0259138 , limit 10, total 7) - 0x00000047 | pc | 0x566 - 0x00000046 | addr | 0x0 - 0x00000045 | nil | - 0x00000044 | num | 0 - 0x00000043 | nil | - 0x00000042 | nil | - 0x00000041 | func | <0x88d2f0> entry:0x5 + 0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1) +--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3) + 0x000853 40 00 08 56 jf 0x856(test/fib.nas:3) + 0x000854 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000855 56 00 00 00 ret 0x0(test/fib.nas:3) + 0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4) + 0x000857 45 00 00 01 calll 0x1(test/fib.nas:4) + +stack (0x55ccd0a1b9d0, limit 10, total 8) + 0x000007 | pc | 0x869 + 0x000006 | addr | 0x0 + 0x000005 | nil | + 0x000004 | nil | + 0x000003 | num | 0 + 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(..) {..}} + +>>> +``` diff --git a/doc/dev.md b/doc/dev.md index 9d61ff64..6f1aca1e 100644 --- a/doc/dev.md +++ b/doc/dev.md @@ -1,5 +1,7 @@ # __Development History__ +![buringship](./pic/burningship.png) + ## __Contents__ * [__Parser__](#parser) @@ -22,6 +24,7 @@ * [__Release Notes__](#release-notes) * [v8.0](#version-80-release) * [v11.0](#version-110-release) + * [v11.1](#version-111-release) ## __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`). 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`. diff --git a/doc/dev_zh.md b/doc/dev_zh.md index 4a8fc240..baeb5635 100644 --- a/doc/dev_zh.md +++ b/doc/dev_zh.md @@ -1,5 +1,7 @@ # __开发历史记录__ +![buringship](./pic/burningship.png) + ## __目录__ * [__语法分析__](#语法分析) @@ -22,6 +24,7 @@ * [__发行日志__](#发行日志) * [v8.0](#version-80-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`中使用)。 10. 全新的自定义类型注册流程。 + +### __version 11.1 release__ + +1. Bug 修复: 修复 v11.0 的 debugger 无法启动的问题。 + +2. Bug 修复: symbol_finder 不检查 foreach/forindex 中的迭代变量声明的问题。 + +3. 扩展语法 `import.xx.xx` 改为 `use xx.xx`。 diff --git a/doc/namespace.md b/doc/namespace.md index de183f3c..8c3be10b 100644 --- a/doc/namespace.md +++ b/doc/namespace.md @@ -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 +``` diff --git a/doc/nasal-http-test-web.html b/doc/nasal-http-test-web.html index 01191754..d2c2e83c 100644 --- a/doc/nasal-http-test-web.html +++ b/doc/nasal-http-test-web.html @@ -33,10 +33,11 @@

 Nasal | Not another scripting language!

+
- +

 Introduction | 介绍

@@ -79,13 +80,14 @@

 Benchmark | 执行效率

在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。

- 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.

- 下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 图形。 + 下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 和 burningship 图形。


+

 Example | 样例代码

diff --git a/doc/pic/burningship.png b/doc/pic/burningship.png new file mode 100644 index 00000000..63df13c8 Binary files /dev/null and b/doc/pic/burningship.png differ diff --git a/module/fib.cpp b/module/fib.cpp index 529d77b9..c54831a1 100644 --- a/module/fib.cpp +++ b/module/fib.cpp @@ -64,7 +64,7 @@ var set_new_ghost(var* args, usize size, gc* ngc) { return nil; } f64 num = args[1].num(); - *((u32*)res.ghost().pointer) = static_cast(num); + *(reinterpret_cast(res.ghost().pointer)) = static_cast(num); std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; return nil; } diff --git a/module/libfib.nas b/module/libfib.nas index 355a3570..a42ed9b8 100644 --- a/module/libfib.nas +++ b/module/libfib.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); diff --git a/module/libkey.nas b/module/libkey.nas index a494c22b..829e99ce 100644 --- a/module/libkey.nas +++ b/module/libkey.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var ( kbhit, diff --git a/module/libmat.nas b/module/libmat.nas index cd069289..be7ef605 100644 --- a/module/libmat.nas +++ b/module/libmat.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so")); diff --git a/module/libsock.nas b/module/libsock.nas index 15a57c19..fa6b3568 100644 --- a/module/libsock.nas +++ b/module/libsock.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var socket=func(){ var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so")); diff --git a/module/nasocket.cpp b/module/nasocket.cpp index 2ac1069c..7b692c6f 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -71,7 +71,7 @@ var nas_bind(var* args, usize size, gc* ngc) { server.sin_port = htons(args[2].num()); return var::num(static_cast(bind( args[0].num(), - (sockaddr*)&server, + reinterpret_cast(&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); return var::num(static_cast(connect( args[0].num(), - (sockaddr*)&addr, + reinterpret_cast(&addr), sizeof(sockaddr_in) ))); } @@ -110,11 +110,19 @@ var nas_accept(var* args, usize size, gc* ngc) { sockaddr_in client; int socklen = sizeof(sockaddr_in); #ifdef _WIN32 - int client_sd = accept(args[0].num(), (sockaddr*)&client, &socklen); + int client_sd = accept( + args[0].num(), + reinterpret_cast(&client), + &socklen + ); #else - int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen); + int client_sd = accept( + args[0].num(), + reinterpret_cast(&client), + reinterpret_cast(&socklen) + ); #endif - var res=ngc->temp = ngc->alloc(vm_hash); + var res = ngc->temp = ngc->alloc(vm_hash); auto& hash = res.hash().elems; hash["sd"] = var::num(static_cast(client_sd)); 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().length(), args[4].num(), - (sockaddr*)&addr, + reinterpret_cast(&addr), sizeof(sockaddr_in) ))); } @@ -205,7 +213,7 @@ var nas_recvfrom(var* args, usize size, gc* ngc) { buf, args[1].num(), args[2].num(), - (sockaddr*)&addr, + reinterpret_cast(&addr), &socklen ); #else @@ -214,8 +222,8 @@ var nas_recvfrom(var* args, usize size, gc* ngc) { buf, args[1].num(), args[2].num(), - (sockaddr*)&addr, - (socklen_t*)&socklen + reinterpret_cast(&addr), + reinterpret_cast(&socklen) ); #endif hash["size"] = var::num(static_cast(recvsize)); diff --git a/src/ast_dumper.cpp b/src/ast_dumper.cpp index 525d42a9..900fdd8e 100644 --- a/src/ast_dumper.cpp +++ b/src/ast_dumper.cpp @@ -4,6 +4,20 @@ 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) { dump_indent(); std::cout << "null" << format_location(node->get_location()); diff --git a/src/ast_dumper.h b/src/ast_dumper.h index 13856986..e20f5c4d 100644 --- a/src/ast_dumper.h +++ b/src/ast_dumper.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace nasal { @@ -41,6 +42,7 @@ class ast_dumper:public ast_visitor { } public: + bool visit_use_stmt(use_stmt*) override; bool visit_null_expr(null_expr*) override; bool visit_nil_expr(nil_expr*) override; bool visit_number_literal(number_literal*) override; diff --git a/src/ast_visitor.cpp b/src/ast_visitor.cpp index 677c0433..f438b4b1 100644 --- a/src/ast_visitor.cpp +++ b/src/ast_visitor.cpp @@ -7,6 +7,13 @@ bool ast_visitor::visit_expr(expr* node) { 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) { node->accept(this); return true; diff --git a/src/ast_visitor.h b/src/ast_visitor.h index 62f4dff3..4235541d 100644 --- a/src/ast_visitor.h +++ b/src/ast_visitor.h @@ -7,6 +7,7 @@ namespace nasal { class ast_visitor { public: virtual bool visit_expr(expr*); + virtual bool visit_use_stmt(use_stmt*); virtual bool visit_call(call*); virtual bool visit_null_expr(null_expr*); virtual bool visit_nil_expr(nil_expr*); diff --git a/src/coroutine.cpp b/src/coroutine.cpp index c0f5003b..3aeed490 100644 --- a/src/coroutine.cpp +++ b/src/coroutine.cpp @@ -45,7 +45,7 @@ var builtin_cocreate(context* ctx, gc* ngc) { coroutine.ctx.top++; // store old localr on stack - coroutine.ctx.top[0] = var::addr((var*)nullptr); + coroutine.ctx.top[0] = var::addr(nullptr); coroutine.ctx.top++; // store old pc on stack diff --git a/src/dylib_lib.cpp b/src/dylib_lib.cpp index 494466d9..a3402cc6 100644 --- a/src/dylib_lib.cpp +++ b/src/dylib_lib.cpp @@ -53,9 +53,9 @@ var builtin_dlopen(context* ctx, gc* ngc) { // get "get" function, to get the register table #ifdef _WIN32 - void* register_table_get_function = (void*)GetProcAddress( + void* register_table_get_function = reinterpret_cast(GetProcAddress( static_cast(library_object.ghost().pointer), "get" - ); + )); #else void* register_table_get_function = dlsym( library_object.ghost().pointer, "get" diff --git a/src/io_lib.cpp b/src/io_lib.cpp index 848ba136..5af223ac 100644 --- a/src/io_lib.cpp +++ b/src/io_lib.cpp @@ -5,9 +5,6 @@ namespace nasal { const auto file_type_name = "file"; void filehandle_destructor(void* ptr) { - if (static_cast(ptr)==stdin) { - return; - } fclose(static_cast(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[] = { {"__readfile", builtin_readfile}, {"__fout", builtin_fout}, @@ -220,6 +236,9 @@ nasal_builtin_table io_lib_native[] = { {"__readln", builtin_readln}, {"__stat", builtin_stat}, {"__eof", builtin_eof}, + {"__stdin", builtin_stdin}, + {"__stdout", builtin_stdout}, + {"__stderr", builtin_stderr}, {nullptr, nullptr} }; diff --git a/src/io_lib.h b/src/io_lib.h index a4dc1364..e1f4e97c 100644 --- a/src/io_lib.h +++ b/src/io_lib.h @@ -32,6 +32,9 @@ var builtin_tell(context*, gc*); var builtin_readln(context*, gc*); var builtin_stat(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[]; diff --git a/src/nasal_ast.cpp b/src/nasal_ast.cpp index a1c88597..b1c27e14 100644 --- a/src/nasal_ast.cpp +++ b/src/nasal_ast.cpp @@ -7,6 +7,16 @@ void expr::accept(ast_visitor* visitor) { 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) { visitor->visit_call(this); } diff --git a/src/nasal_ast.h b/src/nasal_ast.h index 4d8c6c17..480e304d 100644 --- a/src/nasal_ast.h +++ b/src/nasal_ast.h @@ -10,6 +10,7 @@ namespace nasal { enum class expr_type:u32 { ast_null = 0, // null node + ast_use, // use statement ast_block, // code block ast_nil, // nil keyword ast_num, // number, basic value type @@ -46,6 +47,7 @@ enum class expr_type:u32 { }; class ast_visitor; +class identifier; class hash_pair; class parameter; class slice_vector; @@ -77,6 +79,19 @@ class expr { virtual void accept(ast_visitor*); }; +class use_stmt: public expr { +private: + std::vector 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 { public: call(const span& location, expr_type node_type): @@ -121,7 +136,7 @@ class string_literal: public expr { string_literal(const span& location, const std::string& str): expr(location, expr_type::ast_str), content(str) {} ~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; }; diff --git a/src/nasal_builtin.cpp b/src/nasal_builtin.cpp index 47c13d62..0aec1684 100644 --- a/src/nasal_builtin.cpp +++ b/src/nasal_builtin.cpp @@ -260,7 +260,7 @@ var builtin_keys(context* ctx, gc* ngc) { vec.push_back(ngc->newstr(iter.first)); } } - ngc->temp=nil; + ngc->temp = nil; return res; } @@ -309,8 +309,8 @@ var builtin_substr(context* ctx, gc* ngc) { if (len.type!=vm_num || len.num()<0) { return nas_err("substr", "\"length\" should be number >= 0"); } - usize begin = (usize)beg.num(); - usize length = (usize)len.num(); + auto begin = static_cast(beg.num()); + auto length = static_cast(len.num()); if (begin>=str.str().length()) { 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(ctx->localr[1].num()); if (0<=num && num<128) { - return ngc->newstr((char)num); + return ngc->newstr(static_cast(num)); } else if (128<=num && num<256) { return ngc->newstr(extend[num-128]); } diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index bbb7971e..7c5a72c7 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -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)) { return; } @@ -67,7 +67,7 @@ void codegen::regist_num(const f64 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)) { return; } @@ -94,11 +94,11 @@ void codegen::find_symbol(code_block* node) { scope.insert(i.name); } // 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 (global.count(name)) { 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(); - regist_num(num); + regist_number(num); 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(); - regist_str(str); + regist_string(str); emit(op_pstr, const_string_map.at(str), node->get_location()); } void codegen::bool_gen(bool_literal* node) { f64 num = node->get_flag()? 1:0; - regist_num(num); + regist_number(num); 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()) { calc_gen(child); } @@ -180,7 +180,7 @@ void codegen::hash_gen(hash_expr* node) { for(auto child : node->get_members()) { calc_gen(child->get_value()); 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()); } } @@ -243,7 +243,7 @@ void codegen::func_gen(function* node) { tmp->get_location() ); } - regist_str(name); + regist_string(name); switch(tmp->get_parameter_type()) { case parameter::param_type::normal_parameter: 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()); break; } - add_symbol(name); + regist_symbol(name); } code[newf].num = code.size()+1; // entry @@ -282,7 +282,7 @@ void codegen::func_gen(function* node) { while(local_symbol_find(arg)>=0) { arg = "0" + arg; } - add_symbol(arg); + regist_symbol(arg); // generate code block in_foreach_loop_level.push_back(0); @@ -307,20 +307,23 @@ void codegen::func_gen(function* node) { void codegen::call_gen(call_expr* node) { calc_gen(node->get_first()); - if (code.back().op==op_callb) { + if (code.size() && code.back().op==op_callb) { return; } for(auto i : node->get_calls()) { switch(i->get_type()) { - case expr_type::ast_callh: call_hash_gen((call_hash*)i); break; - case expr_type::ast_callv: call_vector_gen((call_vector*)i); break; - case expr_type::ast_callf: call_func_gen((call_function*)i); break; + case expr_type::ast_callh: + call_hash_gen(reinterpret_cast(i)); break; + case expr_type::ast_callv: + call_vector_gen(reinterpret_cast(i)); break; + case expr_type::ast_callf: + call_func_gen(reinterpret_cast(i)); break; default: break; } } } -void codegen::call_id(identifier* node) { +void codegen::call_identifier(identifier* node) { const auto& name = node->get_name(); if (native_function_mapper.count(name)) { emit(op_callb, @@ -349,10 +352,12 @@ void codegen::call_id(identifier* node) { return; } 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) { - regist_str(node->get_field()); + regist_string(node->get_field()); 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) { emit(op_newh, 0, node->get_location()); for(auto child : node->get_argument()) { - calc_gen(((hash_pair*)child)->get_value()); - const auto& field_name = ((hash_pair*)child)->get_name(); - regist_str(field_name); + auto pair_node = reinterpret_cast(child); + calc_gen(pair_node->get_value()); + 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_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 if (node->get_type()==expr_type::ast_id) { - mcall_id((identifier*)node); + mcall_identifier(reinterpret_cast(node)); return; } // generate call expression until the last sub-node @@ -422,17 +428,22 @@ void codegen::mcall(expr* node) { for(usize i = 0; iget_calls().size()-1; ++i) { auto tmp = call_node->get_calls()[i]; switch(tmp->get_type()) { - case expr_type::ast_callh: call_hash_gen((call_hash*)tmp); break; - case expr_type::ast_callv: call_vector_gen((call_vector*)tmp); break; - case expr_type::ast_callf: call_func_gen((call_function*)tmp); break; + case expr_type::ast_callh: + call_hash_gen(reinterpret_cast(tmp)); break; + case expr_type::ast_callv: + call_vector_gen(reinterpret_cast(tmp)); break; + case expr_type::ast_callf: + call_func_gen(reinterpret_cast(tmp)); break; default: break; } } // the last sub-node will be used to generate memory call expression auto tmp = call_node->get_calls().back(); switch(tmp->get_type()) { - case expr_type::ast_callh: mcall_hash((call_hash*)tmp); break; - case expr_type::ast_callv: mcall_vec((call_vector*)tmp); break; + case expr_type::ast_callh: + mcall_hash(reinterpret_cast(tmp)); break; + case expr_type::ast_callv: + mcall_vec(reinterpret_cast(tmp)); break; case expr_type::ast_callf: die("bad left-value: function call", tmp->get_location()); break; 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(); if (native_function_mapper.count(name)) { 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) { - regist_str(node->get_field()); + regist_string(node->get_field()); 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); if (node->get_tuple()) { auto& vals = node->get_tuple()->get_elements(); - if (identifiers.size()vals.size()) { + die("lack values in multi-definition, expect " + + std::to_string(identifiers.size()) + " but get " + + std::to_string(vals.size()), node->get_tuple()->get_location() ); - } else if (identifiers.size()>vals.size()) { - die("too many values in multi-definition", + return; + } else if (identifiers.size()get_tuple()->get_location() ); + return; } for(usize i = 0; iget_location()); } -void codegen::def_gen(definition_expr* node) { +void codegen::definition_gen(definition_expr* node) { if (node->get_variable_name() && node->get_tuple()) { 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) { emit(op_addeq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_addeqc, const_number_map[num], node->get_location()); } break; @@ -567,8 +585,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_subeq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_subeqc, const_number_map[num], node->get_location()); } break; @@ -580,8 +599,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_muleq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_muleqc, const_number_map[num], node->get_location()); } break; @@ -593,8 +613,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_diveq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_diveqc, const_number_map[num], node->get_location()); } break; @@ -606,8 +627,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_str) { emit(op_lnkeq, 0, node->get_location()); } else { - const auto& str = ((string_literal*)node->get_right())->get_content(); - regist_str(str); + const auto& str = reinterpret_cast( + node->get_right())->get_content(); + regist_string(str); emit(op_lnkeqc, const_string_map[str], node->get_location()); } break; @@ -647,7 +669,7 @@ void codegen::gen_assignment_equal_statement(assignment_expr* node) { // generate symbol load calc_gen(node->get_right()); // get memory space of left identifier - mcall_id((identifier*)node->get_left()); + mcall_identifier(reinterpret_cast(node->get_left())); // check memory get operand type and replace it with load operand switch(code.back().op) { 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) { - if (node->get_value()->get_type()==expr_type::ast_tuple && - node->get_tuple()->get_elements().size()<((tuple_expr*)node->get_value())->get_elements().size()) { - die("lack values in multi-assignment", node->get_value()->get_location()); - } else if (node->get_value()->get_type()==expr_type::ast_tuple && - node->get_tuple()->get_elements().size()>((tuple_expr*)node->get_value())->get_elements().size()) { - die("too many values in multi-assignment", node->get_value()->get_location()); + auto tuple_node = node->get_tuple(); + auto value_node = node->get_value(); + if (value_node->get_type()==expr_type::ast_tuple) { + auto tuple_size = tuple_node->get_elements().size(); + auto value_size = reinterpret_cast(value_node) + ->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_sizeget_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); - 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(value_node) + ->get_elements(); 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; iget_value()); - auto& tuple = node->get_tuple()->get_elements(); + calc_gen(value_node); + auto& tuple = tuple_node->get_elements(); for(i32 i = 0; iget_value()->get_location()); + emit(op_callvi, i, value_node->get_location()); mcall(tuple[i]); // use load operands to avoid meq's pop operand // and this operation changes local and global value directly @@ -775,9 +816,12 @@ void codegen::loop_gen(expr* node) { break_ptr.push_front({}); switch(node->get_type()) { - case expr_type::ast_while: while_gen((while_expr*)node); break; - case expr_type::ast_for: for_gen((for_expr*)node); break; - case expr_type::ast_forei: forei_gen((forei_expr*)node); break; + case expr_type::ast_while: + while_gen(reinterpret_cast(node)); break; + case expr_type::ast_for: + for_gen(reinterpret_cast(node)); break; + case expr_type::ast_forei: + forei_gen(reinterpret_cast(node)); break; default: break; } } @@ -809,7 +853,7 @@ void codegen::for_gen(for_expr* node) { statement_generation(node->get_initial()); usize jmp_place = code.size(); 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()); } else { calc_gen(node->get_condition()); @@ -873,11 +917,11 @@ void codegen::statement_generation(expr* node) { switch(node->get_type()) { case expr_type::ast_null: break; case expr_type::ast_def: - def_gen((definition_expr*)node); break; + definition_gen(reinterpret_cast(node)); break; case expr_type::ast_multi_assign: - multi_assign_gen((multi_assign*)node); break; + multi_assign_gen(reinterpret_cast(node)); break; case expr_type::ast_assign: - assignment_statement((assignment_expr*)node); break; + assignment_statement(reinterpret_cast(node)); break; case expr_type::ast_nil: case expr_type::ast_num: case expr_type::ast_str: @@ -936,7 +980,7 @@ void codegen::and_gen(binary_operator* node) { void codegen::unary_gen(unary_operator* node) { // generate optimized result if (node->get_optimized_number()) { - num_gen(node->get_optimized_number()); + number_gen(node->get_optimized_number()); return; } @@ -954,11 +998,11 @@ void codegen::unary_gen(unary_operator* node) { void codegen::binary_gen(binary_operator* node) { // generate optimized result if (node->get_optimized_number()) { - num_gen(node->get_optimized_number()); + number_gen(node->get_optimized_number()); return; } if (node->get_optimized_string()) { - str_gen(node->get_optimized_string()); + string_gen(node->get_optimized_string()); return; } @@ -1002,8 +1046,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_add, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_addc, const_number_map.at(num), node->get_location()); } return; @@ -1013,8 +1058,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_sub, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_subc, const_number_map.at(num), node->get_location()); } return; @@ -1024,8 +1070,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_mul, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_mulc, const_number_map.at(num), node->get_location()); } return; @@ -1035,8 +1082,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_div, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_divc, const_number_map.at(num), node->get_location()); } return; @@ -1046,8 +1094,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_lnk, 0, node->get_location()); } else { - const auto& str = ((string_literal*)node->get_right())->get_content(); - regist_str(str); + const auto& str = reinterpret_cast( + node->get_right())->get_content(); + regist_string(str); emit(op_lnkc, const_string_map.at(str), node->get_location()); } break; @@ -1057,8 +1106,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_less, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_lessc, const_number_map.at(num), node->get_location()); } return; @@ -1068,8 +1118,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_leq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_leqc, const_number_map.at(num), node->get_location()); } return; @@ -1079,8 +1130,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_grt, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_grtc, const_number_map.at(num), node->get_location()); } return; @@ -1090,8 +1142,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_geq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_geqc, const_number_map.at(num), node->get_location()); } return; @@ -1116,33 +1169,38 @@ void codegen::calc_gen(expr* node) { case expr_type::ast_nil: emit(op_pnil, 0, node->get_location()); break; case expr_type::ast_num: - num_gen((number_literal*)node); break; + number_gen(reinterpret_cast(node)); break; case expr_type::ast_str: - str_gen((string_literal*)node); break; + string_gen(reinterpret_cast(node)); break; case expr_type::ast_id: - call_id((identifier*)node); break; + call_identifier(reinterpret_cast(node)); break; case expr_type::ast_bool: - bool_gen((bool_literal*)node); break; + bool_gen(reinterpret_cast(node)); break; case expr_type::ast_vec: - vec_gen((vector_expr*)node); break; + vector_gen(reinterpret_cast(node)); break; case expr_type::ast_hash: - hash_gen((hash_expr*)node); break; + hash_gen(reinterpret_cast(node)); break; case expr_type::ast_func: - func_gen((function*)node); break; + func_gen(reinterpret_cast(node)); break; case expr_type::ast_call: - call_gen((call_expr*)node); break; + call_gen(reinterpret_cast(node)); break; case expr_type::ast_assign: - assignment_expression((assignment_expr*)node); break; + assignment_expression( + reinterpret_cast(node) + ); + break; case expr_type::ast_ternary: - trino_gen((ternary_operator*)node); break; + trino_gen(reinterpret_cast(node)); break; case expr_type::ast_unary: - unary_gen((unary_operator*)node); break; + unary_gen(reinterpret_cast(node)); break; case expr_type::ast_binary: - binary_gen((binary_operator*)node); break; + binary_gen(reinterpret_cast(node)); break; case expr_type::ast_def: // definition in calculation only should be single def - single_def((definition_expr*)node); - call_id(((definition_expr*)node)->get_variable_name()); + single_def(reinterpret_cast(node)); + call_identifier( + (reinterpret_cast(node))->get_variable_name() + ); break; default: break; } @@ -1150,11 +1208,16 @@ void codegen::calc_gen(expr* node) { void codegen::repl_mode_info_output_gen(expr* node) { switch(node->get_type()) { - case expr_type::ast_id: call_id((identifier*)node); break; - case expr_type::ast_nil: emit(op_pnil, 0, node->get_location()); break; - case expr_type::ast_num: num_gen((number_literal*)node); break; - case expr_type::ast_str: str_gen((string_literal*)node); break; - case expr_type::ast_bool: bool_gen((bool_literal*)node); break; + case expr_type::ast_id: + call_identifier(reinterpret_cast(node)); break; + case expr_type::ast_nil: + emit(op_pnil, 0, node->get_location()); break; + case expr_type::ast_num: + number_gen(reinterpret_cast(node)); break; + case expr_type::ast_str: + string_gen(reinterpret_cast(node)); break; + case expr_type::ast_bool: + bool_gen(reinterpret_cast(node)); break; default: return; } // generate repl output operand @@ -1164,26 +1227,41 @@ void codegen::repl_mode_info_output_gen(expr* node) { } void codegen::block_gen(code_block* node) { + bool is_use_statement = true; for(auto tmp : node->get_expressions()) { + if (tmp->get_type()!=expr_type::ast_use) { + is_use_statement = false; + } 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_id: - if (need_repl_output) { + if (need_repl_output && local.empty()) { repl_mode_info_output_gen(tmp); } else { - check_id_exist((identifier*)tmp); + check_id_exist(reinterpret_cast(tmp)); } break; case expr_type::ast_nil: case expr_type::ast_num: case expr_type::ast_str: case expr_type::ast_bool: - if (need_repl_output) { + if (need_repl_output && local.empty()) { repl_mode_info_output_gen(tmp); } break; case expr_type::ast_cond: - cond_gen((condition_expr*)tmp); break; + cond_gen(reinterpret_cast(tmp)); break; case expr_type::ast_continue: continue_ptr.front().push_back(code.size()); emit(op_jmp, 0, tmp->get_location()); @@ -1208,7 +1286,7 @@ void codegen::block_gen(code_block* node) { case expr_type::ast_multi_assign: statement_generation(tmp); break; case expr_type::ast_ret: - ret_gen((return_expr*)tmp); break; + ret_gen(reinterpret_cast(tmp)); 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); // 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_symbol("arg"); + regist_symbol("arg"); // search global symbols first find_symbol(parse.tree()); diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index 4aaf212c..af1635cb 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -78,34 +78,34 @@ class codegen { err.err("code", loc, info); } - void regist_num(const f64); - void regist_str(const std::string&); + void regist_number(const f64); + void regist_string(const std::string&); 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 global_symbol_find(const std::string&); i32 upvalue_symbol_find(const std::string&); void emit(u8, u32, const span&); - void num_gen(number_literal*); - void str_gen(string_literal*); + void number_gen(number_literal*); + void string_gen(string_literal*); void bool_gen(bool_literal*); - void vec_gen(vector_expr*); + void vector_gen(vector_expr*); void hash_gen(hash_expr*); void func_gen(function*); void call_gen(call_expr*); - void call_id(identifier*); + void call_identifier(identifier*); void call_hash_gen(call_hash*); void call_vector_gen(call_vector*); void call_func_gen(call_function*); void mcall(expr*); - void mcall_id(identifier*); + void mcall_identifier(identifier*); void mcall_vec(call_vector*); void mcall_hash(call_hash*); void multi_def(definition_expr*); void single_def(definition_expr*); - void def_gen(definition_expr*); + void definition_gen(definition_expr*); void assignment_expression(assignment_expr*); void gen_assignment_equal_statement(assignment_expr*); void replace_left_assignment_with_load(const span&); diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index f5b31f03..2904b824 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -2,123 +2,122 @@ #include "symbol_finder.h" #include +#include namespace nasal { -linker::linker(): - show_path(false), lib_loaded(false), - this_file(""), lib_path("") { - char sep = is_windows()? ';':':'; - std::string PATH = getenv("PATH"); - usize last = 0, pos = PATH.find(sep, 0); - while(pos!=std::string::npos) { - std::string dirpath = PATH.substr(last, pos-last); +linker::linker(): show_path_flag(false), library_loaded(false), this_file("") { + const auto seperator= is_windows()? ';':':'; + const auto PATH = std::string(getenv("PATH")); + usize last = 0, position = PATH.find(seperator, 0); + while(position!=std::string::npos) { + std::string dirpath = PATH.substr(last, position-last); if (dirpath.length()) { envpath.push_back(dirpath); } - last = pos+1; - pos = PATH.find(sep, last); + last = position+1; + position = PATH.find(seperator, last); } if (last!=PATH.length()) { envpath.push_back(PATH.substr(last)); } } -std::string linker::get_path(call_expr* node) { - if (node->get_calls()[0]->get_type()==expr_type::ast_callf) { - auto tmp = (call_function*)node->get_calls()[0]; - return ((string_literal*)tmp->get_argument()[0])->get_content(); - } - auto fpath = std::string("."); - for(auto i : node->get_calls()) { - fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field(); +std::string linker::get_path(expr* node) { + if (node->get_type()==expr_type::ast_use) { + auto file_relative_path = std::string(""); + const auto& path = reinterpret_cast(node)->get_path(); + for(auto i : path) { + file_relative_path += i->get_name(); + if (i!=path.back()) { + file_relative_path += (is_windows()? "\\":"/"); + } + } + return file_relative_path + ".nas"; } - return fpath + ".nas"; + auto call_node = reinterpret_cast(node); + auto arguments = reinterpret_cast(call_node->get_calls()[0]); + auto content = reinterpret_cast(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) { // first add file name itself into the file path - std::vector fpath = {filename}; + std::vector path_list = {filename}; // generate search path from environ path for(const auto& p : envpath) { - fpath.push_back(p + (is_windows()? "\\":"/") + filename); + path_list.push_back(p + (is_windows()? "\\":"/") + filename); } // search file - for(const auto& i : fpath) { - if (access(i.c_str(), F_OK)!=-1) { - return i; + for(const auto& path : path_list) { + if (access(path.c_str(), F_OK)!=-1) { + return path; } } // we will find lib.nas in nasal std directory if (filename=="lib.nas") { return is_windows()? - find_file("std\\lib.nas", location): - find_file("std/lib.nas", location); + find_real_file_path("std\\lib.nas", location): + find_real_file_path("std/lib.nas", location); } - if (!show_path) { + if (!show_path_flag) { err.err("link", "in <" + location.file + ">: " + "cannot find file <" + filename + ">, " + - "use <-d> to get detail search path"); + "use <-d> to get detail search path" + ); return ""; } - std::string paths = ""; - for(const auto& i : fpath) { - paths += " -> " + i + "\n"; + auto path_list_info = std::string(""); + for(const auto& path : path_list) { + path_list_info += " -> " + path + "\n"; } err.err("link", "in <" + location.file + ">: " + - "cannot find file <" + filename + "> in these paths:\n" + paths); + "cannot find file <" + filename + + "> in these paths:\n" + path_list_info + ); return ""; } bool linker::import_check(expr* node) { + if (node->get_type()==expr_type::ast_use) { + return true; + } /* call |_id:import - |_callh:std - |_callh:file + |_call_func + |_string:'filename' */ 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) { + auto call_node = reinterpret_cast(node); + auto first_expr = call_node->get_first(); + if (first_expr->get_type()!=expr_type::ast_id) { return false; } - if (((identifier*)tmp->get_first())->get_name()!="import") { + if (reinterpret_cast(first_expr)->get_name()!="import") { return false; } - if (!tmp->get_calls().size()) { + if (!call_node->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; - } + // import("xxx"); - if (tmp->get_calls().size()!=1) { + if (call_node->get_calls().size()!=1) { return false; } -/* - call - |_id:import - |_call_func - |_string:'filename' -*/ - if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) { + auto maybe_func_call = call_node->get_calls()[0]; + if (maybe_func_call->get_type()!=expr_type::ast_callf) { return false; } - auto func_call = (call_function*)tmp->get_calls()[0]; + auto func_call = reinterpret_cast(maybe_func_call); if (func_call->get_argument().size()!=1) { return false; } @@ -128,31 +127,20 @@ bool linker::import_check(expr* node) { 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 - for(const auto& fname : files) { - if (file==fname) { + for(const auto& name : imported_files) { + if (file==name) { return true; } } - files.push_back(file); + imported_files.push_back(file); return false; } -u16 linker::find(const std::string& file) { - for(usize i = 0; i(i); - } - } - std::cerr << "unreachable: using this method incorrectly\n"; - std::exit(-1); - return UINT16_MAX; -} - bool linker::check_self_import(const std::string& file) { - for(const auto& i : module_load_stack) { - if (file==i) { + for(const auto& name : module_load_stack) { + if (file==name) { 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(); } -code_block* linker::import_regular_file(call_expr* node) { - lexer lex; - parse par; +code_block* linker::import_regular_file( + expr* node, std::unordered_set& used_modules) { // get filename 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 - filename = find_file(filename, node->get_location()); - if (!filename.length()) { + filename = find_real_file_path(filename, node->get_location()); + // 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}); } + + // check self import, avoid infinite loading loop if (check_self_import(filename)) { err.err("link", "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}); } - exist(filename); + check_exist_or_record_file(filename); module_load_stack.push_back(filename); // 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 + ">"); 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 + ">"); return new code_block({0, 0, 0, 0, filename}); } - - auto parse_result = par.swap(nullptr); + // swap result out + auto parse_result = nasal_parser.swap(nullptr); // check if parse result has 'import' - auto result = load(parse_result, find(filename)); + auto result = load(parse_result, filename); module_load_stack.pop_back(); return result; } code_block* linker::import_nasal_lib() { - lexer lex; - parse par; - auto filename = find_file("lib.nas", {0, 0, 0, 0, files[0]}); - if (!filename.length()) { - return new code_block({0, 0, 0, 0, filename}); + auto path = find_real_file_path( + "lib.nas", {0, 0, 0, 0, this_file} + ); + if (!path.length()) { + return new code_block({0, 0, 0, 0, path}); } - lib_path = filename; // avoid infinite loading library - if (exist(filename)) { - return new code_block({0, 0, 0, 0, filename}); + if (check_exist_or_record_file(path)) { + return new code_block({0, 0, 0, 0, path}); } // start importing... - if (lex.scan(filename).geterr()) { + lexer nasal_lexer; + parse nasal_parser; + if (nasal_lexer.scan(path).geterr()) { 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", - "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}); } - - auto parse_result = par.swap(nullptr); + // swap result out + auto parse_result = nasal_parser.swap(nullptr); // 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) { @@ -301,14 +284,11 @@ std::string linker::generate_module_name(const std::string& file_path) { "get empty module name from <" + file_path + ">, " + "will not be easily accessed." ); + return module_name; } - if (module_name.length() && '0' <= module_name[0] && module_name[0] <= '9') { - err.warn("link", - "get module <" + module_name + "> from <" + file_path + ">, " + - "will not be easily accessed." - ); - } - if (module_name.length() && module_name.find(".")!=std::string::npos) { + if (std::isdigit(module_name[0]) || + module_name.find(".")!=std::string::npos || + module_name.find("-")!=std::string::npos) { err.warn("link", "get module <" + module_name + "> from <" + file_path + ">, " + "will not be easily accessed." @@ -353,32 +333,43 @@ definition_expr* linker::generate_module_definition(code_block* block) { return def; } -code_block* linker::load(code_block* program_root, u16 fileindex) { - auto tree = new code_block({0, 0, 0, 0, files[fileindex]}); +code_block* linker::load(code_block* program_root, const std::string& filename) { + auto tree = new code_block({0, 0, 0, 0, filename}); // load library, this ast will be linked with root directly // so no extra namespace is generated - if (!lib_loaded) { + if (!library_loaded) { auto nasal_lib_code_block = import_nasal_lib(); // insert nasal lib code to the back of tree link(tree, nasal_lib_code_block); delete nasal_lib_code_block; - lib_loaded = true; + library_loaded = true; } // load imported modules - for(auto& import_ast_node : program_root->get_expressions()) { - if (!import_check(import_ast_node)) { + std::unordered_set used_modules = {}; + for(auto& import_node : program_root->get_expressions()) { + if (!import_check(import_node)) { 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 - const auto loc = import_ast_node->get_location(); - delete import_ast_node; + delete import_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, // and export the necessary global symbols in this code block // by generate a return statement, with a hashmap return value + used_modules.insert(module_path); 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( 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 this_file = self; - files = {self}; + imported_files = {self}; module_load_stack = {self}; + // scan root and import files // then generate a new ast and return to import_ast - // the main file's index is 0 - auto new_tree_root = load(parse.tree(), 0); + auto new_tree_root = load(parse.tree(), self); auto old_tree_root = parse.swap(new_tree_root); delete old_tree_root; return err; diff --git a/src/nasal_import.h b/src/nasal_import.h index fc53fe9f..7fa0df36 100644 --- a/src/nasal_import.h +++ b/src/nasal_import.h @@ -18,43 +18,42 @@ #include "nasal_parse.h" #include "symbol_finder.h" +#include +#include #include +#include namespace nasal { class linker { private: - bool show_path; - bool lib_loaded; + bool show_path_flag; + bool library_loaded; std::string this_file; - std::string lib_path; error err; - std::vector files; + std::vector imported_files; std::vector module_load_stack; std::vector envpath; private: bool import_check(expr*); - bool exist(const std::string&); - u16 find(const std::string&); + bool check_exist_or_record_file(const std::string&); bool check_self_import(const std::string&); std::string generate_self_import_path(const std::string&); void link(code_block*, code_block*); - std::string get_path(call_expr*); - std::string find_file(const std::string&, const span&); - code_block* import_regular_file(call_expr*); + std::string get_path(expr*); + std::string find_real_file_path(const std::string&, const span&); + code_block* import_regular_file(expr*, std::unordered_set&); code_block* import_nasal_lib(); std::string generate_module_name(const std::string&); return_expr* generate_module_return(code_block*); definition_expr* generate_module_definition(code_block*); - code_block* load(code_block*, u16); + code_block* load(code_block*, const std::string&); public: linker(); const error& link(parse&, const std::string&, bool); - const auto& get_file_list() const {return files;} - const auto& get_this_file() const {return this_file;} - const auto& get_lib_path() const {return lib_path;} + const auto& get_file_list() const {return imported_files;} }; } diff --git a/src/nasal_lexer.h b/src/nasal_lexer.h index 2862a571..99c200d5 100644 --- a/src/nasal_lexer.h +++ b/src/nasal_lexer.h @@ -28,6 +28,7 @@ enum class tok:u32 { id, // identifier tktrue, // keyword true tkfalse, // keyword false + use, // keyword use rfor, // loop keyword for forindex, // loop keyword forindex foreach, // loop keyword foreach @@ -103,6 +104,7 @@ class lexer { std::vector toks; const std::unordered_map typetbl { + {"use" ,tok::use }, {"true" ,tok::tktrue }, {"false" ,tok::tkfalse }, {"for" ,tok::rfor }, diff --git a/src/nasal_parse.cpp b/src/nasal_parse.cpp index 71111afb..e62ff280 100644 --- a/src/nasal_parse.cpp +++ b/src/nasal_parse.cpp @@ -132,9 +132,13 @@ bool parse::check_func_end(expr* node) { if (type==expr_type::ast_func) { return true; } else if (type==expr_type::ast_def) { - return check_func_end(((definition_expr*)node)->get_value()); + return check_func_end( + reinterpret_cast(node)->get_value() + ); } else if (type==expr_type::ast_assign) { - return check_func_end(((assignment_expr*)node)->get_right()); + return check_func_end( + reinterpret_cast(node)->get_right() + ); } return false; } @@ -197,6 +201,18 @@ void parse::update_location(expr* node) { 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() { return new null_expr(toks[ptr].loc); } @@ -355,6 +371,7 @@ expr* parse::expression() { die(thisspan, "must use return in functions"); } switch(type) { + case tok::use: return use_stmt_gen(); case tok::tknil: case tok::num: case tok::str: diff --git a/src/nasal_parse.h b/src/nasal_parse.h index 1bcf5080..472b3427 100644 --- a/src/nasal_parse.h +++ b/src/nasal_parse.h @@ -24,6 +24,7 @@ class parse { private: const std::unordered_map tokname { + {tok::use ,"use" }, {tok::rfor ,"for" }, {tok::forindex,"forindex"}, {tok::foreach ,"foreach" }, @@ -92,6 +93,7 @@ class parse { void update_location(expr*); private: + use_stmt* use_stmt_gen(); null_expr* null(); nil_expr* nil(); number_literal* num(); diff --git a/src/optimizer.cpp b/src/optimizer.cpp index 25bd769f..357f0f56 100644 --- a/src/optimizer.cpp +++ b/src/optimizer.cpp @@ -12,7 +12,8 @@ void optimizer::const_string( const auto& left = left_node->get_content(); const auto& right = right_node->get_content(); node->set_optimized_string( - new string_literal(node->get_location(), left+right)); + new string_literal(node->get_location(), left+right) + ); } void optimizer::const_number( @@ -43,7 +44,8 @@ void optimizer::const_number( return; } node->set_optimized_number( - new number_literal(node->get_location(), res)); + new number_literal(node->get_location(), res) + ); } void optimizer::const_number( @@ -62,46 +64,56 @@ void optimizer::const_number( return; } 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) { - node->get_left()->accept(this); - node->get_right()->accept(this); + auto left_node = node->get_left(); + auto right_node = node->get_right(); + left_node->accept(this); + right_node->accept(this); + number_literal* left_num_node = nullptr; number_literal* right_num_node = nullptr; string_literal* left_str_node = nullptr; string_literal* right_str_node = nullptr; - if (node->get_left()->get_type()==expr_type::ast_num) { - left_num_node = (number_literal*)node->get_left(); - } else if (node->get_left()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_left())->get_optimized_number()) { - left_num_node = ((binary_operator*)node->get_left())->get_optimized_number(); - } else if (node->get_left()->get_type()==expr_type::ast_unary && - ((unary_operator*)node->get_left())->get_optimized_number()) { - left_num_node = ((unary_operator*)node->get_left())->get_optimized_number(); + if (left_node->get_type()==expr_type::ast_num) { + left_num_node = reinterpret_cast(left_node); + } else if (left_node->get_type()==expr_type::ast_binary && + reinterpret_cast(left_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(left_node); + left_num_node = optimized->get_optimized_number(); + } else if (left_node->get_type()==expr_type::ast_unary && + reinterpret_cast(left_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(left_node); + left_num_node = optimized->get_optimized_number(); } - if (node->get_right()->get_type()==expr_type::ast_num) { - right_num_node = (number_literal*)node->get_right(); - } else if (node->get_right()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_right())->get_optimized_number()) { - right_num_node = ((binary_operator*)node->get_right())->get_optimized_number(); - } else if (node->get_right()->get_type()==expr_type::ast_unary && - ((unary_operator*)node->get_right())->get_optimized_number()) { - right_num_node = ((unary_operator*)node->get_right())->get_optimized_number(); + if (right_node->get_type()==expr_type::ast_num) { + right_num_node = reinterpret_cast(right_node); + } else if (right_node->get_type()==expr_type::ast_binary && + reinterpret_cast(right_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(right_node); + right_num_node = optimized->get_optimized_number(); + } else if (right_node->get_type()==expr_type::ast_unary && + reinterpret_cast(right_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(right_node); + right_num_node = optimized->get_optimized_number(); } - if (node->get_left()->get_type()==expr_type::ast_str) { - left_str_node = (string_literal*)node->get_left(); - } else if (node->get_left()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_left())->get_optimized_string()) { - left_str_node = ((binary_operator*)node->get_left())->get_optimized_string(); + if (left_node->get_type()==expr_type::ast_str) { + left_str_node = reinterpret_cast(left_node); + } else if (left_node->get_type()==expr_type::ast_binary && + reinterpret_cast(left_node)->get_optimized_string()) { + auto optimized = reinterpret_cast(left_node); + left_str_node = optimized->get_optimized_string(); } - if (node->get_right()->get_type()==expr_type::ast_str) { - right_str_node = (string_literal*)node->get_right(); - } else if (node->get_right()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_right())->get_optimized_string()) { - right_str_node = ((binary_operator*)node->get_right())->get_optimized_string(); + if (right_node->get_type()==expr_type::ast_str) { + right_str_node = reinterpret_cast(right_node); + } else if (right_node->get_type()==expr_type::ast_binary && + reinterpret_cast(right_node)->get_optimized_string()) { + auto optimized = reinterpret_cast(right_node); + right_str_node = optimized->get_optimized_string(); } if (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) { - node->get_value()->accept(this); - number_literal* value_node = nullptr; - if (node->get_value()->get_type()==expr_type::ast_num) { - value_node = (number_literal*)node->get_value(); - } else if (node->get_value()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_value())->get_optimized_number()) { - value_node = ((binary_operator*)node->get_value())->get_optimized_number(); - } else if (node->get_value()->get_type()==expr_type::ast_unary && - ((unary_operator*)node->get_value())->get_optimized_number()) { - value_node = ((unary_operator*)node->get_value())->get_optimized_number(); + auto value = node->get_value(); + value->accept(this); + + number_literal* num_node = nullptr; + if (value->get_type()==expr_type::ast_num) { + num_node = reinterpret_cast(value); + } else if (value->get_type()==expr_type::ast_binary && + reinterpret_cast(value)->get_optimized_number()) { + auto optimized = reinterpret_cast(value); + num_node = optimized->get_optimized_number(); + } else if (value->get_type()==expr_type::ast_unary && + reinterpret_cast(value)->get_optimized_number()) { + auto optimized = reinterpret_cast(value); + num_node = optimized->get_optimized_number(); } - if (value_node) { - const_number(node, value_node); + if (num_node) { + const_number(node, num_node); } return true; } diff --git a/std/fg_env.nas b/std/fg_env.nas index 47d435d0..81bfb929 100644 --- a/std/fg_env.nas +++ b/std/fg_env.nas @@ -1,6 +1,6 @@ # flightgear developer environments simulator (beta) # ValKmjolnir 2022 -import.std.runtime; +use std.runtime; println("-------------------------------------------------------------"); println(" FlightGear simulated-env for developers project, since 2019"); diff --git a/std/io.nas b/std/io.nas index cfca4ad6..d2a950a8 100644 --- a/std/io.nas +++ b/std/io.nas @@ -66,3 +66,9 @@ var stat = func(filename) { var eof = func(filehandle) { return __eof(filehandle); } + +var stdin = func() { return __stdin; }(); + +var stdout = func() { return __stdout;}(); + +var stderr = func() { return __stderr; }(); diff --git a/std/lib.nas b/std/lib.nas index 95cb4a60..ce2afeff 100644 --- a/std/lib.nas +++ b/std/lib.nas @@ -1,13 +1,13 @@ # lib.nas # 2019 ValKmjolnir -import.std.coroutine; -import.std.math; -import.std.string; -import.std.io; -import.std.os; -import.std.bits; -import.std.unix; +use std.coroutine; +use std.math; +use std.string; +use std.io; +use std.os; +use std.bits; +use std.unix; # print is used to print all things in nasal, try and see how it works. # this function uses std::cout to output logs. diff --git a/std/props.nas b/std/props.nas index c9ba7488..5719e37c 100644 --- a/std/props.nas +++ b/std/props.nas @@ -379,7 +379,7 @@ var dump = func { # Don't recurse into aliases, lest we get stuck in a loop if(type != "ALIAS") { 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."); return nil; } - foreach (key; keys(property_list)) { + foreach (var key; keys(property_list)) { namespace[key] = props.getNode(property_list[key],1); } } @@ -743,7 +743,7 @@ var UpdateManager = obj.needs_update = 0; obj.property = {}; obj.is_numeric = {}; - foreach (hashkey; obj.hashkeylist) { + foreach (var hashkey; obj.hashkeylist) { obj.property[hashkey] = props.globals.getNode(hashkey); obj.lastval[hashkey] = nil; # var ty = obj.property[hashkey].getType(); @@ -833,7 +833,7 @@ var UpdateManager = me.needs_update = 0; if (obj != nil or me.lastval == nil) { - foreach (hashkey; me.hashkeylist) { + foreach (var hashkey; me.hashkeylist) { if (me.isnum) { if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) { me.needs_update = 1; @@ -847,7 +847,7 @@ var UpdateManager = } if (me.needs_update) { me.changed(obj); - foreach (hashkey; me.hashkeylist) { + foreach (var hashkey; me.hashkeylist) { me.lastval[hashkey] = obj[hashkey]; } } diff --git a/std/stack.nas b/std/stack.nas index 3e6a14b5..596a6036 100644 --- a/std/stack.nas +++ b/std/stack.nas @@ -1,22 +1,23 @@ # stack.nas # valkmjolnir 2021/3/31 -var stack=func(){ - var vec=[]; - return{ - push:func(elem){ - append(vec,elem); +var stack = func() { + var vec = []; + return { + push: func(elem) { + append(vec, elem); }, - pop:func(){ + pop: func() { return pop(vec); }, - top:func(){ - if(size(vec)!=0) + top: func() { + if (size(vec)!=0) { return vec[-1]; + } }, - clear:func(){ - vec=[]; + clear: func() { + vec = []; }, - empty:func(){ + empty: func() { return size(vec)==0; } }; diff --git a/test/ascii-art.nas b/test/ascii-art.nas index 9e8335f9..2f04b89c 100644 --- a/test/ascii-art.nas +++ b/test/ascii-art.nas @@ -1,5 +1,5 @@ -import.std.padding; -import.std.process_bar; +use std.padding; +use std.process_bar; var char_ttf=[ [" "," "," "," "," "," "], diff --git a/test/auto_crash.nas b/test/auto_crash.nas index b277c4b3..37003957 100644 --- a/test/auto_crash.nas +++ b/test/auto_crash.nas @@ -1,5 +1,5 @@ # Road check and auto pilot by ValKmjolnir -import.std.fg_env; +use std.fg_env; var props = fg_env.props; var geodinfo = fg_env.geodinfo; diff --git a/test/bfs.nas b/test/bfs.nas index 4568d6bf..1520e7a0 100644 --- a/test/bfs.nas +++ b/test/bfs.nas @@ -1,4 +1,4 @@ -import.std.queue; +use std.queue; rand(time(0)); var pixel=[' ','#','.','*']; diff --git a/test/bp.nas b/test/bp.nas index f685a68c..88dadf97 100644 --- a/test/bp.nas +++ b/test/bp.nas @@ -1,4 +1,4 @@ -import.std.mat; +use std.mat; rand(time(0)); diff --git a/test/burningship.nas b/test/burningship.nas new file mode 100644 index 00000000..70750598 --- /dev/null +++ b/test/burningship.nas @@ -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; i8) { + 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(); diff --git a/test/calc.nas b/test/calc.nas index 440701ef..516a2db8 100644 --- a/test/calc.nas +++ b/test/calc.nas @@ -1,5 +1,5 @@ -import.std.padding; -import.std.file; +use std.padding; +use std.file; var source=file.find_all_files_with_extension("./src","cpp","h"); sort(source,func(a,b){return cmp(a,b)<0}); diff --git a/test/console3D.nas b/test/console3D.nas index ccc33328..9226d689 100644 --- a/test/console3D.nas +++ b/test/console3D.nas @@ -21,8 +21,8 @@ # 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 # SOFTWARE. -import.module.libmat; -import.std.runtime; +use module.libmat; +use std.runtime; func(){ # allocate more spaces diff --git a/test/coroutine.nas b/test/coroutine.nas index 4d6c09aa..3eeacbbf 100644 --- a/test/coroutine.nas +++ b/test/coroutine.nas @@ -1,7 +1,7 @@ # coroutine.nas by ValKmjolnir # 2022/5/19 -import.std.process_bar; -import.std.padding; +use std.process_bar; +use std.padding; if(os.platform()=="windows"){ system("chcp 65001"); diff --git a/test/datalog.nas b/test/datalog.nas index 6757856c..eab13efe 100644 --- a/test/datalog.nas +++ b/test/datalog.nas @@ -1,5 +1,5 @@ -import.std.padding; -import.std.process_bar; +use std.padding; +use std.process_bar; var mess=func(vec) { srand(); diff --git a/test/donuts.nas b/test/donuts.nas index 5fe5c27e..58dac114 100644 --- a/test/donuts.nas +++ b/test/donuts.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var mod = math.mod; diff --git a/test/feigenbaum.nas b/test/feigenbaum.nas index 6f1535d0..e7aab39f 100644 --- a/test/feigenbaum.nas +++ b/test/feigenbaum.nas @@ -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 # P6 use binary character var fd = io.open(filename, "wb"); @@ -15,8 +15,8 @@ var ppm=func(filename, width, height, RGB){ var width = 1600; var height = 900; -var bar=(os.platform()=="windows")? - process_bar.bar(front:"sharp",back:"point",sep:"line",length:50): +var bar = (os.platform()=="windows")? + process_bar.bar(front:"sharp", back:"point", sep:"line", length:50): process_bar.high_resolution_bar(50); var RGB = func(h, w) { diff --git a/test/flush_screen.nas b/test/flush_screen.nas index 2c241138..499797a9 100644 --- a/test/flush_screen.nas +++ b/test/flush_screen.nas @@ -1,4 +1,4 @@ -import.module.libkey; +use module.libkey; srand(); diff --git a/test/gc_test.nas b/test/gc_test.nas index b7e223c5..fd3f28ec 100644 --- a/test/gc_test.nas +++ b/test/gc_test.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var test_func = func(test_processes...) { var test_process_total = maketimestamp(); diff --git a/test/hexdump.nas b/test/hexdump.nas index dcfaafca..d77c15e0 100644 --- a/test/hexdump.nas +++ b/test/hexdump.nas @@ -1,7 +1,7 @@ # hexdump.nas by ValKmjolnir # 2021/8/13 -import.std.file; -import.std.runtime; +use std.file; +use std.runtime; # init var hex=func(){ diff --git a/test/httptest.nas b/test/httptest.nas index be0f6bb8..ff6edf56 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -1,4 +1,4 @@ -import.module.libsock; +use module.libsock; var socket = libsock.socket; @@ -320,9 +320,11 @@ while(1){ elsif(path=="/license") http.send(client,respond.ok(io.readfile("./LICENSE"))); elsif(path=="/doc/pic/nasal.png" or + path=="/doc/pic/social.png" or path=="/doc/pic/benchmark.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))); else{ var filename=substr(path,1,size(path)-1); diff --git a/test/json.nas b/test/json.nas index d5be9342..fbc053dc 100644 --- a/test/json.nas +++ b/test/json.nas @@ -1,5 +1,5 @@ -import.std.json; -import.std.process_bar; +use std.json; +use std.process_bar; var ss = json.stringify({ vec:[0,1,2], diff --git a/test/jsonrpc.nas b/test/jsonrpc.nas index f976fc5f..56b44847 100644 --- a/test/jsonrpc.nas +++ b/test/jsonrpc.nas @@ -1,6 +1,6 @@ -import.module.libsock; -import.std.json; -import.std.runtime; +use module.libsock; +use std.json; +use std.runtime; var socket = libsock.socket; diff --git a/test/life.nas b/test/life.nas index 9e292988..3b6fb0ea 100644 --- a/test/life.nas +++ b/test/life.nas @@ -1,5 +1,5 @@ -import.std.process_bar; -import.std.runtime; +use std.process_bar; +use std.runtime; var new_map=func(width,height){ var tmp=[]; diff --git a/test/md5compare.nas b/test/md5compare.nas index 7ffca1be..09a8ef16 100644 --- a/test/md5compare.nas +++ b/test/md5compare.nas @@ -1,98 +1,105 @@ -import.test.md5_self; -import.std.process_bar; -import.std.file; +use test.md5_self; +use std.process_bar; +use std.file; srand(); -var compare=func() { - var ch=[ +var compare = func() { + var ch = [ "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+", "_","*","/","\'","\"",".",",",";",":","<",">","!","@","#","$","%", "^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?" ]; - return func(begin,end) { - var byte=0; - var total=end-begin; - var timestamp=maketimestamp(); + return func(begin, end) { + var byte = 0; + var total = end-begin; + var timestamp = maketimestamp(); timestamp.stamp(); - var bar=process_bar.high_resolution_bar(40); - for(var i=begin;i4){ +var f = func(i, j) { + var (yMin, yMax, xMin, xMax) = (-1.35, 1.35, -3.3, 1.5); + 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) { + var (x1, y1) = ((x0*x0)-(y0*y0)+x, 2*x0*y0+y); + (x0, y0) = (x1, y1); + if ((x0*x0)+(y0*y0)>4) { break; } } - var progress=(i*width+j+1)/(width*height); - if(progress*100-int(progress*100)==0){ - print(bar.bar(progress)," ",progress*100,"% \r"); + 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); - return iter~" "~iter~" "~iter~" "; + iter = iter>=25? 255:int(iter/25*255); + var c = char(iter); + return c~c~c; } -ppm("a.ppm",width,height,f); + +ppm("mandelbrotset.ppm", width, height, f); println(); diff --git a/test/scalar.nas b/test/scalar.nas index 3e6039f5..e2c9feba 100644 --- a/test/scalar.nas +++ b/test/scalar.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; # basic type nil; diff --git a/test/self_ref_module/a.nas b/test/self_ref_module/a.nas index d84ea337..a25d913f 100644 --- a/test/self_ref_module/a.nas +++ b/test/self_ref_module/a.nas @@ -1,5 +1,5 @@ # this will cause error -# import.b; +# use b; println("init a"); var a = "hello"; \ No newline at end of file diff --git a/test/self_ref_module/b.nas b/test/self_ref_module/b.nas index dcace502..a8a66342 100644 --- a/test/self_ref_module/b.nas +++ b/test/self_ref_module/b.nas @@ -1,3 +1,3 @@ -import.a; +use a; println("init b"); \ No newline at end of file diff --git a/test/self_ref_module/c.nas b/test/self_ref_module/c.nas index cb81e5d3..039b2f73 100644 --- a/test/self_ref_module/c.nas +++ b/test/self_ref_module/c.nas @@ -1,3 +1,3 @@ -import.b; +use b; println("init c"); \ No newline at end of file diff --git a/test/self_ref_module/main.nas b/test/self_ref_module/main.nas index 36b0a1a7..da69dfff 100644 --- a/test/self_ref_module/main.nas +++ b/test/self_ref_module/main.nas @@ -1,6 +1,6 @@ -import.c; -import.a; -import.b; +use c; +use a; +use b; println(a); println(b); diff --git a/test/snake.nas b/test/snake.nas index 08b82a90..27babbb6 100644 --- a/test/snake.nas +++ b/test/snake.nas @@ -1,6 +1,6 @@ -import.module.libkey; -import.std.list; -import.std.runtime; +use module.libkey; +use std.list; +use std.runtime; var game=func(x,y){ rand(time(0)); diff --git a/test/tetris.nas b/test/tetris.nas index e2e1cf46..0d8ccc95 100644 --- a/test/tetris.nas +++ b/test/tetris.nas @@ -1,5 +1,5 @@ -import.module.libkey; -import.std.runtime; +use module.libkey; +use std.runtime; var color=[ "\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m", diff --git a/test/watchdog.nas b/test/watchdog.nas index a27813c8..f89c59ae 100644 --- a/test/watchdog.nas +++ b/test/watchdog.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var os_time=func(){ return "[\e[33;1m"~os.time()~"\e[0m] "; diff --git a/test/word_collector.nas b/test/word_collector.nas index 87e7151c..32d69162 100644 --- a/test/word_collector.nas +++ b/test/word_collector.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var to_lower=func(s){ var tmp=""; diff --git a/tools/push.nas b/tools/push.nas index 13918d83..51c45d3c 100644 --- a/tools/push.nas +++ b/tools/push.nas @@ -1,7 +1,9 @@ -println("[",os.time(),"] auto push, please wait..."); +println("[",os.time(),"] (=.=) auto push, please wait..."); while(system("git push")!=0) { - println("[",os.time(),"] failed to push, retrying..."); + println("[",os.time(),"] (ToT) failed to push, retrying..."); unix.sleep(0.5); } + +println("[",os.time(),"] (^o^) auto push complete."); diff --git a/tools/search_file.nas b/tools/search_file.nas index 53290259..d35fb025 100644 --- a/tools/search_file.nas +++ b/tools/search_file.nas @@ -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."); + tips(); + exit(-1); +} else if (size(arg)>1) { + println("too many arguments."); + tips(); exit(-1); } @@ -10,9 +22,9 @@ var needle = arg[0]; var do_flat = func(vec) { var flat = []; var bfs = [vec]; - while(size(bfs)) { + while(size(bfs)!=0) { var d = pop(bfs); - foreach(var f;d.files) { + foreach(var f; d.files) { if (ishash(f)) { append(bfs,f); continue; @@ -20,20 +32,48 @@ var do_flat = func(vec) { 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; } -var count = 0; -foreach(var f;do_flat(file.recursive_find_files("."))) { +var result = []; +var all_files = file.recursive_find_files("."); +foreach(var f; do_flat(all_files)) { var pos = find(needle, f); if (pos == -1) { continue; } - count += 1; var begin = substr(f, 0, pos); 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)."); \ No newline at end of file +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)."); \ No newline at end of file