Files
qse/qse/lib/stx/sym.c
2011-06-18 10:17:18 +00:00

185 lines
4.9 KiB
C

/*
* $Id$
*/
#include "stx.h"
#include <qse/cmn/str.h>
/* Implements global symbol table */
struct qse_stx_symtab_t
{
qse_stx_objhdr_t h;
qse_word_t tally;
qse_word_t slot[1];
};
typedef struct qse_stx_symtab_t qse_stx_symtab_t;
static qse_word_t expand (qse_stx_t* stx, qse_word_t tabref)
{
qse_word_t oldcapa, newcapa;
qse_word_t newtab;
qse_stx_symtab_t* oldptr, * newptr;
QSE_ASSERT (REFISIDX(stx,stx->ref.class_systemsymboltable));
QSE_ASSERT (!ISNIL(stx,stx->ref.class_systemsymboltable));
QSE_ASSERTX (
REFISIDX(stx,tabref),
"The reference is not an object index"
);
/* This function can handle expansion of an object whose class is
* SystemSymbolTable. During initial bootstrapping, the class of
* the stock symbol table (stx->ref.symtab) may not be set properly.
* You must make sure that expansion is not triggered until its class
* is set. If this assertion fails, you must increase the value of
* SYMTAB_INIT_CAPA.
*/
QSE_ASSERT (OBJCLASS(stx,tabref) == stx->ref.class_systemsymboltable);
/* get the current table capacity being the size of the object
* excluding the tally field. */
oldcapa = OBJSIZE(stx,tabref) - 1;
/* instantiate a new symbol table with its capacity doubled. */
newcapa = oldcapa * 2;
newtab = qse_stx_instantiate (
stx, OBJCLASS(stx,tabref),
QSE_NULL, QSE_NULL, newcapa
);
if (ISNIL(stx,newtab)) return stx->ref.nil;
oldptr = (qse_stx_symtab_t*)PTRBYREF(stx,tabref);
newptr = (qse_stx_symtab_t*)PTRBYREF(stx,newtab);
newptr->tally = INTTOREF (stx, 0);
QSE_ASSERT (newcapa == OBJSIZE(stx,newtab) - 1);
/* reorganize the symbol table */
while (oldcapa > 0)
{
qse_word_t symbol;
symbol = oldptr->slot[--oldcapa];
if (!ISNIL(stx,symbol))
{
qse_word_t index;
QSE_ASSERT (REFISIDX(stx,symbol));
QSE_ASSERT (OBJCLASS(stx,symbol) == stx->ref.class_symbol);
QSE_ASSERT (OBJTYPE(stx,symbol) == CHAROBJ);
/* qse_stx_newsymbol uses qse_stx_hashstr().
* this function uses qse_stx_hashobj().
* both must return the same value */
QSE_ASSERT (qse_stx_hashobj (stx, symbol) ==
qse_stx_hashstr (stx, &CHARAT(stx,symbol,0)));
index = qse_stx_hashobj (stx, symbol) % newcapa;
while (!ISNIL(stx,newptr->slot[index]))
index = (index + 1) % newcapa;
newptr->slot[index] = symbol;
}
}
newptr->tally = oldptr->tally;
qse_stx_swapmem (stx, REFTOIDX(stx,tabref), REFTOIDX(stx,newtab));
return tabref;
}
static qse_word_t new_symbol (
qse_stx_t* stx, qse_word_t tabref, const qse_char_t* name, qse_size_t len)
{
qse_stx_symtab_t* tabptr;
qse_word_t symref;
qse_word_t capa, hash, index, tally;
/* the table must have at least one slot excluding the tally field */
QSE_ASSERT (OBJSIZE(stx,tabref) > 1);
capa = OBJSIZE(stx,tabref) - 1; /* exclude the tally field */
hash = qse_stx_hashstr (stx, name);
index = hash % capa;
tabptr = (qse_stx_symtab_t*)PTRBYREF(stx,tabref);
do
{
/*symref = WORDAT (stx, tabref, index + 1);*/
symref = tabptr->slot[index];
if (ISNIL(stx,symref)) break; /* not found */
QSE_ASSERT (REFISIDX(stx,symref));
QSE_ASSERT (OBJCLASS(stx,symref) == stx->ref.class_symbol);
QSE_ASSERT (OBJTYPE(stx,symref) == CHAROBJ);
/*if (qse_strxcmp (
&CHARAT(stx,symref,0), OBJSIZE(stx,symref),
name) == 0) return symref;*/
if (qse_strcmp (&CHARAT(stx,symref,0), name) == 0) return symref;
index = (index + 1) % capa;
}
while (0);
/* symbol is not found. let's create a new symbol */
tally = REFTOINT(stx, tabptr->tally);
/* check if the symbol table is getting full soon */
if (tally + 1 >= capa)
{
/* Enlarge the symbol table before it gets full to
* make sure that it has at least one free slot left
* after having added a new symbol. this is to help
* traversal end at a nil slot if no entry is found. */
if (ISNIL (stx, expand (stx, tabref))) return stx->ref.nil;
/* refresh the object pointer */
tabptr = (qse_stx_symtab_t*)PTRBYREF(stx,tabref);
/* refersh capacity and hash index */
capa = OBJSIZE(stx,tabref) - 1; /* exclude the tally field */
index = hash % capa;
/* after expansion, the tally must still be the same */
QSE_ASSERT (tally == REFTOINT (stx, tabptr->tally));
}
symref = qse_stx_alloccharobj (stx, name, len);
if (!ISNIL(stx,symref))
{
OBJCLASS(stx,symref) = stx->ref.class_symbol;
tabptr->tally = INTTOREF (stx, tally + 1);
tabptr->slot[index] = symref;
}
return symref;
}
qse_word_t qse_stx_newsymbol (qse_stx_t* stx, const qse_char_t* name)
{
return new_symbol (stx, stx->ref.symtab, name, qse_strlen(name));
}
qse_word_t qse_stx_newsymbolx (qse_stx_t* stx, const qse_char_t* name, qse_size_t len)
{
return new_symbol (stx, stx->ref.symtab, name, len);
}
#if 0
void qse_stx_traverse_symbol_table (
qse_stx_t* stx, void (*func) (qse_stx_t*,qse_word_t,void*), void* data)
{
qse_word_t index, x;
for (index = 0; index < stx->symtab.capa; index++)
{
x = stx->symtab.slot[index];
if (x != stx->nil) func (stx, x, data);
}
}
#endif