123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- package gnu.kawa.lispexpr;
- import gnu.commonlisp.lang.CommonLisp;
- import gnu.mapping.*;
- import gnu.lists.*;
- import java.util.Iterator;
- import java.util.Stack;
- import gnu.expr.Keyword;
- /** Implement a Common Lisp "package" value.
- *
- * @author Per Bothner
- * @author Charles Turner
- */
- public class LispPackage extends Namespace
- {
- /** The set of exported symbols.
- * This is one of the packages in importing.
- */
- public Namespace exported = new Namespace();
-
- public void setExportedNamespace (Namespace exp)
- {
- this.exported = exp;
- }
-
- /** The nicknames for this package. */
- LList nicknames = LList.Empty;
-
- private static final Object masterLock = new Object();
- LList shadowingSymbols = LList.Empty;
-
- public LList getShadowingSymbols()
- {
- return shadowingSymbols;
- }
-
- public static final LispPackage CLNamespace =
- (LispPackage) valueOf("COMMON-LISP");
- /* Symbols in the KeywordNamespace have type gnu.expr.Keyword. */
- public static final LispPackage KeywordNamespace =
- (LispPackage) valueOf("KEYWORD");
- /* Symbols in the KawaNamespace have type SimpleSymbol. */
- public static final LispPackage KawaNamespace =
- (LispPackage) valueOf("KAWA");
- /* The class namespace is used to resolve Java class names in the context of
- types. The user would specific class::|java.lang.String| if she wanted to
- use java.lang.String as a type name. This avoids various ambiguities. */
- public static final LispPackage ClassNamespace = (LispPackage) valueOf("CLASS");
- /** Common Lisp {@code *package*} special. */
- public static ThreadLocation<LispPackage> currentPackage
- = new ThreadLocation<LispPackage>("*package*");
-
- static
- {
- nsTable.put("CL", CLNamespace);
- CLNamespace.nicknames = Pair.make("CL", CLNamespace.nicknames);
- /* Package CL inherits from this package to facilitate cross language
- * lookups */
- KawaNamespace.setExportedNamespace(EmptyNamespace);
- KeywordNamespace.setExportedNamespace(Keyword.keywordNamespace);
- LispPackage.use(CLNamespace, KawaNamespace); // FIXME: Should be used from CL-USER
- LispPackage.use(CLNamespace, ClassNamespace);
- currentPackage.setGlobal(CLNamespace);
- }
- /** Namespaces that this Namespace imports or uses.
- * These are the <code>imported</code> fields of the
- * <code>NamespaceUse</code>, chained using <code>nextImported</code> fields.
- * The CommonLisp "uses" list. */
- NamespaceUse imported;
- /** Namespaces that import/use this namespace.
- * The CommonLisp "used-by" list. */
- NamespaceUse importing;
-
- /** Used for the CL PACKAGE-USE-LIST function. */
- public static LList pkgUsesList(LispPackage lp)
- {
- LList uses = LList.Empty;
- NamespaceUse it = lp.imported;
- while (it != null) {
- uses = Pair.make(it.imported, uses);
- it = it.nextImported;
- }
- return uses;
- }
-
- /** Used for the CL PACKAGE-USED-BY-LIST function */
- public static LList pkgUsedByList(LispPackage lp)
- {
- LList usedby = LList.Empty;
- NamespaceUse it = lp.importing;
- while (it != null) {
- usedby = Pair.make(it.importing, usedby);
- it = it.nextImporting;
- }
- return usedby;
- }
-
- public static void addNickNames(LispPackage name, LList nicks)
- {
- synchronized (nsTable)
- {
- for (Object nick : nicks) {
- name.nicknames = Pair.make((String) nick, name.nicknames);
- nsTable.put((String) nick, name);
- }
- }
- }
-
- public static void usePackages (LList importees, LispPackage importer)
- {
- for (Object usePkg : importees)
- {
- LispPackage lp;
- if (usePkg instanceof Symbol)
- lp = (LispPackage) LispPackage.valueOfNoCreate(((Symbol) usePkg).getName());//uc
- else if (usePkg instanceof LispPackage)
- lp = (LispPackage) usePkg;
- else
- lp = (LispPackage) LispPackage.valueOfNoCreate((String) usePkg);
- if (lp != null)
- {
- use(importer, lp);
- }
- else
- {
- throw new RuntimeException("The name " + usePkg + " does not designate any package");
- }
- }
- }
-
- public static LispPackage makeLispPackage (Object name, LList nicks,
- LList used)
- {
- LispPackage newpack = (LispPackage) LispPackage.valueOf((String) name);
- addNickNames(newpack, nicks);
- usePackages(used, newpack);
- return newpack;
- }
-
- /** Look up a given package in the {@link Namespace} map.
- *
- * This method creates a new Lisp package in the namespace if it does not
- * already exist.
- *
- * @param name The name of the package to look up.
- *
- * @return The {@link LispPackage} named by {@code name} or {@code null} if a
- * {@link Namespace} is already named by {@code name} but is not a lisp
- * package.
- */
- public static LispPackage valueOf (String name)
- {
- if (name == null)
- name = "";
-
- synchronized (nsTable) {
- Namespace ns = (Namespace) nsTable.get(name);
- if (ns != null)
- return (LispPackage) ns;
- ns = new LispPackage();
- ns.setName(name.intern());
- Namespace.nsTable.put(name, ns);
- return (LispPackage) ns;
- }
- }
-
- public static Namespace valueOfNoCreate (String name)
- {
- return (LispPackage) Namespace.valueOfNoCreate(name);
- }
-
- public Values.Values2 findSymbol (Object name)
- {
- String sname = name.toString();
- Symbol sym = exported.lookup(sname);
- if (sym != null)
- {
- return Values.values2(sym, CommonLisp.externalKeyword);
- }
-
- sym = lookupInternal(sname, sname.hashCode());
- if (sym != null)
- {
- return Values.values2(sym, CommonLisp.internalKeyword);
- }
-
- // It's not an exported or an imported symbol, let's check the inheritance
- // chain.
- NamespaceUse U = imported;
- while (U != null)
- {
- if (U.imported == LispPackage.KawaNamespace)
- sym = U.imported.exported.lookup(sname.toLowerCase());
- else
- sym = U.imported.exported.lookup(sname);
- if (sym != null)
- {
- return Values.values2(sym, CommonLisp.inheritedKeyword);
- }
- U = U.nextImported;
- }
- return Values.values2(CommonLisp.FALSE, CommonLisp.FALSE);
- }
-
- /** Export a list of symbols from a package, checking for conflicts.
- *
- * @param syms The list of symbols to export.
- * @param pkg The package to export the symbols from.
- *
- */
- public static void exportPkg (LList syms, LispPackage pkg)
- {
- Stack<Symbol> validSyms = new Stack<Symbol>();
- Iterator symiter = syms.getIterator();
- Symbol s;
- Values v;
- while (symiter.hasNext())
- {
- s = (Symbol) symiter.next();
- v = pkg.findSymbol(s.getName());//uc
- if (v.get(1) != CommonLisp.FALSE
- && !validSyms.contains(s))
- {
- validSyms.push(s);
- }
- }
- NamespaceUse usedBy = pkg.imported;
- symiter = syms.getIterator();
- while (symiter.hasNext())
- {
- s = (Symbol) symiter.next();
- String sname = s.getName();//uc
- while (usedBy != null)
- {
- v = usedBy.imported.findSymbol(sname);
- if (v.get(1) != CommonLisp.FALSE
- && v.get(0) != s
- && !usedBy.imported.shadowingSymbols.contains(v.get(0)))
- {
- // name conflict in usedBy.imported! Correctable, ask user
- // which name to nuke.
- signal("Name conflict from package " + usedBy.imported + "on symbol"
- + s);
- }
- usedBy = usedBy.nextImported;
- }
- }
- // Check that all symbols are accessible. If not, ask to import them.
- Stack<Symbol> missing = new Stack<Symbol>();
- // syms accessible in the inheritance chain, but not in this package
- Stack<Symbol> imports = new Stack<Symbol>();
- symiter = syms.getIterator();
- while (symiter.hasNext())
- {
- s = (Symbol) symiter.next();
- v = pkg.findSymbol(s.getName());//uc
- if ((v.get(1) == CommonLisp.FALSE)
- && (!(v.get(0).hashCode() == s.hashCode())))
- {
- missing.push(s);
- }
- else if (v.get(1) == KeywordNamespace.valueOf("inherited"))
- {
- imports.push(s);
- }
- }
- if (!missing.isEmpty())
- {
- // correctable error, ask user if they want ot import these
- // missing symbols into the package
- signal("The following symbols are missing: " + missing.toString());
- }
- while (!imports.isEmpty())
- {
- Symbol sym = imports.pop();
- pkg.exported.add(sym, sym.hashCode());
- }
- while (!validSyms.isEmpty())
- {
- s = validSyms.pop();
- pkg.remove(s); // remove internal
- pkg.exported.add(s, s.hashCode()); // add to external
- }
- }
-
- /**
- * Import a list of symbols into the internal table of a package.
- *
- * This method checks for conflicts, and should in the future allow the user
- * to shadow import any conflicts.
- *
- * @param syms the list of symbols to import.
- * @param pkg the package to import into.
- */
- public static void importPkg (LList syms, LispPackage pkg)
- {
- Stack<Symbol> validSyms = new Stack<Symbol>();
- Iterator symiter = syms.getIterator();
- Symbol s;
- Values v;
- while (symiter.hasNext())
- {
- s = (Symbol) symiter.next();
- v = pkg.findSymbol(s.getName());
- if (v.get(1) == CommonLisp.FALSE)
- {
- Iterator symiter2 = syms.getIterator();
- boolean found = false;
- while (symiter2.hasNext())
- {
- Symbol s2 = (Symbol) symiter2.next();
- if (s.getName().equals(s2.getName()))
- {
- if (s != s2)
- {
- validSyms.remove(s2);
- // name conflict
- signal("Symbol " + s2 + " conflicts with this package.");
- }
- }
- }
- if (!found)
- {
- validSyms.push(s);
- }
- }
- else if (v.get(0) != s)
- {
- // name conflict
- signal("Symbol " + v.get(0) + " conflicts in this package");
- }
- else if (v.get(1) == KeywordNamespace.valueOf("inherited"))
- {
- validSyms.add(s);
- }
- }
- while (!validSyms.isEmpty())
- {
- Symbol sym = validSyms.pop();
- pkg.add(sym, sym.hashCode());
- }
- // make any uninterned symbols owned by PKG
- symiter = syms.getIterator();
- while (symiter.hasNext())
- {
- s = (Symbol) symiter.next();
- if (s.getNamespace() == null)
- {
- s.setNamespace(pkg);
- }
- }
- }
-
- /**
- * The list of symbols managed by a given namespace.
- *
- * @param ns The namespace whose symbol table we query
- * @return The list of symbols managed by the given namespace.
- */
- public LList allSymbols (Namespace ns)
- {
- LList res = LList.Empty;
- java.util.Iterator symNameIter = ns.entrySet().iterator();
- while (symNameIter.hasNext())
- {
- res = Pair.make(symNameIter.next(), res);
- }
- return res;
- }
-
- /**
- * The list of symbols exported from this package.
- *
- * @return A list of the the exported symbols from the current package.
- *
- */
- public LList allExternalSymbols ()
- {
- return allSymbols(this.exported);
- }
- /**
- * The list of symbols interned into this package.
- *
- * @return A list of the interned symbols in this package.
- */
- public LList allInternalSymbols ()
- {
- return allSymbols(this);
- }
- public static void use (LispPackage importing, LispPackage imported)
- {
- synchronized (masterLock)
- {
- // FIXME check conflicts.
- NamespaceUse use = new NamespaceUse();
- use.nextImporting = imported.importing;
- use.importing = importing;
- imported.importing = use;
- use.nextImported = importing.imported;
- use.imported = imported;
- importing.imported = use;
- }
- }
- @Override
- public Symbol lookup(String name, int hash, boolean create)
- {
- Symbol sym = exported.lookup(name, hash, false);
- if (sym != null)
- return sym;
- sym = lookupInternal(name, hash);
- if (sym != null)
- return sym;
- // Do we need to synchronize on masterLock as well? FIXME
- for (NamespaceUse used = imported; used != null;
- used = used.nextImported)
- {
- sym = used.imported.exported.lookup(name, hash, false);
- if (sym != null)
- return sym;
- }
- if (create)
- return createSymbol (name, hash); // Optimization
- else
- return null;
- }
- public Symbol lookupPresent (String name, int hash, boolean intern)
- {
- Symbol sym = exported.lookup(name, hash, false);
- if (sym == null)
- sym = super.lookup(name, hash, intern);
- return sym;
- }
- public boolean isPresent (String name)
- {
- return lookupPresent(name, name.hashCode(), false) != null;
- }
- public boolean unintern (Symbol symbol)
- {
- String name = symbol.getName();
- int hash = name.hashCode();
- if (exported.lookup(name, hash, false) == symbol)
- exported.remove(symbol);
- else if (super.lookup(name, hash, false) == symbol)
- super.remove(symbol);
- else
- return false;
- symbol.setNamespace(null);
- if (removeFromShadowingSymbols(symbol))
- {
- // FIXME check use list: If thee are two or more different symbols
- // named 'name' in used packages, then signal a conflict.
- }
- return true;
- }
- private void addToShadowingSymbols (Symbol sym)
- {
- for (Object s = shadowingSymbols; s != LList.Empty; )
- {
- Pair p = (Pair) s;
- if (p.getCar() == sym)
- return;
- s = p.getCdr();
- }
- shadowingSymbols = new Pair(sym, shadowingSymbols);
- }
- private boolean removeFromShadowingSymbols (Symbol sym)
- {
- Pair prev = null;
- for (Object s = shadowingSymbols; s != LList.Empty; )
- {
- Pair p = (Pair) s;
- s = p.getCdr();
- if (p.getCar() == sym)
- {
- if (prev == null)
- shadowingSymbols = (LList) s;
- else
- prev.setCdr(s);
- return true;
- }
- prev = p;
- }
- return false;
- }
- /** The core of the Common Lisp shadow function. */
- public void shadow (String name)
- {
- Symbol sym = lookupPresent(name, name.hashCode(), true);
- addToShadowingSymbols(sym);
- }
- public void shadowingImport (Symbol symbol)
- {
- String name = symbol.getName();
- int hash = name.hashCode();
- Symbol old = lookupPresent(name, name.hashCode(), false);
- if (old != null && old != symbol)
- unintern(old);
- addToShadowingSymbols(symbol);
- }
- private Symbol createSymbol(String name, int hash) {
- if (this == KeywordNamespace) return Keyword.make(name);
- if (this == KawaNamespace) return Symbol.valueOf(name);
- return add(Symbol.makeUninterned(name, this), hash);
- }
- public static boolean keywordp (Object x) {
- return x instanceof Keyword;
- }
- public static Object symbolPackage (Object x) {
- Object nil = CommonLisp.FALSE;
- if (x == nil) return CLNamespace;
- else if (x instanceof Symbol) {
- Namespace ns = ((Symbol)x).getNamespace();
- if (ns instanceof LispPackage) return (LispPackage)ns;
- else if (keywordp (x)) return KeywordNamespace;
- else if (x instanceof SimpleSymbol) return KawaNamespace;
- else return nil;
- }
- else throw new RuntimeException("argument not a symbol: " + x);
- }
- public static Object intern(String name, LispPackage pkg) {
- name = name.intern();
- if (name == "nil" && pkg == CLNamespace) return CommonLisp.FALSE;
- return pkg.lookup(name, name.hashCode(), true);
- }
- /** Returns null if no package exists. */
- public static LispPackage findPackage(String name) {
- Namespace ns = Namespace.valueOfNoCreate(name);
- if (ns instanceof LispPackage) return (LispPackage)ns;
- return null;
- }
- /**
- * Temporary stub until Kawa supports conditional restarts.
- */
- public static void signal (String msg)
- {
- throw new RuntimeException(msg);
- }
- }
- /**
- * This is used to implement two linked lists. For performance they're combined
- * into one object.
- */
- class NamespaceUse
- {
- LispPackage imported = new LispPackage();
- NamespaceUse nextImported;
- LispPackage importing = new LispPackage();
- NamespaceUse nextImporting;
- }
|