diff --git a/include/frame.h b/include/frame.h index 387c027..59b0226 100644 --- a/include/frame.h +++ b/include/frame.h @@ -34,6 +34,8 @@ struct _sm_user_page { struct _sm_frame { uint64_t pc; + + _sm_user* rval; // Pointer to the return value of this function _sm_user_page user; _sm_frame* prev; diff --git a/include/gen.h b/include/gen.h index febba5e..25c4b14 100644 --- a/include/gen.h +++ b/include/gen.h @@ -4,6 +4,34 @@ typedef sm_yield (*sm_gen_fun)(sm_state* state); //NOTE: Same as sm_yield +typedef _sm_user sm_output; + +void sm_free_output(sm_output* data); + +template +inline const T* sm_output_ptr(const sm_output& data) +{ + if(data.set) + return _sm_get(const_cast<_sm_user*>(&data)); + else return nullptr; +} + +template +inline bool sm_output_value(const sm_output& data, T* output) +{ + auto ptr = sm_output_ptr(data); + if(ptr) { + *output = *ptr; + return true; + } else return false; +} + +template +inline T sm_output_assume_value(const sm_output& data) +{ + return *sm_output_ptr(data); +} + // A generator method struct sm_generator; @@ -13,4 +41,5 @@ sm_generator* sm_generate(sm_gen_fun function); void sm_free_generator(sm_generator* generator); /// Run this generator until the next yield. /// Returns false if the generator ends. -bool sm_next(sm_generator** gen, sm_state* state); +/// Any output value yielded from the generator is returned in the output pointer, unless the pointer is NULL, in which case the output is discarded. +bool sm_next(sm_generator** gen, sm_state* state, sm_output* output); diff --git a/include/state.h b/include/state.h index a389770..cec2aa9 100644 --- a/include/state.h +++ b/include/state.h @@ -15,6 +15,29 @@ inline uint64_t _sm_compute_key(uint64_t name) return _sm_confine_key(name); } +template +inline T* _sm_replace(_sm_user* frame, T value) +{ + if(frame->set) _sm_free(frame); + + frame->set = true; + frame->free = false; + + // Key is left the same + + return _sm_init(frame, value); +} + +template +inline T* _sm_replace_ip(_sm_user** frame, T value) +{ + + if(! (*frame)) { + *frame = box<_sm_user, true>(); + } + return _sm_replace(*frame, value); +} + template inline T* _sm_var(_sm_user_page* user, U name, T init) { @@ -76,6 +99,11 @@ inline sm_yield sm_continue() { return (sm_yield)_sm_noop; } #define SM_END } return sm_end() #define SM_YIELD(v) do { state->current->pc = __LINE__; return (sm_yield)(v); case __LINE__:; } while(0) +#define SM_YIELD_VALUE(v) do { _sm_replace_ip(&state->current->rval, (v)); SM_YIELD(sm_continue()); } while(0) + +#define SM_GENERATOR(name) sm_yield name(sm_state* state) + +// --- #define SM_GENERATOR(name) sm_yield name(sm_state* state) diff --git a/src/gen.cpp b/src/gen.cpp index f06c4cd..73eab23 100644 --- a/src/gen.cpp +++ b/src/gen.cpp @@ -22,11 +22,16 @@ void sm_free_generator(sm_generator* gen) } // Returns false if the generator ends. -bool sm_next(sm_generator** gen, sm_state* state) +bool sm_next(sm_generator** gen, sm_state* state, _sm_user* output) { if( !(*gen)->current) return false; sm_yield next = (*gen)->current(state); + + // Set the return value to `output`. + if(output && state->current->rval) *output = *state->current->rval; + else if(output) output->set = false; + switch((uintptr_t)next) { case 0: diff --git a/src/state.cpp b/src/state.cpp index ce84d56..61f3f82 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -23,6 +23,16 @@ inline static void sm_free_user(_sm_user* data) } } +void sm_free_output(_sm_user* data) +{ + if(!data) return; + + if(data->set) + sm_free_user(data); + data->set = data->free = false; + +} + inline static void _sm_free_page(_sm_user_page* page) { for (int i=0;i<_SM_STACK_SIZE;i++) sm_free_user(&page->data[i]); @@ -45,6 +55,11 @@ void sm_free_state(sm_state* state) while(frame) { + if(frame->rval) { + sm_free_user(frame->rval); + unbox(frame->rval); + } + _sm_free_all_pages(frame->user.next); _sm_free_page(&frame->user); @@ -59,6 +74,11 @@ void _sm_pop_stack(sm_state* state) auto last = unbox(state->current); state->current = last.prev; + if(last.rval) + { + sm_free_user(last.rval); + unbox(last.rval); + } _sm_free_all_pages(last.user.next); _sm_free_page(&last.user); } diff --git a/src/test/yield_test.cpp b/src/test/yield_test.cpp new file mode 100644 index 0000000..766ce0f --- /dev/null +++ b/src/test/yield_test.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include + +struct _test { + int a, b; +}; + +SM_GENERATOR(sm_test_2) +{ + int* a = SM_VAR(-10); + + SM_BEGIN; + while( (*a) < 0 ) { + printf("(2) a = %d\n", *a); + SM_YIELD_VALUE(*a); + (*a)+=1; + } + printf("Done!\n"); + SM_END; +} + +SM_GENERATOR(sm_test) +{ + int* a = SM_VAR(10); + + auto c = SM_SLOT(_test); + float* d = SM_SLOT(float); + + *c = { 200, 300 }; + *d = 10.f; + + SM_BEGIN; + *a = 5; + SM_YIELD_VALUE(*a); + printf("IN a = %d\n", *a); + *a = 1; + SM_YIELD_VALUE(*a); + printf("IN a = %d\n", *a); + //printf("Starting function 2\n"); + SM_YIELD(sm_test_2); + //printf("2 done\n"); + + SM_END; +} + +int main() +{ + auto state = sm_new_state(); + auto gen = sm_generate(&sm_test); + + sm_output output; + while(sm_next(&gen, state, &output)) { + int out_int; + if(!sm_output_value(output, &out_int)) + continue; + printf("OUT \t\ta = %d\n", out_int); + } + + sm_free_output(&output); + sm_free_generator(gen); + sm_free_state(state); + + /* + //TODO: `sm_state` creation/initialisation & freeing functions + //TODO: Test `sm_test` + + _test hi = { 0, 0 }; + auto _a = _sm_init(nullptr, hi); + */ + return 0; +}