diff --git a/include/state.h b/include/state.h index a4ac336..7225b86 100644 --- a/include/state.h +++ b/include/state.h @@ -9,10 +9,12 @@ inline T* _sm_var(sm_state* state, U name, T init) { auto val = &state->current->user.data[name]; bool set = val->set; + val->set = true; - val->free = false; + + return ( ! set ) ? - _sm_init(val, init) : + (val->free = false, _sm_init(val, init)) : _sm_get(val); } template @@ -43,3 +45,5 @@ inline sm_yield sm_continue() { return (sm_yield)_sm_noop; } #define SM_YIELD(v) do { state->current->pc = __LINE__; return (sm_yield)(v); case __LINE__:; } while(0) +sm_state* sm_new(); +void sm_free(sm_state* state); diff --git a/src/dev.h b/src/dev.h index fe997d5..47f0ff3 100644 --- a/src/dev.h +++ b/src/dev.h @@ -4,6 +4,8 @@ #ifdef __cplusplus #include +#define restrict __restrict__ + [[noreturn]] #else #include diff --git a/src/frame.cpp b/src/frame.cpp index fcbde01..759517d 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -5,7 +5,6 @@ #define BODY(T, U, nm) \ template<> T* _sm_init(_sm_user* frame, T init) \ { \ - printf("hi\n"); \ frame->free = false; \ frame-> nm = (U)init; \ return (T*)&frame-> nm; \ diff --git a/src/main.cpp b/src/main.cpp index 8b1e513..8510ffa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,21 +16,32 @@ sm_yield sm_test(sm_state* state) *d = 10.f; SM_BEGIN; - SM_YIELD(sm_continue()); *a = 5; SM_YIELD(sm_continue()); + printf("a = %d\n", *a); *a = 0; + SM_YIELD(sm_continue()); + printf("a = %d\n", *a); SM_END; } int main() { + auto state = sm_new(); + + sm_test(state); + sm_test(state); + sm_test(state); + + sm_free(state); + + /* //TODO: `sm_state` creation/initialisation & freeing functions //TODO: Test `sm_test` _test hi = { 0, 0 }; auto _a = _sm_init(nullptr, hi); - + */ return 0; } diff --git a/src/state.cpp b/src/state.cpp index e86c7da..c316c59 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1,13 +1,64 @@ #include #include "dev.h" -void _sm_pop_stack(sm_state* state) +/// A generator that just returns `nullptr` (end). Used for yielding without calling in to another generator. +/// +/// NOTE: To avoid uselessly creating a stack frame for calls to this generator, its address is (TODO) directly compared to when switching into a deeper generator, and then skipped entirely. +sm_yield _sm_noop(sm_state*) { - TODO("Free the current stack frame in `state` and replace it with the previous one"); + return (sm_yield)nullptr; } -sm_yield _sm_noop(sm_state*) +sm_state* sm_new() { - return (sm_yield)nullptr; + auto state = box(); + state->current = box<_sm_frame, true>(); + return state; +} + +inline static void sm_free_user(_sm_user* data) +{ + if (data->free) { + free(data->_ptr); + } +} + +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]); +} + +inline static void _sm_free_all_pages(_sm_user_page* page) +{ + while(page) + { + _sm_free_page(page); + auto old = page; + page = page->next; + free(old); + } +} + +void sm_free(sm_state* state) +{ + _sm_frame* frame = unbox(state).current; + + while(frame) + { + _sm_free_all_pages(frame->user.next); + _sm_free_page(&frame->user); + + frame = unbox(frame).prev; + } } +void _sm_pop_stack(sm_state* state) +{ + if(!state->current) return; + + auto last = unbox(state->current); + state->current = last.prev; + + _sm_free_all_pages(last.user.next); + _sm_free_page(&last.user); +}