source: branches/0.24.x/abcl/src/org/armedbear/lisp/Interpreter.java

Last change on this file was 13168, checked in by ehuelsmann, 15 years ago

Merge r13141-13146 and r13156: Make sure ABCL doesn't call System.exit()
in order to be a well-behaving library.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 23.3 KB
Line 
1/*
2 * Interpreter.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves
5 * $Id: Interpreter.java 13168 2011-01-21 22:12:08Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.io.BufferedReader;
39import java.io.File;
40import java.io.IOException;
41import java.io.InputStream;
42import java.io.InputStreamReader;
43import java.io.OutputStream;
44
45public final class Interpreter
46{
47    // There can only be one interpreter.
48    public static Interpreter interpreter;
49
50    private final boolean jlisp;
51    private final InputStream inputStream;
52    private final OutputStream outputStream;
53
54    private static boolean noinit = false;
55    private static boolean nosystem = false;
56    private static boolean noinform = false;
57    private static boolean help = false;
58
59    public static synchronized Interpreter getInstance()
60    {
61        return interpreter;
62    }
63
64    // Interface.
65    public static synchronized Interpreter createInstance()
66    {
67        if (interpreter != null)
68            return null;
69        interpreter = new Interpreter();
70        _NOINFORM_.setSymbolValue(T);
71        initializeLisp();
72        return interpreter;
73    }
74
75    public static synchronized Interpreter createDefaultInstance(String[] args)
76    {
77        if (interpreter != null)
78            return null;
79        interpreter = new Interpreter();
80
81        if (args != null)
82            preprocessCommandLineArguments(args);
83        if (!noinform) {
84            Stream out = getStandardOutput();
85            out._writeString(banner());
86            out._finishOutput();
87        }
88        if (help) {
89            Stream out = getStandardOutput();
90            out._writeString(help());
91            out._finishOutput();
92            exit(0); // FIXME
93        }
94        if (noinform)
95            _NOINFORM_.setSymbolValue(T);
96        else {
97            double uptime = (System.currentTimeMillis() - Main.startTimeMillis) / 1000.0;
98            getStandardOutput()._writeString("Low-level initialization completed in " +
99                                             uptime + " seconds.\n");
100        }
101        initializeLisp();
102        initializeTopLevel();
103        if (!nosystem) 
104            initializeSystem();
105        if (!noinit)
106            processInitializationFile();
107        if (args != null)
108            postprocessCommandLineArguments(args);
109
110        return interpreter;
111    }
112
113    public static synchronized Interpreter createJLispInstance(
114        InputStream in,
115        OutputStream out,
116        String initialDirectory,
117        String version)
118    {
119        if (interpreter != null)
120            return null;
121        interpreter = new Interpreter(in, out, initialDirectory);
122
123        Stream stdout = getStandardOutput();
124        stdout._writeLine(version);
125        stdout._writeString(banner());
126        stdout._finishOutput();
127
128        initializeJLisp();
129        initializeTopLevel();
130        initializeSystem();
131        processInitializationFile();
132        return interpreter;
133    }
134
135    public static boolean initialized() {
136        return initialized;
137    }
138
139    private Interpreter()
140    {
141        jlisp = false;
142        inputStream = null;
143        outputStream = null;
144    }
145
146    private Interpreter(InputStream inputStream, OutputStream outputStream,
147                        String initialDirectory)
148    {
149        jlisp = true;
150        this.inputStream = inputStream;
151        this.outputStream = outputStream;
152        resetIO(new Stream(Symbol.SYSTEM_STREAM, inputStream, Symbol.CHARACTER),
153                new Stream(Symbol.SYSTEM_STREAM, outputStream, Symbol.CHARACTER));
154        if (!initialDirectory.endsWith(File.separator))
155            initialDirectory = initialDirectory.concat(File.separator);
156        Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(new Pathname(initialDirectory));
157    }
158
159    // Interface.
160    public LispObject eval(String s)
161    {
162        return Lisp.eval(new StringInputStream(s).read(true, NIL, false,
163                                                  LispThread.currentThread(),
164                                                  Stream.currentReadtable));
165    }
166
167    public static synchronized void initializeLisp()
168    {
169        if (!initialized) {
170            Load.loadSystemFile("boot.lisp", false, false, false);
171            initialized = true;
172        }
173    }
174
175    public static synchronized void initializeJLisp()
176    {
177        if (!initialized) {
178            Symbol.FEATURES.setSymbolValue(new Cons(Keyword.J,
179                                               Symbol.FEATURES.getSymbolValue()));
180            Load.loadSystemFile("boot.lisp", false, false, false);
181
182            try {
183                Class.forName("org.armedbear.j.LispAPI");
184            }
185            catch (ClassNotFoundException e) { } // FIXME: what to do?
186
187            Load.loadSystemFile("j.lisp", false); // not being autoloaded
188
189            initialized = true;
190        }
191    }
192
193    private static boolean topLevelInitialized;
194
195    private static synchronized void initializeTopLevel()
196    {
197        if (!topLevelInitialized) {
198            // Resolve top-level-loop autoload.
199            Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
200            LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
201            if (tplFun instanceof Autoload) {
202                Autoload autoload = (Autoload) tplFun;
203                autoload.load();
204            }
205
206            topLevelInitialized = true;
207        }
208    }
209
210    private static synchronized void processInitializationFile()
211    {
212        try {
213            String userHome = System.getProperty("user.home");
214            File file = new File(userHome, ".abclrc");
215            if (file.isFile()) {
216                Load.load(file.getCanonicalPath());
217                return;
218            }
219        }
220        catch (IOException e) {
221            e.printStackTrace();
222        }
223    }
224
225    private static synchronized void initializeSystem() 
226    {
227        Load.loadSystemFile("system", false); // not being autoloaded
228    }
229
230    // Check for --noinit; verify that arguments are supplied for --load and
231    // --eval options.  Copy all unrecognized arguments into
232    // ext:*command-line-argument-list*
233    private static void preprocessCommandLineArguments(String[] args)
234
235    {
236        LispObject arglist = NIL;
237
238        if (args != null) {
239            for (int i = 0; i < args.length; ++i) {
240                String arg = args[i];
241                if (arg.equals("--noinit")) {
242                    noinit = true;
243                } else if (arg.equals("--nosystem")) {
244                    nosystem = true;
245                } else if (arg.equals("--noinform")) {
246                    noinform = true;
247                } else if (arg.equals("--help")) {
248                    help = true;
249                } else if (arg.equals("--batch")) {
250                    _BATCH_MODE_.setSymbolValue(T);
251                } else if (arg.equals("--eval")) {
252                    if (i + 1 < args.length) {
253                        ++i;
254                    } else {
255                        System.err.println("No argument supplied to --eval");
256                        exit(1); // FIXME
257                    }
258                } else if (arg.equals("--load") ||
259                           arg.equals("--load-system-file")) {
260                    if (i + 1 < args.length) {
261                        ++i;
262                    } else {
263                        System.err.println("No argument supplied to --load");
264                        exit(1); // FIXME
265                    }
266                } else {
267                    arglist = new Cons(args[i], arglist);
268                }
269            }
270        }
271        arglist.nreverse();
272
273        _COMMAND_LINE_ARGUMENT_LIST_.setSymbolValue(arglist);
274    }
275
276    // Do the --load and --eval actions.
277    private static void postprocessCommandLineArguments(String[] args)
278
279    {
280        if (args != null) {
281            for (int i = 0; i < args.length; ++i) {
282                String arg = args[i];
283                if (arg.equals("--eval")) {
284                    if (i + 1 < args.length) {
285                        try {
286                            evaluate(args[i + 1]);
287                        }
288                        catch (UnhandledCondition c) {
289                            final String separator =
290                                System.getProperty("line.separator");
291                            StringBuilder sb = new StringBuilder();
292                            sb.append(separator);
293                            sb.append("Caught ");
294                            sb.append(c.getCondition().typeOf().writeToString());
295                            sb.append(" while processing --eval option \"" +
296                                      args[i + 1] + "\":");
297                            sb.append(separator);
298                            sb.append("  ");
299                            final LispThread thread = LispThread.currentThread();
300                            thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
301                            sb.append(c.getCondition().writeToString());
302                            sb.append(separator);
303                            System.err.print(sb.toString());
304                            exit(2); // FIXME
305                        }
306                        ++i;
307                    } else {
308                        // Shouldn't happen.
309                        System.err.println("No argument supplied to --eval");
310                        exit(1); // FIXME
311                    }
312                } else if (arg.equals("--load") ||
313                           arg.equals("--load-system-file")) {
314                    if (i + 1 < args.length) {
315                        if (arg.equals("--load"))
316                            Load.load(new Pathname(args[i + 1]),
317                                      false, false, true);
318
319                        else
320                            Load.loadSystemFile(args[i + 1], false); // not being autoloaded
321                        ++i;
322                    } else {
323                        // Shouldn't happen.
324                        System.err.println("No argument supplied to --load");
325                        exit(1);  // FIXME
326                    }
327                }
328            }
329        }
330        if (_BATCH_MODE_.getSymbolValue() == T) {
331            exit(0); // FIXME
332        }
333    }
334
335    @SuppressWarnings("CallToThreadDumpStack")
336    public void run()
337    {
338        final LispThread thread = LispThread.currentThread();
339        try {
340            Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
341            LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
342            if (tplFun instanceof Function) {
343                thread.execute(tplFun);
344                return;
345            }
346        }
347        catch (ProcessingTerminated e) {
348            throw e;
349        }
350        catch (IntegrityError e) {
351            return;
352        }
353        catch (Throwable t) {
354            t.printStackTrace();
355            return;
356        }
357       
358        // We only arrive here if something went wrong and we weren't able
359        // to load top-level.lisp and run the normal top-level loop.
360        Stream out = getStandardOutput();
361        while (true) {
362            try {
363                thread.resetStack();
364                thread.clearSpecialBindings();
365                out._writeString("* ");
366                out._finishOutput();
367                LispObject object =
368                    getStandardInput().read(false, EOF, false, thread,
369                                            Stream.currentReadtable);
370                if (object == EOF)
371                    break;
372                out.setCharPos(0);
373                Symbol.MINUS.setSymbolValue(object);
374                LispObject result = Lisp.eval(object, new Environment(), thread);
375                Debug.assertTrue(result != null);
376                Symbol.STAR_STAR_STAR.setSymbolValue(Symbol.STAR_STAR.getSymbolValue());
377                Symbol.STAR_STAR.setSymbolValue(Symbol.STAR.getSymbolValue());
378                Symbol.STAR.setSymbolValue(result);
379                Symbol.PLUS_PLUS_PLUS.setSymbolValue(Symbol.PLUS_PLUS.getSymbolValue());
380                Symbol.PLUS_PLUS.setSymbolValue(Symbol.PLUS.getSymbolValue());
381                Symbol.PLUS.setSymbolValue(Symbol.MINUS.getSymbolValue());
382                out = getStandardOutput();
383                out.freshLine();
384                LispObject[] values = thread.getValues();
385                Symbol.SLASH_SLASH_SLASH.setSymbolValue(Symbol.SLASH_SLASH.getSymbolValue());
386                Symbol.SLASH_SLASH.setSymbolValue(Symbol.SLASH.getSymbolValue());
387                if (values != null) {
388                    LispObject slash = NIL;
389                    for (int i = values.length; i-- > 0;)
390                        slash = new Cons(values[i], slash);
391                    Symbol.SLASH.setSymbolValue(slash);
392                    for (int i = 0; i < values.length; i++)
393                        out._writeLine(values[i].writeToString());
394                } else {
395                    Symbol.SLASH.setSymbolValue(new Cons(result));
396                    out._writeLine(result.writeToString());
397                }
398                out._finishOutput();
399            }
400            catch (StackOverflowError e) {
401                getStandardInput().clearInput();
402                out._writeLine("Stack overflow");
403            }
404            catch (ControlTransfer c) {
405                // We're on the toplevel, if this occurs,
406                // we're toast...
407                reportError(c, thread);
408            }
409            catch (ProcessingTerminated e) {
410                throw e;
411            }
412            catch (IntegrityError e) {
413                return;
414            }
415            catch (Throwable t) {
416                getStandardInput().clearInput();
417                out.printStackTrace(t);
418                thread.printBacktrace();
419            }
420        }
421    }
422
423    private static void reportError(ControlTransfer c, LispThread thread)
424    {
425        getStandardInput().clearInput();
426        Stream out = getStandardOutput();
427        out.freshLine();
428        Condition condition = (Condition) c.getCondition();
429        out._writeLine("Error: unhandled condition: " +
430                       condition.writeToString());
431        if (thread != null)
432            thread.printBacktrace();
433    }
434
435    private static void reportError(UnhandledCondition c, LispThread thread)
436    {
437        getStandardInput().clearInput();
438        Stream out = getStandardOutput();
439        out.freshLine();
440        Condition condition = (Condition) c.getCondition();
441        out._writeLine("Error: unhandled condition: " +
442                       condition.writeToString());
443        if (thread != null)
444            thread.printBacktrace();
445    }
446
447    public void kill()
448    {
449        kill(0);
450    }
451
452    public void kill(int status)
453    {
454        if (jlisp) {
455            try {
456                inputStream.close();
457            }
458            catch (IOException e) {
459                Debug.trace(e);
460            }
461            try {
462                outputStream.close();
463            }
464            catch (IOException e) {
465                Debug.trace(e);
466            }
467        } else {
468            ((Stream)Symbol.STANDARD_OUTPUT.getSymbolValue())._finishOutput();
469            ((Stream)Symbol.ERROR_OUTPUT.getSymbolValue())._finishOutput();
470            System.exit(status);
471        }
472    }
473
474    public synchronized void dispose()
475    {
476        Debug.trace("Interpreter.dispose");
477        Debug.assertTrue(interpreter == this);
478        interpreter = null;
479    }
480
481    @Override
482    protected void finalize() throws Throwable
483    {
484        System.err.println("Interpreter.finalize");
485    }
486
487    public static final class UnhandledCondition extends Error
488    {
489        LispObject condition;
490
491        UnhandledCondition(LispObject condition) {
492            this.condition = condition;
493        }
494
495        public LispObject getCondition() {
496            return condition;
497        }
498
499        @Override
500        public String getMessage() {
501            String conditionText;
502            LispThread thread = LispThread.currentThread();
503            SpecialBindingsMark mark = thread.markSpecialBindings();
504            thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
505            try {
506                conditionText = getCondition().writeToString();
507            } catch (Throwable t) {
508                conditionText = "<error printing Lisp condition>";
509            } finally {
510                thread.resetSpecialBindings(mark);
511            }
512
513            return "Unhandled lisp condition: " + conditionText;
514        }
515
516
517    };
518
519    private static final Primitive _DEBUGGER_HOOK_FUNCTION =
520        new Primitive("%debugger-hook-function", PACKAGE_SYS, false)
521    {
522        @Override
523        public LispObject execute(LispObject first, LispObject second)
524        {
525            final LispObject condition = first;
526            if (interpreter == null) {
527                final LispThread thread = LispThread.currentThread();
528                final SpecialBindingsMark mark = thread.markSpecialBindings();
529                thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
530                try {
531                    final LispObject truename =
532                        Symbol.LOAD_TRUENAME.symbolValue(thread);
533                    if (truename != NIL) {
534                        final LispObject stream =
535                            _LOAD_STREAM_.symbolValue(thread);
536                        if (stream instanceof Stream) {
537                            final int lineNumber =
538                                ((Stream)stream).getLineNumber() + 1;
539                            final int offset =
540                                ((Stream)stream).getOffset();
541                            Debug.trace("Error loading " +
542                                        truename.writeToString() +
543                                        " at line " + lineNumber +
544                                        " (offset " + offset + ")");
545                        }
546                    }
547                    Debug.trace("Encountered unhandled condition of type " +
548                                condition.typeOf().writeToString() + ':');
549                    Debug.trace("  " + condition.writeToString());
550                }
551                catch (Throwable t) {} // catch any exception to throw below
552                finally {
553                    thread.resetSpecialBindings(mark);
554                }
555            }
556            UnhandledCondition uc = new UnhandledCondition(condition);
557            if (condition.typep(Symbol.JAVA_EXCEPTION) != NIL)
558                uc.initCause((Throwable)JavaException
559                        .JAVA_EXCEPTION_CAUSE.execute(condition).javaInstance());
560            throw uc;
561        }
562    };
563
564    public static final LispObject readFromString(String s)
565    {
566        return new StringInputStream(s).read(true, NIL, false,
567                                             LispThread.currentThread(),
568                                             Stream.currentReadtable);
569    }
570
571    // For j.
572    /** Runs its input string through the lisp reader and evaluates the result.
573     *
574     * @param s A string with a valid Common Lisp expression
575     * @return The result of the evaluation
576     * @exception UnhandledCondition in case the an error occurs which
577     *      should be passed to the Lisp debugger
578     */
579    public static LispObject evaluate(String s)
580    {
581        if (!initialized)
582            initializeJLisp();
583        StringInputStream stream = new StringInputStream(s);
584        final LispThread thread = LispThread.currentThread();
585        LispObject obj = stream.read(false, EOF, false, thread,
586                                     Stream.currentReadtable);
587        if (obj == EOF)
588            return error(new EndOfFile(stream));
589        final SpecialBindingsMark mark = thread.markSpecialBindings();
590        thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION);
591        try {
592            return Lisp.eval(obj, new Environment(), thread);
593        }
594        finally {
595            thread.resetSpecialBindings(mark);
596        }
597    }
598
599    private static final String build;
600
601    static {
602        String s = null;
603        InputStream in = Interpreter.class.getResourceAsStream("build");
604        if (in != null) {
605            try {
606                BufferedReader reader =
607                    new BufferedReader(new InputStreamReader(in));
608                s = reader.readLine();
609                reader.close();
610            }
611            catch (IOException e) {}
612        }
613        build = s;
614    }
615
616    private static String banner()
617    {
618        final String sep = System.getProperty("line.separator");
619        StringBuilder sb = new StringBuilder("Armed Bear Common Lisp ");
620        sb.append(Version.getVersion());
621        if (build != null) {
622            sb.append(" (built ");
623            sb.append(build);
624            sb.append(')');
625        }
626        sb.append(sep);
627        sb.append("Java ");
628        sb.append(System.getProperty("java.version"));
629        sb.append(' ');
630        sb.append(System.getProperty("java.vendor"));
631        sb.append(sep);
632        String vm = System.getProperty("java.vm.name");
633        if (vm != null) {
634            sb.append(vm);
635            sb.append(sep);
636        }
637        return sb.toString();
638    }
639    private static String help()
640    {
641        final String sep = System.getProperty("line.separator");
642        StringBuilder sb = new StringBuilder("Parameters:");
643        sb.append(sep);
644        sb.append("--help displays this help");
645        sb.append(sep);
646        sb.append("--noinform suppresses the printing of version info");
647        sb.append(sep);
648        sb.append("--eval <form> evaluates the <form> before initializing REPL");
649        sb.append(sep);
650        sb.append("--load <file> loads the file <file> before initializing REPL");
651        sb.append(sep);
652        sb.append("--load-system-file <file> loads the system file <file> before initializing REPL");
653        sb.append(sep);
654        sb.append("--batch enables batch mode. The --load, --load-system-file and --eval parameters are handled, and abcl exits without entering REPL");
655        sb.append(sep);
656        sb.append("--noinit suppresses loading a .abclrc startup file");
657        sb.append(sep); 
658        sb.append("--nosystem suppresses loading the system startup file");
659        sb.append(sep);
660       
661        return sb.toString();
662    }
663}
Note: See TracBrowser for help on using the repository browser.