From 56289b5d22e2a4b72ddc947d15ec0aa0c7d7b2af Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 10 Oct 2021 14:29:23 +0800 Subject: [PATCH] fully functional closure & add benchmark --- nasal_codegen.h | 81 +- nasal_gc.h | 20 +- nasal_vm.h | 225 +-- pic/benchmark.png | Bin 0 -> 11549 bytes test/bf.nas | 3 + test/class.nas | 4 +- test/mandel.nas | 4893 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 5051 insertions(+), 175 deletions(-) create mode 100644 pic/benchmark.png create mode 100644 test/mandel.nas diff --git a/nasal_codegen.h b/nasal_codegen.h index 9b20f2f..873cf4d 100644 --- a/nasal_codegen.h +++ b/nasal_codegen.h @@ -6,9 +6,9 @@ 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_loadu, // load upvalue op_pnum, // push constant number to the stack op_pone, // push 1 to the stack op_pzero, // push 0 to the stack @@ -64,6 +64,7 @@ enum op_code 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_upval, // call upvalue in closure op_callv, // call vec[index] op_callvi, // call vec[immediate] (used in multi-assign/multi-define) op_callh, // call hash.label @@ -76,6 +77,7 @@ enum op_code 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_mupval, // get memory space of value in closure op_mcallv, // get memory space of vec[index] op_mcallh, // get memory space of hash.label op_ret // return @@ -90,9 +92,9 @@ struct {op_nop, "nop "}, {op_intg, "intg "}, {op_intl, "intl "}, - {op_offset, "offset"}, {op_loadg, "loadg "}, {op_loadl, "loadl "}, + {op_loadu, "loadu "}, {op_pnum, "pnum "}, {op_pone, "pone "}, {op_pzero, "pzero "}, @@ -148,6 +150,7 @@ struct {op_feach, "feach "}, {op_callg, "callg "}, {op_calll, "calll "}, + {op_upval, "upval "}, {op_callv, "callv "}, {op_callvi, "callvi"}, {op_callh, "callh "}, @@ -160,6 +163,7 @@ struct {op_slc2, "slc2 "}, {op_mcallg, "mcallg"}, {op_mcalll, "mcalll"}, + {op_mupval, "mupval"}, {op_mcallv, "mcallv"}, {op_mcallh, "mcallh"}, {op_ret, "ret "}, @@ -215,6 +219,7 @@ private: void add_sym(const std::string&); int local_find(const std::string&); int global_find(const std::string&); + int upvalue_find(const std::string&); void gen(uint8_t,uint32_t,uint32_t); void num_gen(const nasal_ast&); void str_gen(const nasal_ast&); @@ -333,15 +338,12 @@ void nasal_codegen::add_sym(const std::string& name) int nasal_codegen::local_find(const std::string& name) { - int index=-1,cnt=0; - for(auto& i:local) - { - for(int j=0;jsize();++j) + if((*iter)[j]==name) + index=((i<<16)|j); + ++iter; + } + return index; +} + void nasal_codegen::gen(uint8_t op,uint32_t num,uint32_t line) { exec_code.push_back({op,fileindex,num,line}); @@ -406,22 +426,14 @@ void nasal_codegen::func_gen(const nasal_ast& ast) gen(op_newf,0,ast.get_line()); int local_label=exec_code.size(); gen(op_intl,0,ast.get_line()); - local.push_back(std::vector()); - + // add special keyword 'me' into symbol table // this symbol is only used in local scope(function's scope) // this keyword is set to nil as default value // after calling a hash, this keyword is set to this hash // this symbol's index will be 0 - if(local.size()==1) - { - std::string me="me"; - add_sym(me); - } - - gen(op_offset,0,ast.get_line()); - for(auto& i:local) - exec_code.back().num+=i.size(); + local.push_back({"me"}); + // generate parameter list for(auto& tmp:ast.get_children()[0].get_children()) { @@ -466,8 +478,7 @@ void nasal_codegen::func_gen(const nasal_ast& ast) // or the location of symbols will change and cause fatal error find_symbol(block); block_gen(block); - for(auto& i:local) - exec_code[local_label].num+=i.size(); + exec_code[local_label].num=local.back().size(); local.pop_back(); if(!block.get_children().size() || block.get_children().back().get_type()!=ast_ret) @@ -503,7 +514,7 @@ void nasal_codegen::call_gen(const nasal_ast& ast) void nasal_codegen::call_id(const nasal_ast& ast) { const std::string& str=ast.get_str(); - for(int i=0;builtin_func[i].name;++i) + for(uint32_t i=0;builtin_func[i].name;++i) if(builtin_func[i].name==str) { gen(op_callb,i,ast.get_line()); @@ -517,6 +528,12 @@ void nasal_codegen::call_id(const nasal_ast& ast) gen(op_calll,index,ast.get_line()); return; } + index=upvalue_find(str); + if(index>=0) + { + gen(op_upval,index,ast.get_line()); + return; + } index=global_find(str); if(index>=0) { @@ -622,6 +639,12 @@ void nasal_codegen::mcall_id(const nasal_ast& ast) gen(op_mcalll,index,ast.get_line()); return; } + index=upvalue_find(str); + if(index>=0) + { + gen(op_mupval,index,ast.get_line()); + return; + } index=global_find(str); if(index>=0) { @@ -705,6 +728,8 @@ void nasal_codegen::multi_assign_gen(const nasal_ast& ast) // and this operation changes local and global value directly if(exec_code.back().op==op_mcalll) exec_code.back().op=op_loadl; + else if(exec_code.back().op==op_mupval) + exec_code.back().op=op_loadu; else if(exec_code.back().op==op_mcallg) exec_code.back().op=op_loadg; else @@ -725,6 +750,8 @@ void nasal_codegen::multi_assign_gen(const nasal_ast& ast) mcall(ast.get_children()[0].get_children()[i]); if(exec_code.back().op==op_mcalll) exec_code.back().op=op_loadl; + else if(exec_code.back().op==op_mupval) + exec_code.back().op=op_loadu; else if(exec_code.back().op==op_mcallg) exec_code.back().op=op_loadg; else @@ -1121,6 +1148,8 @@ void nasal_codegen::block_gen(const nasal_ast& ast) // only the first mcall_id can use load if(exec_code.back().op==op_mcalll) exec_code.back().op=op_loadl; + else if(exec_code.back().op==op_mupval) + exec_code.back().op=op_loadu; else exec_code.back().op=op_loadg; } @@ -1221,6 +1250,8 @@ void nasal_codegen::main_progress(const nasal_ast& ast,const std::vector default_para;// default value(nasal_ref) + std::vector local; // local scope with default value(nasal_ref) + std::vector upvalue; std::unordered_map key_table;// parameter name hash - nasal_ref closure; // closure will be loaded to gc.local.back() as the local scope nasal_func(); void clear(); @@ -269,7 +269,7 @@ nasal_func::nasal_func() void nasal_func::clear() { dynpara=-1; - default_para.clear(); + local.clear(); key_table.clear(); return; } @@ -368,10 +368,10 @@ void nasal_gc::mark() bfs.push(i.second); break; case vm_func: - bfs.push(tmp.func()->closure); - for(auto& i:tmp.func()->default_para) - if(i.type>vm_num) - bfs.push(i); + for(auto& i:tmp.func()->local) + bfs.push(i); + for(auto& i:tmp.func()->upvalue) + bfs.push(i); break; } } diff --git a/nasal_vm.h b/nasal_vm.h index 7d69252..4b8436d 100644 --- a/nasal_vm.h +++ b/nasal_vm.h @@ -8,8 +8,9 @@ private: nasal_ref*& stack_top;// stack top /* values of nasal_vm */ uint32_t pc; // program counter - std::stack cls_stk; // stack to store closure,if same closures detected,load the local scope + uint32_t newf_off; // used to load default parameter to a new function std::stack ret; // ptr stack stores address for function to return + std::stack func_stk; // stack to store function,this is used when getting upvalues std::stack counter; // iterator stack for forindex/foreach std::vector num_table;// numbers used in process(const calculation) std::vector str_table;// symbols used in process @@ -29,9 +30,9 @@ private: bool condition(nasal_ref); void opr_intg(); void opr_intl(); - void opr_offset(); void opr_loadg(); void opr_loadl(); + void opr_loadu(); void opr_pnum(); void opr_pone(); void opr_pzero(); @@ -50,25 +51,25 @@ private: void opr_sub(); void opr_mul(); void opr_div(); - void opr_lnk(); + void opr_lnk();// void opr_addc(); void opr_subc(); void opr_mulc(); void opr_divc(); - void opr_lnkc(); + void opr_lnkc();// void opr_addeq(); void opr_subeq(); void opr_muleq(); void opr_diveq(); - void opr_lnkeq(); + void opr_lnkeq();// void opr_addeqc(); void opr_subeqc(); void opr_muleqc(); void opr_diveqc(); - void opr_lnkeqc(); + void opr_lnkeqc();// void opr_meq(); - void opr_eq(); - void opr_neq(); + void opr_eq();// + void opr_neq();// void opr_less(); void opr_leq(); void opr_grt(); @@ -87,19 +88,21 @@ private: void opr_feach(); void opr_callg(); void opr_calll(); - void opr_callv(); - void opr_callvi(); - void opr_callh(); - void opr_callfv(); - void opr_callfh(); + void opr_upval(); + void opr_callv();// + void opr_callvi();// + void opr_callh();// + void opr_callfv();// + void opr_callfh();// void opr_callb(); void opr_slcbegin(); void opr_slcend(); - void opr_slc(); - void opr_slc2(); + void opr_slc();// + void opr_slc2();// void opr_mcallg(); void opr_mcalll(); - void opr_mcallv(); + void opr_mupval(); + void opr_mcallv();// void opr_mcallh(); void opr_ret(); public: @@ -109,7 +112,9 @@ public: const std::vector&, const std::vector&); void clear(); - void run(std::vector&,bool); + void run( + const std::vector&, + const bool); }; void nasal_vm::init( @@ -129,8 +134,6 @@ void nasal_vm::clear() gc.gc_clear(); while(!ret.empty()) ret.pop(); - while(!cls_stk.empty()) - cls_stk.pop(); while(!counter.empty()) counter.pop(); num_table.clear(); @@ -213,16 +216,14 @@ void nasal_vm::die(std::string str) ret.push(pc); traceback(); stackinfo(10); - exit(1); - return; + std::exit(1); } void nasal_vm::stackoverflow() { printf("[vm] stack overflow\n"); traceback(); stackinfo(10); - exit(1); - return; + std::exit(1); } inline bool nasal_vm::condition(nasal_ref val) { @@ -248,17 +249,7 @@ inline void nasal_vm::opr_intg() } inline void nasal_vm::opr_intl() { - auto& vec=stack_top[0].func()->closure.vec()->elems; - // if many functions share the same closure - // resize will break the size of vector and cause exe_bad_access - // so choose the maximum size as the size of this closure - if(vec.size()offset=imm[pc]; + stack_top[0].func()->local.resize(imm[pc],gc.nil); return; } inline void nasal_vm::opr_loadg() @@ -271,6 +262,11 @@ inline void nasal_vm::opr_loadl() gc.local.back().vec()->elems[imm[pc]]=(stack_top--)[0]; return; } +inline void nasal_vm::opr_loadu() +{ + func_stk.top()->upvalue[(imm[pc]&0xffff0000)>>16].vec()->elems[imm[pc]&0xffff]=(stack_top--)[0]; + return; +} inline void nasal_vm::opr_pnum() { (++stack_top)[0]={vm_num,num_table[imm[pc]]}; @@ -315,13 +311,14 @@ inline void nasal_vm::opr_newh() } inline void nasal_vm::opr_newf() { + newf_off=1; (++stack_top)[0]=gc.gc_alloc(vm_func); stack_top[0].func()->entry=imm[pc]; - stack_top[0].func()->closure.type=vm_nil; - if(gc.local.empty()) - stack_top[0].func()->closure=gc.gc_alloc(vm_vec); - else - stack_top[0].func()->closure=gc.local.back();// local contains 'me' + if(!gc.local.empty()) + { + stack_top[0].func()->upvalue=func_stk.top()->upvalue; + stack_top[0].func()->upvalue.push_back(gc.local.back()); + } return; } inline void nasal_vm::opr_happ() @@ -335,7 +332,8 @@ inline void nasal_vm::opr_para() nasal_func* func=stack_top[0].func(); size_t size=func->key_table.size(); func->key_table[str_table[imm[pc]]]=size; - func->default_para.push_back({vm_none}); + func->local[newf_off]={vm_none}; + ++newf_off; return; } inline void nasal_vm::opr_defpara() @@ -344,7 +342,8 @@ inline void nasal_vm::opr_defpara() nasal_func* func=(--stack_top)[0].func(); size_t size=func->key_table.size(); func->key_table[str_table[imm[pc]]]=size; - func->default_para.push_back(def_val); + func->local[newf_off]=def_val; + ++newf_off; return; } inline void nasal_vm::opr_dynpara() @@ -562,6 +561,11 @@ inline void nasal_vm::opr_calll() (++stack_top)[0]=gc.local.back().vec()->elems[imm[pc]]; return; } +inline void nasal_vm::opr_upval() +{ + (++stack_top)[0]=func_stk.top()->upvalue[(imm[pc]&0xffff0000)>>16].vec()->elems[imm[pc]&0xffff]; + return; +} inline void nasal_vm::opr_callv() { nasal_ref val=stack_top[0]; @@ -575,18 +579,12 @@ inline void nasal_vm::opr_callv() else if(vec.type==vm_hash) { if(val.type!=vm_str) - { die("callv: must use string as the key"); - return; - } stack_top[0]=vec.hash()->get_val(*val.value.gcobj->ptr.str); if(stack_top[0].type==vm_none) - { die("callv: cannot find member \""+*val.str()+"\" of this hash"); - return; - } if(stack_top[0].type==vm_func) - stack_top[0].func()->closure.vec()->elems[0]=val;// me + stack_top[0].func()->local[0]=val;// me } else if(vec.type==vm_str) { @@ -594,10 +592,7 @@ inline void nasal_vm::opr_callv() int num=val.to_number(); int str_size=str.length(); if(num<-str_size || num>=str_size) - { die("callv: index out of range:"+std::to_string(val.to_number())); - return; - } stack_top[0]={vm_num,static_cast(str[num>=0? num:num+str_size])}; } else @@ -608,10 +603,8 @@ inline void nasal_vm::opr_callvi() { nasal_ref val=stack_top[0]; if(val.type!=vm_vec) - { die("callvi: must use a vector"); - return; - } + // cannot use operator[],because this may cause overflow (++stack_top)[0]=val.vec()->get_val(imm[pc]); if(stack_top[0].type==vm_none) @@ -622,18 +615,14 @@ inline void nasal_vm::opr_callh() { nasal_ref val=stack_top[0]; if(val.type!=vm_hash) - { die("callh: must call a hash"); - return; - } + stack_top[0]=val.hash()->get_val(str_table[imm[pc]]); if(stack_top[0].type==vm_none) - { die("callh: member \""+str_table[imm[pc]]+"\" does not exist"); - return; - } + if(stack_top[0].type==vm_func) - stack_top[0].func()->closure.vec()->elems[0]=val;// me + stack_top[0].func()->local[0]=val;// me return; } inline void nasal_vm::opr_callfv() @@ -643,41 +632,32 @@ inline void nasal_vm::opr_callfv() nasal_ref* vec=stack_top-args_size+1; nasal_ref func_addr=vec[-1]; if(func_addr.type!=vm_func) - { die("callfv: must call a function"); - return; - } // push new local scope + func_stk.push(func_addr.func()); auto& ref_func=*func_addr.func(); - cls_stk.push(func_addr.func()->closure); + gc.local.push_back(gc.gc_alloc(vm_vec)); - gc.local.back().vec()->elems=ref_func.closure.vec()->elems; + gc.local.back().vec()->elems=ref_func.local; // load parameters - auto& ref_default=ref_func.default_para; auto& ref_closure=gc.local.back().vec()->elems; - uint32_t offset=ref_func.offset; uint32_t para_size=ref_func.key_table.size(); // load arguments - if(args_sizepara_size,for 0 to args_size will cause corruption uint32_t min_size=std::min(para_size,args_size); for(uint32_t i=0;i=para_size if(ref_func.dynpara>=0) { nasal_ref vec_addr=gc.gc_alloc(vm_vec); for(uint32_t i=para_size;ielems.push_back(vec[i]); - ref_closure[para_size+offset]=vec_addr; + ref_closure.back()=vec_addr; } stack_top-=args_size;// pop arguments @@ -691,36 +671,24 @@ inline void nasal_vm::opr_callfh() auto& ref_hash=stack_top[0].hash()->elems; nasal_ref func_addr=stack_top[-1]; if(func_addr.type!=vm_func) - { die("callfh: must call a function"); - return; - } // push new local scope + func_stk.push(func_addr.func()); auto& ref_func=*func_addr.func(); - cls_stk.push(func_addr.func()->closure); gc.local.push_back(gc.gc_alloc(vm_vec)); - gc.local.back().vec()->elems=ref_func.closure.vec()->elems; + gc.local.back().vec()->elems=ref_func.local; // load parameters - auto& ref_default=ref_func.default_para; - auto& ref_closure=gc.local.back().vec()->elems; + auto& ref_closure=gc.local.back().vec()->elems; if(ref_func.dynpara>=0) - { die("callfh: special call cannot use dynamic argument"); - return; - } - uint32_t offset=ref_func.offset; + for(auto& i:ref_func.key_table) { if(ref_hash.count(i.first)) - ref_closure[i.second+offset]=ref_hash[i.first]; - else if(ref_default[i.second].type!=vm_none) - ref_closure[i.second+offset]=ref_default[i.second]; - else - { + ref_closure[i.second+1]=ref_hash[i.first]; + else if(ref_func.local[i.second+1/*1 is reserved for 'me'*/].type==vm_none) die("callfh: lack argument(s): \""+i.first+"\""); - return; - } } --stack_top;// pop hash @@ -805,6 +773,12 @@ inline void nasal_vm::opr_mcalll() (++stack_top)[0]=mem_addr[0]; return; } +inline void nasal_vm::opr_mupval() +{ + mem_addr=&func_stk.top()->upvalue[(imm[pc]&0xffff0000)>>16].vec()->elems[imm[pc]&0xffff]; + (++stack_top)[0]=mem_addr[0]; + return; +} inline void nasal_vm::opr_mcallv() { nasal_ref val=stack_top[0]; @@ -818,10 +792,7 @@ inline void nasal_vm::opr_mcallv() else if(vec_addr.type==vm_hash) { if(val.type!=vm_str) - { die("mcallv: must use string as the key"); - return; - } nasal_hash& ref=*vec_addr.hash(); std::string& str=*val.str(); mem_addr=ref.get_mem(str); @@ -839,10 +810,7 @@ inline void nasal_vm::opr_mcallh() { nasal_ref hash_addr=stack_top[0]; if(hash_addr.type!=vm_hash) - { die("mcallh: must call a hash"); - return; - } nasal_hash& ref=*hash_addr.hash(); std::string& str=str_table[imm[pc]]; mem_addr=ref.get_mem(str); @@ -855,44 +823,24 @@ inline void nasal_vm::opr_mcallh() } inline void nasal_vm::opr_ret() { - nasal_func* func=stack_top[-1].func(); - uint32_t offset=func->offset; - nasal_ref cls=cls_stk.top();cls_stk.pop(); - // same closure detected,update the last local scope instead of the closure - // because when calling a function,local scope get a copy of the func's closure,not the reference - if(!cls_stk.empty() && cls_stk.top()==cls) - { - // this condition in fact is that two called function are using the same closure - // if this copy of closure is changed, the closure will be updated at the same time - // also the copy of closure that still in using will alse be updated - auto& vec=gc.local.back().vec()->elems; - auto& func_vec=func->closure.vec()->elems; - gc.local.pop_back(); - for(uint32_t i=0;ielems[i]=func_vec[i]=vec[i]; - } - else - { - // two closures are not the same, update the func's closure and drop gc.local.back() - auto& vec=func->closure.vec()->elems; - for(uint32_t i=0;ielems[i]; - gc.local.pop_back(); - vec[0].type=vm_nil;// set 'me' to nil - } - - pc=ret.top();ret.pop();// fetch pc + // get nasal_func and set 'me' to nil + // and rewrite nasal_func with returned value + stack_top[-1].func()->local[0]={vm_nil}; --stack_top; - stack_top[0]=stack_top[1];// rewrite nasal_func with returned value + stack_top[0]=stack_top[1]; + + func_stk.pop(); // pop function stack + gc.local.pop_back(); // pop local scope + pc=ret.top();ret.pop(); // fetch pc return; } -void nasal_vm::run(std::vector& exec,bool op_cnt) +void nasal_vm::run(const std::vector& exec,const bool op_cnt) { uint64_t count[op_ret+1]={0}; const void* opr_table[]= { - &&nop, &&intg, &&intl, &&offset, - &&loadg, &&loadl, &&pnum, &&pone, + &&nop, &&intg, &&intl, &&loadg, + &&loadl, &&loadu, &&pnum, &&pone, &&pzero, &&pnil, &&pstr, &&newv, &&newh, &&newf, &&happ, &¶, &&defpara, &&dynpara, &&unot, &&usub, @@ -906,10 +854,11 @@ void nasal_vm::run(std::vector& exec,bool op_cnt) &&leqc, &&grtc, &&geqc, &&pop, &&jmp, &&jt, &&jf, &&counter, &&cntpop, &&findex, &&feach, &&callg, - &&calll, &&callv, &&callvi, &&callh, - &&callfv, &&callfh, &&callb, &&slcbegin, - &&slcend, &&slc, &&slc2, &&mcallg, - &&mcalll, &&mcallv, &&mcallh, &&ret + &&calll, &&upval, &&callv, &&callvi, + &&callh, &&callfv, &&callfh, &&callb, + &&slcbegin,&&slcend, &&slc, &&slc2, + &&mcallg, &&mcalll, &&mupval, &&mcallv, + &&mcallh, &&ret }; bytecode=exec; @@ -953,9 +902,9 @@ nop: intg: exec_opnodie(opr_intg ,op_intg ); // stack+=imm[pc] (detected at codegen) intl: exec_opnodie(opr_intl ,op_intl ); // stack-=0 -offset: exec_opnodie(opr_offset ,op_offset ); // stack-=0 loadg: exec_opnodie(opr_loadg ,op_loadg ); // stack-=1 loadl: exec_opnodie(opr_loadl ,op_loadl ); // stack-=1 +loadu: exec_opnodie(opr_loadu ,op_loadu ); // stack-=1 pnum: exec_operand(opr_pnum ,op_pnum ); // stack+=1 pone: exec_operand(opr_pone ,op_pone ); // stack+=1 pzero: exec_operand(opr_pzero ,op_pzero ); // stack+=1 @@ -1011,6 +960,7 @@ findex: exec_operand(opr_findex ,op_findex ); // stack+=1 feach: exec_operand(opr_feach ,op_feach ); // stack+=1 callg: exec_operand(opr_callg ,op_callg ); // stack+=1 calll: exec_operand(opr_calll ,op_calll ); // stack+=1 +upval: exec_operand(opr_upval ,op_upval ); // stack+=1 callv: exec_opnodie(opr_callv ,op_callv ); // stack-=0 callvi: exec_opnodie(opr_callvi ,op_callvi ); // stack-=0 callh: exec_opnodie(opr_callh ,op_callh ); // stack-=0 @@ -1023,6 +973,7 @@ slc: exec_opnodie(opr_slc ,op_slc ); // stack-=1 slc2: exec_opnodie(opr_slc2 ,op_slc2 ); // stack-=2 mcallg: exec_operand(opr_mcallg ,op_mcallg ); // stack+=1 mcalll: exec_operand(opr_mcalll ,op_mcalll ); // stack+=1 +mupval: exec_operand(opr_mupval ,op_mupval ); // stack+=1 mcallv: exec_opnodie(opr_mcallv ,op_mcallv ); // stack-=0 mcallh: exec_opnodie(opr_mcallh ,op_mcallh ); // stack-=0 ret: exec_opnodie(opr_ret ,op_ret ); // stack-=1 diff --git a/pic/benchmark.png b/pic/benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..692a6b34e0fc41c7f88358817789a7180b67d64a GIT binary patch literal 11549 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 G?f(K{R2Urq literal 0 HcmV?d00001 diff --git a/test/bf.nas b/test/bf.nas index f1e75dd..2f6b1f7 100644 --- a/test/bf.nas +++ b/test/bf.nas @@ -163,6 +163,8 @@ var func_table=[ var bf=func(program) { setsize(paper,131072); + for(var i=0;i<131072;i+=1) + paper[i]=0; (ptr,code,inum,stack)=(0,[],[],[]); var len=size(program); @@ -234,6 +236,7 @@ var bf=func(program) die("lack ]"); return; } + len=size(code); for(pc=0;pc