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

Last change on this file was 12547, checked in by Mark Evenson, 15 years ago

backport r12456: nreverse() *command-line-arguments-list* to natural order.

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