org.armedbear.lisp.Interpreter 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
/*
* Interpreter.java
*
* Copyright (C) 2002-2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public final class Interpreter
{
// There can only be one interpreter.
public static Interpreter interpreter;
private final boolean jlisp;
private final InputStream inputStream;
private final OutputStream outputStream;
private static boolean noinit = false;
private static boolean nosystem = false;
private static boolean noinform = false;
private static boolean help = false;
private static boolean doubledash = false;
public static synchronized Interpreter getInstance()
{
return interpreter;
}
// Interface.
public static synchronized Interpreter createInstance()
{
if (interpreter != null)
return null;
interpreter = new Interpreter();
_NOINFORM_.setSymbolValue(T);
initializeLisp();
return interpreter;
}
public static synchronized Interpreter createDefaultInstance(String[] args)
{
if (interpreter != null)
return null;
interpreter = new Interpreter();
if (args != null)
preprocessCommandLineArguments(args);
if (!noinform) {
Stream out = getStandardOutput();
out._writeString(banner());
out._finishOutput();
}
if (help) {
Stream out = getStandardOutput();
out._writeString(help());
out._finishOutput();
exit(0); // FIXME
}
if (noinform)
_NOINFORM_.setSymbolValue(T);
else {
double uptime = (System.currentTimeMillis() - Main.startTimeMillis) / 1000.0;
getStandardOutput()._writeString("Low-level initialization completed in " +
uptime + " seconds.\n");
}
initializeLisp();
initializeTopLevel();
if (!nosystem)
initializeSystem();
if (!noinit)
processInitializationFile();
doubledash = false;
if (args != null)
postprocessCommandLineArguments(args);
return interpreter;
}
public static synchronized Interpreter createJLispInstance(
InputStream in,
OutputStream out,
String initialDirectory,
String version)
{
if (interpreter != null)
return null;
interpreter = new Interpreter(in, out, initialDirectory);
Stream stdout = getStandardOutput();
stdout._writeLine(version);
stdout._writeString(banner());
stdout._finishOutput();
initializeJLisp();
initializeTopLevel();
initializeSystem();
processInitializationFile();
return interpreter;
}
public static boolean initialized() {
return initialized;
}
private Interpreter()
{
jlisp = false;
inputStream = null;
outputStream = null;
}
private Interpreter(InputStream inputStream, OutputStream outputStream,
String initialDirectory)
{
jlisp = true;
this.inputStream = inputStream;
this.outputStream = outputStream;
resetIO(new Stream(Symbol.SYSTEM_STREAM, inputStream, Symbol.CHARACTER),
new Stream(Symbol.SYSTEM_STREAM, outputStream, Symbol.CHARACTER));
if (!initialDirectory.endsWith(File.separator))
initialDirectory = initialDirectory.concat(File.separator);
Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(new Pathname(initialDirectory));
}
// Interface.
public LispObject eval(String s)
{
return Lisp.eval(new StringInputStream(s).read(true, NIL, false,
LispThread.currentThread(),
Stream.currentReadtable));
}
public static synchronized void initializeLisp()
{
if (!initialized) {
Load.loadSystemFile("boot.lisp", false, false, false);
initialized = true;
}
}
public static synchronized void initializeJLisp()
{
if (!initialized) {
Symbol.FEATURES.setSymbolValue(new Cons(Keyword.J,
Symbol.FEATURES.getSymbolValue()));
Load.loadSystemFile("boot.lisp", false, false, false);
try {
Class.forName("org.armedbear.j.LispAPI");
}
catch (ClassNotFoundException e) { } // FIXME: what to do?
Load.loadSystemFile("j.lisp", false); // not being autoloaded
initialized = true;
}
}
private static boolean topLevelInitialized;
private static synchronized void initializeTopLevel()
{
if (!topLevelInitialized) {
// Resolve top-level-loop autoload.
Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
if (tplFun instanceof Autoload) {
Autoload autoload = (Autoload) tplFun;
autoload.load();
}
topLevelInitialized = true;
}
}
private static synchronized void processInitializationFile()
{
try {
String userHome = System.getProperty("user.home");
File file = new File(userHome, ".abclrc");
if (file.isFile()) {
final double startLoad = System.currentTimeMillis();
Load.load(file.getCanonicalPath());
if (!noinform) {
final double loadtime
= (System.currentTimeMillis() - startLoad) / 1000.0;
getStandardOutput()
._writeString("Loading " + file + " completed in "
+ loadtime + " seconds.\n");
}
return;
}
}
catch (IOException e) {
e.printStackTrace();
}
}
private static synchronized void initializeSystem()
{
Load.loadSystemFile("system", false); // not being autoloaded
}
// Check for --noinit; verify that arguments are supplied for --load and
// --eval options. Copy all unrecognized arguments into
// ext:*command-line-argument-list*
private static void preprocessCommandLineArguments(String[] args)
{
LispObject arglist = NIL;
if (args != null) {
for (int i = 0; i < args.length; ++i) {
String arg = args[i];
if (doubledash) {
arglist = new Cons(args[i], arglist);
} else if (arg.equals("--")) {
doubledash = true;
} else if (arg.equals("--noinit")) {
noinit = true;
} else if (arg.equals("--nosystem")) {
nosystem = true;
} else if (arg.equals("--noinform")) {
noinform = true;
} else if (arg.equals("--help")) {
help = true;
} else if (arg.equals("--batch")) {
_BATCH_MODE_.setSymbolValue(T);
} else if (arg.equals("--eval")) {
if (i + 1 < args.length) {
++i;
} else {
System.err.println("No argument supplied to --eval");
exit(1); // FIXME
}
} else if (arg.equals("--load") ||
arg.equals("--load-system-file")) {
if (i + 1 < args.length) {
++i;
} else {
System.err.println("No argument supplied to --load");
exit(1); // FIXME
}
} else {
arglist = new Cons(args[i], arglist);
}
}
}
arglist.nreverse();
_COMMAND_LINE_ARGUMENT_LIST_.setSymbolValue(arglist);
}
// Do the --load and --eval actions.
private static void postprocessCommandLineArguments(String[] args)
{
if (args != null) {
for (int i = 0; i < args.length; ++i) {
String arg = args[i];
if (doubledash) {
continue;
} else if (arg.equals("--")) {
doubledash = true;
} else if (arg.equals("--eval")) {
if (i + 1 < args.length) {
try {
evaluate(args[i + 1]);
}
catch (UnhandledCondition c) {
final String separator =
System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append(separator);
sb.append("Caught ");
sb.append(c.getCondition().typeOf().printObject());
sb.append(" while processing --eval option \"" +
args[i + 1] + "\":");
sb.append(separator);
sb.append(" ");
final LispThread thread = LispThread.currentThread();
thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
sb.append(c.getCondition().princToString());
sb.append(separator);
System.err.print(sb.toString());
exit(2); // FIXME
}
++i;
} else {
// Shouldn't happen.
System.err.println("No argument supplied to --eval");
exit(1); // FIXME
}
} else if (arg.equals("--load") ||
arg.equals("--load-system-file")) {
if (i + 1 < args.length) {
if (arg.equals("--load"))
Load.load(Pathname.mergePathnames(new Pathname(args[i + 1]),
checkPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.getSymbolValue())),
false, false, true);
else
Load.loadSystemFile(args[i + 1], false); // not being autoloaded
++i;
} else {
// Shouldn't happen.
System.err.println("No argument supplied to --load");
exit(1); // FIXME
}
}
}
}
if (_BATCH_MODE_.getSymbolValue() == T) {
exit(0); // FIXME
}
}
@SuppressWarnings("CallToThreadDumpStack")
public void run()
{
final LispThread thread = LispThread.currentThread();
try {
Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
if (tplFun instanceof Function) {
thread.execute(tplFun);
return;
}
}
catch (ProcessingTerminated e) {
throw e;
}
catch (IntegrityError e) {
return;
}
catch (Throwable t) {
t.printStackTrace();
return;
}
// We only arrive here if something went wrong and we weren't able
// to load top-level.lisp and run the normal top-level loop.
Stream out = getStandardOutput();
while (true) {
try {
thread.resetStack();
thread.clearSpecialBindings();
out._writeString("* ");
out._finishOutput();
LispObject object =
getStandardInput().read(false, EOF, false, thread,
Stream.currentReadtable);
if (object == EOF)
break;
out.setCharPos(0);
Symbol.MINUS.setSymbolValue(object);
LispObject result = Lisp.eval(object, new Environment(), thread);
Debug.assertTrue(result != null);
Symbol.STAR_STAR_STAR.setSymbolValue(Symbol.STAR_STAR.getSymbolValue());
Symbol.STAR_STAR.setSymbolValue(Symbol.STAR.getSymbolValue());
Symbol.STAR.setSymbolValue(result);
Symbol.PLUS_PLUS_PLUS.setSymbolValue(Symbol.PLUS_PLUS.getSymbolValue());
Symbol.PLUS_PLUS.setSymbolValue(Symbol.PLUS.getSymbolValue());
Symbol.PLUS.setSymbolValue(Symbol.MINUS.getSymbolValue());
out = getStandardOutput();
out.freshLine();
LispObject[] values = thread.getValues();
Symbol.SLASH_SLASH_SLASH.setSymbolValue(Symbol.SLASH_SLASH.getSymbolValue());
Symbol.SLASH_SLASH.setSymbolValue(Symbol.SLASH.getSymbolValue());
if (values != null) {
LispObject slash = NIL;
for (int i = values.length; i-- > 0;)
slash = new Cons(values[i], slash);
Symbol.SLASH.setSymbolValue(slash);
for (int i = 0; i < values.length; i++)
out._writeLine(values[i].printObject());
} else {
Symbol.SLASH.setSymbolValue(new Cons(result));
out._writeLine(result.printObject());
}
out._finishOutput();
}
catch (StackOverflowError e) {
getStandardInput().clearInput();
out._writeLine("Stack overflow");
}
catch (ControlTransfer c) {
// We're on the toplevel, if this occurs,
// we're toast...
reportError(c, thread);
}
catch (ProcessingTerminated e) {
throw e;
}
catch (IntegrityError e) {
return;
}
catch (Throwable t) {
getStandardInput().clearInput();
out.printStackTrace(t);
thread.printBacktrace();
}
}
}
private static void reportError(ControlTransfer c, LispThread thread)
{
getStandardInput().clearInput();
Stream out = getStandardOutput();
out.freshLine();
Condition condition = (Condition) c.getCondition();
out._writeLine("Error: unhandled condition: " +
condition.princToString());
if (thread != null)
thread.printBacktrace();
}
private static void reportError(UnhandledCondition c, LispThread thread)
{
getStandardInput().clearInput();
Stream out = getStandardOutput();
out.freshLine();
Condition condition = (Condition) c.getCondition();
out._writeLine("Error: unhandled condition: " +
condition.princToString());
if (thread != null)
thread.printBacktrace();
}
public void kill(int status)
{
if (jlisp) {
try {
inputStream.close();
}
catch (IOException e) {
Debug.trace(e);
}
try {
outputStream.close();
}
catch (IOException e) {
Debug.trace(e);
}
} else {
((Stream)Symbol.STANDARD_OUTPUT.getSymbolValue())._finishOutput();
((Stream)Symbol.ERROR_OUTPUT.getSymbolValue())._finishOutput();
System.exit(status);
}
}
public synchronized void dispose()
{
Debug.trace("Interpreter.dispose");
Debug.assertTrue(interpreter == this);
interpreter = null;
}
@Override
protected void finalize() throws Throwable
{
System.err.println("Interpreter.finalize");
}
public static final class UnhandledCondition extends Error
{
LispObject condition;
UnhandledCondition(LispObject condition) {
this.condition = condition;
}
public LispObject getCondition() {
return condition;
}
@Override
public String getMessage() {
String conditionText;
LispThread thread = LispThread.currentThread();
SpecialBindingsMark mark = thread.markSpecialBindings();
thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
try {
conditionText = getCondition().princToString();
} catch (Throwable t) {
conditionText = "";
} finally {
thread.resetSpecialBindings(mark);
}
return "Unhandled lisp condition: " + conditionText;
}
};
private static final Primitive _DEBUGGER_HOOK_FUNCTION =
new Primitive("%debugger-hook-function", PACKAGE_SYS, false)
{
@Override
public LispObject execute(LispObject first, LispObject second)
{
final LispObject condition = first;
if (interpreter == null) {
final LispThread thread = LispThread.currentThread();
final SpecialBindingsMark mark = thread.markSpecialBindings();
thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
try {
final LispObject truename =
Symbol.LOAD_TRUENAME.symbolValue(thread);
if (truename != NIL) {
final LispObject stream =
_LOAD_STREAM_.symbolValue(thread);
if (stream instanceof Stream) {
final int lineNumber =
((Stream)stream).getLineNumber() + 1;
final int offset =
((Stream)stream).getOffset();
Debug.trace("Error loading " +
truename.princToString() +
" at line " + lineNumber +
" (offset " + offset + ")");
}
}
Debug.trace("Encountered unhandled condition of type " +
condition.typeOf().princToString() + ':');
Debug.trace(" " + condition.princToString());
}
catch (Throwable t) {} // catch any exception to throw below
finally {
thread.resetSpecialBindings(mark);
}
}
UnhandledCondition uc = new UnhandledCondition(condition);
if (condition.typep(Symbol.JAVA_EXCEPTION) != NIL)
uc.initCause((Throwable)JavaException
.JAVA_EXCEPTION_CAUSE.execute(condition).javaInstance());
throw uc;
}
};
public static final LispObject readFromString(String s)
{
return new StringInputStream(s).read(true, NIL, false,
LispThread.currentThread(),
Stream.currentReadtable);
}
// For j.
/** Runs its input string through the lisp reader and evaluates the result.
*
* @param s A string with a valid Common Lisp expression
* @return The result of the evaluation
* @exception UnhandledCondition in case the an error occurs which
* should be passed to the Lisp debugger
*/
public static LispObject evaluate(String s)
{
if (!initialized)
initializeJLisp();
StringInputStream stream = new StringInputStream(s);
final LispThread thread = LispThread.currentThread();
LispObject obj = null;
final SpecialBindingsMark mark0 = thread.markSpecialBindings();
thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION);
try { // catch possible errors from use of SHARPSIGN_DOT macros in --eval stanzas
obj = stream.read(false, EOF, false, thread,
Stream.currentReadtable);
} finally {
thread.resetSpecialBindings(mark0);
}
if (obj == EOF)
return error(new EndOfFile(stream));
final SpecialBindingsMark mark = thread.markSpecialBindings();
thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION);
try {
return Lisp.eval(obj, new Environment(), thread);
}
finally {
thread.resetSpecialBindings(mark);
}
}
private static final String build;
static {
String s = null;
InputStream in = Interpreter.class.getResourceAsStream("build");
if (in != null) {
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(in));
s = reader.readLine();
reader.close();
}
catch (IOException e) {}
}
build = s;
}
private static String banner()
{
final String sep = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder("Armed Bear Common Lisp ");
sb.append(Version.getVersion());
if (build != null) {
sb.append(" (built ");
sb.append(build);
sb.append(')');
}
sb.append(sep);
sb.append("Java ");
sb.append(System.getProperty("java.version"));
sb.append(' ');
sb.append(System.getProperty("java.vendor"));
sb.append(sep);
String vm = System.getProperty("java.vm.name");
if (vm != null) {
sb.append(vm);
sb.append(sep);
}
return sb.toString();
}
private static String help()
{
final String sep = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder("Parameters:");
sb.append(sep);
sb.append("--help").append(sep)
.append(" Displays this message.");
sb.append(sep);
sb.append("--noinform").append(sep)
.append(" Suppresses the printing of startup information and banner.");
sb.append(sep);
sb.append("--noinit").append(sep)
.append(" Suppresses the loading of the '~/.abclrc' startup file.");
sb.append(sep);
sb.append("--nosystem").append(sep)
.append(" Suppresses loading the 'system.lisp' customization file. ");
sb.append(sep);
sb.append("--eval