diff --git a/CMakeLists.txt b/CMakeLists.txt index dae8956..b86c9bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ set(NASAL_OBJECT_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp ${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp ${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp + ${CMAKE_SOURCE_DIR}/src/coroutine.cpp ${CMAKE_SOURCE_DIR}/src/fg_props.cpp ${CMAKE_SOURCE_DIR}/src/math_lib.cpp ${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp diff --git a/makefile b/makefile index 636cd5f..713cd42 100644 --- a/makefile +++ b/makefile @@ -18,7 +18,8 @@ NASAL_HEADER=\ src/optimizer.h\ src/symbol_finder.h\ src/fg_props.h\ - src/math_lib.h + src/math_lib.h\ + src/coroutine.h NASAL_OBJECT=\ build/nasal_err.o\ @@ -37,6 +38,7 @@ NASAL_OBJECT=\ build/nasal_builtin.o\ build/fg_props.o\ build/math_lib.o\ + build/coroutine.o\ build/nasal_vm.o\ build/nasal_dbg.o\ build/main.o @@ -89,6 +91,12 @@ build/nasal_builtin.o: \ src/nasal_builtin.h src/nasal_builtin.cpp | build $(CXX) -std=$(STD) -c -O3 src/nasal_builtin.cpp -fno-exceptions -fPIC -o build/nasal_builtin.o -I . +build/coroutine.o: \ + src/nasal.h\ + src/nasal_gc.h\ + src/coroutine.h src/coroutine.cpp | build + $(CXX) -std=$(STD) -c -O3 src/coroutine.cpp -fno-exceptions -fPIC -o build/coroutine.o -I . + build/math_lib.o: \ src/nasal.h\ src/nasal_gc.h\ diff --git a/src/coroutine.cpp b/src/coroutine.cpp new file mode 100644 index 0000000..ea10295 --- /dev/null +++ b/src/coroutine.cpp @@ -0,0 +1,116 @@ +#include "coroutine.h" + +var builtin_cocreate(var* local, gc& ngc) { + // +-------------+ + // | old pc | <- top[0] + // +-------------+ + // | old localr | <- top[-1] + // +-------------+ + // | old upvalr | <- top[-2] + // +-------------+ + // | local scope | + // | ... | + // +-------------+ <- local pointer stored in localr + // | old funcr | <- old function stored in funcr + // +-------------+ + var func = local[1]; + if (func.type!=vm_func) { + return nas_err("coroutine::create", "must use a function to create coroutine"); + } + if (ngc.cort) { + return nas_err("coroutine::create", "cannot create another coroutine in a coroutine"); + } + var co = ngc.alloc(vm_co); + nas_co& cort = co.co(); + cort.ctx.pc = func.func().entry-1; + + cort.ctx.top[0] = nil; + cort.ctx.localr = cort.ctx.top+1; + cort.ctx.top = cort.ctx.localr+func.func().lsize; + cort.ctx.localr[0] = func.func().local[0]; + cort.ctx.top[0] = nil; // old upvalr + cort.ctx.top++; + cort.ctx.top[0] = var::addr((var*)nullptr); // old localr + cort.ctx.top++; + cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function + + cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues + cort.status = nas_co::status::suspended; + + return co; +} + +var builtin_coresume(var* local, gc& ngc) { + if (ngc.cort) { + return nas_err("coroutine::resume", "cannot start another coroutine when one is running"); + } + var co = local[1]; + // return nil if is not a coroutine object + if (co.type!=vm_co) { + return nil; + } + // cannot resume a dead coroutine + if (co.co().status==nas_co::status::dead) { + return nil; + } + + // change to coroutine context + ngc.ctxchg(co.co()); + + // fetch coroutine's stack top and return + // so the coroutine's stack top in fact is not changed + if (ngc.rctx->top[0].type==vm_ret) { + // when first calling this coroutine, the stack top must be vm_ret + return ngc.rctx->top[0]; + } + + // after first calling the coroutine, each time coroutine.yield triggered + // a new space will be reserved on stack with value nil + // so we could fill this place with args + + // the coroutine seems like coroutine.yield returns the value + // but in fact coroutine.yield stop the coroutine + // until main context calls the coroutine.resume + return local[2]; +} + +var builtin_coyield(var* local, gc& ngc) { + if (!ngc.cort) { + return nas_err("coroutine::yield", "no coroutine is running"); + } + + // this will set to main stack top + ngc.ctxreserve(); + + // then this will return value to main's stack top[0] + // the procedure seems like coroutine.resume returns the value + // but in fact coroutine.resume stop the main context + // until coroutine calls the coroutine.yield + return local[1]; +} + +var builtin_costatus(var* local, gc& ngc) { + var co = local[1]; + if (co.type!=vm_co) { + return ngc.newstr("error"); + } + switch(co.co().status) { + case nas_co::status::suspended: return ngc.newstr("suspended"); + case nas_co::status::running: return ngc.newstr("running"); + case nas_co::status::dead: return ngc.newstr("dead"); + } + return nil; +} + +var builtin_corun(var* local, gc& ngc) { + return ngc.cort? one:zero; +} + +nasal_builtin_table coroutine_native[] = { + {"__cocreate", builtin_cocreate}, + {"__coresume", builtin_coresume}, + {"__coyield", builtin_coyield}, + {"__costatus", builtin_costatus}, + {"__corun", builtin_corun}, + {nullptr, nullptr} +}; \ No newline at end of file diff --git a/src/coroutine.h b/src/coroutine.h new file mode 100644 index 0000000..83911aa --- /dev/null +++ b/src/coroutine.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nasal.h" +#include "nasal_gc.h" +#include "nasal_builtin.h" + +var builtin_cocreate(var*, gc&); +var builtin_coresume(var*, gc&); +var builtin_coyield(var*, gc&); +var builtin_costatus(var*, gc&); +var builtin_corun(var*, gc&); + +extern nasal_builtin_table coroutine_native[]; \ No newline at end of file diff --git a/src/nasal_builtin.cpp b/src/nasal_builtin.cpp index bf9b4a5..c42f94d 100644 --- a/src/nasal_builtin.cpp +++ b/src/nasal_builtin.cpp @@ -1040,112 +1040,6 @@ var builtin_md5(var* local, gc& ngc) { return ngc.newstr(md5(str.str())); } -var builtin_cocreate(var* local, gc& ngc) { - // +-------------+ - // | old pc | <- top[0] - // +-------------+ - // | old localr | <- top[-1] - // +-------------+ - // | old upvalr | <- top[-2] - // +-------------+ - // | local scope | - // | ... | - // +-------------+ <- local pointer stored in localr - // | old funcr | <- old function stored in funcr - // +-------------+ - var func = local[1]; - if (func.type!=vm_func) { - return nas_err("coroutine::create", "must use a function to create coroutine"); - } - if (ngc.cort) { - return nas_err("coroutine::create", "cannot create another coroutine in a coroutine"); - } - var co = ngc.alloc(vm_co); - nas_co& cort = co.co(); - cort.ctx.pc = func.func().entry-1; - - cort.ctx.top[0] = nil; - cort.ctx.localr = cort.ctx.top+1; - cort.ctx.top = cort.ctx.localr+func.func().lsize; - cort.ctx.localr[0] = func.func().local[0]; - cort.ctx.top[0] = nil; // old upvalr - cort.ctx.top++; - cort.ctx.top[0] = var::addr((var*)nullptr); // old localr - cort.ctx.top++; - cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function - - cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues - cort.status = nas_co::status::suspended; - - return co; -} - -var builtin_coresume(var* local, gc& ngc) { - if (ngc.cort) { - return nas_err("coroutine::resume", "cannot start another coroutine when one is running"); - } - var co = local[1]; - // return nil if is not a coroutine object - if (co.type!=vm_co) { - return nil; - } - // cannot resume a dead coroutine - if (co.co().status==nas_co::status::dead) { - return nil; - } - - // change to coroutine context - ngc.ctxchg(co.co()); - - // fetch coroutine's stack top and return - // so the coroutine's stack top in fact is not changed - if (ngc.rctx->top[0].type==vm_ret) { - // when first calling this coroutine, the stack top must be vm_ret - return ngc.rctx->top[0]; - } - - // after first calling the coroutine, each time coroutine.yield triggered - // a new space will be reserved on stack with value nil - // so we could fill this place with args - - // the coroutine seems like coroutine.yield returns the value - // but in fact coroutine.yield stop the coroutine - // until main context calls the coroutine.resume - return local[2]; -} - -var builtin_coyield(var* local, gc& ngc) { - if (!ngc.cort) { - return nas_err("coroutine::yield", "no coroutine is running"); - } - - // this will set to main stack top - ngc.ctxreserve(); - - // then this will return value to main's stack top[0] - // the procedure seems like coroutine.resume returns the value - // but in fact coroutine.resume stop the main context - // until coroutine calls the coroutine.yield - return local[1]; -} - -var builtin_costatus(var* local, gc& ngc) { - var co = local[1]; - if (co.type!=vm_co) { - return ngc.newstr("error"); - } - switch(co.co().status) { - case nas_co::status::suspended: return ngc.newstr("suspended"); - case nas_co::status::running: return ngc.newstr("running"); - case nas_co::status::dead: return ngc.newstr("dead"); - } - return nil; -} - -var builtin_corun(var* local, gc& ngc) { - return ngc.cort? one:zero; -} - var builtin_millisec(var* local, gc& ngc) { f64 res = std::chrono::duration_cast (std::chrono::high_resolution_clock::now().time_since_epoch()) @@ -1295,11 +1189,6 @@ nasal_builtin_table builtin[] = { {"__platform", builtin_platform}, {"__arch", builtin_arch}, {"__md5", builtin_md5}, - {"__cocreate", builtin_cocreate}, - {"__coresume", builtin_coresume}, - {"__coyield", builtin_coyield}, - {"__costatus", builtin_costatus}, - {"__corun", builtin_corun}, {"__millisec", builtin_millisec}, {"__gcextd", builtin_gcextend}, {"__gcinfo", builtin_gcinfo}, diff --git a/src/nasal_builtin.h b/src/nasal_builtin.h index 85a8050..5c62658 100644 --- a/src/nasal_builtin.h +++ b/src/nasal_builtin.h @@ -109,11 +109,6 @@ var builtin_arch(var*, gc&); std::string tohex(u32); std::string md5(const std::string&); var builtin_md5(var*, gc&); -var builtin_cocreate(var*, gc&); -var builtin_coresume(var*, gc&); -var builtin_coyield(var*, gc&); -var builtin_costatus(var*, gc&); -var builtin_corun(var*, gc&); var builtin_millisec(var*, gc&); var builtin_gcextend(var*, gc&); var builtin_gcinfo(var*, gc&); diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index 6d16310..3101188 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -15,6 +15,7 @@ void codegen::load_native_function_table(nasal_builtin_table* table) { void codegen::init_native_function() { load_native_function_table(builtin); load_native_function_table(math_lib_native); + load_native_function_table(coroutine_native); load_native_function_table(flight_gear_native); } diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index fbfe11b..3913790 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -9,6 +9,7 @@ #include "nasal_import.h" #include "nasal_builtin.h" +#include "coroutine.h" #include "math_lib.h" #include "fg_props.h"