573 lines
15 KiB
C
573 lines
15 KiB
C
/*
|
|
*
|
|
* m o d u l e . c -- A simple module sytem
|
|
*
|
|
* Copyright © 1997-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
|
|
*
|
|
*
|
|
* 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;
|
|
}
|