/* * * m o d u l e . c -- A simple module sytem * * Copyright © 1997-1999 Erick Gallesio - I3S-CNRS/ESSI * * * Permission to use, copy, modify, distribute,and license this * software and its documentation for any purpose is hereby granted, * provided that existing copyright notices are retained in all * copies and that this notice is included verbatim in any * distributions. No written agreement, license, or royalty fee is * required for any of the authorized uses. * This software is provided ``AS IS'' without express or implied * warranty. * * Author: Erick Gallesio [eg@unice.fr] * Creation date: 13-Mar-1997 20:11 * Last file update: 3-Sep-1999 20:21 (eg) */ #include "stk.h" #include "module.h" #include "extend.h" /* Public variables */ SCM STk_global_module; /* Global module -- i.e STk pseudo-module */ SCM STk_scheme_module; /* The Scheme module */ SCM STk_selected_module; /* Selected module */ /* Private variables */ static Tcl_HashTable module_table; /* Table of all modules */ static SCM find_module(SCM name, int fake_module, int create) { SCM z; struct Tcl_HashTable *h; struct module_info *p; Tcl_HashEntry *entry; int new; entry = Tcl_FindHashEntry(&module_table, PNAME(name)); if (!entry) { /* This is a new module */ if (!create) return UNBOUND; if (!fake_module) { h = STk_must_malloc(sizeof(struct Tcl_HashTable)); Tcl_InitHashTable(h, TCL_ONE_WORD_KEYS); } else h = NULL; entry = Tcl_CreateHashEntry(&module_table, PNAME(name), &new); p = STk_must_malloc(sizeof(struct module_info)); p->name = name; p->exported_symbols = p->imports = p->env = p->ref_module = NIL; p->export_all = FALSE; NEWCELL(z, tc_module); MOD_TABLE(z) = h; MOD_INFO(z) = p; /* set now p->env to the module itself */ p->env = LIST1(z); /* Enter this module in the table of modules */ Tcl_SetHashValue(entry, z); return z; } else { /* This module was alreay defined. Return the value stored in table */ return (SCM) Tcl_GetHashValue(entry); } } SCM STk_make_module(SCM name) { return find_module(name, FALSE, TRUE); } SCM STk_make_parent_module(SCM module) { SCM z, m; struct module_info *p; if (NULLP(MOD_IMPORTED(module))) return Ntruth; /* Build a fake module starting from the infos found in "module" * Fake module is identical to the first imported module, excepting * its imported list which is based on the one of "module" */ p = STk_must_malloc(sizeof(struct module_info)); m = CAR(MOD_IMPORTED(module)); *p = *(struct module_info *)MOD_INFO(m); p->imports = CDR(MOD_IMPORTED(module)); p->ref_module = m; NEWCELL(z, tc_module); MOD_TABLE(z) = NULL; MOD_INFO(z) = p; /* set now p->env to the module itself */ p->env = LIST1(z); return z; } void STk_mark_module(SCM m) { struct Tcl_HashTable *H = MOD_TABLE(m); register SCM var, value; STk_gc_mark(MOD_NAME(m)); STk_gc_mark(MOD_EXPORTS(m)); STk_gc_mark(MOD_IMPORTED(m)); STk_gc_mark(MOD_REFMOD(m)); STk_gc_mark(MOD_ENV(m)); if (H) { /* mark the symbols of the table and their value */ register Tcl_HashEntry *ent; Tcl_HashSearch tmp; for (ent=Tcl_FirstHashEntry(H, &tmp); ent; ent=Tcl_NextHashEntry(&tmp)){ var = (SCM) Tcl_GetHashKey(H, ent); value = (SCM) Tcl_GetHashValue(ent); if (value == UNBOUND) { /* Delete var from the table */ Tcl_DeleteHashEntry(ent); } else { STk_gc_mark((SCM) Tcl_GetHashKey(H, ent)); STk_gc_mark((SCM) Tcl_GetHashValue(ent)); } } } } void STk_free_module(SCM m) { struct Tcl_HashTable *H = MOD_TABLE(m); if (H) { Tcl_DeleteHashTable(H); free(H); } free(m->storage_as.module.data); } void STk_mark_module_table(void) { Tcl_HashEntry *ent; Tcl_HashSearch tmp; for (ent=Tcl_FirstHashEntry(&module_table, &tmp);ent;ent=Tcl_NextHashEntry(&tmp)) STk_gc_mark((SCM) Tcl_GetHashValue(ent)); } void STk_define_public_var(SCM module, SCM var, SCM value) { Tcl_HashEntry *entry; int new; if (NULLP(module) || module == STk_global_module) { /* Global variable */ if (VCELL(var) == UNBOUND && (var->cell_info&CELL_INFO_C_VAR)) { /* This is not an unbound variable but rather a C variable */ STk_apply_setter_C_variable(PNAME(var), value); } else { VCELL(var) = value; } } else { /* Module variable */ entry = Tcl_CreateHashEntry(FIND_TABLE(module), (char *) var, &new); Tcl_SetHashValue(entry, value); } } SCM STk_modulevalue(SCM obj) { return (SCM) Tcl_GetHashValue((Tcl_HashEntry *) CDR(obj)); } SCM STk_module_env2list(SCM module) { register Tcl_HashEntry *ent; struct Tcl_HashTable *H; Tcl_HashSearch tmp; SCM l, m, res=NIL; if (FAKE_MODULEP(module)) { m = (module == STk_global_module) ? MOD_IMPORTED(module) : Cons(module, MOD_IMPORTED(module)); } else { /* Build the list of all module variables */ H = MOD_TABLE(module); for (ent=Tcl_FirstHashEntry(H,&tmp); ent; ent=Tcl_NextHashEntry(&tmp)) { res = Cons(Cons((SCM)Tcl_GetHashKey(H, ent), (SCM)Tcl_GetHashValue(ent)),res); } m = MOD_IMPORTED(module); res = LIST1(res); } /* For all imported modules, build the list of its exported variables */ for ( ; NNULLP(m); m = CDR(m)) { H = FIND_TABLE(CAR(m)); for (l=NIL,ent=Tcl_FirstHashEntry(H,&tmp); ent; ent=Tcl_NextHashEntry(&tmp)) { SCM var = (SCM) Tcl_GetHashKey(H, ent); if (MOD_EXPORTALL(CAR(m)) || STk_memq(var, MOD_EXPORTS(CAR(m))) != Ntruth) l = Cons(Cons(var, (SCM) Tcl_GetHashValue(ent)), l); } res = STk_append2(LIST1(l), res); } return res; } static SCM module_body(SCM module, SCM l) { SCM prev_module = STk_selected_module; SCM env = MOD_ENV(module); volatile SCM res = UNDEFINED; STk_selected_module = module; PUSH_ERROR_HANDLER { register SCM tmp; for (tmp=l; NNULLP(tmp); tmp = CDR(tmp)) res = EVALCAR(tmp); } WHEN_ERROR { STk_selected_module = prev_module; PROPAGATE_ERROR(); } POP_ERROR_HANDLER; STk_selected_module = prev_module; return res; } /****************************************************************************** * * d e f i n e - m o d u l e * ******************************************************************************/ PRIMITIVE STk_define_module(SCM l, SCM env, int len) { SCM name, module; ENTER_PRIMITIVE("define-module"); if (!len) Serror("bad module definition", NIL); if (NSYMBOLP(name=CAR(l))) Serror("bad name", name); module = find_module(name, FALSE, TRUE); if (len > 1) module_body(module, CDR(l)); STk_last_defined = name ; return UNDEFINED; } PRIMITIVE STk_modulep(SCM obj) { return MODULEP(obj)? Truth: Ntruth; } /****************************************************************************** * * w i t h - m o d u l e * ******************************************************************************/ PRIMITIVE STk_with_module(SCM l, SCM env, int len) { SCM name, module; ENTER_PRIMITIVE("with-module"); if (len < 2) Serror("no body", NIL); if (NSYMBOLP(name=CAR(l))) Serror("bad name", name); module = find_module(name, FALSE, FALSE); if (module == UNBOUND) Serror("module does not exist", name); /* Everything is correct. Evaluate the module body in its environment */ return module_body(module, CDR(l)); } /****************************************************************************** * * i m p o r t * ******************************************************************************/ static void import_module(SCM importer, SCM imported) { if (NEQ(importer, imported) && NEQ(imported, STk_global_module) && STk_memv(imported, MOD_IMPORTED(importer)) == Ntruth) MOD_IMPORTED(importer) = STk_append2(MOD_IMPORTED(importer), LIST1(imported)); } PRIMITIVE STk_import(SCM l, SCM env, int len) { SCM current_module = NULLP(env) ? STk_global_module : CAR(env); ENTER_PRIMITIVE("import"); if (NMODULEP(current_module)) Serror("not allowed in a nested environment", NIL); if (len) { /* Import into a module */ for ( ; NNULLP(l); l = CDR(l)) { SCM name, module; if (NSYMBOLP(name=CAR(l))) Serror("bad name", name); if ((module=find_module(name, FALSE, FALSE)) == UNBOUND) Serror("bad module", name); import_module(current_module, module); } } return UNDEFINED; } /****************************************************************************** * * e x p o r t - s y m b o l * ******************************************************************************/ PRIMITIVE STk_export_symbol(SCM symbol, SCM module) { ENTER_PRIMITIVE("export-symbol"); if (NSYMBOLP(symbol)) Serror("bad symbol", symbol); if (NMODULEP(module)) Serror("bad module", module); if (! MOD_EXPORTALL(module)) { if (STk_memv(symbol, MOD_EXPORTS(module)) == Ntruth) { MOD_EXPORTS(module) = Cons(symbol, MOD_EXPORTS(module)); } } return UNDEFINED; } /****************************************************************************** * * e x p o r t - a l l - s y m b o l s * ******************************************************************************/ PRIMITIVE STk_export_all_symbols(void) { MOD_EXPORTALL(STk_selected_module) = TRUE; MOD_EXPORTS(STk_selected_module) = NIL; /* let GC continue to do the job */ return UNDEFINED; } /****************************************************************************** * * select-module/current-module * ******************************************************************************/ PRIMITIVE STk_select_module(SCM l, SCM env, int len) { SCM name, module; ENTER_PRIMITIVE("select-module"); if (NNULLP(env) && NMODULEP(CAR(env))) Serror("not allowed in a nested environment", NIL); if (len != 1) Serror("no module specified", NIL); if (NSYMBOLP(name=CAR(l))) Serror("bad name", name); module = find_module(name, FALSE, FALSE); if (module == UNBOUND) Serror("module does not exist", name); STk_selected_module = module; return UNDEFINED; } static SCM get_module(SCM env) { /* find the last pair */ for ( ; CONSP(env); env = CDR(env)) { if (NULLP(CDR(env))) return MODULEP(CAR(env)) ? CAR(env) : STk_global_module; } /* If we are here, env must be NULL, return STk_global_module */ return STk_global_module; } PRIMITIVE STk_get_module(SCM env) /* subr */ { ENTER_PRIMITIVE("%get-module"); if (NENVP(env)) Serror("bad environment", env); return get_module(env->storage_as.env.data); } PRIMITIVE STk_current_module(SCM l, SCM env, int len) /* fsubr */ { /* Find the module from the environment. This is important to * compute the module from the environment (instead of just returning * the value of STk_selected_module); otherwise closures would return * a value which don't agree with their own environment (see it as a * static current_environment). */ ENTER_PRIMITIVE("current-module"); if (len) Serror("no parameter required", l); return get_module(env); } /****************************************************************************** * * Introspective primitives * ******************************************************************************/ PRIMITIVE STk_find_module(SCM name, SCM default_value) { SCM module; ENTER_PRIMITIVE("find-module"); if (NSYMBOLP(name)) Serror("bad name", name); module = find_module(name, FALSE, FALSE); if (module == UNBOUND) { if (default_value == UNBOUND) Serror("module does not exists", name); return default_value; } return module; } PRIMITIVE STk_module_name(SCM module) { ENTER_PRIMITIVE("module-name"); if (NMODULEP(module)) Serror("bad module", module); return MOD_NAME(module); } PRIMITIVE STk_module_imports(SCM module) { ENTER_PRIMITIVE("module-imports"); if (NMODULEP(module)) Serror("bad module", module); return MOD_IMPORTED(module); } PRIMITIVE STk_module_exports(SCM module) { ENTER_PRIMITIVE("module-exports"); if (NMODULEP(module)) Serror("bad module", module); return MOD_EXPORTALL(module)? STk_module_symbols(module) : MOD_EXPORTS(module); } PRIMITIVE STk_module_env(SCM module) { ENTER_PRIMITIVE("module-environment"); if (NMODULEP(module)) Serror("bad module", module); return STk_makeenv(MOD_ENV(module), FALSE); } PRIMITIVE STk_module_symbols(SCM module) { register Tcl_HashEntry *ent; struct Tcl_HashTable *H; Tcl_HashSearch tmp; SCM res=NIL; ENTER_PRIMITIVE("module-symbols"); if (NMODULEP(module)) Serror("bad module", module); if (FAKE_MODULEP(module)) return EQ(module, STk_global_module)? STk_global_symbols(): Ntruth; else { /* Build the list of all module variables */ H = MOD_TABLE(module); for (ent=Tcl_FirstHashEntry(H,&tmp); ent; ent=Tcl_NextHashEntry(&tmp)) { if ((SCM) Tcl_GetHashValue(ent) != UNBOUND) res = Cons((SCM)Tcl_GetHashKey(H, ent), res); } return res; } } PRIMITIVE STk_get_selected_module(void) { return STk_selected_module; } PRIMITIVE STk_all_modules(void) { Tcl_HashEntry *entry; Tcl_HashSearch search; SCM result = NIL; for (entry = Tcl_FirstHashEntry(&module_table, &search); entry; entry = Tcl_NextHashEntry(&search)) { result = Cons((SCM) Tcl_GetHashValue(entry), result); } return result; } void STk_initialize_stk_module(void) { struct Tcl_HashTable *h = FIND_TABLE(STk_scheme_module); SCM symbols = STk_module_symbols(STk_scheme_module); register SCM var, value, tmp; /* Duplicate all the bindings from the Scheme module to the global one * So, if the user redefines a standard binding, the old one will be * still available in the Scheme module (except if explicitly destroyed). * The copy is only done iff the global variable is unbound * (primitives are already in STk module and don't need to be copied * again but it is also useful to let the user redefine standard behaviour * in his/her .stkrc file) */ for (tmp = symbols; NNULLP(tmp); tmp = CDR(tmp)) { var = CAR(tmp); value = (SCM) Tcl_GetHashValue(Tcl_FindHashEntry(h, (char*) var)); if (NTYPEP(value, tc_autoload) && EQ(VCELL(var), UNBOUND)) VCELL(var) = value; } /* And now set the default module to the global one */ STk_selected_module = STk_global_module; } /****************************************************************************** * * init * ******************************************************************************/ void STk_init_modules(void) { SCM stk_module, scheme_module; /* Initialize the table of modules */ Tcl_InitHashTable(&module_table, TCL_STRING_KEYS); /* Define the STk and Scheme modules -- Params: fake, create */ stk_module = find_module(Intern("stk"), TRUE, TRUE); scheme_module = find_module(Intern("scheme"), FALSE, TRUE); /* Theses module export all their symbols */ MOD_EXPORTALL(stk_module) = MOD_EXPORTALL(scheme_module) = TRUE; STk_global_module = stk_module; STk_scheme_module = STk_selected_module = scheme_module; }