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

Last change on this file was 13227, checked in by vvoutilainen, 14 years ago

Add --help documentation for --.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 23.8 KB
Line 
1/*
2 * Interpreter.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves
5 * $Id: Interpreter.java 13227 2011-02-20 20:25:46Z vvoutilainen $
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                Load.load(file.getCanonicalPath());
219                return;
220            }
221        }
222        catch (IOException e) {
223            e.printStackTrace();
224        }
225    }
226
227    private static synchronized void initializeSystem() 
228    {
229        Load.loadSystemFile("system", false); // not being autoloaded
230    }
231
232    // Check for --noinit; verify that arguments are supplied for --load and
233    // --eval options.  Copy all unrecognized arguments into
234    // ext:*command-line-argument-list*
235    private static void preprocessCommandLineArguments(String[] args)
236
237    {
238        LispObject arglist = NIL;
239
240        if (args != null) {
241            for (int i = 0; i < args.length; ++i) {
242                String arg = args[i];
243                if (doubledash) {
244                    arglist = new Cons(args[i], arglist);
245                } else if (arg.equals("--")) {
246                    doubledash = true;
247                } else if (arg.equals("--noinit")) {
248                    noinit = true;
249                } else if (arg.equals("--nosystem")) {
250                    nosystem = true;
251                } else if (arg.equals("--noinform")) {
252                    noinform = true;
253                } else if (arg.equals("--help")) {
254                    help = true;
255                } else if (arg.equals("--batch")) {
256                    _BATCH_MODE_.setSymbolValue(T);
257                } else if (arg.equals("--eval")) {
258                    if (i + 1 < args.length) {
259                        ++i;
260                    } else {
261                        System.err.println("No argument supplied to --eval");
262                        exit(1); // FIXME
263                    }
264                } else if (arg.equals("--load") ||
265                           arg.equals("--load-system-file")) {
266                    if (i + 1 < args.length) {
267                        ++i;
268                    } else {
269                        System.err.println("No argument supplied to --load");
270                        exit(1); // FIXME
271                    }
272                } else {
273                    arglist = new Cons(args[i], arglist);
274                }
275            }
276        }
277        arglist.nreverse();
278
279        _COMMAND_LINE_ARGUMENT_LIST_.setSymbolValue(arglist);
280    }
281
282    // Do the --load and --eval actions.
283    private static void postprocessCommandLineArguments(String[] args)
284
285    {
286        if (args != null) {
287            for (int i = 0; i < args.length; ++i) {
288                String arg = args[i];
289                if (doubledash) {
290                    continue;
291                } else if (arg.equals("--")) {
292                    doubledash = true;
293                } else if (arg.equals("--eval")) {
294                    if (i + 1 < args.length) {
295                        try {
296                            evaluate(args[i + 1]);
297                        }
298                        catch (UnhandledCondition c) {
299                            final String separator =
300                                System.getProperty("line.separator");
301                            StringBuilder sb = new StringBuilder();
302                            sb.append(separator);
303                            sb.append("Caught ");
304                            sb.append(c.getCondition().typeOf().writeToString());
305                            sb.append(" while processing --eval option \"" +
306                                      args[i + 1] + "\":");
307                            sb.append(separator);
308                            sb.append("  ");
309                            final LispThread thread = LispThread.currentThread();
310                            thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
311                            sb.append(c.getCondition().writeToString());
312                            sb.append(separator);
313                            System.err.print(sb.toString());
314                            exit(2); // FIXME
315                        }
316                        ++i;
317                    } else {
318                        // Shouldn't happen.
319                        System.err.println("No argument supplied to --eval");
320                        exit(1); // FIXME
321                    }
322                } else if (arg.equals("--load") ||
323                           arg.equals("--load-system-file")) {
324                    if (i + 1 < args.length) {
325                        if (arg.equals("--load"))
326                            Load.load(new Pathname(args[i + 1]),
327                                      false, false, true);
328
329                        else
330                            Load.loadSystemFile(args[i + 1], false); // not being autoloaded
331                        ++i;
332                    } else {
333                        // Shouldn't happen.
334                        System.err.println("No argument supplied to --load");
335                        exit(1);  // FIXME
336                    }
337                }
338            }
339        }
340        if (_BATCH_MODE_.getSymbolValue() == T) {
341            exit(0); // FIXME
342        }
343    }
344
345    @SuppressWarnings("CallToThreadDumpStack")
346    public void run()
347    {
348        final LispThread thread = LispThread.currentThread();
349        try {
350            Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL);
351            LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction();
352            if (tplFun instanceof Function) {
353                thread.execute(tplFun);
354                return;
355            }
356        }
357        catch (ProcessingTerminated e) {
358            throw e;
359        }
360        catch (IntegrityError e) {
361            return;
362        }
363        catch (Throwable t) {
364            t.printStackTrace();
365            return;
366        }
367       
368        // We only arrive here if something went wrong and we weren't able
369        // to load top-level.lisp and run the normal top-level loop.
370        Stream out = getStandardOutput();
371        while (true) {
372            try {
373                thread.resetStack();
374                thread.clearSpecialBindings();
375                out._writeString("* ");
376                out._finishOutput();
377                LispObject object =
378                    getStandardInput().read(false, EOF, false, thread,
379                                            Stream.currentReadtable);
380                if (object == EOF)
381                    break;
382                out.setCharPos(0);
383                Symbol.MINUS.setSymbolValue(object);
384                LispObject result = Lisp.eval(object, new Environment(), thread);
385                Debug.assertTrue(result != null);
386                Symbol.STAR_STAR_STAR.setSymbolValue(Symbol.STAR_STAR.getSymbolValue());
387                Symbol.STAR_STAR.setSymbolValue(Symbol.STAR.getSymbolValue());
388                Symbol.STAR.setSymbolValue(result);
389                Symbol.PLUS_PLUS_PLUS.setSymbolValue(Symbol.PLUS_PLUS.getSymbolValue());
390                Symbol.PLUS_PLUS.setSymbolValue(Symbol.PLUS.getSymbolValue());
391                Symbol.PLUS.setSymbolValue(Symbol.MINUS.getSymbolValue());
392                out = getStandardOutput();
393                out.freshLine();
394                LispObject[] values = thread.getValues();
395                Symbol.SLASH_SLASH_SLASH.setSymbolValue(Symbol.SLASH_SLASH.getSymbolValue());
396                Symbol.SLASH_SLASH.setSymbolValue(Symbol.SLASH.getSymbolValue());
397                if (values != null) {
398                    LispObject slash = NIL;
399                    for (int i = values.length; i-- > 0;)
400                        slash = new Cons(values[i], slash);
401                    Symbol.SLASH.setSymbolValue(slash);
402                    for (int i = 0; i < values.length; i++)
403                        out._writeLine(values[i].writeToString());
404                } else {
405                    Symbol.SLASH.setSymbolValue(new Cons(result));
406                    out._writeLine(result.writeToString());
407                }
408                out._finishOutput();
409            }
410            catch (StackOverflowError e) {
411                getStandardInput().clearInput();
412                out._writeLine("Stack overflow");
413            }
414            catch (ControlTransfer c) {
415                // We're on the toplevel, if this occurs,
416                // we're toast...
417                reportError(c, thread);
418            }
419            catch (ProcessingTerminated e) {
420                throw e;
421            }
422            catch (IntegrityError e) {
423                return;
424            }
425            catch (Throwable t) {
426                getStandardInput().clearInput();
427                out.printStackTrace(t);
428                thread.printBacktrace();
429            }
430        }
431    }
432
433    private static void reportError(ControlTransfer c, LispThread thread)
434    {
435        getStandardInput().clearInput();
436        Stream out = getStandardOutput();
437        out.freshLine();
438        Condition condition = (Condition) c.getCondition();
439        out._writeLine("Error: unhandled condition: " +
440                       condition.writeToString());
441        if (thread != null)
442            thread.printBacktrace();
443    }
444
445    private static void reportError(UnhandledCondition c, LispThread thread)
446    {
447        getStandardInput().clearInput();
448        Stream out = getStandardOutput();
449        out.freshLine();
450        Condition condition = (Condition) c.getCondition();
451        out._writeLine("Error: unhandled condition: " +
452                       condition.writeToString());
453        if (thread != null)
454            thread.printBacktrace();
455    }
456
457    public void kill(int status)
458    {
459        if (jlisp) {
460            try {
461                inputStream.close();
462            }
463            catch (IOException e) {
464                Debug.trace(e);
465            }
466            try {
467                outputStream.close();
468            }
469            catch (IOException e) {
470                Debug.trace(e);
471            }
472        } else {
473            ((Stream)Symbol.STANDARD_OUTPUT.getSymbolValue())._finishOutput();
474            ((Stream)Symbol.ERROR_OUTPUT.getSymbolValue())._finishOutput();
475            System.exit(status);
476        }
477    }
478
479    public synchronized void dispose()
480    {
481        Debug.trace("Interpreter.dispose");
482        Debug.assertTrue(interpreter == this);
483        interpreter = null;
484    }
485
486    @Override
487    protected void finalize() throws Throwable
488    {
489        System.err.println("Interpreter.finalize");
490    }
491
492    public static final class UnhandledCondition extends Error
493    {
494        LispObject condition;
495
496        UnhandledCondition(LispObject condition) {
497            this.condition = condition;
498        }
499
500        public LispObject getCondition() {
501            return condition;
502        }
503
504        @Override
505        public String getMessage() {
506            String conditionText;
507            LispThread thread = LispThread.currentThread();
508            SpecialBindingsMark mark = thread.markSpecialBindings();
509            thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
510            try {
511                conditionText = getCondition().writeToString();
512            } catch (Throwable t) {
513                conditionText = "<error printing Lisp condition>";
514            } finally {
515                thread.resetSpecialBindings(mark);
516            }
517
518            return "Unhandled lisp condition: " + conditionText;
519        }
520
521
522    };
523
524    private static final Primitive _DEBUGGER_HOOK_FUNCTION =
525        new Primitive("%debugger-hook-function", PACKAGE_SYS, false)
526    {
527        @Override
528        public LispObject execute(LispObject first, LispObject second)
529        {
530            final LispObject condition = first;
531            if (interpreter == null) {
532                final LispThread thread = LispThread.currentThread();
533                final SpecialBindingsMark mark = thread.markSpecialBindings();
534                thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL);
535                try {
536                    final LispObject truename =
537                        Symbol.LOAD_TRUENAME.symbolValue(thread);
538                    if (truename != NIL) {
539                        final LispObject stream =
540                            _LOAD_STREAM_.symbolValue(thread);
541                        if (stream instanceof Stream) {
542                            final int lineNumber =
543                                ((Stream)stream).getLineNumber() + 1;
544                            final int offset =
545                                ((Stream)stream).getOffset();
546                            Debug.trace("Error loading " +
547                                        truename.writeToString() +
548                                        " at line " + lineNumber +
549                                        " (offset " + offset + ")");
550                        }
551                    }
552                    Debug.trace("Encountered unhandled condition of type " +
553                                condition.typeOf().writeToString() + ':');
554                    Debug.trace("  " + condition.writeToString());
555                }
556                catch (Throwable t) {} // catch any exception to throw below
557                finally {
558                    thread.resetSpecialBindings(mark);
559                }
560            }
561            UnhandledCondition uc = new UnhandledCondition(condition);
562            if (condition.typep(Symbol.JAVA_EXCEPTION) != NIL)
563                uc.initCause((Throwable)JavaException
564                        .JAVA_EXCEPTION_CAUSE.execute(condition).javaInstance());
565            throw uc;
566        }
567    };
568
569    public static final LispObject readFromString(String s)
570    {
571        return new StringInputStream(s).read(true, NIL, false,
572                                             LispThread.currentThread(),
573                                             Stream.currentReadtable);
574    }
575
576    // For j.
577    /** Runs its input string through the lisp reader and evaluates the result.
578     *
579     * @param s A string with a valid Common Lisp expression
580     * @return The result of the evaluation
581     * @exception UnhandledCondition in case the an error occurs which
582     *      should be passed to the Lisp debugger
583     */
584    public static LispObject evaluate(String s)
585    {
586        if (!initialized)
587            initializeJLisp();
588        StringInputStream stream = new StringInputStream(s);
589        final LispThread thread = LispThread.currentThread();
590        LispObject obj = stream.read(false, EOF, false, thread,
591                                     Stream.currentReadtable);
592        if (obj == EOF)
593            return error(new EndOfFile(stream));
594        final SpecialBindingsMark mark = thread.markSpecialBindings();
595        thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION);
596        try {
597            return Lisp.eval(obj, new Environment(), thread);
598        }
599        finally {
600            thread.resetSpecialBindings(mark);
601        }
602    }
603
604    private static final String build;
605
606    static {
607        String s = null;
608        InputStream in = Interpreter.class.getResourceAsStream("build");
609        if (in != null) {
610            try {
611                BufferedReader reader =
612                    new BufferedReader(new InputStreamReader(in));
613                s = reader.readLine();
614                reader.close();
615            }
616            catch (IOException e) {}
617        }
618        build = s;
619    }
620
621    private static String banner()
622    {
623        final String sep = System.getProperty("line.separator");
624        StringBuilder sb = new StringBuilder("Armed Bear Common Lisp ");
625        sb.append(Version.getVersion());
626        if (build != null) {
627            sb.append(" (built ");
628            sb.append(build);
629            sb.append(')');
630        }
631        sb.append(sep);
632        sb.append("Java ");
633        sb.append(System.getProperty("java.version"));
634        sb.append(' ');
635        sb.append(System.getProperty("java.vendor"));
636        sb.append(sep);
637        String vm = System.getProperty("java.vm.name");
638        if (vm != null) {
639            sb.append(vm);
640            sb.append(sep);
641        }
642        return sb.toString();
643    }
644    private static String help()
645    {
646        final String sep = System.getProperty("line.separator");
647        StringBuilder sb = new StringBuilder("Parameters:");
648        sb.append(sep);
649        sb.append("--help displays this help");
650        sb.append(sep);
651        sb.append("--noinform suppresses the printing of version info");
652        sb.append(sep);
653        sb.append("--eval <form> evaluates the <form> before initializing REPL");
654        sb.append(sep);
655        sb.append("--load <file> loads the file <file> before initializing REPL");
656        sb.append(sep);
657        sb.append("--load-system-file <file> loads the system file <file> before initializing REPL");
658        sb.append(sep);
659        sb.append("--batch enables batch mode. The --load, --load-system-file and --eval parameters are handled, and abcl exits without entering REPL");
660        sb.append(sep);
661        sb.append("--noinit suppresses loading a .abclrc startup file");
662        sb.append(sep); 
663        sb.append("--nosystem suppresses loading the system startup file");
664        sb.append(sep);
665        sb.append("-- alone prevents further argument handling");
666        sb.append(sep);
667       
668        return sb.toString();
669    }
670}
Note: See TracBrowser for help on using the repository browser.