diff options
Diffstat (limited to 'old-extension/spec_array.c')
-rw-r--r-- | old-extension/spec_array.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/old-extension/spec_array.c b/old-extension/spec_array.c new file mode 100644 index 00000000..78b24018 --- /dev/null +++ b/old-extension/spec_array.c @@ -0,0 +1,416 @@ +/* + * spec_array.c - Support for specialized associative arrays. + */ + +/* + * Copyright (C) 2012 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Programming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "awk.h" +#include "spec_array.h" + +typedef struct spec_array { + Fetch_func_t fetch_func; + Store_func_t store_func; + Load_func_t load_func; + void *data; +} array_t; + +/* + * The array_t structure is attached to the array itself without + * the necessity to maintain a list of symbols; this works only + * because there is just enough free space in the NODE strcture when + * the base array is str_array. + */ + +#define SUPER(F) (*str_array_func[AFUNC(F)]) + + +/* + * deferred_array --- Deferred loading of array at run-time. + * + * The load routine takes two arguments, the array and + * a void * data: + * + * void load_func(NODE *array, void *data) + * + * Use register_deferred_array(array, load_func, void *data) to + * bind an array to the load routine. + */ + +static NODE **deferred_array_init(NODE *, NODE *); +static NODE **deferred_array_lookup(NODE *, NODE *); +static NODE **deferred_array_exists(NODE *, NODE *); +static NODE **deferred_array_remove(NODE *, NODE *); +static NODE **deferred_array_clear(NODE *, NODE *); +static NODE **deferred_array_list(NODE *, NODE *); +static NODE **deferred_array_copy(NODE *, NODE *); +static NODE **deferred_array_length(NODE *, NODE *); + +static afunc_t deferred_array_func[] = { + deferred_array_init, + (afunc_t) 0, /* typeof */ + deferred_array_length, + deferred_array_lookup, + deferred_array_exists, + deferred_array_clear, + deferred_array_remove, + deferred_array_list, + deferred_array_copy, + null_afunc, /* dump */ + (afunc_t) 0, /* store */ +}; + + +/* deferred_array_init --- called when array becomes empty, e.g: delete BOUND_ARRAY */ + +static NODE ** +deferred_array_init(NODE *symbol, NODE *subs) +{ + if (symbol != NULL) { + array_t *av = (array_t *) symbol->xarray; + symbol->xarray = NULL; /* this is to avoid an assertion failure in null_array */ + null_array(symbol); /* typeless empty array */ + if (symbol->parent_array == NULL) { + /* main array */ + symbol->array_funcs = deferred_array_func; /* restore type */ + symbol->xarray = (NODE *) av; + } else if (av) /* sub-array */ + efree(av); + } + return NULL; +} + +/* deferred_array_length --- get the length of the array */ + +static NODE ** +deferred_array_length(NODE *symbol, NODE *subs) +{ + static NODE *length_node; + array_t *av = (array_t *) symbol->xarray; + if (av) { + symbol->xarray = NULL; + (*av->load_func)(symbol, av->data); + symbol->xarray = (NODE *) av; + } + length_node = symbol; + return & length_node; +} + +#define DEF_ARR(F) static NODE ** \ +deferred_array_##F(NODE *symbol, NODE *subs) \ +{ \ + array_t *av = (array_t *) symbol->xarray; \ + if (av) { \ + symbol->xarray = NULL; \ + (*av->load_func)(symbol, av->data); \ + symbol->xarray = (NODE *) av; \ + } \ + return SUPER(a##F)(symbol, subs); \ +} + +/* the rest of the routines */ + +DEF_ARR(exists) +DEF_ARR(lookup) +DEF_ARR(list) +DEF_ARR(copy) + +#undef DEF_ARR + +/* deferred_array_remove --- remove the index from the array */ + +static NODE ** +deferred_array_remove(NODE *symbol, NODE *subs) +{ + array_t *av = (array_t *) symbol->xarray; + + (void) SUPER(aremove)(symbol, subs); + if (av) { + symbol->xarray = NULL; + (*av->load_func)(symbol, av->data); + symbol->xarray = (NODE *) av; + } + return NULL; +} + +/* deferred_array_clear --- flush all the values in symbol[] */ + +static NODE ** +deferred_array_clear(NODE *symbol, NODE *subs) +{ + array_t *av = (array_t *) symbol->xarray; + + (void) SUPER(aclear)(symbol, subs); + if (av) { + symbol->xarray = NULL; + (*av->load_func)(symbol, av->data); + symbol->xarray = (NODE *) av; + } + return NULL; +} + + +/* + * dyn_array --- array with triggers for reading and writing + * an element. + * + * The fetch routine should expect three arguments, the array, + * the subscript and optional void * data. It should return the value + * if it exists or NULL otherwise. + * + * NODE *fetch_func(NODE *array, NODE *subs, void *data) + * + * The store routine must take an additional argument for the + * value. The value can be NULL if the specific element is + * removed from the array. The subscript (and the value) is NULL + * when the entire array is deleted. + * + * void store_func(NODE *array, NODE *subs, NODE *value, void *data) + * + * Use register_dyn_array(array, fetch_func, store_func, void *data) to + * bind an array to the fetch/store routine. + */ + + +static NODE **dyn_array_init(NODE *, NODE *); +static NODE **dyn_array_lookup(NODE *, NODE *); +static NODE **dyn_array_exists(NODE *, NODE *); +static NODE **dyn_array_remove(NODE *, NODE *); +static NODE **dyn_array_clear(NODE *, NODE *); +static NODE **dyn_array_list(NODE *, NODE *); +static NODE **dyn_array_copy(NODE *, NODE *); +static NODE **dyn_array_store(NODE *, NODE *); + +static afunc_t dyn_array_func[] = { + dyn_array_init, + (afunc_t) 0, /* typeof */ + null_length, /* length */ + dyn_array_lookup, + dyn_array_exists, + dyn_array_clear, + dyn_array_remove, + dyn_array_list, + dyn_array_copy, + null_afunc, /* dump */ + dyn_array_store, +}; + +/* dyn_array_init --- called when array becomes empty */ + +static NODE ** +dyn_array_init(NODE *symbol, NODE *subs) +{ + if (symbol != NULL) { + array_t *av = (array_t *) symbol->xarray; + symbol->xarray = NULL; + null_array(symbol); /* typeless empty array */ + if (symbol->parent_array == NULL) { + /* main array */ + symbol->array_funcs = dyn_array_func; /* restore type */ + symbol->xarray = (NODE *) av; + } else if (av) /* sub-array */ + efree(av); + } + return NULL; +} + +/* dyn_array_exists --- check if the SUBS exists */ + +static NODE ** +dyn_array_exists(NODE *symbol, NODE *subs) +{ + NODE *r; + array_t *av = (array_t *) symbol->xarray; + + if (av && av->fetch_func) { + symbol->xarray = NULL; + r = (*av->fetch_func)(symbol, subs, av->data); + symbol->xarray = (NODE *) av; + if (r != NULL) { + NODE **lhs; + lhs = SUPER(alookup)(symbol, subs); + unref(*lhs); + *lhs = r; + return lhs; + } + } + + return SUPER(aexists)(symbol, subs); +} + +/* dyn_array_lookup --- lookup SUBS and return a pointer to store its value */ + +static NODE ** +dyn_array_lookup(NODE *symbol, NODE *subs) +{ + NODE **lhs; + NODE *r; + array_t *av = (array_t *) symbol->xarray; + + lhs = SUPER(alookup)(symbol, subs); + if (av && av->fetch_func) { + symbol->xarray = NULL; + r = (*av->fetch_func)(symbol, subs, av->data); + symbol->xarray = (NODE *) av; + if (r != NULL) { + unref(*lhs); + *lhs = r; + } + } + return lhs; +} + +/* dyn_array_store --- call the store routine after an assignment */ + +static NODE ** +dyn_array_store(NODE *symbol, NODE *subs) +{ + array_t *av = (array_t *) symbol->xarray; + + if (av && av->store_func) { + NODE **lhs; + lhs = SUPER(aexists)(symbol, subs); + symbol->xarray = NULL; + (*av->store_func)(symbol, subs, *lhs, av->data); + symbol->xarray = (NODE *) av; + } + return NULL; +} + +/* dyn_array_remove --- remove the index from the array */ + +static NODE ** +dyn_array_remove(NODE *symbol, NODE *subs) +{ + array_t *av = (array_t *) symbol->xarray; + + (void) SUPER(aremove)(symbol, subs); + if (av && av->store_func) { + symbol->xarray = NULL; + (*av->store_func)(symbol, subs, NULL, av->data); + symbol->xarray = (NODE *) av; + } + return NULL; +} + +/* dyn_array_clear --- flush all the values in symbol[] */ + +static NODE ** +dyn_array_clear(NODE *symbol, NODE *subs) +{ + array_t *av = (array_t *) symbol->xarray; + + (void) SUPER(aclear)(symbol, subs); + if (av && av->store_func) { + symbol->xarray = NULL; + (*av->store_func)(symbol, NULL, NULL, av->data); + symbol->xarray = (NODE *) av; + } + return NULL; +} + +/* dyn_array_list --- return a list of items in symbol[] */ + +static NODE ** +dyn_array_list(NODE *symbol, NODE *subs) +{ + return SUPER(alist)(symbol, subs); +} + +/* dyn_array_copy --- duplicate the array */ + +static NODE ** +dyn_array_copy(NODE *symbol, NODE *subs) +{ + return SUPER(acopy)(symbol, subs); +} + +/* register_array_s --- attach the specified routine(s) to an array */ + +static void +register_array_s(NODE *symbol, Fetch_func_t fetch_func, + Store_func_t store_func, Load_func_t load_func, void *data) +{ + array_t *av; + + if (symbol->type != Node_var_array) + fatal(_("register_array_s: argument is not an array")); + + if (symbol->array_funcs == deferred_array_func + || symbol->array_funcs == dyn_array_func) + fatal(_("register_array_s: `%s' already is a deferred/dyn array"), + array_vname(symbol)); + + assoc_clear(symbol); + assert(symbol->xarray == NULL); + emalloc(av, array_t *, sizeof (array_t), "register_spec_array"); + av->fetch_func = fetch_func; + av->store_func = store_func; + av->load_func = load_func; + av->data = data; + symbol->xarray = (NODE *) av; +} + +/* register_deferred_array --- make the array to be loaded at run-time */ + +void +register_deferred_array(NODE *symbol, Load_func_t load_func, void *dq) +{ + if (! load_func) + fatal(_("register_deferred_array: null load function")); + register_array_s(symbol, 0, 0, load_func, dq); + symbol->array_funcs = deferred_array_func; +} + +/* register_dyn_array --- attach read and write triggers to an array */ + +void +register_dyn_array(NODE *symbol, Fetch_func_t fetch_func, + Store_func_t store_func, void *dq) +{ + register_array_s(symbol, fetch_func, store_func, 0, dq); + symbol->array_funcs = dyn_array_func; +} + +/* unregister_array_s --- un-special the array */ + +void * +unregister_array_s(NODE *symbol) +{ + void *data = NULL; + if (symbol->type != Node_var_array) + fatal(_("unregister_array_s: argument is not an array")); + + if (symbol->array_funcs == dyn_array_func + || symbol->array_funcs == deferred_array_func + ) { + array_t *av; + + av = (array_t *) symbol->xarray; + assert(av != NULL); + data = av->data; + efree(av); + symbol->array_funcs = str_array_func; + symbol->xarray = NULL; + /* FIXME: do we assoc_clear the array ? */ + } + return data; +} |