From 54969681fc93a28d40b3e27180a6a5140a46fa97 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 20 Nov 2022 17:06:13 +0800 Subject: [PATCH] :zap: move lvalue check from parse to codegen --- README.md | 15 +++++--- doc/README_zh.md | 15 +++++--- main.cpp | 2 +- nasal_ast.h | 91 ++++++++++++++++++++++++++++++++------------- nasal_codegen.h | 24 ++++++++++++ nasal_err.h | 6 +-- nasal_parse.h | 41 +++++--------------- stl/lib.nas | 16 ++++---- test/nasal_test.nas | 80 ++++++++++++++++++++++++++++++--------- 9 files changed, 191 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 46b5506..30167fb 100644 --- a/README.md +++ b/README.md @@ -593,16 +593,19 @@ Luckily, we have developed some useful native-functions to help you add modules After 2021/12/3, there are some new functions added to `lib.nas`: ```javascript -var dylib= -{ - dlopen: func(libname){return __dlopen;}, - dlsym: func(lib,sym){return __dlsym; }, +var dylib={ + dlopen: func(libname){ + ... + }, dlclose: func(lib){return __dlclose; }, - dlcall: func(funcptr,args...){return __dlcall;} + dlcall: func(ptr,args...){return __dlcallv}, + limitcall: func(arg_size=0){ + ... + } }; ``` -Aha, as you could see, these functions are used to load dynamic libraries into the nasal runtime and execute. +As you could see, these functions are used to load dynamic libraries into the nasal runtime and execute. Let's see how they work. First, write a cpp file that you want to generate the dynamic lib, take the `fib.cpp` as the example(example codes are in `./module`): diff --git a/doc/README_zh.md b/doc/README_zh.md index 249d828..0702e4c 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -569,16 +569,19 @@ import("./dirname/dirname/filename.nas"); 在2021/12/3更新后,我们给`lib.nas`添加了下面的这一批函数: ```javascript -var dylib= -{ - dlopen: func(libname){return __dlopen;}, - dlsym: func(lib,sym){return __dlsym; }, +var dylib={ + dlopen: func(libname){ + ... + }, dlclose: func(lib){return __dlclose; }, - dlcall: func(funcptr,args...){return __dlcall;} + dlcall: func(ptr,args...){return __dlcallv}, + limitcall: func(arg_size=0){ + ... + } }; ``` -看名字就大概能猜出来,这些函数就是用来加载动态库的,这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。 +这些函数是用来加载动态库的,这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。 首先,用C++写个项目,并且编译成动态库。我们就拿`fib.cpp`作为例子来说明(样例代码可以在`./module`中找到): diff --git a/main.cpp b/main.cpp index bd9e6f7..834bd88 100644 --- a/main.cpp +++ b/main.cpp @@ -99,7 +99,7 @@ void execute(const string& file,const std::vector& argv,const u32 cmd) if(cmd&VM_OPT) optimize(parse.tree()); if(cmd&VM_AST) - parse.print(); + parse.tree().dump(); // code generator gets parser's ast and linker's import file list to generate code gen.compile(parse,ld).chkerr(); diff --git a/nasal_ast.h b/nasal_ast.h index 391274d..5523b9e 100644 --- a/nasal_ast.h +++ b/nasal_ast.h @@ -3,8 +3,7 @@ #include #include -enum ast_node:u32 -{ +enum ast_node:u32{ ast_null=0, // null node ast_root, // mark the root node of ast ast_block, // expression block @@ -22,7 +21,7 @@ enum ast_node:u32 ast_callv, // id[index] ast_callf, // id() ast_subvec, // id[index:index] - ast_args, // mark a sub-tree of function parameters + ast_params, // mark a sub-tree of function parameters ast_default, // default parameter ast_dynamic, // dynamic parameter ast_and, // and keyword @@ -65,23 +64,65 @@ enum ast_node:u32 ast_ret // return keyword, only used in function block }; -const char* ast_name[]= -{ - "null", "root", "block", "file", - "nil", "num", "str", "id", - "func", "hash", "vec", "pair", - "call", "callh", "callv", "callf", - "subvec", "args", "default", "dynamic", - "and", "or", "=", "+=", - "-=", "*=", "/=", "~=", - "==", "!=", "<", "<=", - ">", ">=", "+", "-", - "*", "/", "~", "neg", - "!", "trino", "for", "forindex", - "foreach", "while", "iter", "cond", - "if", "elsif", "else", "ltuple", - "tuple", "def", "massign", "continue", - "break", "return" +const char* ast_name[]={ + "NullNode", + "AbstractSyntaxTreeRoot", + "CodeBlock", + "FileIndex", + "LiteralNil", + "LiteralNumber", + "LiteralString", + "Identifier", + "Function", + "HashMap", + "Vector", + "HashMapPair", + "IdentifierCallExpression", + "HashMapCallExpression", + "VectorCallExpression", + "FunctionCallExpression", + "SubVector", + "ParameterList", + "DefaultParameter", + "DynamicParameter", + "AndExpression", + "OrExpression", + "EqualExpression", + "AddEqualExpression", + "SubEqualExpression", + "MultEqualExpression", + "DivEqualExpression", + "LinkEqualExpression", + "CompareEqualExpression", + "NotEqualExpression", + "LessExpression", + "LessOrEqualExpression", + "GreatExpression", + "GreatOrEqualExpression", + "AddExpression", + "SubExpression", + "MultExpression", + "DivExpression", + "LinkExpression", + "NegativeExpression", + "NotExpression", + "TrinocularExpression", + "ForLoop", + "ForindexLoop", + "ForeachLoop", + "WhileLoop", + "Iterator", + "ConditionExpression", + "IfExpression", + "ElsifExpression", + "ElseExpression", + "LeftTuple", + "Tuple", + "Definition", + "MultipleAssignment", + "ContinueExpression", + "BreakExpression", + "ReturnExpression" }; class ast @@ -98,8 +139,8 @@ public: nd_line(l),nd_col(c),nd_type(t),nd_num(0){} ast(const ast&); ast(ast&&); - void print_tree(); - void print(u32,bool,std::vector&); + void dump() const; + void print(u32,bool,std::vector&) const; void clear(); ast& operator=(const ast&); @@ -174,13 +215,13 @@ void ast::clear() nd_child.clear(); } -void ast::print_tree() +void ast::dump() const { std::vector tmp; print(0,false,tmp); } -void ast::print(u32 depth,bool last,std::vector& indent) +void ast::print(u32 depth,bool last,std::vector& indent) const { for(auto& i:indent) std::cout<& indent) std::cout<<":"< "< fbstk; std::stack festk; + + bool check_memory_reachable(const ast&); void die(const string&,const u32,const u32,const u32); void regist_num(const f64); @@ -272,6 +274,25 @@ public: const std::vector& codes() const {return code;} }; +bool codegen::check_memory_reachable(const ast& node) +{ + if(node.type()==ast_call){ + const ast& tmp=node.child().back(); + if(tmp.type()==ast_callf){ + die("bad left-value",tmp.line(),tmp.col(),1); + return false; + } + if(tmp.type()==ast_callv && (tmp.size()==0 || tmp.size()>1 || tmp[0].type()==ast_subvec)){ + die("bad left-value",tmp.line(),tmp.col(),1); + return false; + } + }else if(node.type()!=ast_id){ + die("bad left-value",node.line(),node.col(),1); + return false; + } + return true; +} + void codegen::die(const string& info,const u32 line,const u32 col,const u32 len=1) { err.load(file[fileindex]); @@ -592,6 +613,9 @@ void codegen::call_func(const ast& node) */ void codegen::mcall(const ast& node) { + if(!check_memory_reachable(node)){ + return; + } if(node.type()==ast_id) { mcall_id(node); diff --git a/nasal_err.h b/nasal_err.h index e7468f5..9619d09 100644 --- a/nasal_err.h +++ b/nasal_err.h @@ -99,9 +99,9 @@ public: res.push_back(line); } } - const string& operator[](usize n){return res[n];} - const string& name(){return file;} - usize size(){return res.size();} + const string& operator[](usize n) const {return res[n];} + const string& name() const {return file;} + usize size() const {return res.size();} }; class error:public flstream diff --git a/nasal_parse.h b/nasal_parse.h index 7a3a524..400ba51 100644 --- a/nasal_parse.h +++ b/nasal_parse.h @@ -107,7 +107,6 @@ private: bool check_func_end(const ast&); bool check_special_call(); bool need_semi_check(const ast&); - void check_memory_reachable(const ast&); ast null(); ast nil(); ast num(); @@ -117,7 +116,7 @@ private: ast hash(); ast pair(); ast func(); - ast args(); + ast params(); ast lcurve_expr(); ast expr(); ast exprs(); @@ -138,7 +137,7 @@ private: ast incurve_def(); ast outcurve_def(); ast multi_id(); - ast multi_scalar(bool); + ast multi_scalar(); ast multi_assgin(); ast loop(); ast while_loop(); @@ -154,7 +153,6 @@ public: ptr(0),in_func(0),in_loop(0), toks(nullptr),root(0,0,ast_root), err(e){} - void print(){root.print_tree();} const error& compile(const lexer&); ast& tree(){return root;} const ast& tree() const {return root;} @@ -183,7 +181,6 @@ void parse::die(u32 line,u32 col,u32 len,string info,bool prev=false) col-=2; len+=2; } - // used to report lack of ',' ';' if(prev && ptr){ line=toks[ptr-1].line; @@ -191,7 +188,6 @@ void parse::die(u32 line,u32 col,u32 len,string info,bool prev=false) len=toks[ptr-1].str.length(); len+=toks[ptr-1].type==tok_str?2:0; } - err.err("parse",line,col,lookahead(tok_eof)?1:len,info); } void parse::match(u32 type,const char* info) @@ -305,20 +301,6 @@ bool parse::need_semi_check(const ast& node) } return !check_func_end(node); } -void parse::check_memory_reachable(const ast& node) -{ - if(node.type()==ast_call){ - const ast& tmp=node.child().back(); - if(tmp.type()==ast_callf){ - die(tmp.line(),tmp.col(),1,"bad left-value"); - } - if(tmp.type()==ast_callv && (tmp.size()==0 || tmp.size()>1 || tmp[0].type()==ast_subvec)){ - die(tmp.line(),tmp.col(),1,"bad left-value"); - } - }else if(node.type()!=ast_id){ - die(node.line(),node.col(),1,"bad left-value"); - } -} ast parse::null() { return {toks[ptr].line,toks[ptr].col,ast_null}; @@ -411,7 +393,7 @@ ast parse::func() ast node(toks[ptr].line,toks[ptr].col,ast_func); match(tok_func); if(lookahead(tok_lcurve)){ - node.add(args()); + node.add(params()); }else{ node.add(null()); } @@ -419,9 +401,9 @@ ast parse::func() --in_func; return node; } -ast parse::args() +ast parse::params() { - ast node(toks[ptr].line,toks[ptr].col,ast_args); + ast node(toks[ptr].line,toks[ptr].col,ast_params); match(tok_lcurve); while(!lookahead(tok_rcurve)){ ast tmp=id(); @@ -534,7 +516,6 @@ ast parse::calc() tmp.add(calc()); node=std::move(tmp); }else if(tok_eq<=toks[ptr].type && toks[ptr].type<=tok_lnkeq){ - check_memory_reachable(node); // tok_eq~tok_lnkeq is 37 to 42,ast_equal~ast_lnkeq is 21~26 ast tmp(toks[ptr].line,toks[ptr].col,toks[ptr].type-tok_eq+ast_equal); tmp.add(std::move(node)); @@ -764,7 +745,7 @@ ast parse::definition() } match(tok_eq); if(lookahead(tok_lcurve)){ - node.add(check_tuple()?multi_scalar(false):calc()); + node.add(check_tuple()?multi_scalar():calc()); }else{ node.add(calc()); } @@ -804,7 +785,7 @@ ast parse::multi_id() } return node; } -ast parse::multi_scalar(bool check_call_memory) +ast parse::multi_scalar() { // if check_call_memory is true,we will check if value called here can reach a memory space const u32 panic_set[]={ @@ -817,9 +798,6 @@ ast parse::multi_scalar(bool check_call_memory) match(tok_lcurve); while(!lookahead(tok_rcurve)){ node.add(calc()); - if(check_call_memory){ - check_memory_reachable(node.child().back()); - } if(lookahead(tok_comma)){ match(tok_comma); }else if(lookahead(tok_eof)){ @@ -834,14 +812,14 @@ ast parse::multi_scalar(bool check_call_memory) ast parse::multi_assgin() { ast node(toks[ptr].line,toks[ptr].col,ast_multi_assign); - node.add(multi_scalar(true)); + node.add(multi_scalar()); match(tok_eq); if(lookahead(tok_eof)){ die(thisline,thiscol,thislen,"expected value list"); return node; } if(lookahead(tok_lcurve)){ - node.add(check_tuple()?multi_scalar(false):calc()); + node.add(check_tuple()?multi_scalar():calc()); }else{ node.add(calc()); } @@ -948,7 +926,6 @@ ast parse::iter_gen() while(is_call(toks[ptr].type)){ node.add(call_scalar()); } - check_memory_reachable(node); } return node; } diff --git a/stl/lib.nas b/stl/lib.nas index f91941b..4e7b60a 100644 --- a/stl/lib.nas +++ b/stl/lib.nas @@ -456,14 +456,14 @@ var dylib={ # get dlcall function with limited parameter list limitcall: func(arg_size=0){ if(arg_size==0){return func(ptr){return __dlcall};} - else if(arg_size==1){return func(ptr,_0){return __dlcall};} - else if(arg_size==2){return func(ptr,_0,_1){return __dlcall};} - else if(arg_size==3){return func(ptr,_0,_1,_2){return __dlcall};} - else if(arg_size==4){return func(ptr,_0,_1,_2,_3){return __dlcall};} - else if(arg_size==5){return func(ptr,_0,_1,_2,_3,_4){return __dlcall};} - else if(arg_size==6){return func(ptr,_0,_1,_2,_3,_4,_5){return __dlcall};} - else if(arg_size==7){return func(ptr,_0,_1,_2,_3,_4,_5,_6){return __dlcall};} - else if(arg_size==8){return func(ptr,_0,_1,_2,_3,_4,_5,_6,_7){return __dlcall};} + elsif(arg_size==1){return func(ptr,_0){return __dlcall};} + elsif(arg_size==2){return func(ptr,_0,_1){return __dlcall};} + elsif(arg_size==3){return func(ptr,_0,_1,_2){return __dlcall};} + elsif(arg_size==4){return func(ptr,_0,_1,_2,_3){return __dlcall};} + elsif(arg_size==5){return func(ptr,_0,_1,_2,_3,_4){return __dlcall};} + elsif(arg_size==6){return func(ptr,_0,_1,_2,_3,_4,_5){return __dlcall};} + elsif(arg_size==7){return func(ptr,_0,_1,_2,_3,_4,_5,_6){return __dlcall};} + elsif(arg_size==8){return func(ptr,_0,_1,_2,_3,_4,_5,_6,_7){return __dlcall};} else{return func(ptr,args...){return __dlcallv};} } }; diff --git a/test/nasal_test.nas b/test/nasal_test.nas index ea8a639..42b7b58 100644 --- a/test/nasal_test.nas +++ b/test/nasal_test.nas @@ -32,22 +32,66 @@ var z2 = { listt2: y2, }; -println(w);#//1 -println(x);#//hello -println(y);#//[1,hello] -println(z);#//{...} -println(z1);#//{...} -println(y2);#//[...] -println(y[0]);#//1 -println(y1[2][1]);#//hello -println(z.numb);#//1 -println(z.listt[2][1]);#//hello -println(z1.hashh.listt[2][1]);#//hello +println(w); #//1 +println(x); #//hello +println(y); #//[1,hello] +println(z); #//{...} +println(z1); #//{...} +println(y2); #//[...] +println(y[0]); #//1 +println(y1[2][1]); #//hello +println(z.numb); #//1 +println(z.listt[2][1]); #//hello +println(z1.hashh.listt[2][1]); #//hello println(y2[3].hashh.listt[2][1]);#//hello -println(f);#//func(..){..} -f();#//f is called -println(z.funcc);#//func(..){..} -z.funcc();#//f is called -println(z.funcccall);#//func(..){..} -z2.listt2[3].hashh.funcc();#//f is called -println(y1[f2()][w]);#//hello \ No newline at end of file +println(f); #//func(..){..} +f(); #//f is called +println(z.funcc); #//func(..){..} +z.funcc(); #//f is called +println(z.funcccall); #//func(..){..} +z2.listt2[3].hashh.funcc(); #//f is called +println(y1[f2()][w]); #//hello + + +# ValKmjolnir +func(){ + var tm=maketimestamp(); + var duration=0; + var f1=func(){} + var f2=func(){var a=1;return a+1;} + var f3=func(){var (a,b)=(1,1);return a+b+1;} + tm.stamp(); + for(var i=0;i<1e7;i+=1); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); + tm.stamp(); + for(var i=0;i<1e7;i+=1)f1(); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); + tm.stamp(); + for(var i=0;i<1e7;i+=1)func{}(); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); + tm.stamp(); + for(var i=0;i<1e7;i+=1)f2(); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); + for(var i=0;i<1e7;i+=1) + func{ + var a=1; + return a+1; + }(); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); + tm.stamp(); + for(var i=0;i<1e7;i+=1)f3(); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); + for(var i=0;i<1e7;i+=1) + func{ + var (a,b)=(1,1); + return a+b+1; + }(); + duration=tm.elapsedMSec()/1e3; + println("total ",duration," sec, ",str(int(1e7/duration/1e6))," M calc/sec"); +}(); \ No newline at end of file