From 189d49fa4a40298fc3cdc4703a0859b6ad1ec4df Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 20 Dec 2021 21:33:22 +0800 Subject: [PATCH] add debugger framework with function:backtrace,run,help,show global/local/upvalue,exit --- README.md | 2 +- main.cpp | 25 +++-- makefile | 2 +- nasal.h | 1 + nasal_builtin.h | 1 - nasal_codegen.h | 85 ++++++++-------- nasal_dbg.h | 258 ++++++++++++++++++++++++++++++++++++++++++++++++ nasal_vm.h | 153 +++++++++++++++++----------- stl/lib.nas | 6 ++ test/bfs.nas | 20 ++-- test/calc.nas | 4 +- 11 files changed, 440 insertions(+), 117 deletions(-) create mode 100644 nasal_dbg.h diff --git a/README.md b/README.md index fcbde33..949d568 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Also remember to use g++ or clang++.(mingw-w64 in Windows) Or use this in linux/macOS/Unix -> [cpp compiler] -std=c++11 -O3 main.cpp -o nasal -fno-exceptions +> [cpp compiler] -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl ## How to Use? diff --git a/main.cpp b/main.cpp index b4c9ee9..f71acac 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include "nasal.h" + constexpr uint32_t VM_LEXINFO =1; constexpr uint32_t VM_ASTINFO =2; constexpr uint32_t VM_CODEINFO =4; @@ -6,6 +7,8 @@ constexpr uint32_t VM_EXECTIME =8; constexpr uint32_t VM_OPCALLNUM=16; constexpr uint32_t VM_EXEC =32; constexpr uint32_t VM_DBGINFO =64; +constexpr uint32_t VM_DEBUG =128; + void help() { std::cout @@ -21,12 +24,13 @@ void help() <<" input file name to execute script file.\n\n" <<"nasal [options...] \n" <<"option:\n" - <<" -l, --lex | view token info.\n" - <<" -a, --ast | view abstract syntax tree.\n" - <<" -c, --code | view bytecode.\n" - <<" -t, --time | execute and get the running time.\n" - <<" -o, --opcnt | count operands while running.\n" - <<" -d, --detail | get detail crash info.\n" + <<" -l, --lex | view token info.\n" + <<" -a, --ast | view abstract syntax tree.\n" + <<" -c, --code | view bytecode.\n" + <<" -t, --time | execute and get the running time.\n" + <<" -o, --opcnt | execute and count used operands.\n" + <<" -d, --detail| execute and get detail crash info.\n" + <<" -dbg, --debug | debug mode (this will ignore -t -o -d).\n" <<"file:\n" <<" input file name to execute script file.\n"; } @@ -95,7 +99,12 @@ void execute(const std::string& file,const uint32_t cmd) parse.print(); if(cmd&VM_CODEINFO) gen.print(); - if(cmd&VM_EXECTIME) + if(cmd&VM_DEBUG) + { + nasal_dbg dbg; + dbg.run(gen,linker); + } + else if(cmd&VM_EXECTIME) { clock_t t=clock(); vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO); @@ -141,6 +150,8 @@ int main(int argc,const char* argv[]) cmd|=VM_EXECTIME; else if(s=="--detail" || s=="-d") cmd|=VM_DBGINFO|VM_EXEC; + else if(s=="--debug" || s=="-dbg") + cmd|=VM_DEBUG; else err(); } diff --git a/makefile b/makefile index 3da4ecf..c65115b 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ .PHONY=test -nasal:main.cpp nasal_ast.h nasal_builtin.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal.h +nasal:main.cpp nasal_ast.h nasal_builtin.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal_dbg.h nasal.h clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall test:nasal ./nasal test/ascii-art.nas diff --git a/nasal.h b/nasal.h index b6ff229..402c5a1 100644 --- a/nasal.h +++ b/nasal.h @@ -135,5 +135,6 @@ std::string rawstr(const std::string& str) #include "nasal_builtin.h" #include "nasal_codegen.h" #include "nasal_vm.h" +#include "nasal_dbg.h" #endif diff --git a/nasal_builtin.h b/nasal_builtin.h index c8d2296..ab2e711 100644 --- a/nasal_builtin.h +++ b/nasal_builtin.h @@ -1059,7 +1059,6 @@ nasal_ref builtin_platform(std::vector& local,nasal_gc& gc) #elif defined __APPLE__ *ret.str()="macOS"; #endif - return ret; } #endif \ No newline at end of file diff --git a/nasal_codegen.h b/nasal_codegen.h index 6689a9f..8c531e5 100644 --- a/nasal_codegen.h +++ b/nasal_codegen.h @@ -430,7 +430,7 @@ void nasal_codegen::func_gen(const nasal_ast& ast) code[newfunc_label].num=code.size()+1; int jmp_ptr=code.size(); - gen(op_jmp,0,0); + gen(op_jmp,0,ast.line()); const nasal_ast& block=ast[1]; // search symbols first, must use after loading parameters @@ -722,13 +722,13 @@ void nasal_codegen::conditional_gen(const nasal_ast& ast) { calc_gen(tmp[0]); int ptr=code.size(); - gen(op_jf,0,0); + gen(op_jf,0,tmp.line()); block_gen(tmp[1]); // without 'else' the last condition doesn't need to jmp if(&tmp!=&ast.child().back()) { jmp_label.push_back(code.size()); - gen(op_jmp,0,0); + gen(op_jmp,0,tmp.line()); } code[ptr].num=code.size(); } @@ -770,10 +770,10 @@ void nasal_codegen::while_gen(const nasal_ast& ast) int loop_ptr=code.size(); calc_gen(ast[0]); int condition_ptr=code.size(); - gen(op_jf,0,0); + gen(op_jf,0,ast[0].line()); block_gen(ast[1]); - gen(op_jmp,loop_ptr,0); + gen(op_jmp,loop_ptr,ast[1].line()); code[condition_ptr].num=code.size(); load_continue_break(code.size()-1,code.size()); } @@ -813,7 +813,7 @@ void nasal_codegen::for_gen(const nasal_ast& ast) case ast_grt: case ast_trino: calc_gen(ast[0]); - gen(op_pop,0,0); + gen(op_pop,0,ast[0].line()); break; } int jmp_place=code.size(); @@ -822,7 +822,7 @@ void nasal_codegen::for_gen(const nasal_ast& ast) else calc_gen(ast[1]); int label_exit=code.size(); - gen(op_jf,0,0); + gen(op_jf,0,ast[1].line()); block_gen(ast[3]); int continue_place=code.size(); @@ -844,10 +844,10 @@ void nasal_codegen::for_gen(const nasal_ast& ast) case ast_less:case ast_geq:case ast_grt: case ast_trino: calc_gen(ast[2]); - gen(op_pop,0,0); + gen(op_pop,0,ast[2].line()); break; } - gen(op_jmp,jmp_place,0); + gen(op_jmp,jmp_place,ast[2].line()); code[label_exit].num=code.size(); load_continue_break(continue_place,code.size()); @@ -869,16 +869,16 @@ void nasal_codegen::forindex_gen(const nasal_ast& ast) { mcall(ast[0]); gen(op_meq,0,ast[0].line()); - gen(op_pop,0,0); + gen(op_pop,0,ast[0].line()); } ++in_iterloop.top(); block_gen(ast[2]); --in_iterloop.top(); - gen(op_jmp,ptr,0); + gen(op_jmp,ptr,ast.line()); code[ptr].num=code.size(); load_continue_break(code.size()-1,code.size()); - gen(op_pop,0,0);// pop vector - gen(op_pop,0,0);// pop iterator + gen(op_pop,0,ast[1].line());// pop vector + gen(op_pop,0,ast.line());// pop iterator } void nasal_codegen::foreach_gen(const nasal_ast& ast) { @@ -897,31 +897,31 @@ void nasal_codegen::foreach_gen(const nasal_ast& ast) { mcall(ast[0]); gen(op_meq,0,ast[0].line()); - gen(op_pop,0,0); + gen(op_pop,0,ast[0].line()); } ++in_iterloop.top(); block_gen(ast[2]); --in_iterloop.top(); - gen(op_jmp,ptr,0); + gen(op_jmp,ptr,ast.line()); code[ptr].num=code.size(); load_continue_break(code.size()-1,code.size()); - gen(op_pop,0,0);// pop vector - gen(op_pop,0,0);// pop iterator + gen(op_pop,0,ast[1].line());// pop vector + gen(op_pop,0,ast.line());// pop iterator } void nasal_codegen::or_gen(const nasal_ast& ast) { calc_gen(ast[0]); int l1=code.size(); - gen(op_jt,0,0); + gen(op_jt,0,ast[0].line()); - gen(op_pop,0,0); + gen(op_pop,0,ast[0].line()); calc_gen(ast[1]); int l2=code.size(); - gen(op_jt,0,0); + gen(op_jt,0,ast[1].line()); - gen(op_pop,0,0); - gen(op_pnil,0,0); + gen(op_pop,0,ast[1].line()); + gen(op_pnil,0,ast[1].line()); code[l1].num=code[l2].num=code.size(); } @@ -929,18 +929,18 @@ void nasal_codegen::or_gen(const nasal_ast& ast) void nasal_codegen::and_gen(const nasal_ast& ast) { calc_gen(ast[0]); - gen(op_jt,code.size()+2,0); + gen(op_jt,code.size()+2,ast[0].line()); int lfalse=code.size(); - gen(op_jmp,0,0); - gen(op_pop,0,0);// jt jumps here + gen(op_jmp,0,ast[0].line()); + gen(op_pop,0,ast[1].line());// jt jumps here calc_gen(ast[1]); - gen(op_jt,code.size()+3,0); + gen(op_jt,code.size()+3,ast[1].line()); code[lfalse].num=code.size(); - gen(op_pop,0,0); - gen(op_pnil,0,0); + gen(op_pop,0,ast[1].line()); + gen(op_pnil,0,ast[1].line()); //jt jumps here } @@ -948,10 +948,10 @@ void nasal_codegen::trino_gen(const nasal_ast& ast) { calc_gen(ast[0]); int lfalse=code.size(); - gen(op_jf,0,0); + gen(op_jf,0,ast[0].line()); calc_gen(ast[1]); int lexit=code.size(); - gen(op_jmp,0,0); + gen(op_jmp,0,ast[1].line()); code[lfalse].num=code.size(); calc_gen(ast[2]); code[lexit].num=code.size(); @@ -1073,11 +1073,11 @@ void nasal_codegen::block_gen(const nasal_ast& ast) case ast_conditional:conditional_gen(tmp);break; case ast_continue: continue_ptr.front().push_back(code.size()); - gen(op_jmp,0,0); + gen(op_jmp,0,tmp.line()); break; case ast_break: break_ptr.front().push_back(code.size()); - gen(op_jmp,0,0); + gen(op_jmp,0,tmp.line()); break; case ast_while: case ast_for: @@ -1126,7 +1126,7 @@ void nasal_codegen::block_gen(const nasal_ast& ast) case ast_grt: case ast_or: case ast_and: - case ast_trino:calc_gen(tmp);gen(op_pop,0,0);break; + case ast_trino:calc_gen(tmp);gen(op_pop,0,tmp.line());break; case ast_ret:ret_gen(tmp);break; } } @@ -1135,8 +1135,8 @@ void nasal_codegen::ret_gen(const nasal_ast& ast) { for(uint32_t i=0;i\n",c.num,builtin[c.num].name,(uint64_t)builtin[c.num].func);break; + printf("0x%x <%s@0x%lx>",c.num,builtin[c.num].name,(uint64_t)builtin[c.num].func);break; case op_callg: case op_mcallg: case op_loadg: case op_calll: case op_mcalll: case op_loadl: - printf("0x%x\n",c.num);break; + printf("0x%x",c.num);break; case op_upval:case op_mupval: case op_loadu: - printf("0x%x[0x%x]\n",(c.num>>16)&0xffff,c.num&0xffff);break; + printf("0x%x[0x%x]",(c.num>>16)&0xffff,c.num&0xffff);break; case op_happ: case op_pstr: case op_lnkc: case op_lnkeqc: case op_callh: case op_mcallh: case op_para: case op_defpara:case op_dynpara: - printf("0x%x (\"%s\")\n",c.num,rawstr(str_res[c.num]).c_str());break; - default:printf("\n");break; + printf("0x%x (\"%s\")",c.num,rawstr(str_res[c.num]).c_str());break; + default:break; } + printf("\n"); } void nasal_codegen::print() diff --git a/nasal_dbg.h b/nasal_dbg.h new file mode 100644 index 0000000..fa36a56 --- /dev/null +++ b/nasal_dbg.h @@ -0,0 +1,258 @@ +#ifndef __NASAL_DBG_H__ +#define __NASAL_DBG_H__ + +#include "nasal_vm.h" + +class nasal_dbg:public nasal_vm +{ +private: + std::vector parse(std::string&); + uint16_t get_fileindex(std::string); + void err(); + void help(); + void interact(); +public: + void run( + const nasal_codegen&, + const nasal_import& + ); +}; + +std::vector nasal_dbg::parse(std::string& cmd) +{ + std::vector res; + std::string tmp=""; + for(uint32_t i=0;i\n" + <<"\th, help | get help\n" + <<"\tbt, backtrace | get function call trace\n" + <<"\tr, run | run program until break point or exit\n" + <<"\tg, global | see global values\n" + <<"\tl, local | see local values\n" + <<"\tu, upval | see upvalue\n" + <<"\tquit, exit | exit debugger\n"; +} + +void nasal_dbg::interact() +{ + static uint16_t last_fidx=0; + static uint32_t last_line=0; + + if(bytecode[pc].op==op_intg) + { + std::cout + <<"nasal debug mode\n" + <<"input \'h\' to get help\n"; + } + else if(bytecode[pc].op==op_exit) + { + std::cout<<"debugger exited successfully\n"; + return; + } + else if(bytecode[pc].op==op_nop) + return; + if(bytecode[pc].fidx!=last_fidx || bytecode[pc].line!=last_line) + return; + + last_fidx=0; + last_line=0; + std::string cmd; + bytecodeinfo("->\t",pc); + for(uint32_t i=1;i<5 && bytecode[pc+i].op!=op_exit;++i) + bytecodeinfo(" \t",pc+i); + while(1) + { + printf(">> "); + std::getline(std::cin,cmd); + auto res=parse(cmd); + switch(res.size()) + { + case 1: + if(res[0]=="h" || res[0]=="help") + help(); + else if(res[0]=="bt" || res[0]=="backtrace") + traceback(); + else if(res[0]=="r" || res[0]=="run") + return; + else if(res[0]=="g" || res[0]=="global") + global_state(); + else if(res[0]=="l" || res[0]=="local") + local_state(); + else if(res[0]=="u" || res[0]=="upval") + upval_state(); + else if(res[0]=="quit" || res[0]=="exit") + std::exit(0); + else + err(); + break; + case 3: + std::cout<<"unfinished\n"; + break; + default:err();break; + } + } +} + +void nasal_dbg::run( + const nasal_codegen& gen, + const nasal_import& linker) +{ + detail_info=true; + init(gen.get_strs(),gen.get_nums(),linker.get_file()); + const void* opr_table[]= + { + &&nop, &&intg, &&intl, &&loadg, + &&loadl, &&loadu, &&pnum, &&pone, + &&pzero, &&pnil, &&pstr, &&newv, + &&newh, &&newf, &&happ, &¶, + &&defpara, &&dynpara, &&unot, &&usub, + &&add, &&sub, &&mul, &&div, + &&lnk, &&addc, &&subc, &&mulc, + &&divc, &&lnkc, &&addeq, &&subeq, + &&muleq, &&diveq, &&lnkeq, &&addeqc, + &&subeqc, &&muleqc, &&diveqc, &&lnkeqc, + &&meq, &&eq, &&neq, &&less, + &&leq, &&grt, &&geq, &&lessc, + &&leqc, &&grtc, &&geqc, &&pop, + &&jmp, &&jt, &&jf, &&counter, + &&findex, &&feach, &&callg, &&calll, + &&upval, &&callv, &&callvi, &&callh, + &&callfv, &&callfh, &&callb, &&slcbegin, + &&slcend, &&slc, &&slc2, &&mcallg, + &&mcalll, &&mupval, &&mcallv, &&mcallh, + &&ret, &&vmexit + }; + bytecode=gen.get_code().data(); + std::vector code; + for(auto& i:gen.get_code()) + { + code.push_back(opr_table[i.op]); + imm.push_back(i.num); + } + + // set canary and program counter + auto canary=gc.stack+STACK_MAX_DEPTH-1; + pc=0; + // goto the first operand + goto *code[pc]; + +vmexit: + if(gc.top>=canary) + die("stack overflow"); + gc.clear(); + imm.clear(); + return; +#define dbg(op) {interact();op();if(gc.top&, @@ -25,9 +26,12 @@ private: /* debug functions */ bool detail_info; void valinfo(nasal_ref&); - void bytecodeinfo(const uint32_t); + void bytecodeinfo(const char*,const uint32_t); void traceback(); void stackinfo(const uint32_t); + void global_state(); + void local_state(); + void upval_state(); void detail(); void opcallsort(const uint64_t*); void die(std::string); @@ -116,6 +120,7 @@ public: const nasal_import&, const bool, const bool); + }; void nasal_vm::init( @@ -127,32 +132,50 @@ void nasal_vm::init( num_table=nums.data(); str_table=strs.data(); files=filenames.data(); + files_size=filenames.size(); } void nasal_vm::valinfo(nasal_ref& val) { const nasal_val* p=val.value.gcobj; + printf("\t"); switch(val.type) { - case vm_none: printf("\t| null |\n");break; - case vm_ret: printf("\t| addr | pc=0x%x\n",val.ret());break; - case vm_cnt: printf("\t| cnt | %ld\n",val.cnt());break; - case vm_nil: printf("\t| nil |\n");break; - case vm_num: printf("\t| num | %lf\n",val.num());break; - case vm_str: printf("\t| str | <0x%lx> %s\n",(uint64_t)p,rawstr(*val.str()).c_str());break; - case vm_func: printf("\t| func | <0x%lx> entry=0x%x\n",(uint64_t)p,val.func()->entry);break; - case vm_vec: printf("\t| vec | <0x%lx> [%lu val]\n",(uint64_t)p,val.vec()->elems.size());break; - case vm_hash: printf("\t| hash | <0x%lx> {%lu member}\n",(uint64_t)p,val.hash()->elems.size());break; - case vm_obj: printf("\t| obj | <0x%lx> object=0x%lx\n",(uint64_t)p,(uint64_t)val.obj()->ptr);break; - default: printf("\t| ??? | <0x%lx>\n",(uint64_t)p);break; + case vm_none: printf("| null |\n");break; + case vm_ret: printf("| addr | pc=0x%x\n",val.ret());break; + case vm_cnt: printf("| cnt | %ld\n",val.cnt());break; + case vm_nil: printf("| nil |\n");break; + case vm_num: printf("| num | ");std::cout< %s\n",(uint64_t)p,rawstr(*val.str()).c_str());break; + case vm_func: printf("| func | <0x%lx> entry=0x%x\n",(uint64_t)p,val.func()->entry);break; + case vm_vec: printf("| vec | <0x%lx> [%lu val]\n",(uint64_t)p,val.vec()->elems.size());break; + case vm_hash: printf("| hash | <0x%lx> {%lu member}\n",(uint64_t)p,val.hash()->elems.size());break; + case vm_obj: printf("| obj | <0x%lx> object=0x%lx\n",(uint64_t)p,(uint64_t)val.obj()->ptr);break; + default: printf("| ??? | <0x%lx>\n",(uint64_t)p);break; } } -void nasal_vm::bytecodeinfo(const uint32_t p) +void nasal_vm::bytecodeinfo(const char* header,const uint32_t p) { - const opcode& code=bytecode[p]; - printf("\t0x%.8x: %s 0x%x",p,code_table[code.op].name,code.num); - if(code.op==op_callb) - printf(" <%s@0x%lx>",builtin[code.num].name,(uint64_t)builtin[code.num].func); - printf(" (<%s> line %d)\n",files[code.fidx].c_str(),code.line); + const opcode& c=bytecode[p]; + printf("%s0x%.8x: %s 0x%x",header,p,code_table[c.op].name,c.num); + switch(c.op) + { + case op_addc: case op_subc: case op_mulc: case op_divc: + case op_addeqc:case op_subeqc: case op_muleqc:case op_diveqc: + case op_lessc: case op_leqc: case op_grtc: case op_geqc: + case op_pnum: + std::cout<<" ("<",builtin[c.num].name,(uint64_t)builtin[c.num].func);break; + case op_happ: case op_pstr: + case op_lnkc: case op_lnkeqc: + case op_callh: case op_mcallh: + case op_para: case op_defpara:case op_dynpara: + printf(" (\"%s\")",rawstr(str_table[c.num]).c_str());break; + case op_upval:case op_mupval: case op_loadu: + printf(" (0x%x[0x%x])",(c.num>>16)&0xffff,c.num&0xffff);break; + default:break; + } + printf(" (<%s> line %d)\n",files[c.fidx].c_str(),c.line); } void nasal_vm::traceback() { @@ -177,7 +200,7 @@ void nasal_vm::traceback() if(same) printf("\t0x%.8x: %d same call(s)\n",last,same); same=0; - bytecodeinfo(point); + bytecodeinfo("\t",point); } if(same) printf("\t0x%.8x: %d same call(s)\n",last,same); @@ -191,42 +214,60 @@ void nasal_vm::stackinfo(const uint32_t limit=10) for(uint32_t i=0;i=bottom;++i,--top) valinfo(top[0]); } +void nasal_vm::global_state() +{ + if(!bytecode[0].num) // bytecode[0].op is op_intg + { + printf("no global value exists\n"); + return; + } + printf("global:\n"); + for(uint32_t i=0;ielems.size()) + { + printf("no local value exists\n"); + return; + } + printf("local:\n"); + auto& vec=gc.local.back().vec()->elems; + for(uint32_t i=0;iupvalue.empty()) + { + printf("no upvalue exists\n"); + return; + } + printf("upvalue:\n"); + auto& upval=func_stk.top()->upvalue; + for(uint32_t i=0;ielems; + for(uint32_t j=0;jelems; - for(uint32_t i=0;iupvalue.empty()) - { - printf("upvalue:\n"); - auto& upval=func_stk.top()->upvalue; - for(uint32_t i=0;ielems; - for(uint32_t j=0;j0.7)); } var prt=func() { - var s=""; + if(os.platform()=="windows") + system("cls"); + else + system("clear"); + var s="+--------------------+\n"; for(var i=0;i<10;i+=1) { - for(var j=0;j<10;j+=1) + s~="|"; + for(var j=0;j<20;j+=1) s~=pixel[map[i][j]]; - s~='\n'; + s~='|\n'; } - s~='----------\n'; + s~='+--------------------+\n'; print(s); } @@ -31,6 +36,7 @@ var bfs=func(begin,end) var que=queue(); que.push(begin); map[begin[0]][begin[1]]=2; + map[end[0]][end[1]]=0; while(!que.empty()) { var vertex=que.front(); @@ -45,7 +51,7 @@ var bfs=func(begin,end) prt(); return; } - if(0<=x and x<10 and 0<=y and y<10 and map[x][y]==0) + if(0<=x and x<10 and 0<=y and y<20 and map[x][y]==0) { que.push([x,y]); map[x][y]=2; @@ -57,4 +63,4 @@ var bfs=func(begin,end) return; } -bfs([0,0],[9,9]); \ No newline at end of file +bfs([0,0],[9,19]); \ No newline at end of file diff --git a/test/calc.nas b/test/calc.nas index 71b3c53..39edc3c 100644 --- a/test/calc.nas +++ b/test/calc.nas @@ -1,6 +1,6 @@ import("lib.nas"); -var filename=["main.cpp","nasal_ast.h","nasal_builtin.h","nasal_codegen.h","nasal_gc.h","nasal_import.h","nasal_lexer.h","nasal_parse.h","nasal_vm.h","nasal.h"]; -var space=[" "," ","",""," "," "," "," "," "," "]; +var filename=["main.cpp","nasal_ast.h","nasal_builtin.h","nasal_codegen.h","nasal_gc.h","nasal_import.h","nasal_lexer.h","nasal_parse.h","nasal_vm.h","nasal_dbg.h","nasal.h"]; +var space=[" "," ","",""," "," "," "," "," "," "," "]; var enter_cnt=func(s) { var (cnt,len,enter)=(0,size(s),'\n'[0]);