diff options
Diffstat (limited to 'symbol.c')
-rw-r--r-- | symbol.c | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/symbol.c b/symbol.c new file mode 100644 index 00000000..7c770b4b --- /dev/null +++ b/symbol.c @@ -0,0 +1,855 @@ +/* + * symbol.c - routines for symbol table management and code allocation + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991-2011 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" + +extern SRCFILE *srcfiles; +extern INSTRUCTION *rule_list; + +#define HASHSIZE 1021 + +static int func_count; /* total number of functions */ +static int var_count; /* total number of global variables and functions */ + +static NODE *symbol_list; +static void (*install_func)(NODE *) = NULL; +static NODE *make_symbol(char *name, NODETYPE type); +static NODE *install(char *name, NODE *parm, NODETYPE type); +static void free_bcpool(INSTRUCTION *pl); + +static AWK_CONTEXT *curr_ctxt = NULL; +static int ctxt_level; + +static NODE *global_table, *param_table; +NODE *symbol_table, *func_table; + +/* Use a flag to avoid a strcmp() call inside install() */ +static bool installing_specials = false; + +/* init_symbol_table --- make sure the symbol tables are initialized */ + +void +init_symbol_table() +{ + getnode(global_table); + memset(global_table, '\0', sizeof(NODE)); + init_array(global_table); + + getnode(param_table); + memset(param_table, '\0', sizeof(NODE)); + init_array(param_table); + + installing_specials = true; + func_table = install_symbol(estrdup("FUNCTAB", 7), Node_var_array); + + symbol_table = install_symbol(estrdup("SYMTAB", 6), Node_var_array); + installing_specials = false; +} + +/* + * install_symbol: + * Install a global name in the symbol table, even if it is already there. + * Caller must check against redefinition if that is desired. + */ + +NODE * +install_symbol(char *name, NODETYPE type) +{ + return install(name, NULL, type); +} + + +/* + * lookup --- find the most recent global or param node for name + * installed by install_symbol + */ + +NODE * +lookup(const char *name) +{ + NODE *n; + NODE *tmp; + NODE *tables[5]; /* manual init below, for z/OS */ + int i; + + /* ``It's turtles, all the way down.'' */ + tables[0] = param_table; /* parameters shadow everything */ + tables[1] = global_table; /* SYMTAB and FUNCTAB found first, can't be redefined */ + tables[2] = func_table; /* then functions */ + tables[3] = symbol_table; /* then globals */ + tables[4] = NULL; + + tmp = make_string(name, strlen(name)); + + n = NULL; + for (i = 0; tables[i] != NULL; i++) { + if (tables[i]->table_size == 0) + continue; + + if ((do_posix || do_traditional) && tables[i] == global_table) + continue; + + n = in_array(tables[i], tmp); + if (n != NULL) { + unref(tmp); + return n; + } + } + + unref(tmp); + return n; /* NULL */ +} + +/* make_params --- allocate function parameters for the symbol table */ + +NODE * +make_params(char **pnames, int pcount) +{ + NODE *p, *parms; + int i; + + if (pcount <= 0 || pnames == NULL) + return NULL; + + emalloc(parms, NODE *, pcount * sizeof(NODE), "make_params"); + memset(parms, '\0', pcount * sizeof(NODE)); + + for (i = 0, p = parms; i < pcount; i++, p++) { + p->type = Node_param_list; + p->param = pnames[i]; /* shadows pname and vname */ + p->param_cnt = i; + } + + return parms; +} + +/* install_params --- install function parameters into the symbol table */ + +void +install_params(NODE *func) +{ + int i, pcount; + NODE *parms; + + if (func == NULL) + return; + assert(func->type == Node_func); + if ((pcount = func->param_cnt) <= 0 + || (parms = func->fparms) == NULL + ) + return; + for (i = 0; i < pcount; i++) + (void) install(parms[i].param, parms + i, Node_param_list); +} + + +/* + * remove_params --- remove function parameters out of the symbol table. + */ + +void +remove_params(NODE *func) +{ + NODE *parms, *p; + int i, pcount; + + if (func == NULL) + return; + assert(func->type == Node_func); + if ((pcount = func->param_cnt) <= 0 + || (parms = func->fparms) == NULL + ) + return; + + for (i = pcount - 1; i >= 0; i--) { + NODE *tmp; + NODE *tmp2; + + p = parms + i; + assert(p->type == Node_param_list); + tmp = make_string(p->vname, strlen(p->vname)); + tmp2 = in_array(param_table, tmp); + if (tmp2 != NULL && tmp2->dup_ent != NULL) { + tmp2->dup_ent = tmp2->dup_ent->dup_ent; + } else { + (void) assoc_remove(param_table, tmp); + } + unref(tmp); + } + + assoc_clear(param_table); /* shazzam! */ +} + + +/* remove_symbol --- remove a symbol from the symbol table */ + +NODE * +remove_symbol(NODE *r) +{ + NODE *n = in_array(symbol_table, r); + + if (n == NULL) + return n; + + n = dupnode(n); + + (void) assoc_remove(symbol_table, r); + + return n; +} + + +/* destroy_symbol --- remove a symbol from symbol table +* and free all associated memory. +*/ + +void +destroy_symbol(NODE *r) +{ + r = remove_symbol(r); + if (r == NULL) + return; + + switch (r->type) { + case Node_func: + if (r->param_cnt > 0) { + NODE *n; + int i; + int pcount = r->param_cnt; + + /* function parameters of type Node_param_list */ + for (i = 0; i < pcount; i++) { + n = r->fparms + i; + efree(n->param); + } + efree(r->fparms); + } + break; + + case Node_ext_func: + bcfree(r->code_ptr); + break; + + case Node_var_array: + assoc_clear(r); + break; + + case Node_var: + unref(r->var_value); + break; + + default: + /* Node_param_list -- YYABORT */ + return; + } + + efree(r->vname); + freenode(r); +} + + +/* make_symbol --- allocates a global symbol for the symbol table. */ + +static NODE * +make_symbol(char *name, NODETYPE type) +{ + NODE *r; + + getnode(r); + memset(r, '\0', sizeof(NODE)); + if (type == Node_var_array) + init_array(r); + else if (type == Node_var) + r->var_value = dupnode(Nnull_string); + r->vname = name; + r->type = type; + + return r; +} + +/* install --- install a global name or function parameter in the symbol table */ + +static NODE * +install(char *name, NODE *parm, NODETYPE type) +{ + NODE *r; + NODE **aptr; + NODE *table; + NODE *n_name; + NODE *prev; + + n_name = make_string(name, strlen(name)); + table = symbol_table; + + if (type == Node_param_list) { + table = param_table; + } else if (type == Node_func || type == Node_ext_func) { + table = func_table; + } else if (installing_specials) { + table = global_table; + } + + if (parm != NULL) { + r = parm; + } else { + /* global symbol */ + r = make_symbol(name, type); + if (type == Node_func) + func_count++; + if (type != Node_ext_func && table != global_table) + var_count++; /* total, includes Node_func */ + } + + if (type == Node_param_list) { + prev = in_array(table, n_name); + if (prev == NULL) + goto simple; + r->dup_ent = prev->dup_ent; + prev->dup_ent = r; + } else { +simple: + /* the simple case */ + aptr = assoc_lookup(table, n_name); + unref(*aptr); + *aptr = r; + } + unref(n_name); + + if (install_func) + (*install_func)(r); + + return r; +} + + +/* comp_symbol --- compare two (variable or function) names */ + +static int +comp_symbol(const void *v1, const void *v2) +{ + const NODE *const *npp1, *const *npp2; + const NODE *n1, *n2; + + npp1 = (const NODE *const *) v1; + npp2 = (const NODE *const *) v2; + n1 = *npp1; + n2 = *npp2; + + return strcmp(n1->vname, n2->vname); +} + + +typedef enum { FUNCTION = 1, VARIABLE } SYMBOL_TYPE; + +/* get_symbols --- return a list of optionally sorted symbols */ + +static NODE ** +get_symbols(SYMBOL_TYPE what, int sort) +{ + int i; + NODE **table; + NODE **list; + NODE *r; + long j, count = 0; + long max; + NODE *the_table; + + if (what == FUNCTION) { + count = func_count; + the_table = func_table; + + max = the_table->table_size * 2; + list = assoc_list(the_table, "@unsorted", ASORTI); + emalloc(table, NODE **, (count + 1) * sizeof(NODE *), "symbol_list"); + + for (i = j = 0; i < max; i += 2) { + r = list[i+1]; + if (r->type == Node_ext_func) + continue; + assert(r->type == Node_func); + table[j++] = r; + } + } else { /* what == VARIABLE */ + the_table = symbol_table; + count = var_count; + + update_global_values(); + + max = the_table->table_size * 2; + list = assoc_list(the_table, "@unsorted", ASORTI); + emalloc(table, NODE **, (count + 1) * sizeof(NODE *), "symbol_list"); + + for (i = j = 0; i < max; i += 2) { + r = list[i+1]; + if (r->type == Node_val) /* non-variable in SYMTAB */ + continue; + table[j++] = r; + } + } + + efree(list); + + if (sort && count > 1) + qsort(table, count, sizeof(NODE *), comp_symbol); /* Shazzam! */ + table[count] = NULL; /* null terminate the list */ + return table; +} + + +/* variable_list --- list of global variables */ + +NODE ** +variable_list() +{ + return get_symbols(VARIABLE, true); +} + +/* function_list --- list of functions */ + +NODE ** +function_list(int sort) +{ + return get_symbols(FUNCTION, sort); +} + +/* print_vars --- print names and values of global variables */ + +void +print_vars(NODE **table, int (*print_func)(FILE *, const char *, ...), FILE *fp) +{ + int i; + NODE *r; + + assert(table != NULL); + + for (i = 0; (r = table[i]) != NULL; i++) { + if (r->type == Node_func || r->type == Node_ext_func) + continue; + print_func(fp, "%s: ", r->vname); + if (r->type == Node_var_array) + print_func(fp, "array, %ld elements\n", r->table_size); + else if (r->type == Node_var_new) + print_func(fp, "untyped variable\n"); + else if (r->type == Node_var) + valinfo(r->var_value, print_func, fp); + } +} + + +/* foreach_func --- execute given function for each awk function in table. */ + +int +foreach_func(NODE **table, int (*pfunc)(INSTRUCTION *, void *), void *data) +{ + int i; + NODE *r; + int ret = 0; + + assert(table != NULL); + + for (i = 0; (r = table[i]) != NULL; i++) { + if ((ret = pfunc(r->code_ptr, data)) != 0) + break; + } + return ret; +} + +/* release_all_vars --- free all variable memory */ + +void +release_all_vars() +{ + assoc_clear(symbol_table); + assoc_clear(func_table); + assoc_clear(global_table); +} + + +/* append_symbol --- append symbol to the list of symbols + * installed in the symbol table. + */ + +void +append_symbol(NODE *r) +{ + NODE *p; + + getnode(p); + p->lnode = r; + p->rnode = symbol_list->rnode; + symbol_list->rnode = p; +} + +/* release_symbol --- free symbol list and optionally remove symbol from symbol table */ + +void +release_symbols(NODE *symlist, int keep_globals) +{ + NODE *p, *next; + + for (p = symlist->rnode; p != NULL; p = next) { + if (! keep_globals) { + /* destroys globals, function, and params + * if still in symbol table + */ + destroy_symbol(p->lnode); + } + next = p->rnode; + freenode(p); + } + symlist->rnode = NULL; +} + +/* load_symbols --- fill in symbols' information */ + +void +load_symbols() +{ + NODE *r; + NODE *tmp; + NODE *sym_array; + NODE **aptr; + long i, j, max; + NODE *user, *extension, *untyped, *scalar, *array; + NODE **list; + NODE *tables[4]; + + if (PROCINFO_node == NULL) + return; + + tables[0] = func_table; + tables[1] = symbol_table; + tables[2] = global_table; + tables[3] = NULL; + + tmp = make_string("identifiers", 11); + aptr = assoc_lookup(PROCINFO_node, tmp); + + getnode(sym_array); + memset(sym_array, '\0', sizeof(NODE)); /* PPC Mac OS X wants this */ + init_array(sym_array); + + unref(*aptr); + *aptr = sym_array; + + sym_array->parent_array = PROCINFO_node; + sym_array->vname = estrdup("identifiers", 11); + make_aname(sym_array); + + user = make_string("user", 4); + extension = make_string("extension", 9); + scalar = make_string("scalar", 6); + untyped = make_string("untyped", 7); + array = make_string("array", 5); + + for (i = 0; tables[i] != NULL; i++) { + list = assoc_list(tables[i], "@unsorted", ASORTI); + max = tables[i]->table_size * 2; + if (max == 0) + continue; + for (j = 0; j < max; j += 2) { + r = list[j+1]; + if ( r->type == Node_ext_func + || r->type == Node_func + || r->type == Node_var + || r->type == Node_var_array + || r->type == Node_var_new) { + tmp = make_string(r->vname, strlen(r->vname)); + aptr = assoc_lookup(sym_array, tmp); + unref(tmp); + unref(*aptr); + switch (r->type) { + case Node_ext_func: + *aptr = dupnode(extension); + break; + case Node_func: + *aptr = dupnode(user); + break; + case Node_var: + *aptr = dupnode(scalar); + break; + case Node_var_array: + *aptr = dupnode(array); + break; + case Node_var_new: + *aptr = dupnode(untyped); + break; + default: + cant_happen(); + break; + } + } + } + efree(list); + } + + unref(user); + unref(extension); + unref(scalar); + unref(untyped); + unref(array); +} + +#define pool_size d.dl +#define freei x.xi +static INSTRUCTION *pool_list; + +/* INSTR_CHUNK must be > largest code size (3) */ +#define INSTR_CHUNK 127 + +/* bcfree --- deallocate instruction */ + +void +bcfree(INSTRUCTION *cp) +{ + cp->opcode = 0; + cp->nexti = pool_list->freei; + pool_list->freei = cp; +} + +/* bcalloc --- allocate a new instruction */ + +INSTRUCTION * +bcalloc(OPCODE op, int size, int srcline) +{ + INSTRUCTION *cp; + + if (size > 1) { + /* wide instructions Op_rule, Op_func_call .. */ + emalloc(cp, INSTRUCTION *, (size + 1) * sizeof(INSTRUCTION), "bcalloc"); + cp->pool_size = size; + cp->nexti = pool_list->nexti; + pool_list->nexti = cp++; + } else { + INSTRUCTION *pool; + + pool = pool_list->freei; + if (pool == NULL) { + INSTRUCTION *last; + emalloc(cp, INSTRUCTION *, (INSTR_CHUNK + 1) * sizeof(INSTRUCTION), "bcalloc"); + + cp->pool_size = INSTR_CHUNK; + cp->nexti = pool_list->nexti; + pool_list->nexti = cp; + pool = ++cp; + last = &pool[INSTR_CHUNK - 1]; + for (; cp <= last; cp++) { + cp->opcode = 0; + cp->nexti = cp + 1; + } + --cp; + cp->nexti = NULL; + } + cp = pool; + pool_list->freei = cp->nexti; + } + + memset(cp, 0, size * sizeof(INSTRUCTION)); + cp->opcode = op; + cp->source_line = srcline; + return cp; +} + +/* new_context --- create a new execution context. */ + +AWK_CONTEXT * +new_context() +{ + AWK_CONTEXT *ctxt; + + emalloc(ctxt, AWK_CONTEXT *, sizeof(AWK_CONTEXT), "new_context"); + memset(ctxt, 0, sizeof(AWK_CONTEXT)); + ctxt->srcfiles.next = ctxt->srcfiles.prev = & ctxt->srcfiles; + ctxt->rule_list.opcode = Op_list; + ctxt->rule_list.lasti = & ctxt->rule_list; + return ctxt; +} + +/* set_context --- change current execution context. */ + +static void +set_context(AWK_CONTEXT *ctxt) +{ + pool_list = & ctxt->pools; + symbol_list = & ctxt->symbols; + srcfiles = & ctxt->srcfiles; + rule_list = & ctxt->rule_list; + install_func = ctxt->install_func; + curr_ctxt = ctxt; +} + +/* + * push_context: + * + * Switch to the given context after saving the current one. The set + * of active execution contexts forms a stack; the global or main context + * is at the bottom of the stack. + */ + +void +push_context(AWK_CONTEXT *ctxt) +{ + ctxt->prev = curr_ctxt; + /* save current source and sourceline */ + if (curr_ctxt != NULL) { + curr_ctxt->sourceline = sourceline; + curr_ctxt->source = source; + } + sourceline = 0; + source = NULL; + set_context(ctxt); + ctxt_level++; +} + +/* pop_context --- switch to previous execution context. */ + +void +pop_context() +{ + AWK_CONTEXT *ctxt; + + assert(curr_ctxt != NULL); + if (curr_ctxt->prev == NULL) + fatal(_("can not pop main context")); + ctxt = curr_ctxt->prev; + /* restore source and sourceline */ + sourceline = ctxt->sourceline; + source = ctxt->source; + set_context(ctxt); + ctxt_level--; +} + +/* in_main_context --- are we in the main context ? */ + +int +in_main_context() +{ + assert(ctxt_level > 0); + return (ctxt_level == 1); +} + +/* free_context --- free context structure and related data. */ + +void +free_context(AWK_CONTEXT *ctxt, bool keep_globals) +{ + SRCFILE *s, *sn; + + if (ctxt == NULL) + return; + + assert(curr_ctxt != ctxt); + + /* free all code including function codes */ + + free_bcpool(& ctxt->pools); + + /* free symbols */ + + release_symbols(& ctxt->symbols, keep_globals); + + /* free srcfiles */ + + for (s = & ctxt->srcfiles; s != & ctxt->srcfiles; s = sn) { + sn = s->next; + if (s->stype != SRC_CMDLINE && s->stype != SRC_STDIN) + efree(s->fullpath); + efree(s->src); + efree(s); + } + + efree(ctxt); +} + +/* free_bc_internal --- free internal memory of an instruction. */ + +static void +free_bc_internal(INSTRUCTION *cp) +{ + NODE *m; + + switch(cp->opcode) { + case Op_func_call: + if (cp->func_name != NULL) + efree(cp->func_name); + break; + case Op_push_re: + case Op_match_rec: + case Op_match: + case Op_nomatch: + m = cp->memory; + if (m->re_reg != NULL) + refree(m->re_reg); + if (m->re_exp != NULL) + unref(m->re_exp); + if (m->re_text != NULL) + unref(m->re_text); + freenode(m); + break; + case Op_token: + /* token lost during error recovery in yyparse */ + if (cp->lextok != NULL) + efree(cp->lextok); + break; + case Op_push_i: + m = cp->memory; + unref(m); + break; + case Op_store_var: + m = cp->initval; + if (m != NULL) + unref(m); + break; + case Op_illegal: + cant_happen(); + default: + break; + } +} + +/* free_bcpool --- free list of instruction memory pools */ + +static void +free_bcpool(INSTRUCTION *pl) +{ + INSTRUCTION *pool, *tmp; + + for (pool = pl->nexti; pool != NULL; pool = tmp) { + INSTRUCTION *cp, *last; + long psiz; + psiz = pool->pool_size; + if (psiz == INSTR_CHUNK) + last = pool + psiz; + else + last = pool + 1; + for (cp = pool + 1; cp <= last ; cp++) { + if (cp->opcode != 0) + free_bc_internal(cp); + } + tmp = pool->nexti; + efree(pool); + } + memset(pl, 0, sizeof(INSTRUCTION)); +} |