static environment_t* import_set(cons_t* p) { std::string s = symbol_name(car(p)); /* * Each import set can be either of: */ // (rename <import set> (<identifier1> <identifier2>) ...) if ( s == "rename" ) return rename(import_set(cadr(p)), cddr(p)); // (prefix <import set> <identifier>) else if ( s == "prefix" ) return prefix(import_set(cadr(p)), caddr(p)); // (only <import set> <identifier> ...) else if ( s == "only" ) return only(import_set(cadr(p)), cddr(p)); // (except <import set> <identifier> ...) else if ( s == "except" ) return except(import_set(cadr(p)), cddr(p)); // <library name> else if ( !s.empty() ) return import_library(sprint(p)); raise(runtime_exception("Unknown import set: " + sprint(p))); return NULL; }
cons_t* proc_import(cons_t* p, environment_t* e) { assert_length_min(p, 1); assert_type(PAIR, car(p)); /* * Handle all import sets in (import <import set> ...) */ for ( ; !nullp(p); p = cdr(p) ) { environment_t *impenv = import_set(car(p)); /* * Now we need to bring the imported environment to the environment, * so that the new definitions are available there. * * We do this by copying the definitions. */ merge(e, impenv); /* * But we also need to connect the lower level imported environment to * definitions found in its outer environment. * * This is because the exported functions in impenv must be able to see * definitions in the toplevel, controlling, environment. * * Consider the (mickey environment) module, which has a "syntactic" * procedure bound?. * * If we (import (scheme write)) then we get the procedure display. But * if we now (import (mickey environment)) and call (bound? display) * then bound? will not be able to see any definition of display, and * will wrongly return #f. * * Note that I'm not entirely certain that this is the correct way of * handling things, since closures must be evaluated in the environment * they were defined in. * * TODO: Think hard about this and write some tests. * * Note that this behaviour might be different for libraries that are * imported as scheme source code. They must be first evaluated in * their own closed environment (to bind definitions) before being * connected to the outer one. * * I think what we need is a global pointer to the ACTUAL top-level * environment. * */ impenv->outer = e; } /* * TODO: Should we return the final environment, so we can easily run * cond-expand on it from outside define-library? E.g., (cond-expand * (import (foo bar))) */ return unspecified(nil()); }
cons_t* proc_environment(cons_t* p, environment_t*) { assert_length_min(p, 1); environment_t *out = null_environment(7); // Handle import sets for ( ; !nullp(p); p = cdr(p) ) { environment_t *impenv = import_set(car(p)); merge(out, impenv); impenv->outer = out; } return environment(out); }