org.armedbear.lisp.Package Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of abcl Show documentation
Show all versions of abcl Show documentation
Common Lisp implementation running on the JVM
/*
* Package.java
*
* Copyright (C) 2002-2007 Peter Graves
* $Id$
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from
* or based on this library. If you modify this library, you may extend
* this exception to your version of the library, but you are not
* obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package org.armedbear.lisp;
import static org.armedbear.lisp.Lisp.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public final class Package extends LispObject implements java.io.Serializable
{
private String name;
private transient SimpleString lispName;
private transient LispObject propertyList;
/** Symbols internal to the package. */
private transient final ConcurrentHashMap internalSymbols
= new ConcurrentHashMap(16);
/** Symbols exported from the package.
*
* Those symbols in this collection are not contained in the internalSymbols
*/
private transient final ConcurrentHashMap externalSymbols
= new ConcurrentHashMap(16);
private transient HashMap shadowingSymbols;
private transient ArrayList nicknames;
private transient LispObject useList = null;
private transient ArrayList usedByList = null;
private transient ConcurrentHashMap localNicknames;
// Anonymous package.
public Package()
{
}
public Package(String name)
{
this.name = name;
lispName = new SimpleString(name);
}
public Package(String name, int size)
{
this.name = name;
lispName = new SimpleString(name);
}
@Override
public LispObject typeOf()
{
return Symbol.PACKAGE;
}
@Override
public LispObject classOf()
{
return BuiltInClass.PACKAGE;
}
@Override
public LispObject getDescription()
{
if (name != null) {
StringBuilder sb = new StringBuilder("The ");
sb.append(name);
sb.append(" package");
return new SimpleString(sb);
}
return new SimpleString("PACKAGE");
}
@Override
public LispObject typep(LispObject type)
{
if (type == Symbol.PACKAGE)
return T;
if (type == BuiltInClass.PACKAGE)
return T;
return super.typep(type);
}
public final String getName()
{
return name;
}
public final LispObject NAME()
{
return lispName != null ? lispName : NIL;
}
@Override
public final LispObject getPropertyList()
{
if (propertyList == null)
propertyList = NIL;
return propertyList;
}
@Override
public final void setPropertyList(LispObject obj)
{
if (obj == null)
throw new NullPointerException();
propertyList = obj;
}
public final List getNicknames()
{
return nicknames;
}
private void makeSymbolsUninterned(ConcurrentHashMap symbolMap) {
Symbol sym;
for (Iterator it = symbolMap.values().iterator();
it.hasNext();) {
sym = it.next();
if (sym.getPackage() == this) {
sym.setPackage(NIL);
}
}
symbolMap.clear();
}
public final synchronized boolean delete()
{
if (name != null) {
if(useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
unusePackage(pkg);
usedPackages = usedPackages.cdr();
}
}
if (usedByList != null) {
while (!usedByList.isEmpty()) {
usedByList.get(0).unusePackage(this);
}
}
LispObject packages = Packages.getPackagesNicknamingPackage(this);
while (packages != NIL) {
Package p = (Package)((Cons)packages).car();
packages = ((Cons)packages).cdr();
p.removeLocalPackageNicknamesForPackage(this);
}
Packages.deletePackage(this);
makeSymbolsUninterned(internalSymbols);
makeSymbolsUninterned(externalSymbols); // also clears externalSymbols
name = null;
lispName = null;
nicknames = null;
return true;
}
return false;
}
public final synchronized void rename(String newName, LispObject newNicks)
{
ArrayList arrayList = null;
while (newNicks != NIL) {
if (arrayList == null)
arrayList = new ArrayList();
arrayList.add(javaString(newNicks.car()));
newNicks = newNicks.cdr();
}
// Remove old name and nicknames from Packages map.
Packages.deletePackage(this);
// Now change the names...
name = newName;
lispName = new SimpleString(newName);
nicknames = arrayList;
// And add the package back.
Packages.addPackage(this);
}
public Symbol findInternalSymbol(SimpleString name)
{
return internalSymbols.get(name.toString());
}
public Symbol findInternalSymbol(String name)
{
return internalSymbols.get(name);
}
public Symbol findExternalSymbol(SimpleString name)
{
return externalSymbols.get(name.toString());
}
public Symbol findExternalSymbol(String name)
{
return externalSymbols.get(name);
}
public Symbol findExternalSymbol(SimpleString name, int hash)
{
return externalSymbols.get(name.toString());
}
// Returns null if symbol is not accessible in this package.
public Symbol findAccessibleSymbol(String name)
{
return findAccessibleSymbol(new SimpleString(name));
}
// Returns null if symbol is not accessible in this package.
public Symbol findAccessibleSymbol(SimpleString name)
{
// Look in external and internal symbols of this package.
Symbol symbol = externalSymbols.get(name.toString());
if (symbol != null)
return symbol;
symbol = internalSymbols.get(name.toString());
if (symbol != null)
return symbol;
// Look in external symbols of used packages.
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
symbol = pkg.findExternalSymbol(name);
if (symbol != null)
return symbol;
usedPackages = usedPackages.cdr();
}
}
// Not found.
return null;
}
public LispObject findSymbol(String name)
{
final SimpleString s = new SimpleString(name);
final LispThread thread = LispThread.currentThread();
// Look in external and internal symbols of this package.
Symbol symbol = externalSymbols.get(name);
if (symbol != null)
return thread.setValues(symbol, Keyword.EXTERNAL);
symbol = internalSymbols.get(name);
if (symbol != null)
return thread.setValues(symbol, Keyword.INTERNAL);
// Look in external symbols of used packages.
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
symbol = pkg.findExternalSymbol(s);
if (symbol != null)
return thread.setValues(symbol, Keyword.INHERITED);
usedPackages = usedPackages.cdr();
}
}
// Not found.
return thread.setValues(NIL, NIL);
}
// Helper function to add NIL to PACKAGE_CL.
public void addSymbol(Symbol symbol)
{
Debug.assertTrue(symbol.getPackage() == this);
Debug.assertTrue(symbol.getName().equals("NIL"));
externalSymbols.put(symbol.name.toString(), symbol);
}
private Symbol addSymbol(String name)
{
Symbol symbol = new Symbol(name, this);
if (this == PACKAGE_KEYWORD) {
symbol.initializeConstant(symbol);
externalSymbols.put(name.toString(), symbol);
} else
internalSymbols.put(name.toString(), symbol);
return symbol;
}
private Symbol addSymbol(SimpleString name)
{
return addSymbol(name.toString());
}
public Symbol addInternalSymbol(String symbolName)
{
final Symbol symbol = new Symbol(symbolName, this);
internalSymbols.put(symbolName, symbol);
return symbol;
}
public Symbol addExternalSymbol(String symbolName)
{
final Symbol symbol = new Symbol(symbolName, this);
externalSymbols.put(symbolName, symbol);
return symbol;
}
public synchronized Symbol intern(SimpleString symbolName)
{
return intern(symbolName.toString());
}
public synchronized Symbol intern(String symbolName)
{
// Look in external and internal symbols of this package.
Symbol symbol = externalSymbols.get(symbolName);
if (symbol != null)
return symbol;
symbol = internalSymbols.get(symbolName);
if (symbol != null)
return symbol;
// Look in external symbols of used packages.
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
symbol = pkg.externalSymbols.get(symbolName);
if (symbol != null)
return symbol;
usedPackages = usedPackages.cdr();
}
}
// Not found.
return addSymbol(symbolName);
}
public synchronized Symbol intern(final SimpleString s,
final LispThread thread)
{
// Look in external and internal symbols of this package.
Symbol symbol = externalSymbols.get(s.toString());
if (symbol != null)
return (Symbol) thread.setValues(symbol, Keyword.EXTERNAL);
symbol = internalSymbols.get(s.toString());
if (symbol != null)
return (Symbol) thread.setValues(symbol, Keyword.INTERNAL);
// Look in external symbols of used packages.
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
symbol = pkg.findExternalSymbol(s);
if (symbol != null)
return (Symbol) thread.setValues(symbol, Keyword.INHERITED);
usedPackages = usedPackages.cdr();
}
}
// Not found.
return (Symbol) thread.setValues(addSymbol(s), NIL);
}
public synchronized Symbol internAndExport(String symbolName)
{
final SimpleString s = new SimpleString(symbolName);
// Look in external and internal symbols of this package.
Symbol symbol = externalSymbols.get(s.toString());
if (symbol != null)
return symbol;
symbol = internalSymbols.get(s.toString());
if (symbol != null) {
export(symbol);
return symbol;
}
if (useList instanceof Cons) {
// Look in external symbols of used packages.
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
symbol = pkg.findExternalSymbol(s);
if (symbol != null) {
export(symbol);
return symbol;
}
usedPackages = usedPackages.cdr();
}
}
// Not found.
symbol = new Symbol(s, this);
if (this == PACKAGE_KEYWORD)
symbol.initializeConstant(symbol);
externalSymbols.put(s.toString(), symbol);
return symbol;
}
public synchronized LispObject unintern(final Symbol symbol)
{
final String symbolName = symbol.getName();
final boolean shadow;
if (shadowingSymbols != null && shadowingSymbols.get(symbolName) == symbol)
shadow = true;
else
shadow = false;
if (shadow) {
// Check for conflicts that might be exposed in used package list
// if we remove the shadowing symbol.
Symbol sym = null;
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
Symbol s = pkg.findExternalSymbol(symbol.name);
if (s != null) {
if (sym == null)
sym = s;
else if (sym != s) {
StringBuilder sb =
new StringBuilder("Uninterning the symbol ");
sb.append(symbol.getQualifiedName());
sb.append(" causes a name conflict between ");
sb.append(sym.getQualifiedName());
sb.append(" and ");
sb.append(s.getQualifiedName());
return error(new PackageError(sb.toString(), this));
}
}
usedPackages = usedPackages.cdr();
}
}
}
// Reaching here, it's OK to remove the symbol.
boolean found = false;
if (externalSymbols.get(symbol.name.toString()) == symbol) {
externalSymbols.remove(symbol.name.toString());
found = true;
}
if (internalSymbols.get(symbol.name.toString()) == symbol) {
internalSymbols.remove(symbol.name.toString());
found = true;
}
if (! found)
return NIL;
if (shadow) {
Debug.assertTrue(shadowingSymbols != null);
shadowingSymbols.remove(symbolName);
}
if (symbol.getPackage() == this)
symbol.setPackage(NIL);
return T;
}
public synchronized void importSymbol(Symbol symbol)
{
if (symbol.getPackage() == this)
return; // Nothing to do.
Symbol sym = findAccessibleSymbol(symbol.name);
if (sym != null && sym != symbol) {
StringBuilder sb = new StringBuilder("The symbol ");
sb.append(sym.getQualifiedName());
sb.append(" is already accessible in package ");
sb.append(name);
sb.append('.');
error(new PackageError(sb.toString(), this));
}
internalSymbols.put(symbol.name.toString(), symbol);
if (symbol.getPackage() == NIL)
symbol.setPackage(this);
}
public synchronized void export(final Symbol symbol)
{
final String symbolName = symbol.getName();
boolean added = false;
if (symbol.getPackage() != this) {
Symbol sym = findAccessibleSymbol(symbol.name);
if (sym != symbol) {
StringBuilder sb = new StringBuilder("The symbol ");
sb.append(symbol.getQualifiedName());
sb.append(" is not accessible in package ");
sb.append(name);
sb.append('.');
error(new PackageError(sb.toString(), this));
return;
}
internalSymbols.put(symbol.name.toString(), symbol);
added = true;
}
if (added || internalSymbols.get(symbol.name.toString()) == symbol) {
if (usedByList != null) {
for (Iterator it = usedByList.iterator(); it.hasNext();) {
Package pkg = (Package) it.next();
Symbol sym = pkg.findAccessibleSymbol(symbol.name);
if (sym != null && sym != symbol) {
if (pkg.shadowingSymbols != null &&
pkg.shadowingSymbols.get(symbolName) == sym) {
// OK.
} else {
StringBuilder sb = new StringBuilder("The symbol ");
sb.append(sym.getQualifiedName());
sb.append(" is already accessible in package ");
sb.append(pkg.getName());
sb.append('.');
error(new PackageError(sb.toString(), pkg));
return;
}
}
}
}
// No conflicts.
internalSymbols.remove(symbol.name.toString());
externalSymbols.put(symbol.name.toString(), symbol);
return;
}
if (externalSymbols.get(symbol.name.toString()) == symbol)
// Symbol is already exported; there's nothing to do.
return;
StringBuilder sb = new StringBuilder("The symbol ");
sb.append(symbol.getQualifiedName());
sb.append(" is not accessible in package ");
sb.append(name);
sb.append('.');
error(new PackageError(sb.toString(), this));
}
public synchronized void unexport(final Symbol symbol)
{
if (externalSymbols.get(symbol.name.toString()) == symbol) {
externalSymbols.remove(symbol.name.toString());
internalSymbols.put(symbol.name.toString(), symbol);
} else if (findAccessibleSymbol(symbol.name.toString()) != symbol) {
StringBuilder sb = new StringBuilder("The symbol ");
sb.append(symbol.getQualifiedName());
sb.append(" is not accessible in package ");
sb.append(name);
error(new PackageError(sb.toString(), this));
}
}
public synchronized void shadow(final String symbolName)
{
if (shadowingSymbols == null)
shadowingSymbols = new HashMap();
final SimpleString s = new SimpleString(symbolName);
Symbol symbol = externalSymbols.get(s.toString());
if (symbol != null) {
shadowingSymbols.put(symbolName, symbol);
return;
}
symbol = internalSymbols.get(s.toString());
if (symbol != null) {
shadowingSymbols.put(symbolName, symbol);
return;
}
if (shadowingSymbols.get(symbolName) != null)
return;
symbol = new Symbol(s, this);
internalSymbols.put(s.toString(), symbol);
shadowingSymbols.put(symbolName, symbol);
}
public synchronized void shadowingImport(Symbol symbol)
{
final String symbolName = symbol.getName();
Symbol sym = externalSymbols.get(symbolName);
if (sym == null)
sym = internalSymbols.get(symbol.name.toString());
// if a different symbol with the same name is accessible,
// [..] which implies that it must be uninterned if it was present
if (sym != null && sym != symbol) {
if (shadowingSymbols != null)
shadowingSymbols.remove(symbolName);
unintern(sym);
}
if (sym == null || sym != symbol) {
// there was no symbol, or we just unintered it another one
// intern the new one
internalSymbols.put(symbol.name.toString(), symbol);
}
if (shadowingSymbols == null)
shadowingSymbols = new HashMap();
shadowingSymbols.put(symbolName, symbol);
}
// "USE-PACKAGE causes PACKAGE to inherit all the external symbols of
// PACKAGES-TO-USE. The inherited symbols become accessible as internal
// symbols of PACKAGE."
public void usePackage(Package pkg)
{
if (useList == null)
useList = NIL;
if (!memq(pkg, useList)) {
// "USE-PACKAGE checks for name conflicts between the newly
// imported symbols and those already accessible in package."
Collection symbols = pkg.getExternalSymbols();
for (Iterator i = symbols.iterator(); i.hasNext();) {
Symbol symbol = i.next();
Symbol existing = findAccessibleSymbol(symbol.name);
if (existing != null && existing != symbol) {
if (shadowingSymbols == null ||
shadowingSymbols.get(symbol.getName()) == null)
{
error(new PackageError("A symbol named " + symbol.getName() +
" is already accessible in package " +
name + ".", this));
return;
}
}
}
useList = useList.push(pkg);
// Add this package to the used-by list of pkg.
if (pkg.usedByList != null)
Debug.assertTrue(!pkg.usedByList.contains(this));
if (pkg.usedByList == null)
pkg.usedByList = new ArrayList();
pkg.usedByList.add(this);
}
}
public void unusePackage(Package pkg)
{
if (useList instanceof Cons) {
if (memq(pkg, useList)) {
// FIXME Modify the original list instead of copying it!
LispObject newList = NIL;
while (useList != NIL) {
if (useList.car() != pkg)
newList = newList.push(useList.car());
useList = useList.cdr();
}
useList = newList.nreverse();
Debug.assertTrue(!memq(pkg, useList));
Debug.assertTrue(pkg.usedByList != null);
Debug.assertTrue(pkg.usedByList.contains(this));
pkg.usedByList.remove(this);
}
}
}
public final void addNickname(String s)
{
// This call will signal an error if there's a naming conflict.
Packages.addNickname(this, s);
if (nicknames != null) {
if (nicknames.contains(s))
return; // Nothing to do.
} else
nicknames = new ArrayList();
nicknames.add(s);
}
public String getNickname()
{
if (nicknames != null && nicknames.size() > 0)
return (String) nicknames.get(0);
return null;
}
public LispObject packageNicknames()
{
LispObject list = NIL;
if (nicknames != null) {
for (int i = nicknames.size(); i-- > 0;) {
String nickname = (String) nicknames.get(i);
list = new Cons(new SimpleString(nickname), list);
}
}
return list;
}
public LispObject getUseList()
{
if (useList == null)
useList = NIL;
return useList;
}
public boolean uses(LispObject pkg)
{
return (useList instanceof Cons) && memq(pkg, useList);
}
public LispObject getUsedByList()
{
LispObject list = NIL;
if (usedByList != null) {
for (Iterator it = usedByList.iterator(); it.hasNext();) {
Package pkg = (Package) it.next();
list = new Cons(pkg, list);
}
}
return list;
}
public LispObject getLocalPackageNicknames()
{
LispObject list = NIL;
if (localNicknames != null) {
for (Map.Entry entry : localNicknames.entrySet()) {
list = new Cons(new Cons(entry.getKey(), entry.getValue()), list);
}
}
return list;
}
public LispObject addLocalPackageNickname(String name, Package pack)
{
if (localNicknames == null) {
localNicknames = new ConcurrentHashMap();
}
if (localNicknames.containsKey(name)) {
if (localNicknames.get(name) != pack) {
return error(new LispError(name + " is already a nickname for "
+ pack.getName()));
} else {
// nothing to do
return this;
}
} else {
localNicknames.put(name, pack);
return this;
}
}
public LispObject removeLocalPackageNickname(String name)
{
if (localNicknames == null || !localNicknames.containsKey(name)) {
return NIL;
} else {
// return generalized boolean: package that was nicknamed to `name'
return localNicknames.remove(name);
}
}
public void removeLocalPackageNicknamesForPackage(Package p)
{
if (localNicknames == null || !localNicknames.containsValue(p)) {
return;
} else {
for (Map.Entry entry : localNicknames.entrySet()) {
if (entry.getValue() == p) {
localNicknames.remove(entry.getKey());
}
}
}
}
public Collection getLocallyNicknamedPackages()
{
// for implementing package-locally-nicknamed-by-list
if (localNicknames == null) return new ArrayList();
else return localNicknames.values();
}
// Find package named `name', taking local nicknames into account
public Package findPackage(String name)
{
if (localNicknames != null) {
Package pkg = localNicknames.get(name);
if (pkg != null) return pkg;
}
return Packages.findPackageGlobally(name);
}
public LispObject getShadowingSymbols()
{
LispObject list = NIL;
if (shadowingSymbols != null) {
for (Iterator it = shadowingSymbols.values().iterator(); it.hasNext();) {
Symbol symbol = (Symbol) it.next();
list = new Cons(symbol, list);
}
}
return list;
}
public synchronized Collection getExternalSymbols()
{
return externalSymbols.values();
}
public synchronized List getAccessibleSymbols()
{
ArrayList list = new ArrayList();
list.addAll(internalSymbols.values());
list.addAll(externalSymbols.values());
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
list.addAll(pkg.externalSymbols.values());
usedPackages = usedPackages.cdr();
}
}
return list;
}
public synchronized LispObject PACKAGE_INTERNAL_SYMBOLS()
{
LispObject list = NIL;
Collection symbols = internalSymbols.values();
for (Iterator i = symbols.iterator(); i.hasNext();)
list = new Cons(i.next(), list);
return list;
}
public synchronized LispObject PACKAGE_EXTERNAL_SYMBOLS()
{
LispObject list = NIL;
Collection symbols = externalSymbols.values();
for (Iterator i = symbols.iterator(); i.hasNext();)
list = new Cons(i.next(), list);
return list;
}
public synchronized LispObject PACKAGE_INHERITED_SYMBOLS()
{
LispObject list = NIL;
if (useList instanceof Cons) {
LispObject usedPackages = useList;
while (usedPackages != NIL) {
Package pkg = (Package) usedPackages.car();
Collection externals = pkg.getExternalSymbols();
for (Iterator i = externals.iterator(); i.hasNext();) {
Symbol symbol = i.next();
if (shadowingSymbols != null && shadowingSymbols.get(symbol.getName()) != null)
continue;
if (externalSymbols.get(symbol.name.toString()) == symbol)
continue;
list = new Cons(symbol, list);
}
usedPackages = usedPackages.cdr();
}
}
return list;
}
public synchronized LispObject getSymbols()
{
LispObject list = NIL;
Collection internals = internalSymbols.values();
for (Iterator i = internals.iterator(); i.hasNext();)
list = new Cons(i.next(), list);
Collection externals = externalSymbols.values();
for (Iterator i = externals.iterator(); i.hasNext();)
list = new Cons(i.next(), list);
return list;
}
public synchronized Symbol[] symbols()
{
Collection internals = internalSymbols.values();
Collection externals = externalSymbols.values();
Symbol[] array = new Symbol[internals.size() + externals.size()];
int i = 0;
for (Iterator it = internals.iterator(); it.hasNext();) {
Symbol symbol = (Symbol) it.next();
array[i++] = symbol;
}
for (Iterator it = externals.iterator(); it.hasNext();) {
Symbol symbol = (Symbol) it.next();
array[i++] = symbol;
}
return array;
}
@Override
public String printObject()
{
if (_PRINT_FASL_.symbolValue() != NIL && name != null) {
StringBuilder sb = new StringBuilder("#.(CL:FIND-PACKAGE \"");
sb.append(name);
sb.append("\")");
return sb.toString();
} else {
if (name != null) {
return unreadableString("PACKAGE " + name, false);
} else
return unreadableString("PACKAGE");
}
}
public Object readResolve() throws java.io.ObjectStreamException {
Package pkg = findPackage(name);
if(pkg != null) {
return pkg;
} else {
return error(new PackageError(name + " is not the name of a package.", new SimpleString(name)));
}
}
}