1 /* 2 * Interpreter.java 3 * 4 * Copyright (C) 2002-2006 Peter Graves 5 * $Id$ 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 34 package org.armedbear.lisp; 35 36 import static org.armedbear.lisp.Lisp.*; 37 38 import java.io.BufferedReader; 39 import java.io.File; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.InputStreamReader; 43 import java.io.OutputStream; 44 45 public 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 getInstance()60 public static synchronized Interpreter getInstance() 61 { 62 return interpreter; 63 } 64 65 // Interface. createInstance()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 createDefaultInstance(String[] args)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 createJLispInstance( InputStream in, OutputStream out, String initialDirectory, String version)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 initialized()137 public static boolean initialized() { 138 return initialized; 139 } 140 Interpreter()141 private Interpreter() 142 { 143 jlisp = false; 144 inputStream = null; 145 outputStream = null; 146 } 147 Interpreter(InputStream inputStream, OutputStream outputStream, String initialDirectory)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(Pathname.create(initialDirectory)); 159 } 160 161 // Interface. eval(String s)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 initializeLisp()169 public static synchronized void initializeLisp() 170 { 171 if (!initialized) { 172 Load.loadSystemFile("boot.lisp", false, false, false); 173 initialized = true; 174 } 175 } 176 initializeJLisp()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 initializeTopLevel()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 processInitializationFile()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 initializeSystem()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* preprocessCommandLineArguments(String[] args)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. postprocessCommandLineArguments(String[] args)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(Pathname.mergePathnames((Pathname)Pathname.create(args[i + 1]), 334 checkPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.getSymbolValue())), 335 false, false, true); 336 337 else 338 Load.loadSystemFile(args[i + 1], false); // not being autoloaded 339 ++i; 340 } else { 341 // Shouldn't happen. 342 System.err.println("No argument supplied to --load"); 343 exit(1); // FIXME 344 } 345 } 346 } 347 } 348 if (_BATCH_MODE_.getSymbolValue() == T) { 349 exit(0); // FIXME 350 } 351 } 352 353 @SuppressWarnings("CallToThreadDumpStack") run()354 public void run() 355 { 356 final LispThread thread = LispThread.currentThread(); 357 try { 358 Symbol TOP_LEVEL_LOOP = intern("TOP-LEVEL-LOOP", PACKAGE_TPL); 359 LispObject tplFun = TOP_LEVEL_LOOP.getSymbolFunction(); 360 if (tplFun instanceof Function) { 361 thread.execute(tplFun); 362 return; 363 } 364 } 365 catch (ProcessingTerminated e) { 366 throw e; 367 } 368 catch (IntegrityError e) { 369 return; 370 } 371 catch (Throwable t) { 372 t.printStackTrace(); 373 return; 374 } 375 376 // We only arrive here if something went wrong and we weren't able 377 // to load top-level.lisp and run the normal top-level loop. 378 Stream out = getStandardOutput(); 379 while (true) { 380 try { 381 thread.resetStack(); 382 thread.clearSpecialBindings(); 383 out._writeString("* "); 384 out._finishOutput(); 385 LispObject object = 386 getStandardInput().read(false, EOF, false, thread, 387 Stream.currentReadtable); 388 if (object == EOF) 389 break; 390 out.setCharPos(0); 391 Symbol.MINUS.setSymbolValue(object); 392 LispObject result = Lisp.eval(object, new Environment(), thread); 393 Debug.assertTrue(result != null); 394 Symbol.STAR_STAR_STAR.setSymbolValue(Symbol.STAR_STAR.getSymbolValue()); 395 Symbol.STAR_STAR.setSymbolValue(Symbol.STAR.getSymbolValue()); 396 Symbol.STAR.setSymbolValue(result); 397 Symbol.PLUS_PLUS_PLUS.setSymbolValue(Symbol.PLUS_PLUS.getSymbolValue()); 398 Symbol.PLUS_PLUS.setSymbolValue(Symbol.PLUS.getSymbolValue()); 399 Symbol.PLUS.setSymbolValue(Symbol.MINUS.getSymbolValue()); 400 out = getStandardOutput(); 401 out.freshLine(); 402 LispObject[] values = thread.getValues(); 403 Symbol.SLASH_SLASH_SLASH.setSymbolValue(Symbol.SLASH_SLASH.getSymbolValue()); 404 Symbol.SLASH_SLASH.setSymbolValue(Symbol.SLASH.getSymbolValue()); 405 if (values != null) { 406 LispObject slash = NIL; 407 for (int i = values.length; i-- > 0;) 408 slash = new Cons(values[i], slash); 409 Symbol.SLASH.setSymbolValue(slash); 410 for (int i = 0; i < values.length; i++) 411 out._writeLine(values[i].printObject()); 412 } else { 413 Symbol.SLASH.setSymbolValue(new Cons(result)); 414 out._writeLine(result.printObject()); 415 } 416 out._finishOutput(); 417 } 418 catch (StackOverflowError e) { 419 getStandardInput().clearInput(); 420 out._writeLine("Stack overflow"); 421 } 422 catch (ControlTransfer c) { 423 // We're on the toplevel, if this occurs, 424 // we're toast... 425 reportError(c, thread); 426 } 427 catch (ProcessingTerminated e) { 428 throw e; 429 } 430 catch (IntegrityError e) { 431 return; 432 } 433 catch (Throwable t) { 434 getStandardInput().clearInput(); 435 out.printStackTrace(t); 436 thread.printBacktrace(); 437 } 438 } 439 } 440 reportError(ControlTransfer c, LispThread thread)441 private static void reportError(ControlTransfer c, LispThread thread) 442 { 443 getStandardInput().clearInput(); 444 Stream out = getStandardOutput(); 445 out.freshLine(); 446 Condition condition = (Condition) c.getCondition(); 447 out._writeLine("Error: unhandled condition: " + 448 condition.princToString()); 449 if (thread != null) 450 thread.printBacktrace(); 451 } 452 reportError(UnhandledCondition c, LispThread thread)453 private static void reportError(UnhandledCondition c, LispThread thread) 454 { 455 getStandardInput().clearInput(); 456 Stream out = getStandardOutput(); 457 out.freshLine(); 458 Condition condition = (Condition) c.getCondition(); 459 out._writeLine("Error: unhandled condition: " + 460 condition.princToString()); 461 if (thread != null) 462 thread.printBacktrace(); 463 } 464 kill(int status)465 public void kill(int status) 466 { 467 if (jlisp) { 468 try { 469 inputStream.close(); 470 } 471 catch (IOException e) { 472 Debug.trace(e); 473 } 474 try { 475 outputStream.close(); 476 } 477 catch (IOException e) { 478 Debug.trace(e); 479 } 480 } else { 481 ((Stream)Symbol.STANDARD_OUTPUT.getSymbolValue())._finishOutput(); 482 ((Stream)Symbol.ERROR_OUTPUT.getSymbolValue())._finishOutput(); 483 System.exit(status); 484 } 485 } 486 dispose()487 public synchronized void dispose() 488 { 489 Debug.trace("Interpreter.dispose"); 490 Debug.assertTrue(interpreter == this); 491 interpreter = null; 492 } 493 494 @Override finalize()495 protected void finalize() throws Throwable 496 { 497 System.err.println("Interpreter.finalize"); 498 } 499 500 public static final class UnhandledCondition extends Error 501 { 502 LispObject condition; 503 UnhandledCondition(LispObject condition)504 UnhandledCondition(LispObject condition) { 505 this.condition = condition; 506 } 507 getCondition()508 public LispObject getCondition() { 509 return condition; 510 } 511 512 @Override getMessage()513 public String getMessage() { 514 String conditionText; 515 LispThread thread = LispThread.currentThread(); 516 SpecialBindingsMark mark = thread.markSpecialBindings(); 517 thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL); 518 try { 519 conditionText = getCondition().princToString(); 520 } catch (Throwable t) { 521 conditionText = "<error printing Lisp condition>"; 522 } finally { 523 thread.resetSpecialBindings(mark); 524 } 525 526 return "Unhandled lisp condition: " + conditionText; 527 } 528 529 530 }; 531 532 private static final Primitive _DEBUGGER_HOOK_FUNCTION = 533 new Primitive("%debugger-hook-function", PACKAGE_SYS, false) 534 { 535 @Override 536 public LispObject execute(LispObject first, LispObject second) 537 { 538 final LispObject condition = first; 539 if (interpreter == null) { 540 final LispThread thread = LispThread.currentThread(); 541 final SpecialBindingsMark mark = thread.markSpecialBindings(); 542 thread.bindSpecial(Symbol.PRINT_ESCAPE, NIL); 543 try { 544 final LispObject truename = 545 Symbol.LOAD_TRUENAME.symbolValue(thread); 546 if (truename != NIL) { 547 final LispObject stream = 548 _LOAD_STREAM_.symbolValue(thread); 549 if (stream instanceof Stream) { 550 final int lineNumber = 551 ((Stream)stream).getLineNumber() + 1; 552 final int offset = 553 ((Stream)stream).getOffset(); 554 Debug.trace("Error loading " + 555 truename.princToString() + 556 " at line " + lineNumber + 557 " (offset " + offset + ")"); 558 } 559 } 560 Debug.trace("Encountered unhandled condition of type " + 561 condition.typeOf().princToString() + ':'); 562 Debug.trace(" " + condition.princToString()); 563 } 564 catch (Throwable t) {} // catch any exception to throw below 565 finally { 566 thread.resetSpecialBindings(mark); 567 } 568 } 569 UnhandledCondition uc = new UnhandledCondition(condition); 570 if (condition.typep(Symbol.JAVA_EXCEPTION) != NIL) 571 uc.initCause((Throwable)JavaException 572 .JAVA_EXCEPTION_CAUSE.execute(condition).javaInstance()); 573 throw uc; 574 } 575 }; 576 readFromString(String s)577 public static final LispObject readFromString(String s) 578 { 579 return new StringInputStream(s).read(true, NIL, false, 580 LispThread.currentThread(), 581 Stream.currentReadtable); 582 } 583 584 // For j. 585 /** Runs its input string through the lisp reader and evaluates the result. 586 * 587 * @param s A string with a valid Common Lisp expression 588 * @return The result of the evaluation 589 * @exception UnhandledCondition in case the an error occurs which 590 * should be passed to the Lisp debugger 591 */ evaluate(String s)592 public static LispObject evaluate(String s) 593 { 594 if (!initialized) 595 initializeJLisp(); 596 StringInputStream stream = new StringInputStream(s); 597 final LispThread thread = LispThread.currentThread(); 598 LispObject obj = null; 599 600 final SpecialBindingsMark mark0 = thread.markSpecialBindings(); 601 thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION); 602 try { // catch possible errors from use of SHARPSIGN_DOT macros in --eval stanzas 603 obj = stream.read(false, EOF, false, thread, 604 Stream.currentReadtable); 605 } finally { 606 thread.resetSpecialBindings(mark0); 607 } 608 if (obj == EOF) 609 return error(new EndOfFile(stream)); 610 611 final SpecialBindingsMark mark = thread.markSpecialBindings(); 612 thread.bindSpecial(Symbol.DEBUGGER_HOOK, _DEBUGGER_HOOK_FUNCTION); 613 try { 614 return Lisp.eval(obj, new Environment(), thread); 615 } 616 finally { 617 thread.resetSpecialBindings(mark); 618 } 619 } 620 621 private static final String build; 622 623 static { 624 String s = null; 625 InputStream in = Interpreter.class.getResourceAsStream("build"); 626 if (in != null) { 627 try { 628 BufferedReader reader = 629 new BufferedReader(new InputStreamReader(in)); 630 s = reader.readLine(); reader.close()631 reader.close(); 632 } 633 catch (IOException e) {} 634 } 635 build = s; 636 } 637 banner()638 private static String banner() 639 { 640 final String sep = System.getProperty("line.separator"); 641 StringBuilder sb = new StringBuilder("Armed Bear Common Lisp "); 642 sb.append(Version.getVersion()); 643 if (build != null) { 644 sb.append(" (built "); 645 sb.append(build); 646 sb.append(')'); 647 } 648 sb.append(sep); 649 sb.append("Java "); 650 sb.append(System.getProperty("java.version")); 651 sb.append(' '); 652 sb.append(System.getProperty("java.vendor")); 653 sb.append(sep); 654 String vm = System.getProperty("java.vm.name"); 655 if (vm != null) { 656 sb.append(vm); 657 sb.append(sep); 658 } 659 return sb.toString(); 660 } help()661 private static String help() 662 { 663 final String sep = System.getProperty("line.separator"); 664 StringBuilder sb = new StringBuilder("Parameters:"); 665 sb.append(sep); 666 sb.append("--help").append(sep) 667 .append(" Displays this message."); 668 sb.append(sep); 669 sb.append("--noinform").append(sep) 670 .append(" Suppresses the printing of startup information and banner."); 671 sb.append(sep); 672 sb.append("--noinit").append(sep) 673 .append(" Suppresses the loading of the '~/.abclrc' startup file."); 674 sb.append(sep); 675 sb.append("--nosystem").append(sep) 676 .append(" Suppresses loading the 'system.lisp' customization file. "); 677 sb.append(sep); 678 sb.append("--eval <FORM>").append(sep) 679 .append(" Evaluates the <FORM> before initializing REPL."); 680 sb.append(sep); 681 sb.append("--load <FILE>").append(sep) 682 .append(" Loads the file <FILE> before initializing REPL."); 683 sb.append(sep); 684 sb.append("--load-system-file <FILE>").append(sep) 685 .append(" Loads the system file <FILE> before initializing REPL."); 686 sb.append(sep); 687 sb.append("--batch").append(sep) 688 .append(" The process evaluates forms specified by arguments and possibly by those").append(sep) 689 .append(" by those in the intialization file '~/.abcl', and then exits."); 690 sb.append(sep); 691 sb.append(sep); 692 sb.append("The occurance of '--' copies the remaining arguments, unprocessed, into").append(sep) 693 .append("the variable EXTENSIONS:*COMMAND-LINE-ARGUMENT-LIST*."); 694 sb.append(sep); 695 696 return sb.toString(); 697 } 698 } 699