source: trunk/abcl/src/org/armedbear/lisp/Interpreter.java @ 12642

Last change on this file since 12642 was 12629, checked in by ehuelsmann, 15 years ago

Running 'ant test.abcl' showed conditions don't have to be of Java type Condition.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 20.3 KB
Line 
1/*
2 * Interpreter.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves
5 * $Id: Interpreter.java 12629 2010-04-23 21:15:16Z 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 noinform = false;
56
57    public static synchronized Interpreter getInstance()
58    {
59        return interpreter;
60    }
61
62    // Interface.
63    public static synchronized Interpreter createInstance()
64    {
65        if (interpreter != null)
66            return null;
67        interpreter = new Interpreter();
68        _NOINFORM_.setSymbolValue(T);
69        initializeLisp();
70        return interpreter;
71    }
72
73    public static synchronized Interpreter createDefaultInstance(String[] args)
74    {
75        if (interpreter != null)
76            return null;
77        interpreter = new Interpreter();
78
79        if (args != null)
80            preprocessCommandLineArguments(args);
81        if (!noinform) {
82            Stream out = getStandardOutput();
83            out._writeString(banner());
84            out._finishOutput();
85        }
86        if (noinform)
87            _NOINFORM_.setSymbolValue(T);
88        else {
89            double uptime = (System.currentTimeMillis() - Main.startTimeMillis) / 1000.0;
90            getStandardOutput()._writeString("Low-level initialization completed in " +
91                                             uptime + " seconds.\n");
92        }
93        initializeLisp();
94        initializeTopLevel();
95        if (!noinit)
96            processInitializationFile();
97        if (args != null)
98            postprocessCommandLineArguments(args);
99
100        return interpreter;
101    }
102
103    public static synchronized Interpreter createJLispInstance(
104        InputStream in,
105        OutputStream out,
106        String initialDirectory,
107        String version)
108    {
109        if (interpreter != null)
110            return null;
111        interpreter = new Interpreter(in, out, initialDirectory);
112
113        Stream stdout = getStandardOutput();
114        stdout._writeLine(version);
115        stdout._writeString(banner());
116        stdout._finishOutput();
117
118        initializeJLisp();
119        initializeTopLevel();
120        processInitializationFile();
121        return interpreter;
122    }
123
124    public static boolean initialized() {
125        return interpreter != null;
126    }
127
128    private Interpreter()
129    {
130        jlisp = false;
131        inputStream = null;
132        outputStream = null;
133    }
134
135    private Interpreter(InputStream inputStream, OutputStream outputStream,
136                        String initialDirectory)
137    {
138        jlisp = true;
139        this.inputStream = inputStream;
140        this.outputStream = outputStream;
141        resetIO(new Stream(Symbol.SYSTEM_STREAM, inputStream, Symbol.CHARACTER),
142                new Stream(Symbol.SYSTEM_STREAM, outputStream, Symbol.CHARACTER));
143        if (!initialDirectory.endsWith(File.separator))
144            initialDirectory = initialDirectory.concat(File.separator);
145        Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(new Pathname(initialDirectory));
146    }
147
148    // Interface.
149    public LispObject eval(String s)
150    {
151        return Lisp.eval(new StringInputStream(s).read(true, NIL, false,
152                                                  LispThread.currentThread(),
153                                                  Stream.currentReadtable));
154    }
155
156    public static synchronized void initializeLisp()
157    {
158        if (!initialized) {
159            Load.loadSystemFile("boot.lisp", false, false, false);
160            initialized = true;
161        }
162    }
163
164    public static synchronized void initializeJLisp()
165    {
166        if (!initialized) {
167            Symbol.FEATURES.setSymbolValue(new Cons(Keyword.J,
168                                               Symbol.FEATURES.getSymbolValue()));
169            Load.loadSystemFile("boot.lisp", false, false, false);
170
171            try {
172                Class.forName("org.armedbear.j.LispAPI");
173            }
174            catch (ClassNotFoundException e) { } // FIXME: what to do?
175
176            Load.loadSystemFile("j.lisp");
177
178            initialized = true;
179        }
180    }
181
182    private static boolean topLevelInitialized;
183
184    private static synchronized void initializeTopLevel()
185    {
186        if (!topLevelInitialized) {
187            // Resolve top-level-loop autoload.
188            Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
189            LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
190            if (tplFun instanceof Autoload) {
191                Autoload autoload = (Autoload) tplFun;
192                autoload.load();
193            }
194
195            topLevelInitialized = true;
196        }
197    }
198
199    private static synchronized void processInitializationFile()
200    {
201        try {
202            String userHome = System.getProperty("user.home");
203            File file = new File(userHome, ".abclrc");
204            if (file.isFile()) {
205                Load.load(file.getCanonicalPath());
206                return;
207            }
208        }
209        catch (IOException e) {
210            e.printStackTrace();
211        }
212    }
213
214    // Check for --noinit; verify that arguments are supplied for --load and
215    // --eval options.  Copy all unrecognized arguments into
216    // ext:*command-line-argument-list*
217    private static void preprocessCommandLineArguments(String[] args)
218
219    {
220        LispObject arglist = NIL;
221
222        if (args != null) {
223            for (int i = 0; i < args.length; ++i) {
224                String arg = args[i];
225                if (arg.equals("--noinit")) {
226                    noinit = true;
227                } else if (arg.equals("--noinform")) {
228                    noinform = true;
229                } else if (arg.equals("--batch")) {
230                    _BATCH_MODE_.setSymbolValue(T);
231                } else if (arg.equals("--eval")) {
232                    if (i + 1 < args.length) {
233                        ++i;
234                    } else {
235                        System.err.println("No argument supplied to --eval");
236                        System.exit(1);
237                    }
238                } else if (arg.equals("--load") ||
239                           arg.equals("--load-system-file")) {
240                    if (i + 1 < args.length) {
241                        ++i;
242                    } else {
243                        System.err.println("No argument supplied to --load");
244                        System.exit(1);
245                    }
246                } else {
247                    arglist = new Cons(args[i], arglist);
248                }
249            }
250        }
251        arglist.nreverse();
252
253        _COMMAND_LINE_ARGUMENT_LIST_.setSymbolValue(arglist);
254    }
255
256    // Do the --load and --eval actions.
257    private static void postprocessCommandLineArguments(String[] args)
258
259    {
260        if (args != null) {
261            for (int i = 0; i < args.length; ++i) {
262                String arg = args[i];
263                if (arg.equals("--eval")) {
264                    if (i + 1 < args.length) {
265                        try {
266                            evaluate(args[i + 1]);
267                        }
268                        catch (UnhandledCondition c) {
269                            final String separator =
270                                System.getProperty("line.separator");
271                            StringBuilder sb = new StringBuilder();
272                            sb.append(separator);
273                            sb.append("Caught ");
274                            sb.append(c.getCondition().typeOf().writeToString());
275                            sb.append(" while processing --eval option \"" +
276                                      args[i + 1] + "\":");
277                            sb.append(separator);
278                            sb.append("  ");
279                            final LispThread thread = LispThread.currentThread();
280                            thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
281                            sb.append(c.getCondition().writeToString());
282                            sb.append(separator);
283                            System.err.print(sb.toString());
284                            System.exit(2);
285                        }
286                        ++i;
287                    } else {
288                        // Shouldn't happen.
289                        System.err.println("No argument supplied to --eval");
290                        System.exit(1);
291                    }
292                } else if (arg.equals("--load") ||
293                           arg.equals("--load-system-file")) {
294                    if (i + 1 < args.length) {
295                        if (arg.equals("--load"))
296                            Load.load(new Pathname(args[i + 1]),
297                                      false, false, true);
298
299                        else
300                            Load.loadSystemFile(args[i + 1]);
301                        ++i;
302                    } else {
303                        // Shouldn't happen.
304                        System.err.println("No argument supplied to --load");
305                        System.exit(1);
306                    }
307                }
308            }
309        }
310    }
311
312    public void run()
313    {
314        final LispThread thread = LispThread.currentThread();
315        try {
316            Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
317            LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
318            if (tplFun instanceof Function) {
319                thread.execute(tplFun);
320                return;
321            }
322            // We only arrive here if something went wrong and we weren't able
323            // to load top-level.lisp and run the normal top-level loop.
324            Stream out = getStandardOutput();
325            while (true) {
326                try {
327                    thread.resetStack();
328                    thread.clearSpecialBindings();
329                    out._writeString("* ");
330                    out._finishOutput();
331                    LispObject object =
332                        getStandardInput().read(false, EOF, false, thread,
333                                                Stream.currentReadtable);
334                    if (object == EOF)
335                        break;
336                    out.setCharPos(0);
337                    Symbol.MINUS.setSymbolValue(object);
338                    LispObject result = Lisp.eval(object, new Environment(), thread);
339                    Debug.assertTrue(result != null);
340                    Symbol.STAR_STAR_STAR.setSymbolValue(Symbol.STAR_STAR.getSymbolValue());
341                    Symbol.STAR_STAR.setSymbolValue(Symbol.STAR.getSymbolValue());
342                    Symbol.STAR.setSymbolValue(result);
343                    Symbol.PLUS_PLUS_PLUS.setSymbolValue(Symbol.PLUS_PLUS.getSymbolValue());
344                    Symbol.PLUS_PLUS.setSymbolValue(Symbol.PLUS.getSymbolValue());
345                    Symbol.PLUS.setSymbolValue(Symbol.MINUS.getSymbolValue());
346                    out = getStandardOutput();
347                    out.freshLine();
348                    LispObject[] values = thread.getValues();
349                    Symbol.SLASH_SLASH_SLASH.setSymbolValue(Symbol.SLASH_SLASH.getSymbolValue());
350                    Symbol.SLASH_SLASH.setSymbolValue(Symbol.SLASH.getSymbolValue());
351                    if (values != null) {
352                        LispObject slash = NIL;
353                        for (int i = values.length; i-- > 0;)
354                            slash = new Cons(values[i], slash);
355                        Symbol.SLASH.setSymbolValue(slash);
356                        for (int i = 0; i < values.length; i++)
357                            out._writeLine(values[i].writeToString());
358                    } else {
359                        Symbol.SLASH.setSymbolValue(new Cons(result));
360                        out._writeLine(result.writeToString());
361                    }
362                    out._finishOutput();
363                }
364                catch (StackOverflowError e) {
365                    getStandardInput().clearInput();
366                    out._writeLine("Stack overflow");
367                }
368                catch (ControlTransfer c) {
369                    // We're on the toplevel, if this occurs,
370                    // we're toast...
371                    reportError(c, thread);
372                }
373                catch (Throwable t) {
374                    getStandardInput().clearInput();
375                    out.printStackTrace(t);
376                    thread.printBacktrace();
377                }
378            }
379        }
380        catch (Throwable t) {
381            t.printStackTrace();
382        }
383    }
384
385    private static void reportError(ControlTransfer c, LispThread thread)
386    {
387        getStandardInput().clearInput();
388        Stream out = getStandardOutput();
389        out.freshLine();
390        Condition condition = (Condition) c.getCondition();
391        out._writeLine("Error: unhandled condition: " +
392                       condition.writeToString());
393        if (thread != null)
394            thread.printBacktrace();
395    }
396
397    private static void reportError(UnhandledCondition c, LispThread thread)
398    {
399        getStandardInput().clearInput();
400        Stream out = getStandardOutput();
401        out.freshLine();
402        Condition condition = (Condition) c.getCondition();
403        out._writeLine("Error: unhandled condition: " +
404                       condition.writeToString());
405        if (thread != null)
406            thread.printBacktrace();
407    }
408
409    public void kill()
410    {
411        kill(0);
412    }
413
414    public void kill(int status)
415    {
416        if (jlisp) {
417            try {
418                inputStream.close();
419            }
420            catch (IOException e) {
421                Debug.trace(e);
422            }
423            try {
424                outputStream.close();
425            }
426            catch (IOException e) {
427                Debug.trace(e);
428            }
429        } else
430            System.exit(status);
431    }
432
433    public synchronized void dispose()
434    {
435        Debug.trace("Interpreter.dispose");
436        Debug.assertTrue(interpreter == this);
437        interpreter = null;
438    }
439
440    @Override
441    protected void finalize() throws Throwable
442    {
443        System.err.println("Interpreter.finalize");
444    }
445
446    public static final class UnhandledCondition extends Error
447    {
448        LispObject condition;
449
450        UnhandledCondition(LispObject condition) {
451            this.condition = condition;
452        }
453
454        public LispObject getCondition() {
455            return condition;
456        }
457    };
458
459    private static final Primitive _DEBUGGER_HOOK_FUNCTION =
460        new Primitive("%debugger-hook-function", PACKAGE_SYS, false)
461    {
462        @Override
463        public LispObject execute(LispObject first, LispObject second)
464            throws UnhandledCondition
465        {
466            final LispObject condition = first;
467            if (interpreter == null) {
468                final LispThread thread = LispThread.currentThread();
469                final SpecialBindingsMark mark = thread.markSpecialBindings();
470                thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
471                try {
472                    final LispObject truename =
473                        Symbol.LOAD_TRUENAME.symbolValue(thread);
474                    if (truename != NIL) {
475                        final LispObject stream =
476                            _LOAD_STREAM_.symbolValue(thread);
477                        if (stream instanceof Stream) {
478                            final int lineNumber =
479                                ((Stream)stream).getLineNumber() + 1;
480                            final int offset =
481                                ((Stream)stream).getOffset();
482                            Debug.trace("Error loading " +
483                                        truename.writeToString() +
484                                        " at line " + lineNumber +
485                                        " (offset " + offset + ")");
486                        }
487                    }
488                    Debug.trace("Encountered unhandled condition of type " +
489                                condition.typeOf().writeToString() + ':');
490                    Debug.trace("  " + condition.writeToString());
491                }
492                catch (Throwable t) {} // catch any exception to throw below
493                finally {
494                    thread.resetSpecialBindings(mark);
495                }
496            }
497            throw new UnhandledCondition(condition);
498        }
499    };
500
501    public static final LispObject readFromString(String s)
502    {
503        return new StringInputStream(s).read(true, NIL, false,
504                                             LispThread.currentThread(),
505                                             Stream.currentReadtable);
506    }
507
508    // For j.
509    /** Runs its input string through the lisp reader and evaluates the result.
510     *
511     * @param s A string with a valid Common Lisp expression
512     * @return The result of the evaluation
513     * @exception UnhandledCondition in case the an error occurs which
514     *      should be passed to the Lisp debugger
515     */
516    public static LispObject evaluate(String s)
517    {
518        if (!initialized)
519            initializeJLisp();
520        StringInputStream stream = new StringInputStream(s);
521        final LispThread thread = LispThread.currentThread();
522        LispObject obj = stream.read(false, EOF, false, thread,
523                                     Stream.currentReadtable);
524        if (obj == EOF)
525            return error(new EndOfFile(stream));
526        final SpecialBindingsMark mark = thread.markSpecialBindings();
527        thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION);
528        try {
529            return Lisp.eval(obj, new Environment(), thread);
530        }
531        finally {
532            thread.resetSpecialBindings(mark);
533        }
534    }
535
536    private static final String build;
537
538    static {
539        String s = null;
540        InputStream in = Interpreter.class.getResourceAsStream("build");
541        if (in != null) {
542            try {
543                BufferedReader reader =
544                    new BufferedReader(new InputStreamReader(in));
545                s = reader.readLine();
546                reader.close();
547            }
548            catch (IOException e) {}
549        }
550        build = s;
551    }
552
553    private static String banner()
554    {
555        final String sep = System.getProperty("line.separator");
556        StringBuilder sb = new StringBuilder("Armed Bear Common Lisp ");
557        sb.append(Version.getVersion());
558        if (build != null) {
559            sb.append(" (built ");
560            sb.append(build);
561            sb.append(')');
562        }
563        sb.append(sep);
564        sb.append("Java ");
565        sb.append(System.getProperty("java.version"));
566        sb.append(' ');
567        sb.append(System.getProperty("java.vendor"));
568        sb.append(sep);
569        String vm = System.getProperty("java.vm.name");
570        if (vm != null) {
571            sb.append(vm);
572            sb.append(sep);
573        }
574        return sb.toString();
575    }
576}
Note: See TracBrowser for help on using the repository browser.