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

Last change on this file was 13454, checked in by Mark Evenson, 14 years ago

Tweak appearance and contents of help message.

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