diff --git a/README.md b/README.md index 8c6990c..85e7854 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,92 @@ f(1024,2048); 0x00000011: nop 0x00000000 ``` +2021/6/21 update: Now gc will not collect nullptr.And the function of assignment is complete,now these kinds of assignment is allowed: + +```javascript +var f=func() +{ + var _=[{_:0},{_:1}]; + return func(x) + { + return _[x]; + } +} +var m=f(); +m(0)._=m(1)._=10; + +[0,1,2][1:2][0]=0; +``` + +In the old version,parser will check this left-value and tells that these kinds of left-value are not allowed(bad lvalue). + +But now it can work.And you could see its use by reading the code above.To make sure this assignment works correctly,codegen will generate byte code by nasal_codegen::call_gen() instead of nasal_codegen::mcall_gen(),and the last child of the ast will be generated by nasal_codegen::mcall_gen().So the bytecode is totally different now: + +```asm +.number 10 +.number 2 +.symbol _ +.symbol x +0x00000000: intg 0x00000002 +0x00000001: newf 0x00000005 +0x00000002: intl 0x00000002 +0x00000003: offset 0x00000001 +0x00000004: jmp 0x00000017 +0x00000005: newh 0x00000000 +0x00000006: pzero 0x00000000 +0x00000007: happ 0x00000000 (_) +0x00000008: newh 0x00000000 +0x00000009: pone 0x00000000 +0x0000000a: happ 0x00000000 (_) +0x0000000b: newv 0x00000002 +0x0000000c: loadl 0x00000001 +0x0000000d: newf 0x00000012 +0x0000000e: intl 0x00000003 +0x0000000f: offset 0x00000002 +0x00000010: para 0x00000001 (x) +0x00000011: jmp 0x00000016 +0x00000012: calll 0x00000001 +0x00000013: calll 0x00000002 +0x00000014: callv 0x00000000 +0x00000015: ret 0x00000000 +0x00000016: ret 0x00000000 +0x00000017: loadg 0x00000000 +0x00000018: callg 0x00000000 +0x00000019: callfv 0x00000000 +0x0000001a: loadg 0x00000001 +0x0000001b: pnum 0x00000000 (10.000000) +0x0000001c: callg 0x00000001 +0x0000001d: pone 0x00000000 +0x0000001e: callfv 0x00000001 +0x0000001f: mcallh 0x00000000 (_) +0x00000020: meq 0x00000000 +0x00000021: callg 0x00000001 +0x00000022: pzero 0x00000000 +0x00000023: callfv 0x00000001 +0x00000024: mcallh 0x00000000 (_) +0x00000025: meq 0x00000000 +0x00000026: pop 0x00000000 +0x00000027: pzero 0x00000000 +0x00000028: pzero 0x00000000 +0x00000029: pone 0x00000000 +0x0000002a: pnum 0x00000001 (2.000000) +0x0000002b: newv 0x00000003 +0x0000002c: slcbeg 0x00000000 +0x0000002d: pone 0x00000000 +0x0000002e: pnum 0x00000001 (2.000000) +0x0000002f: slc2 0x00000000 +0x00000030: slcend 0x00000000 +0x00000031: pzero 0x00000000 +0x00000032: mcallv 0x00000000 +0x00000033: meq 0x00000000 +0x00000034: pop 0x00000000 +0x00000035: nop 0x00000000 +``` + +As you could see from the bytecode above,mcall/mcallv/mcallh operands' using frequency will reduce,call/callv/callh/callfv/callfh at the opposite. + +And because of the new structure of mcall, addr_stack, a stack used to store the memory address, is deleted from nasal_vm, and now nasal_vm use nasal_val** mem_addr to store the memory address. This will not cause fatal errors because the memory address is used __immediately__ after getting it. + ## Test data ### version 6.5(i5-8250U windows10 2021/6/19) diff --git a/nasal_codegen.h b/nasal_codegen.h index 8bdc797..3d69d86 100644 --- a/nasal_codegen.h +++ b/nasal_codegen.h @@ -3,68 +3,68 @@ enum op_code { - op_nop, // do nothing and end the vm main loop - op_intg, // global scope size - op_intl, // local scope size - op_offset, // offset of local scope of function in closure - op_loadg, // load global symbol value - op_loadl, // load local symbol value - op_pnum, // push constant number to the stack - op_pone, // push 1 to the stack - op_pzero, // push 0 to the stack - op_pnil, // push constant nil to the stack - op_pstr, // push constant string to the stack - op_newv, // push new vector with initial values from stack - op_newh, // push new hash to the stack - op_newf, // push new function to the stack - op_happ, // hash append - op_para, // normal parameter - op_defpara, // default parameter - op_dynpara, // dynamic parameter - op_unot, // ! - op_usub, // - - op_add, // + - op_sub, // - - op_mul, // * - op_div, // / - op_lnk, // ~ - op_addeq, // += - op_subeq, // -= - op_muleq, // *= - op_diveq, // /= - op_lnkeq, // ~= - op_meq, // = - op_eq, // == - op_neq, // != - op_less, // < - op_leq, // <= - op_grt, // > - op_geq, // >= - op_pop, // pop a value from stack - op_jmp, // jump with no condition - op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP - op_jf, // used in conditional/loop,jmp when condition is false and POP STACK - op_cnt, // add counter for forindex/foreach - op_cntpop, // pop counter - op_findex, // index counter on the top of forindex_stack plus 1 - op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector - op_callg, // call value in global scope - op_calll, // call value in local scope - op_callv, // call vec[index] - op_callvi, // call vec[immediate] (used in multi-assign/multi-define) - op_callh, // call hash.label - op_callfv, // call function(vector as parameters) - op_callfh, // call function(hash as parameters) - op_callb, // call builtin-function - op_slcbegin, // begin of slice like: vec[1,2,3:6,0,-1] - op_slcend, // end of slice - op_slc, // slice like vec[1] - op_slc2, // slice like vec[nil:10] - op_mcallg, // get memory space of value in global scope - op_mcalll, // get memory space of value in local scope - op_mcallv, // get memory space of vec[index] - op_mcallh, // get memory space of hash.label - op_ret // return + op_nop, // do nothing and end the vm main loop + op_intg, // global scope size + op_intl, // local scope size + op_offset, // offset of local scope of function in closure + op_loadg, // load global symbol value + op_loadl, // load local symbol value + op_pnum, // push constant number to the stack + op_pone, // push 1 to the stack + op_pzero, // push 0 to the stack + op_pnil, // push constant nil to the stack + op_pstr, // push constant string to the stack + op_newv, // push new vector with initial values from stack + op_newh, // push new hash to the stack + op_newf, // push new function to the stack + op_happ, // hash append + op_para, // normal parameter + op_defpara, // default parameter + op_dynpara, // dynamic parameter + op_unot, // ! + op_usub, // - + op_add, // + + op_sub, // - + op_mul, // * + op_div, // / + op_lnk, // ~ + op_addeq, // += + op_subeq, // -= + op_muleq, // *= + op_diveq, // /= + op_lnkeq, // ~= + op_meq, // = + op_eq, // == + op_neq, // != + op_less, // < + op_leq, // <= + op_grt, // > + op_geq, // >= + op_pop, // pop a value from stack + op_jmp, // jump with no condition + op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP + op_jf, // used in conditional/loop,jmp when condition is false and POP STACK + op_cnt, // add counter for forindex/foreach + op_cntpop, // pop counter + op_findex, // index counter on the top of forindex_stack plus 1 + op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector + op_callg, // call value in global scope + op_calll, // call value in local scope + op_callv, // call vec[index] + op_callvi, // call vec[immediate] (used in multi-assign/multi-define) + op_callh, // call hash.label + op_callfv, // call function(vector as parameters) + op_callfh, // call function(hash as parameters) + op_callb, // call builtin-function + op_slcbegin,// begin of slice like: vec[1,2,3:6,0,-1] + op_slcend, // end of slice + op_slc, // slice like vec[1] + op_slc2, // slice like vec[nil:10] + op_mcallg, // get memory space of value in global scope + op_mcalll, // get memory space of value in local scope + op_mcallv, // get memory space of vec[index] + op_mcallh, // get memory space of hash.label + op_ret // return }; struct @@ -499,16 +499,27 @@ void nasal_codegen::call_func(nasal_ast& ast) void nasal_codegen::mcall(nasal_ast& ast) { - mcall_id(ast.get_type()==ast_id?ast:ast.get_children()[0]); - int child_size=ast.get_children().size(); - for(int i=1;i1 || tmp.get_children()[0].get_type()==ast_subvec)) - die(tmp.get_line(),"cannot get memory of temp sliced vector"); - } + nasal_ast& tmp=node.get_children().back(); + if(tmp.get_type()==ast_callf) + die(tmp.get_line(),"bad left-value"); + if(tmp.get_type()==ast_callv && (tmp.get_children().size()>1 || tmp.get_children()[0].get_type()==ast_subvec)) + die(tmp.get_line(),"bad left-value"); } else if(node.get_type()!=ast_id) - die(node.get_line(),"cannot get memory of temp data"); + die(node.get_line(),"bad left-value"); return; } nasal_ast nasal_parse::null_node_gen() @@ -428,9 +424,9 @@ nasal_ast nasal_parse::args_gen() else if(tmp.get_type()==ast_dynamic_id) checked_dynamic_ids=true; if(checked_default_val && tmp.get_type()!=ast_default_arg) - die(tmp.get_line(),"must use default parameters after using it once: "+args_format); + die(tmp.get_line(),"must use default paras after using it once: "+args_format); if(checked_dynamic_ids && &tmp!=&node.get_children().back()) - die(tmp.get_line(),"dynamic parameter must be the end: "+args_format); + die(tmp.get_line(),"dynamic para must be the end: "+args_format); } std::unordered_map argname_table; for(auto& tmp:node.get_children()) @@ -439,8 +435,8 @@ nasal_ast nasal_parse::args_gen() switch(tmp.get_type()) { case ast_dynamic_id: - case ast_id:new_name=tmp.get_str();break; - case ast_default_arg:tmp.get_children()[0].get_str();break; + case ast_id: new_name=tmp.get_str();break; + case ast_default_arg:new_name=tmp.get_children()[0].get_str();break; } if(argname_table.count(new_name)) die(tmp.get_line(),"parameter's name repeats: "+new_name); diff --git a/nasal_vm.h b/nasal_vm.h index fa0564e..02fc863 100644 --- a/nasal_vm.h +++ b/nasal_vm.h @@ -5,16 +5,16 @@ class nasal_vm { private: /* reference from nasal_gc */ - nasal_val**& stack_top; // stack top + nasal_val**& stack_top;// stack top /* values of nasal_vm */ - int pc; // program counter - bool loop_mark; // when mark is false,break the main loop - std::stack ret; // ptr stack stores address for function to return - std::stack counter; // iterator stack for forindex/foreach - std::vector str_table; // symbols used in process - std::vector exec_code; // byte codes store here - std::stack addr_stack;// used for mem_call - nasal_gc gc; // garbage collector + int pc; // program counter + bool loop_mark;// when mark is false,break the main loop + std::stack ret; // ptr stack stores address for function to return + std::stack counter; // iterator stack for forindex/foreach + std::vector str_table;// symbols used in process + std::vector exec_code;// byte codes store here + nasal_val** mem_addr; // used for mem_call + nasal_gc gc; // garbage collector void die(std::string); bool condition(nasal_val*); @@ -308,48 +308,42 @@ void nasal_vm::opr_lnk() } void nasal_vm::opr_addeq() { - nasal_val** mem_addr=addr_stack.top();addr_stack.pop(); nasal_val* new_val=gc.gc_alloc(vm_num); - new_val->ptr.num=mem_addr[0]->to_number()+stack_top[0]->to_number(); - stack_top[0]=mem_addr[0]=new_val; + new_val->ptr.num=mem_addr[0]->to_number()+stack_top[-1]->to_number(); + (--stack_top)[0]=mem_addr[0]=new_val; return; } void nasal_vm::opr_subeq() { - nasal_val** mem_addr=addr_stack.top();addr_stack.pop(); nasal_val* new_val=gc.gc_alloc(vm_num); - new_val->ptr.num=mem_addr[0]->to_number()-stack_top[0]->to_number(); - stack_top[0]=mem_addr[0]=new_val; + new_val->ptr.num=mem_addr[0]->to_number()-stack_top[-1]->to_number(); + (--stack_top)[0]=mem_addr[0]=new_val; return; } void nasal_vm::opr_muleq() { - nasal_val** mem_addr=addr_stack.top();addr_stack.pop(); nasal_val* new_val=gc.gc_alloc(vm_num); - new_val->ptr.num=mem_addr[0]->to_number()*stack_top[0]->to_number(); - stack_top[0]=mem_addr[0]=new_val; + new_val->ptr.num=mem_addr[0]->to_number()*stack_top[-1]->to_number(); + (--stack_top)[0]=mem_addr[0]=new_val; return; } void nasal_vm::opr_diveq() { - nasal_val** mem_addr=addr_stack.top();addr_stack.pop(); nasal_val* new_val=gc.gc_alloc(vm_num); - new_val->ptr.num=mem_addr[0]->to_number()/stack_top[0]->to_number(); - stack_top[0]=mem_addr[0]=new_val; + new_val->ptr.num=mem_addr[0]->to_number()/stack_top[-1]->to_number(); + (--stack_top)[0]=mem_addr[0]=new_val; return; } void nasal_vm::opr_lnkeq() { - nasal_val** mem_addr=addr_stack.top();addr_stack.pop(); nasal_val* new_val=gc.gc_alloc(vm_str); - *new_val->ptr.str=mem_addr[0]->to_string()+stack_top[0]->to_string(); - stack_top[0]=mem_addr[0]=new_val; + *new_val->ptr.str=mem_addr[0]->to_string()+stack_top[-1]->to_string(); + (--stack_top)[0]=mem_addr[0]=new_val; return; } void nasal_vm::opr_meq() { - addr_stack.top()[0]=stack_top[0]; - addr_stack.pop(); + mem_addr[0]=(--stack_top)[0]; return; } void nasal_vm::opr_eq() @@ -738,18 +732,20 @@ void nasal_vm::opr_slc2() } void nasal_vm::opr_mcallg() { - addr_stack.push(&gc.global[exec_code[pc].num]); + mem_addr=&gc.global[exec_code[pc].num]; + (++stack_top)[0]=mem_addr[0]; return; } void nasal_vm::opr_mcalll() { - addr_stack.push(&gc.local.back()[exec_code[pc].num]); + mem_addr=&gc.local.back()[exec_code[pc].num]; + (++stack_top)[0]=mem_addr[0]; return; } void nasal_vm::opr_mcallv() { - nasal_val* val=*stack_top--; - nasal_val* vec_addr=*addr_stack.top(); + nasal_val* val=stack_top[0]; + nasal_val* vec_addr=(--stack_top)[0]; int type=vec_addr->type; if(type==vm_vec) { @@ -760,10 +756,9 @@ void nasal_vm::opr_mcallv() case vm_str:num=(int)str2num(val->ptr.str->c_str());break; default:die("mcallv: error value type");break; } - nasal_val** mem_addr=vec_addr->ptr.vec->get_mem(num); + mem_addr=vec_addr->ptr.vec->get_mem(num); if(!mem_addr) die("mcallv: index out of range:"+num2str(num)); - addr_stack.top()=mem_addr; } else if(type==vm_hash) { @@ -774,13 +769,12 @@ void nasal_vm::opr_mcallv() } nasal_hash& ref=*vec_addr->ptr.hash; std::string& str=*val->ptr.str; - nasal_val** mem_addr=ref.get_mem(str); + mem_addr=ref.get_mem(str); if(!mem_addr) { ref.elems[str]=gc.nil_addr; mem_addr=ref.get_mem(str); } - addr_stack.top()=mem_addr; } else die("mcallv: cannot get memory space in other types"); @@ -788,7 +782,7 @@ void nasal_vm::opr_mcallv() } void nasal_vm::opr_mcallh() { - nasal_val* hash_addr=*addr_stack.top(); + nasal_val* hash_addr=stack_top[0]; if(hash_addr->type!=vm_hash) { die("mcallh: must call a hash"); @@ -796,13 +790,12 @@ void nasal_vm::opr_mcallh() } nasal_hash& ref=*hash_addr->ptr.hash; std::string& str=str_table[exec_code[pc].num]; - nasal_val** mem_addr=ref.get_mem(str); + mem_addr=ref.get_mem(str); if(!mem_addr) // create a new key { ref.elems[str]=gc.nil_addr; mem_addr=ref.get_mem(str); } - addr_stack.top()=mem_addr; return; } void nasal_vm::opr_ret()