⚡ merge runtime regs into struct context
This commit is contained in:
parent
3852ce23a3
commit
5519dc7a29
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 ValKmjolnir
|
Copyright (c) 2019-2023 ValKmjolnir
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
12
main.cpp
12
main.cpp
|
@ -13,8 +13,6 @@
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
using ch_clk=std::chrono::high_resolution_clock;
|
|
||||||
|
|
||||||
const u32 VM_AST =0x01;
|
const u32 VM_AST =0x01;
|
||||||
const u32 VM_CODE =0x02;
|
const u32 VM_CODE =0x02;
|
||||||
const u32 VM_TIME =0x04;
|
const u32 VM_TIME =0x04;
|
||||||
|
@ -73,6 +71,8 @@ void err() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(const string& file,const std::vector<string>& argv,const u32 cmd) {
|
void execute(const string& file,const std::vector<string>& argv,const u32 cmd) {
|
||||||
|
using clk=std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
error err;
|
error err;
|
||||||
lexer lex(err);
|
lexer lex(err);
|
||||||
parse parse(err);
|
parse parse(err);
|
||||||
|
@ -103,12 +103,12 @@ void execute(const string& file,const std::vector<string>& argv,const u32 cmd) {
|
||||||
|
|
||||||
// run
|
// run
|
||||||
if (cmd&VM_DEBUG) {
|
if (cmd&VM_DEBUG) {
|
||||||
debugger(err).run(gen,ld,argv);
|
dbg(err).run(gen,ld,argv);
|
||||||
} else if (cmd&VM_TIME) {
|
} else if (cmd&VM_TIME) {
|
||||||
auto start=ch_clk::now();
|
auto start=clk::now();
|
||||||
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||||
auto end=ch_clk::now();
|
auto end=clk::now();
|
||||||
std::clog<<"process exited after "<<(end-start).count()*1.0/ch_clk::duration::period::den<<"s.\n\n";
|
std::clog<<"process exited after "<<(end-start).count()*1.0/clk::duration::period::den<<"s.\n\n";
|
||||||
} else if (cmd&VM_EXEC) {
|
} else if (cmd&VM_EXEC) {
|
||||||
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||||
}
|
}
|
||||||
|
|
2
makefile
2
makefile
|
@ -58,7 +58,7 @@ test:nasal
|
||||||
@ ./nasal -t test/mandelbrot.nas
|
@ ./nasal -t test/mandelbrot.nas
|
||||||
@ ./nasal -t test/md5.nas
|
@ ./nasal -t test/md5.nas
|
||||||
@ ./nasal -t -d test/md5compare.nas
|
@ ./nasal -t -d test/md5compare.nas
|
||||||
-@ ./nasal -d test/module_test.nas
|
@ ./nasal -d test/module_test.nas
|
||||||
@ ./nasal -e test/nasal_test.nas
|
@ ./nasal -e test/nasal_test.nas
|
||||||
@ ./nasal -t -d test/occupation.nas 2
|
@ ./nasal -t -d test/occupation.nas 2
|
||||||
@ ./nasal -t -d test/pi.nas
|
@ ./nasal -t -d test/pi.nas
|
||||||
|
|
|
@ -981,7 +981,7 @@ var builtin_dlcall(var* local,gc& ngc) {
|
||||||
return nas_err("dlcall","\"ptr\" is not a valid function pointer");
|
return nas_err("dlcall","\"ptr\" is not a valid function pointer");
|
||||||
}
|
}
|
||||||
// arguments' stored place begins at local +2
|
// arguments' stored place begins at local +2
|
||||||
return ((mod)fp.obj().ptr)(local+2,ngc.top-local-2,&ngc);
|
return ((mod)fp.obj().ptr)(local+2,ngc.rctx->top-local-2,&ngc);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_platform(var* local,gc& ngc) {
|
var builtin_platform(var* local,gc& ngc) {
|
||||||
|
@ -1108,19 +1108,19 @@ var builtin_cocreate(var* local,gc& ngc) {
|
||||||
}
|
}
|
||||||
var co=ngc.alloc(vm_co);
|
var co=ngc.alloc(vm_co);
|
||||||
nas_co& cort=co.co();
|
nas_co& cort=co.co();
|
||||||
cort.pc=func.func().entry-1;
|
cort.ctx.pc=func.func().entry-1;
|
||||||
|
|
||||||
cort.top[0]=nil;
|
cort.ctx.top[0]=nil;
|
||||||
cort.localr=cort.top+1;
|
cort.ctx.localr=cort.ctx.top+1;
|
||||||
cort.top=cort.localr+func.func().lsize;
|
cort.ctx.top=cort.ctx.localr+func.func().lsize;
|
||||||
cort.localr[0]=func.func().local[0];
|
cort.ctx.localr[0]=func.func().local[0];
|
||||||
cort.top[0]=nil; // old upvalr
|
cort.ctx.top[0]=nil; // old upvalr
|
||||||
cort.top++;
|
cort.ctx.top++;
|
||||||
cort.top[0]=var::addr((var*)nullptr); // old localr
|
cort.ctx.top[0]=var::addr((var*)nullptr); // old localr
|
||||||
cort.top++;
|
cort.ctx.top++;
|
||||||
cort.top[0]=var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
|
cort.ctx.top[0]=var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
|
||||||
|
|
||||||
cort.funcr=func; // make sure the coroutine function can use correct upvalues
|
cort.ctx.funcr=func; // make sure the coroutine function can use correct upvalues
|
||||||
cort.status=nas_co::suspended;
|
cort.status=nas_co::suspended;
|
||||||
|
|
||||||
return co;
|
return co;
|
||||||
|
@ -1145,9 +1145,9 @@ var builtin_coresume(var* local,gc& ngc) {
|
||||||
|
|
||||||
// fetch coroutine's stack top and return
|
// fetch coroutine's stack top and return
|
||||||
// so the coroutine's stack top in fact is not changed
|
// so the coroutine's stack top in fact is not changed
|
||||||
if (ngc.top[0].type==vm_ret) {
|
if (ngc.rctx->top[0].type==vm_ret) {
|
||||||
// when first calling this coroutine, the stack top must be vm_ret
|
// when first calling this coroutine, the stack top must be vm_ret
|
||||||
return ngc.top[0];
|
return ngc.rctx->top[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// after first calling the coroutine, each time coroutine.yield triggered
|
// after first calling the coroutine, each time coroutine.yield triggered
|
||||||
|
|
276
nasal_dbg.h
276
nasal_dbg.h
|
@ -6,7 +6,7 @@
|
||||||
#include "nasal_vm.h"
|
#include "nasal_vm.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
class debugger:public vm {
|
class dbg:public vm {
|
||||||
private:
|
private:
|
||||||
bool next;
|
bool next;
|
||||||
usize fsize;
|
usize fsize;
|
||||||
|
@ -22,7 +22,7 @@ private:
|
||||||
void stepinfo();
|
void stepinfo();
|
||||||
void interact();
|
void interact();
|
||||||
public:
|
public:
|
||||||
debugger(error& err):
|
dbg(error& err):
|
||||||
next(false),fsize(0),
|
next(false),fsize(0),
|
||||||
bk_fidx(0),bk_line(0),
|
bk_fidx(0),bk_line(0),
|
||||||
src(err) {}
|
src(err) {}
|
||||||
|
@ -33,7 +33,7 @@ public:
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<string> debugger::parse(const string& cmd) {
|
std::vector<string> dbg::parse(const string& cmd) {
|
||||||
std::vector<string> res;
|
std::vector<string> res;
|
||||||
usize last=0,pos=cmd.find(" ",0);
|
usize last=0,pos=cmd.find(" ",0);
|
||||||
while(pos!=string::npos) {
|
while(pos!=string::npos) {
|
||||||
|
@ -49,7 +49,7 @@ std::vector<string> debugger::parse(const string& cmd) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 debugger::fileindex(const string& filename) {
|
u16 dbg::fileindex(const string& filename) {
|
||||||
for(u16 i=0;i<fsize;++i) {
|
for(u16 i=0;i<fsize;++i) {
|
||||||
if (filename==files[i]) {
|
if (filename==files[i]) {
|
||||||
return i;
|
return i;
|
||||||
|
@ -58,13 +58,13 @@ u16 debugger::fileindex(const string& filename) {
|
||||||
return 65535;
|
return 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger::err() {
|
void dbg::err() {
|
||||||
std::cerr
|
std::cerr
|
||||||
<<"incorrect command\n"
|
<<"incorrect command\n"
|
||||||
<<"input \'h\' to get help\n";
|
<<"input \'h\' to get help\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger::help() {
|
void dbg::help() {
|
||||||
std::cout
|
std::cout
|
||||||
<<"<option>\n"
|
<<"<option>\n"
|
||||||
<<"\th, help | get help\n"
|
<<"\th, help | get help\n"
|
||||||
|
@ -82,7 +82,7 @@ void debugger::help() {
|
||||||
<<"\tbk, break | set break point\n";
|
<<"\tbk, break | set break point\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger::callsort(const u64* arr) {
|
void dbg::callsort(const u64* arr) {
|
||||||
typedef std::pair<u32,u64> op;
|
typedef std::pair<u32,u64> op;
|
||||||
std::vector<op> opcall;
|
std::vector<op> opcall;
|
||||||
u64 total=0;
|
u64 total=0;
|
||||||
|
@ -104,36 +104,38 @@ void debugger::callsort(const u64* arr) {
|
||||||
std::clog<<" total : "<<total<<'\n';
|
std::clog<<" total : "<<total<<'\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger::stepinfo() {
|
void dbg::stepinfo() {
|
||||||
u32 line=bytecode[pc].line==0?0:bytecode[pc].line-1;
|
u32 line=bytecode[ctx.pc].line==0?0:bytecode[ctx.pc].line-1;
|
||||||
u32 begin=(line>>3)==0?0:((line>>3)<<3);
|
u32 begin=(line>>3)==0?0:((line>>3)<<3);
|
||||||
u32 end=(1+(line>>3))<<3;
|
u32 end=(1+(line>>3))<<3;
|
||||||
src.load(files[bytecode[pc].fidx]);
|
src.load(files[bytecode[ctx.pc].fidx]);
|
||||||
std::cout<<"\nsource code:\n";
|
std::cout<<"\nsource code:\n";
|
||||||
for(u32 i=begin;i<end && i<src.size();++i) {
|
for(u32 i=begin;i<end && i<src.size();++i) {
|
||||||
std::cout<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
|
std::cout<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
|
||||||
}
|
}
|
||||||
std::cout<<"next bytecode:\n";
|
|
||||||
begin=(pc>>3)==0?0:((pc>>3)<<3);
|
begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3);
|
||||||
end=(1+(pc>>3))<<3;
|
end=(1+(ctx.pc>>3))<<3;
|
||||||
codestream::set(cnum,cstr,files);
|
codestream::set(cnum,cstr,files);
|
||||||
|
std::cout<<"next bytecode:\n";
|
||||||
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) {
|
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) {
|
||||||
std::cout
|
std::cout
|
||||||
<<(i==pc?back_white:reset)<<(i==pc?"--> ":" ")
|
<<(i==ctx.pc?back_white:reset)
|
||||||
|
<<(i==ctx.pc?"--> ":" ")
|
||||||
<<codestream(bytecode[i],i)
|
<<codestream(bytecode[i],i)
|
||||||
<<reset<<"\n";
|
<<reset<<"\n";
|
||||||
}
|
}
|
||||||
stackinfo(10);
|
stackinfo(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger::interact() {
|
void dbg::interact() {
|
||||||
// special operand
|
// special operand
|
||||||
if (bytecode[pc].op==op_exit) {
|
if (bytecode[ctx.pc].op==op_exit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(bytecode[pc].fidx!=bk_fidx || bytecode[pc].line!=bk_line) && // break point
|
(bytecode[ctx.pc].fidx!=bk_fidx || bytecode[ctx.pc].line!=bk_line) && // break point
|
||||||
!next // next step
|
!next // next step
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
|
@ -146,7 +148,9 @@ void debugger::interact() {
|
||||||
std::cout<<">> ";
|
std::cout<<">> ";
|
||||||
std::getline(std::cin,cmd);
|
std::getline(std::cin,cmd);
|
||||||
auto res=parse(cmd);
|
auto res=parse(cmd);
|
||||||
if (res.size()==1) {
|
if (res.size()==0) {
|
||||||
|
stepinfo();
|
||||||
|
} else if (res.size()==1) {
|
||||||
if (res[0]=="h" || res[0]=="help") {
|
if (res[0]=="h" || res[0]=="help") {
|
||||||
help();
|
help();
|
||||||
} else if (res[0]=="bt" || res[0]=="backtrace") {
|
} else if (res[0]=="bt" || res[0]=="backtrace") {
|
||||||
|
@ -193,7 +197,7 @@ void debugger::interact() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void debugger::run(
|
void dbg::run(
|
||||||
const codegen& gen,
|
const codegen& gen,
|
||||||
const linker& linker,
|
const linker& linker,
|
||||||
const std::vector<string>& argv)
|
const std::vector<string>& argv)
|
||||||
|
@ -202,205 +206,71 @@ void debugger::run(
|
||||||
fsize=linker.filelist().size();
|
fsize=linker.filelist().size();
|
||||||
init(gen.strs(),gen.nums(),gen.codes(),linker.filelist(),argv);
|
init(gen.strs(),gen.nums(),gen.codes(),linker.filelist(),argv);
|
||||||
u64 count[op_ret+1]={0};
|
u64 count[op_ret+1]={0};
|
||||||
#ifndef _MSC_VER
|
typedef void (dbg::*nafunc)();
|
||||||
const void* oprs[]={
|
|
||||||
&&vmexit, &&intg, &&intl, &&loadg,
|
|
||||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
|
||||||
&&pstr, &&newv, &&newh, &&newf,
|
|
||||||
&&happ, &¶, &&deft, &&dyn,
|
|
||||||
&&lnot, &&usub, &&bnot, &&btor,
|
|
||||||
&&btxor, &&btand, &&add, &&sub,
|
|
||||||
&&mul, &&div, &&lnk, &&addc,
|
|
||||||
&&subc, &&mulc, &&divc, &&lnkc,
|
|
||||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
|
||||||
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
|
|
||||||
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
|
|
||||||
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
|
|
||||||
&&divecp, &&lnkecp, &&meq, &&eq,
|
|
||||||
&&neq, &&less, &&leq, &&grt,
|
|
||||||
&&geq, &&lessc, &&leqc, &&grtc,
|
|
||||||
&&geqc, &&pop, &&jmp, &&jt,
|
|
||||||
&&jf, &&cnt, &&findex, &&feach,
|
|
||||||
&&callg, &&calll, &&upval, &&callv,
|
|
||||||
&&callvi, &&callh, &&callfv, &&callfh,
|
|
||||||
&&callb, &&slcbeg, &&slcend, &&slc,
|
|
||||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
|
||||||
&&mcallv, &&mcallh, &&ret
|
|
||||||
};
|
|
||||||
std::vector<const void*> code;
|
|
||||||
for(auto& i:gen.codes()) {
|
|
||||||
code.push_back(oprs[i.op]);
|
|
||||||
imm.push_back(i.num);
|
|
||||||
}
|
|
||||||
// goto the first operand
|
|
||||||
goto *code[pc];
|
|
||||||
#else
|
|
||||||
typedef void (debugger::*nafunc)();
|
|
||||||
const nafunc oprs[]={
|
const nafunc oprs[]={
|
||||||
nullptr, &debugger::o_intg,
|
nullptr, &dbg::o_intg,
|
||||||
&debugger::o_intl, &debugger::o_loadg,
|
&dbg::o_intl, &dbg::o_loadg,
|
||||||
&debugger::o_loadl, &debugger::o_loadu,
|
&dbg::o_loadl, &dbg::o_loadu,
|
||||||
&debugger::o_pnum, &debugger::o_pnil,
|
&dbg::o_pnum, &dbg::o_pnil,
|
||||||
&debugger::o_pstr, &debugger::o_newv,
|
&dbg::o_pstr, &dbg::o_newv,
|
||||||
&debugger::o_newh, &debugger::o_newf,
|
&dbg::o_newh, &dbg::o_newf,
|
||||||
&debugger::o_happ, &debugger::o_para,
|
&dbg::o_happ, &dbg::o_para,
|
||||||
&debugger::o_deft, &debugger::o_dyn,
|
&dbg::o_deft, &dbg::o_dyn,
|
||||||
&debugger::o_lnot, &debugger::o_usub,
|
&dbg::o_lnot, &dbg::o_usub,
|
||||||
&debugger::o_bnot, &debugger::o_btor,
|
&dbg::o_bnot, &dbg::o_btor,
|
||||||
&debugger::o_btxor, &debugger::o_btand,
|
&dbg::o_btxor, &dbg::o_btand,
|
||||||
&debugger::o_add, &debugger::o_sub,
|
&dbg::o_add, &dbg::o_sub,
|
||||||
&debugger::o_mul, &debugger::o_div,
|
&dbg::o_mul, &dbg::o_div,
|
||||||
&debugger::o_lnk, &debugger::o_addc,
|
&dbg::o_lnk, &dbg::o_addc,
|
||||||
&debugger::o_subc, &debugger::o_mulc,
|
&dbg::o_subc, &dbg::o_mulc,
|
||||||
&debugger::o_divc, &debugger::o_lnkc,
|
&dbg::o_divc, &dbg::o_lnkc,
|
||||||
&debugger::o_addeq, &debugger::o_subeq,
|
&dbg::o_addeq, &dbg::o_subeq,
|
||||||
&debugger::o_muleq, &debugger::o_diveq,
|
&dbg::o_muleq, &dbg::o_diveq,
|
||||||
&debugger::o_lnkeq, &debugger::o_bandeq,
|
&dbg::o_lnkeq, &dbg::o_bandeq,
|
||||||
&debugger::o_boreq, &debugger::o_bxoreq,
|
&dbg::o_boreq, &dbg::o_bxoreq,
|
||||||
&debugger::o_addeqc, &debugger::o_subeqc,
|
&dbg::o_addeqc, &dbg::o_subeqc,
|
||||||
&debugger::o_muleqc, &debugger::o_diveqc,
|
&dbg::o_muleqc, &dbg::o_diveqc,
|
||||||
&debugger::o_lnkeqc, &debugger::o_addecp,
|
&dbg::o_lnkeqc, &dbg::o_addecp,
|
||||||
&debugger::o_subecp, &debugger::o_mulecp,
|
&dbg::o_subecp, &dbg::o_mulecp,
|
||||||
&debugger::o_divecp, &debugger::o_lnkecp,
|
&dbg::o_divecp, &dbg::o_lnkecp,
|
||||||
&debugger::o_meq, &debugger::o_eq,
|
&dbg::o_meq, &dbg::o_eq,
|
||||||
&debugger::o_neq, &debugger::o_less,
|
&dbg::o_neq, &dbg::o_less,
|
||||||
&debugger::o_leq, &debugger::o_grt,
|
&dbg::o_leq, &dbg::o_grt,
|
||||||
&debugger::o_geq, &debugger::o_lessc,
|
&dbg::o_geq, &dbg::o_lessc,
|
||||||
&debugger::o_leqc, &debugger::o_grtc,
|
&dbg::o_leqc, &dbg::o_grtc,
|
||||||
&debugger::o_geqc, &debugger::o_pop,
|
&dbg::o_geqc, &dbg::o_pop,
|
||||||
&debugger::o_jmp, &debugger::o_jt,
|
&dbg::o_jmp, &dbg::o_jt,
|
||||||
&debugger::o_jf, &debugger::o_cnt,
|
&dbg::o_jf, &dbg::o_cnt,
|
||||||
&debugger::o_findex, &debugger::o_feach,
|
&dbg::o_findex, &dbg::o_feach,
|
||||||
&debugger::o_callg, &debugger::o_calll,
|
&dbg::o_callg, &dbg::o_calll,
|
||||||
&debugger::o_upval, &debugger::o_callv,
|
&dbg::o_upval, &dbg::o_callv,
|
||||||
&debugger::o_callvi, &debugger::o_callh,
|
&dbg::o_callvi, &dbg::o_callh,
|
||||||
&debugger::o_callfv, &debugger::o_callfh,
|
&dbg::o_callfv, &dbg::o_callfh,
|
||||||
&debugger::o_callb, &debugger::o_slcbeg,
|
&dbg::o_callb, &dbg::o_slcbeg,
|
||||||
&debugger::o_slcend, &debugger::o_slc,
|
&dbg::o_slcend, &dbg::o_slc,
|
||||||
&debugger::o_slc2, &debugger::o_mcallg,
|
&dbg::o_slc2, &dbg::o_mcallg,
|
||||||
&debugger::o_mcalll, &debugger::o_mupval,
|
&dbg::o_mcalll, &dbg::o_mupval,
|
||||||
&debugger::o_mcallv, &debugger::o_mcallh,
|
&dbg::o_mcallv, &dbg::o_mcallh,
|
||||||
&debugger::o_ret
|
&dbg::o_ret
|
||||||
};
|
};
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
for(auto& i:gen.codes()) {
|
for(auto& i:gen.codes()) {
|
||||||
code.push_back(i.op);
|
code.push_back(i.op);
|
||||||
imm.push_back(i.num);
|
imm.push_back(i.num);
|
||||||
}
|
}
|
||||||
while(oprs[code[pc]]) {
|
while(oprs[code[ctx.pc]]) {
|
||||||
interact();
|
interact();
|
||||||
++count[code[pc]];
|
++count[code[ctx.pc]];
|
||||||
(this->*oprs[code[pc]])();
|
(this->*oprs[code[ctx.pc]])();
|
||||||
if (top>=canary) {
|
if (ctx.top>=ctx.canary) {
|
||||||
die("stack overflow");
|
die("stack overflow");
|
||||||
}
|
}
|
||||||
++pc;
|
++ctx.pc;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
vmexit:
|
|
||||||
callsort(count);
|
callsort(count);
|
||||||
ngc.info();
|
ngc.info();
|
||||||
ngc.clear();
|
ngc.clear();
|
||||||
imm.clear();
|
imm.clear();
|
||||||
return;
|
return;
|
||||||
#ifndef _MSC_VER
|
|
||||||
#define dbg(op,num) {\
|
|
||||||
interact();\
|
|
||||||
op();\
|
|
||||||
++count[num];\
|
|
||||||
if (top<canary) {\
|
|
||||||
goto *code[++pc];\
|
|
||||||
}\
|
|
||||||
die("stack overflow");\
|
|
||||||
goto *code[++pc];\
|
|
||||||
}
|
|
||||||
|
|
||||||
intg: dbg(o_intg ,op_intg );
|
|
||||||
intl: dbg(o_intl ,op_intl );
|
|
||||||
loadg: dbg(o_loadg ,op_loadg );
|
|
||||||
loadl: dbg(o_loadl ,op_loadl );
|
|
||||||
loadu: dbg(o_loadu ,op_loadu );
|
|
||||||
pnum: dbg(o_pnum ,op_pnum );
|
|
||||||
pnil: dbg(o_pnil ,op_pnil );
|
|
||||||
pstr: dbg(o_pstr ,op_pstr );
|
|
||||||
newv: dbg(o_newv ,op_newv );
|
|
||||||
newh: dbg(o_newh ,op_newh );
|
|
||||||
newf: dbg(o_newf ,op_newf );
|
|
||||||
happ: dbg(o_happ ,op_happ );
|
|
||||||
para: dbg(o_para ,op_para );
|
|
||||||
deft: dbg(o_deft ,op_deft );
|
|
||||||
dyn: dbg(o_dyn ,op_dyn );
|
|
||||||
lnot: dbg(o_lnot ,op_lnot );
|
|
||||||
usub: dbg(o_usub ,op_usub );
|
|
||||||
bnot: dbg(o_bnot ,op_bnot );
|
|
||||||
btor: dbg(o_btor ,op_btor );
|
|
||||||
btxor: dbg(o_btxor ,op_btxor );
|
|
||||||
btand: dbg(o_btand ,op_btand );
|
|
||||||
add: dbg(o_add ,op_add );
|
|
||||||
sub: dbg(o_sub ,op_sub );
|
|
||||||
mul: dbg(o_mul ,op_mul );
|
|
||||||
div: dbg(o_div ,op_div );
|
|
||||||
lnk: dbg(o_lnk ,op_lnk );
|
|
||||||
addc: dbg(o_addc ,op_addc );
|
|
||||||
subc: dbg(o_subc ,op_subc );
|
|
||||||
mulc: dbg(o_mulc ,op_mulc );
|
|
||||||
divc: dbg(o_divc ,op_divc );
|
|
||||||
lnkc: dbg(o_lnkc ,op_lnkc );
|
|
||||||
addeq: dbg(o_addeq ,op_addeq );
|
|
||||||
subeq: dbg(o_subeq ,op_subeq );
|
|
||||||
muleq: dbg(o_muleq ,op_muleq );
|
|
||||||
diveq: dbg(o_diveq ,op_diveq );
|
|
||||||
lnkeq: dbg(o_lnkeq ,op_lnkeq );
|
|
||||||
bandeq: dbg(o_bandeq,op_btandeq);
|
|
||||||
boreq: dbg(o_boreq, op_btoreq);
|
|
||||||
bxoreq: dbg(o_bxoreq,op_btxoreq);
|
|
||||||
addeqc: dbg(o_addeqc,op_addeqc);
|
|
||||||
subeqc: dbg(o_subeqc,op_subeqc);
|
|
||||||
muleqc: dbg(o_muleqc,op_muleqc);
|
|
||||||
diveqc: dbg(o_diveqc,op_diveqc);
|
|
||||||
lnkeqc: dbg(o_lnkeqc,op_lnkeqc);
|
|
||||||
addecp: dbg(o_addecp,op_addecp);
|
|
||||||
subecp: dbg(o_subecp,op_subecp);
|
|
||||||
mulecp: dbg(o_mulecp,op_mulecp);
|
|
||||||
divecp: dbg(o_divecp,op_divecp);
|
|
||||||
lnkecp: dbg(o_lnkecp,op_lnkecp);
|
|
||||||
meq: dbg(o_meq ,op_meq );
|
|
||||||
eq: dbg(o_eq ,op_eq );
|
|
||||||
neq: dbg(o_neq ,op_neq );
|
|
||||||
less: dbg(o_less ,op_less );
|
|
||||||
leq: dbg(o_leq ,op_leq );
|
|
||||||
grt: dbg(o_grt ,op_grt );
|
|
||||||
geq: dbg(o_geq ,op_geq );
|
|
||||||
lessc: dbg(o_lessc ,op_lessc );
|
|
||||||
leqc: dbg(o_leqc ,op_leqc );
|
|
||||||
grtc: dbg(o_grtc ,op_grtc );
|
|
||||||
geqc: dbg(o_geqc ,op_geqc );
|
|
||||||
pop: dbg(o_pop ,op_pop );
|
|
||||||
jmp: dbg(o_jmp ,op_jmp );
|
|
||||||
jt: dbg(o_jt ,op_jt );
|
|
||||||
jf: dbg(o_jf ,op_jf );
|
|
||||||
cnt: dbg(o_cnt ,op_cnt );
|
|
||||||
findex: dbg(o_findex,op_findex);
|
|
||||||
feach: dbg(o_feach ,op_feach );
|
|
||||||
callg: dbg(o_callg ,op_callg );
|
|
||||||
calll: dbg(o_calll ,op_calll );
|
|
||||||
upval: dbg(o_upval ,op_upval );
|
|
||||||
callv: dbg(o_callv ,op_callv );
|
|
||||||
callvi: dbg(o_callvi,op_callvi);
|
|
||||||
callh: dbg(o_callh ,op_callh );
|
|
||||||
callfv: dbg(o_callfv,op_callfv);
|
|
||||||
callfh: dbg(o_callfh,op_callfh);
|
|
||||||
callb: dbg(o_callb ,op_callb );
|
|
||||||
slcbeg: dbg(o_slcbeg,op_slcbeg);
|
|
||||||
slcend: dbg(o_slcend,op_slcend);
|
|
||||||
slc: dbg(o_slc ,op_slc );
|
|
||||||
slc2: dbg(o_slc2 ,op_slc2 );
|
|
||||||
mcallg: dbg(o_mcallg,op_mcallg);
|
|
||||||
mcalll: dbg(o_mcalll,op_mcalll);
|
|
||||||
mupval: dbg(o_mupval,op_mupval);
|
|
||||||
mcallv: dbg(o_mcallv,op_mcallv);
|
|
||||||
mcallh: dbg(o_mcallh,op_mcallh);
|
|
||||||
ret: dbg(o_ret ,op_ret );
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
124
nasal_gc.h
124
nasal_gc.h
|
@ -196,6 +196,17 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
u32 pc;
|
||||||
|
var* localr;
|
||||||
|
var* memr;
|
||||||
|
var funcr;
|
||||||
|
var upvalr;
|
||||||
|
var* canary;
|
||||||
|
var* stack;
|
||||||
|
var* top;
|
||||||
|
};
|
||||||
|
|
||||||
struct nas_co {
|
struct nas_co {
|
||||||
enum costat:u32{
|
enum costat:u32{
|
||||||
suspended,
|
suspended,
|
||||||
|
@ -204,15 +215,8 @@ struct nas_co {
|
||||||
};
|
};
|
||||||
|
|
||||||
// calculation stack
|
// calculation stack
|
||||||
var stack[STACK_DEPTH];
|
var stack[STACK_DEPTH];
|
||||||
|
context ctx;
|
||||||
u32 pc;
|
|
||||||
var* top;
|
|
||||||
var* canary=stack+STACK_DEPTH-1;
|
|
||||||
var* localr;
|
|
||||||
var* memr;
|
|
||||||
var funcr;
|
|
||||||
var upvalr;
|
|
||||||
|
|
||||||
u32 status;
|
u32 status;
|
||||||
nas_co() {clear();}
|
nas_co() {clear();}
|
||||||
|
@ -220,13 +224,16 @@ struct nas_co {
|
||||||
for(u32 i=0;i<STACK_DEPTH;++i) {
|
for(u32 i=0;i<STACK_DEPTH;++i) {
|
||||||
stack[i]=var::nil();
|
stack[i]=var::nil();
|
||||||
}
|
}
|
||||||
pc=0;
|
ctx.pc=0;
|
||||||
localr=nullptr;
|
ctx.localr=nullptr;
|
||||||
memr=nullptr;
|
ctx.memr=nullptr;
|
||||||
top=stack;
|
ctx.canary=stack+STACK_DEPTH-1;
|
||||||
|
ctx.top=stack;
|
||||||
|
ctx.funcr=var::nil();
|
||||||
|
ctx.upvalr=var::nil();
|
||||||
|
ctx.stack=stack;
|
||||||
|
|
||||||
status=nas_co::suspended;
|
status=nas_co::suspended;
|
||||||
funcr=var::nil();
|
|
||||||
upvalr=var::nil();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -489,30 +496,14 @@ const var nil =var::nil();
|
||||||
|
|
||||||
struct gc {
|
struct gc {
|
||||||
/* main context */
|
/* main context */
|
||||||
struct {
|
context mctx;
|
||||||
u32 pc;
|
|
||||||
var* top;
|
|
||||||
var* localr;
|
|
||||||
var* memr;
|
|
||||||
var funcr;
|
|
||||||
var upvalr;
|
|
||||||
var* canary;
|
|
||||||
var* stack;
|
|
||||||
} mctx;
|
|
||||||
|
|
||||||
/* runtime context */
|
/* runtime context */
|
||||||
u32& pc; // program counter
|
context* rctx;
|
||||||
var*& localr; // local scope register
|
nas_co* cort=nullptr; // running coroutine
|
||||||
var*& memr; // used for mem_call
|
|
||||||
var& funcr; // function register
|
|
||||||
var& upvalr; // upvalue register
|
|
||||||
var*& canary; // avoid stackoverflow
|
|
||||||
var*& top; // stack top
|
|
||||||
var* stack; // stack pointer
|
|
||||||
nas_co* cort; // running coroutine
|
|
||||||
|
|
||||||
/* native function used */
|
/* native function used */
|
||||||
var temp; // temporary place used in builtin/module functions
|
var temp=nil; // temporary place used in builtin/module functions
|
||||||
|
|
||||||
/* constants and memory pool */
|
/* constants and memory pool */
|
||||||
std::vector<var> strs; // reserved address for const vm_str
|
std::vector<var> strs; // reserved address for const vm_str
|
||||||
|
@ -535,13 +526,9 @@ struct gc {
|
||||||
u64 size[gc_tsize];
|
u64 size[gc_tsize];
|
||||||
u64 gcnt[gc_tsize];
|
u64 gcnt[gc_tsize];
|
||||||
u64 acnt[gc_tsize];
|
u64 acnt[gc_tsize];
|
||||||
i64 worktime;
|
i64 worktime=0;
|
||||||
|
|
||||||
gc(u32& _pc, var*& _localr, var*& _memr, var& _funcr,
|
gc(context* _ctx): rctx(_ctx) {}
|
||||||
var& _upvalr, var*& _canary, var*& _top, var* _stk):
|
|
||||||
pc(_pc),localr(_localr),memr(_memr),funcr(_funcr),upvalr(_upvalr),
|
|
||||||
canary(_canary),top(_top),stack(_stk),cort(nullptr),temp(nil),
|
|
||||||
worktime(0) {}
|
|
||||||
void mark();
|
void mark();
|
||||||
void sweep();
|
void sweep();
|
||||||
void extend(u8);
|
void extend(u8);
|
||||||
|
@ -563,11 +550,11 @@ void gc::mark() {
|
||||||
// scan main process stack when coroutine ptr is null
|
// scan main process stack when coroutine ptr is null
|
||||||
// this scan process must execute because when running coroutine,
|
// this scan process must execute because when running coroutine,
|
||||||
// the nas_co related to it will not update it's context(like `top`) until the coroutine suspends or exits.
|
// the nas_co related to it will not update it's context(like `top`) until the coroutine suspends or exits.
|
||||||
for(var* i=stack;i<=top;++i) {
|
for(var* i=rctx->stack;i<=rctx->top;++i) {
|
||||||
bfs.push_back(*i);
|
bfs.push_back(*i);
|
||||||
}
|
}
|
||||||
bfs.push_back(funcr);
|
bfs.push_back(rctx->funcr);
|
||||||
bfs.push_back(upvalr);
|
bfs.push_back(rctx->upvalr);
|
||||||
bfs.push_back(temp);
|
bfs.push_back(temp);
|
||||||
|
|
||||||
// if coroutine is running, scan main process stack from mctx
|
// if coroutine is running, scan main process stack from mctx
|
||||||
|
@ -611,9 +598,9 @@ void gc::mark() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case vm_co:
|
case vm_co:
|
||||||
bfs.push_back(tmp.co().funcr);
|
bfs.push_back(tmp.co().ctx.funcr);
|
||||||
bfs.push_back(tmp.co().upvalr);
|
bfs.push_back(tmp.co().ctx.upvalr);
|
||||||
for(var* i=tmp.co().stack;i<=tmp.co().top;++i) {
|
for(var* i=tmp.co().stack;i<=tmp.co().ctx.top;++i) {
|
||||||
bfs.push_back(*i);
|
bfs.push_back(*i);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -653,7 +640,7 @@ void gc::extend(u8 type) {
|
||||||
|
|
||||||
void gc::init(const std::vector<string>& s,const std::vector<string>& argv) {
|
void gc::init(const std::vector<string>& s,const std::vector<string>& argv) {
|
||||||
// initialize function register
|
// initialize function register
|
||||||
funcr=nil;
|
rctx->funcr=nil;
|
||||||
worktime=0;
|
worktime=0;
|
||||||
|
|
||||||
// initialize counters
|
// initialize counters
|
||||||
|
@ -762,29 +749,15 @@ var gc::newstr(const string& buff) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc::ctxchg(nas_co& ctx) {
|
void gc::ctxchg(nas_co& co) {
|
||||||
// store running state to main context
|
// store running state to main context
|
||||||
mctx.pc=pc;
|
mctx=*rctx;
|
||||||
mctx.top=top;
|
|
||||||
mctx.localr=localr;
|
|
||||||
mctx.memr=memr;
|
|
||||||
mctx.funcr=funcr;
|
|
||||||
mctx.upvalr=upvalr;
|
|
||||||
mctx.canary=canary;
|
|
||||||
mctx.stack=stack;
|
|
||||||
|
|
||||||
// restore coroutine context state
|
// restore coroutine context state
|
||||||
pc=ctx.pc;
|
*rctx=co.ctx;
|
||||||
top=ctx.top;
|
|
||||||
localr=ctx.localr;
|
|
||||||
memr=ctx.memr;
|
|
||||||
funcr=ctx.funcr;
|
|
||||||
upvalr=ctx.upvalr;
|
|
||||||
canary=ctx.canary;
|
|
||||||
stack=ctx.stack;
|
|
||||||
|
|
||||||
// set coroutine pointer
|
// set coroutine pointer
|
||||||
cort=&ctx;
|
cort=&co;
|
||||||
|
|
||||||
// set coroutine state to running
|
// set coroutine state to running
|
||||||
cort->status=nas_co::running;
|
cort->status=nas_co::running;
|
||||||
|
@ -792,26 +765,13 @@ void gc::ctxchg(nas_co& ctx) {
|
||||||
|
|
||||||
void gc::ctxreserve() {
|
void gc::ctxreserve() {
|
||||||
// pc=0 means this coroutine is finished
|
// pc=0 means this coroutine is finished
|
||||||
cort->status=pc? nas_co::suspended:nas_co::dead;
|
cort->status=rctx->pc? nas_co::suspended:nas_co::dead;
|
||||||
|
|
||||||
// store running state to coroutine
|
// store running state to coroutine
|
||||||
cort->pc=pc;
|
cort->ctx=*rctx;
|
||||||
cort->localr=localr;
|
|
||||||
cort->memr=memr;
|
|
||||||
cort->funcr=funcr;
|
|
||||||
cort->upvalr=upvalr;
|
|
||||||
cort->canary=canary;
|
|
||||||
cort->top=top;
|
|
||||||
|
|
||||||
// restore main context state
|
// restore main context state
|
||||||
pc=mctx.pc;
|
*rctx=mctx;
|
||||||
localr=mctx.localr;
|
|
||||||
memr=mctx.memr;
|
|
||||||
funcr=mctx.funcr;
|
|
||||||
upvalr=mctx.upvalr;
|
|
||||||
canary=mctx.canary;
|
|
||||||
top=mctx.top;
|
|
||||||
stack=mctx.stack;
|
|
||||||
|
|
||||||
// set coroutine pointer to nullptr
|
// set coroutine pointer to nullptr
|
||||||
cort=nullptr;
|
cort=nullptr;
|
||||||
|
|
|
@ -141,11 +141,6 @@ public:
|
||||||
codestream(const opcode& c,const u32 i):code(c),index(i) {}
|
codestream(const opcode& c,const u32 i):code(c),index(i) {}
|
||||||
static void set(const f64*,const string*,const string*);
|
static void set(const f64*,const string*,const string*);
|
||||||
void dump(std::ostream&) const;
|
void dump(std::ostream&) const;
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& out,const codestream& ins) {
|
|
||||||
ins.dump(out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const f64* codestream::nums=nullptr;
|
const f64* codestream::nums=nullptr;
|
||||||
|
@ -172,25 +167,19 @@ void codestream::dump(std::ostream& out) const {
|
||||||
case op_addeq: case op_subeq: case op_muleq: case op_diveq:
|
case op_addeq: case op_subeq: case op_muleq: case op_diveq:
|
||||||
case op_lnkeq: case op_meq: case op_btandeq: case op_btoreq:
|
case op_lnkeq: case op_meq: case op_btandeq: case op_btoreq:
|
||||||
case op_btxoreq:
|
case op_btxoreq:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec
|
out<<std::hex<<"0x"<<num<<std::dec<<" sp-"<<num;break;
|
||||||
<<" sp-"<<num;break;
|
|
||||||
case op_addeqc:case op_subeqc: case op_muleqc:case op_diveqc:
|
case op_addeqc:case op_subeqc: case op_muleqc:case op_diveqc:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec
|
out<<std::hex<<"0x"<<num<<std::dec<<" ("<<nums[num]<<")";break;
|
||||||
<<" ("<<nums[num]<<")";break;
|
|
||||||
case op_lnkeqc:
|
case op_lnkeqc:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec<<" (\""
|
out<<std::hex<<"0x"<<num<<std::dec<<" ("<<rawstr(strs[num],16)<<")";break;
|
||||||
<<rawstr(strs[num],16)<<"\")";break;
|
|
||||||
case op_addecp:case op_subecp:case op_mulecp:case op_divecp:
|
case op_addecp:case op_subecp:case op_mulecp:case op_divecp:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec
|
out<<std::hex<<"0x"<<num<<std::dec<<" ("<<nums[num]<<") sp-1";break;
|
||||||
<<" ("<<nums[num]<<") sp-1";break;
|
|
||||||
case op_lnkecp:
|
case op_lnkecp:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec<<" (\""
|
out<<std::hex<<"0x"<<num<<std::dec<<" ("<<rawstr(strs[num],16)<<") sp-1";break;
|
||||||
<<rawstr(strs[num],16)<<"\") sp-1";break;
|
|
||||||
case op_addc: case op_subc: case op_mulc: case op_divc:
|
case op_addc: case op_subc: case op_mulc: case op_divc:
|
||||||
case op_lessc: case op_leqc: case op_grtc: case op_geqc:
|
case op_lessc: case op_leqc: case op_grtc: case op_geqc:
|
||||||
case op_pnum:
|
case op_pnum:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec<<" ("
|
out<<std::hex<<"0x"<<num<<std::dec<<" ("<<nums[num]<<")";break;
|
||||||
<<nums[num]<<")";break;
|
|
||||||
case op_callvi:case op_newv: case op_callfv:
|
case op_callvi:case op_newv: case op_callfv:
|
||||||
case op_intg: case op_intl:
|
case op_intg: case op_intl:
|
||||||
case op_findex:case op_feach:
|
case op_findex:case op_feach:
|
||||||
|
@ -208,8 +197,7 @@ void codestream::dump(std::ostream& out) const {
|
||||||
case op_lnkc:
|
case op_lnkc:
|
||||||
case op_callh: case op_mcallh:
|
case op_callh: case op_mcallh:
|
||||||
case op_para: case op_deft: case op_dyn:
|
case op_para: case op_deft: case op_dyn:
|
||||||
out<<std::hex<<"0x"<<num<<std::dec
|
out<<std::hex<<"0x"<<num<<std::dec<<" ("<<rawstr(strs[num],16)<<")";break;
|
||||||
<<" (\""<<rawstr(strs[num],16)<<"\")";break;
|
|
||||||
default:
|
default:
|
||||||
if (files) {
|
if (files) {
|
||||||
out<<std::hex<<"0x"<<num<<std::dec;
|
out<<std::hex<<"0x"<<num<<std::dec;
|
||||||
|
@ -217,6 +205,11 @@ void codestream::dump(std::ostream& out) const {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (files) {
|
if (files) {
|
||||||
out<<" ("<<files[code.fidx]<<":"<<code.line<<")";
|
out<<"("<<files[code.fidx]<<":"<<code.line<<")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out,const codestream& ins) {
|
||||||
|
ins.dump(out);
|
||||||
|
return out;
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ private:
|
||||||
const token* toks;
|
const token* toks;
|
||||||
ast root;
|
ast root;
|
||||||
error& err;
|
error& err;
|
||||||
std::unordered_map<tok,string> tokname {
|
const std::unordered_map<tok,string> tokname {
|
||||||
{tok::rfor ,"for" },
|
{tok::rfor ,"for" },
|
||||||
{tok::forindex,"forindex"},
|
{tok::forindex,"forindex"},
|
||||||
{tok::foreach ,"foreach" },
|
{tok::foreach ,"foreach" },
|
||||||
|
@ -204,7 +204,7 @@ void parse::match(tok type,const char* info) {
|
||||||
case tok::num:die(thisspan,"expected number"); break;
|
case tok::num:die(thisspan,"expected number"); break;
|
||||||
case tok::str:die(thisspan,"expected string"); break;
|
case tok::str:die(thisspan,"expected string"); break;
|
||||||
case tok::id: die(thisspan,"expected identifier");break;
|
case tok::id: die(thisspan,"expected identifier");break;
|
||||||
default: die(thisspan,"expected '"+tokname[type]+"'"); break;
|
default: die(thisspan,"expected '"+tokname.at(type)+"'"); break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
538
nasal_vm.h
538
nasal_vm.h
File diff suppressed because it is too large
Load Diff
|
@ -41,7 +41,7 @@ var run=func(width,height){
|
||||||
forindex(var j;map[i])
|
forindex(var j;map[i])
|
||||||
map[i][j]=rand()<0.45?'O':'.';
|
map[i][j]=rand()<0.45?'O':'.';
|
||||||
|
|
||||||
for(var r=0;r<200;r+=1){
|
for(var r=0;r<100;r+=1){
|
||||||
prt(map);
|
prt(map);
|
||||||
for(var i=0;i<height;i+=1)
|
for(var i=0;i<height;i+=1)
|
||||||
for(var j=0;j<width;j+=1){
|
for(var j=0;j<width;j+=1){
|
||||||
|
|
Loading…
Reference in New Issue