diff --git a/c/containers/Makefile.am b/c/containers/Makefile.am index 1326927d1..ece305d8d 100644 --- a/c/containers/Makefile.am +++ b/c/containers/Makefile.am @@ -27,6 +27,8 @@ libcontainers_la_SOURCES= \ lhmss.h \ lhmsv.c \ lhmsv.h \ + local_stack.c \ + local_stack.h \ loop_stack.c \ loop_stack.h \ lrec.c \ diff --git a/c/containers/local_stack.c b/c/containers/local_stack.c new file mode 100644 index 000000000..8f814af7b --- /dev/null +++ b/c/containers/local_stack.c @@ -0,0 +1,194 @@ +#include +#include "lib/mlrutil.h" +#include "lib/mlr_globals.h" +#include "containers/local_stack.h" + +#define INITIAL_SIZE 32 + +// ---------------------------------------------------------------- +// Private to .c file +struct _local_stack_frame_t { + lhmsmv_t* pbindings; + char fenced; + char ephemeral; + char in_use; +}; + +// ---------------------------------------------------------------- +local_stack_t* local_stack_alloc() { + local_stack_t* pstack = mlr_malloc_or_die(sizeof(local_stack_t)); + + pstack->num_used = 0; + pstack->num_allocated = INITIAL_SIZE; + + pstack->ppframes = mlr_malloc_or_die(pstack->num_allocated * sizeof(local_stack_frame_t*)); + memset(pstack->ppframes, 0, pstack->num_allocated * sizeof(local_stack_frame_t*)); + + pstack->pbase_frame = local_stack_frame_alloc_unfenced(); + local_stack_push(pstack, pstack->pbase_frame); + + return pstack; +} + +// ---------------------------------------------------------------- +void local_stack_free(local_stack_t* pstack) { + if (pstack == NULL) + return; + + local_stack_frame_free(pstack->pbase_frame); + free(pstack->ppframes); + free(pstack); +} + +// ---------------------------------------------------------------- +static inline local_stack_frame_t* local_stack_frame_alloc(int fenced, int ephemeral) { + local_stack_frame_t* pframe = mlr_malloc_or_die(sizeof(local_stack_frame_t)); + pframe->pbindings = lhmsmv_alloc(); + pframe->fenced = fenced; + pframe->ephemeral = ephemeral; + pframe->in_use = FALSE; + return pframe; +} + +local_stack_frame_t* local_stack_frame_alloc_unfenced() { + return local_stack_frame_alloc(FALSE, FALSE); +} + +local_stack_frame_t* local_stack_frame_alloc_fenced() { + return local_stack_frame_alloc(TRUE, FALSE); +} + +// ---------------------------------------------------------------- +void local_stack_frame_free(local_stack_frame_t* pframe) { + lhmsmv_free(pframe->pbindings); + free(pframe); +} + +// ---------------------------------------------------------------- +// xxx cmt +local_stack_frame_t* local_stack_frame_enter(local_stack_frame_t* pframe) { + if (pframe->in_use) { + local_stack_frame_t* pephemeral = local_stack_frame_alloc(pframe->fenced, TRUE); + pephemeral->in_use = TRUE; + return pephemeral; + } else { + pframe->in_use = TRUE; + return pframe; + } +} + +void local_stack_frame_exit(local_stack_frame_t* pframe) { + if (pframe->ephemeral) { + local_stack_frame_free(pframe); + } else { + lhmsmv_clear(pframe->pbindings); + pframe->in_use = FALSE; + } +} + +// ---------------------------------------------------------------- +void local_stack_push(local_stack_t* pstack, local_stack_frame_t* pframe) { + if (pstack->num_used >= pstack->num_allocated) { + pstack->num_allocated += INITIAL_SIZE; + pstack->ppframes = mlr_realloc_or_die(pstack->ppframes, + pstack->num_allocated * sizeof(local_stack_frame_t*)); + } + pstack->ppframes[pstack->num_used] = pframe; + pstack->num_used++; +} + +// ---------------------------------------------------------------- +local_stack_frame_t* local_stack_pop(local_stack_t* pstack) { + if (pstack->num_used <= 0) { + fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", + MLR_GLOBALS.bargv0, __FILE__, __LINE__); + exit(1); + } + + local_stack_frame_t* pframe = pstack->ppframes[pstack->num_used-1]; + pstack->num_used--; + return pframe; +} + +// ---------------------------------------------------------------- +// xxx cmt +mv_t* local_stack_resolve(local_stack_t* pstack, char* name) { + for (int i = pstack->num_used - 1; i >= 0; i--) { + mv_t* pval = lhmsmv_get(pstack->ppframes[i]->pbindings, name); + if (pval != NULL) { + return pval; + } + if (pstack->ppframes[i]->fenced) { + break; + } + } + return NULL; +} + +// ---------------------------------------------------------------- +// xxx +// +// run_mlr --opprint --from $indir/abixy put ' +// func f(x) { +// local a = 1; +// if (NR > 5) { +// a = 2; +// } +// return a; +// } +// +// func g(x) { +// local b = 1; +// if (NR > 5) { +// local b = 2; +// } +// return b; +// } +// $of = f(NR); +// $og = g(NR); +// ' + +void local_stack_define(local_stack_t* pstack, char* name, mv_t* pmv, char free_flags) { + local_stack_frame_t* ptop_frame = pstack->ppframes[pstack->num_used - 1]; + lhmsmv_put(ptop_frame->pbindings, name, pmv, free_flags); +} + +void local_stack_set(local_stack_t* pstack, char* name, mv_t* pmv, char free_flags) { + + // xxx comment + // xxx test thoroughly + for (int i = pstack->num_used - 1; i >= 0; i--) { + local_stack_frame_t* pframe = pstack->ppframes[i]; + if (lhmsmv_get(pframe->pbindings, name)) { + lhmsmv_put(pframe->pbindings, name, pmv, free_flags); + return; + } + if (pstack->ppframes[i]->fenced) { + break; + } + } + + local_stack_frame_t* ptop_frame = pstack->ppframes[pstack->num_used - 1]; + lhmsmv_put(ptop_frame->pbindings, name, pmv, free_flags); +} + +// ---------------------------------------------------------------- +void local_stack_clear(local_stack_t* pstack) { + if (pstack->num_used <= 0) { + fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n", + MLR_GLOBALS.bargv0, __FILE__, __LINE__); + exit(1); + } + lhmsmv_clear(pstack->ppframes[pstack->num_used-1]->pbindings); +} + +// ---------------------------------------------------------------- +void local_stack_print(local_stack_t* pstack) { + printf("BIND STACK BEGIN (#frames %d):\n", pstack->num_used); + for (int i = pstack->num_used - 1; i >= 0; i--) { + printf("-- FRAME %d (fenced:%d):\n", i, pstack->ppframes[i]->fenced); + lhmsmv_dump(pstack->ppframes[i]->pbindings); + } + printf("BIND STACK END\n"); +} + diff --git a/c/containers/local_stack.h b/c/containers/local_stack.h new file mode 100644 index 000000000..ea0029c85 --- /dev/null +++ b/c/containers/local_stack.h @@ -0,0 +1,81 @@ +#ifndef LOCAL_STACK_H +#define LOCAL_STACK_H + +#include "containers/mlrval.h" +#include "containers/lhmsmv.h" + +// Bound & scoped variables for use in for-loops, function bodies, and +// subroutine bodies. A frame is 'fenced' if it's at entry to a +// function/subroutine body. Example: +// +// mlr ... put -f ' +// func f(x) { +// return k; +// } +// for (k,v in $*) { +// $[k] = f(v) +// } +// ' +// +// There is a bind-stack created on entering the put. A frame is pushed in the +// for-loop. A second frame is pushed in the call to f(x). The former should +// have access to k; the latter should not. + +// ---------------------------------------------------------------- +// Data private to .c file +typedef struct _local_stack_frame_t local_stack_frame_t; + +typedef struct _local_stack_t { + int num_used; + int num_allocated; + local_stack_frame_t** ppframes; + local_stack_frame_t* pbase_frame; +} local_stack_t; + +// ---------------------------------------------------------------- +// Constructors/destructors + +local_stack_t* local_stack_alloc(); +void local_stack_free(local_stack_t* pstack); + +local_stack_frame_t* local_stack_frame_alloc_unfenced(); +local_stack_frame_t* local_stack_frame_alloc_fenced(); +void local_stack_frame_free(local_stack_frame_t* pframe); + +// xxx comment +local_stack_frame_t* local_stack_frame_enter(local_stack_frame_t* pframe); +void local_stack_frame_exit(local_stack_frame_t* pframe); + +// ---------------------------------------------------------------- +// Scope entry/exit + +// To be called on entry to scoped block +void local_stack_push(local_stack_t* pstack, local_stack_frame_t* pframe); + +// To be called on exit from scoped block. +local_stack_frame_t* local_stack_pop(local_stack_t* pstack); + +// ---------------------------------------------------------------- +// Access within scope + +// Use of local variables on expression right-hand sides +mv_t* local_stack_resolve(local_stack_t* pstack, char* key); + +// Use of local variables on expression left-hand sides +// The pmv is not copied. You may wish to mv_copy the argument you pass in. +// The pmv will be freed. + +// xxx cmt +void local_stack_define(local_stack_t* pstack, char* name, mv_t* pmv, char free_flags); +void local_stack_set(local_stack_t* pstack, char* name, mv_t* pmv, char free_flags); + +// Clears the binding from the top frame without popping it. Useful +// for clearing the baseframe which is never popped. +void local_stack_clear(local_stack_t* pstack); + +// ---------------------------------------------------------------- +// Test/debug + +void local_stack_print(local_stack_t* pstack); + +#endif // LOCAL_STACK_H