123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- package kawa;
- import gnu.mapping.*;
- import gnu.expr.*;
- import java.io.*;
- import gnu.lists.*;
- import gnu.bytecode.ArrayClassLoader;
- import gnu.bytecode.ZipLoader;
- import gnu.kawa.format.AbstractFormat;
- import gnu.kawa.io.BinaryInPort;
- import gnu.kawa.io.FilePath;
- import gnu.kawa.io.InPort;
- import gnu.kawa.io.NBufferedInputStream;
- import gnu.kawa.io.OutPort;
- import gnu.kawa.io.Path;
- import gnu.kawa.io.TtyInPort;
- import gnu.kawa.util.ExitCalled;
- import gnu.kawa.util.Signals;
- import gnu.text.Lexer;
- import gnu.text.SourceMessages;
- import gnu.text.SyntaxException;
- import java.net.URL;
- /** Utility functions (static methods) for kawa.repl.
- * It also contains methods for file loading (source and compiled).
- */
- public class Shell
- {
- /* #ifdef JAVA2 */
- public static ThreadLocal currentLoadPath = new ThreadLocal();
- /* #else */
- // public static Location currentLoadPath =
- // Location.make(null, "load-path");
- /* #endif */
- private static Class[] noClasses = { };
- private static Class[] boolClasses = { Boolean.TYPE };
- private static Class[] lispPushClasses = { OutPort.class, Character.TYPE, Boolean.TYPE };
- private static Class[] xmlPrinterClasses = {Consumer.class, Object.class };
- private static Class[] httpPrinterClasses = {OutPort.class };
- private static Object consumerArg = "(consumer)";
- /** A table of names of known output formats.
- * For each entry, the first Object is the format name.
- * The next entries are a class name, the name of a static method in that
- * class, and the parameter types (as a Class[] suitable for getMethod).
- * The remain values are arguments (passed to invoke), except that if an
- * argument is the spacial value consumerArg, it is replaced by the
- * destination Consumer. */
-
- static Object[][] formats = {
- { "scheme", "gnu.kawa.functions.DisplayFormat",
- "getSchemeFormat", boolClasses,
- Boolean.FALSE },
- { "readable-scheme", "gnu.kawa.functions.DisplayFormat",
- "getSchemeFormat", boolClasses,
- Boolean.TRUE },
- { "elisp", "gnu.kawa.functions.DisplayFormat",
- "getEmacsLispFormat", boolClasses,
- Boolean.FALSE },
- { "readable-elisp", "gnu.kawa.functions.DisplayFormat",
- "getEmacsLispFormat", boolClasses,
- Boolean.TRUE },
- { "clisp", "gnu.kawa.functions.DisplayFormat",
- "getCommonLispFormat", boolClasses,
- Boolean.FALSE },
- { "readable-clisp", "gnu.kawa.functions.DisplayFormat",
- "getCommonLispFormat", boolClasses,
- Boolean.TRUE },
- { "commonlisp", "gnu.kawa.functions.DisplayFormat",
- "getCommonLispFormat", boolClasses,
- Boolean.FALSE },
- { "readable-commonlisp", "gnu.kawa.functions.DisplayFormat",
- "getCommonLispFormat", boolClasses,
- Boolean.TRUE },
- { "xml", "gnu.xml.XMLPrinter",
- "make", xmlPrinterClasses,
- consumerArg, null },
- { "html", "gnu.xml.XMLPrinter",
- "make", xmlPrinterClasses,
- consumerArg, "html" },
- { "xhtml", "gnu.xml.XMLPrinter",
- "make", xmlPrinterClasses,
- consumerArg, "xhtml" },
- { "cgi", "gnu.kawa.xml.HttpPrinter",
- "make", httpPrinterClasses,
- consumerArg },
- { "ignore", "gnu.lists.VoidConsumer",
- "make", new Class[] { Consumer.class },
- consumerArg },
- { null }
- };
- public static String defaultFormatName;
- public static Object[] defaultFormatInfo;
- public static java.lang.reflect.Method defaultFormatMethod;
- /** Specify the default output format.
- * @param name The name of the format, as an entry in the formats table.
- */
- public static void setDefaultFormat(String name)
- {
- name = name.intern();
- defaultFormatName = name;
- for (int i = 0; ; i++)
- {
- Object[] info = formats[i];
- Object iname = info[0];
- if (iname == null)
- {
- System.err.println ("kawa: unknown output format '"+name+"'");
- System.exit (-1);
- }
- else if (iname == name)
- {
- defaultFormatInfo = info;
- try
- {
- Class formatClass = Class.forName((String) info[1]);
- defaultFormatMethod
- = formatClass.getMethod((String) info[2], (Class[]) info[3]);
-
- }
- catch (Throwable ex)
- {
- System.err.println("kawa: caught "+ex+" while looking for format '"+name+"'");
- System.exit (-1);
- }
- break;
- }
- }
- if (! defaultFormatInfo[1].equals("gnu.lists.VoidConsumer"))
- ModuleBody.setMainPrintValues(true);
- }
- /** Return a Consumer that formats using the appropriate format.
- * The format is chosen depending on specified defaults.
- * @param out The output where formatted output is sent to.
- */
- public static Consumer getOutputConsumer(OutPort out)
- {
- Object[] info = defaultFormatInfo;
- if (out == null)
- return VoidConsumer.getInstance();
- else if (info == null)
- return Language.getDefaultLanguage().getOutputConsumer(out);
- try
- {
- Object args[] = new Object[info.length - 4];
- System.arraycopy(info, 4, args, 0, args.length);
- boolean useConsumer = args[0] == consumerArg;
- for (int i = args.length; --i >= 0; ) {
- if (args[i] == consumerArg)
- args[i] = out;
- }
- Object format = defaultFormatMethod.invoke(null, args);
- if (format instanceof AbstractFormat)
- return ((AbstractFormat) format).makeConsumer(out);
- else
- return (Consumer) format;
- }
- catch (Exception ex)
- {
- throw new RuntimeException("cannot get output-format '"
- + defaultFormatName + "' - caught " + ex);
- }
- }
- public static boolean run (Language language, Environment env)
- {
- InPort inp = InPort.inDefault ();
- SourceMessages messages = new SourceMessages();
- OutPort perr;
- if (inp instanceof TtyInPort) // Interactive?
- {
- ((TtyInPort)inp).setPrompter(defaultPrompter);
- perr = OutPort.errDefault();
- }
- else
- perr = null; // Non-interactive.
- Throwable ex = run(language, env, inp, OutPort.outDefault(),
- perr, messages);
- if (ex == null)
- return true;
- printError(ex, messages, OutPort.errDefault());
- return false;
- }
- public static Throwable run (Language language, Environment env,
- InPort inp, OutPort pout, OutPort perr,
- SourceMessages messages)
- {
- Consumer out = getOutputConsumer(pout);
- return run(language, env, inp, out, perr, null, messages);
- }
- public static boolean run (Language language, Environment env,
- InPort inp, Consumer out, OutPort perr,
- URL url)
- {
- SourceMessages messages = new SourceMessages();
- Throwable ex = run(language, env, inp, out, perr, url, messages);
- if (ex != null)
- printError(ex, messages, perr);
- return ex == null;
- }
- public static Throwable run (Language language, Environment env,
- InPort inp, Consumer out, OutPort perr,
- java.net.URL url, SourceMessages messages)
- {
- Language saveLanguage = Language.setSaveCurrent(language);
- Lexer lexer = language.getLexer(inp, messages);
- boolean interactive = inp instanceof TtyInPort;
- lexer.setInteractive(interactive);
- CallContext ctx = CallContext.getInstance();
- Consumer saveConsumer = null;
- if (out != null)
- {
- saveConsumer = ctx.consumer;
- ctx.consumer = out;
- }
- try
- {
- Thread thread = Thread.currentThread();
- ClassLoader parentLoader = thread.getContextClassLoader();
- // Create a "session" ClassLoader. Use this for remembering classes
- // created in one command (Compilation) through further command,
- // while still allowing the classes to be replaced and collected.
- if (!(parentLoader instanceof ArrayClassLoader))
- thread.setContextClassLoader(new ArrayClassLoader(parentLoader));
- }
- catch (SecurityException ex)
- {
- // Nothing - we'll just lose some minor functionality.
- }
- java.lang.reflect.Method parserMethod = getJLineParserMethod(inp);
- Environment saveEnv = Environment.setSaveCurrent(env);
- try
- {
- SigIntHandler sigIntHandler = null;
- if (interactive) {
- sigIntHandler = new SigIntHandler();
- ((TtyInPort) inp).sigIntHandler = sigIntHandler;
- }
- for (;;)
- {
- Object oldIntHandler = null;
- int opts = Language.PARSE_FOR_EVAL|Language.PARSE_ONE_LINE|Language.PARSE_INTERACTIVE_MODULE;
- try
- {
- Compilation comp;
- if (interactive)
- oldIntHandler =
- Signals.register("INT",
- ((TtyInPort) inp).sigIntHandler);
- if (parserMethod != null) {
- try {
- comp = (Compilation) parserMethod.invoke(null, language, lexer);
- } catch (java.lang.reflect.InvocationTargetException ex) {
- throw ex.getTargetException();
- }
- } else {
- for (;;) {
- try {
- comp = language.parse(lexer, opts, null);
- break;
- } catch (TtyInPort.MoreInputNeeded ex) {
- }
- }
- }
- boolean sawError;
- if (interactive) {
- sawError = messages.checkErrors(perr, Compilation.maxErrors());
- perr.flush();
- }
- else if (messages.seenErrors())
- throw new SyntaxException(messages);
- else
- sawError = false;
- if (comp == null) // ??? end-of-file
- break;
- if (sawError) {
- comp.lexical.pop(comp.mainLambda);
- continue;
- }
- if (! ModuleExp.evalModule(env, ctx, comp, url, perr))
- throw new SyntaxException(messages);
- if (out instanceof Writer)
- ((Writer) out).flush();
- if (inp.eofSeen())
- break;
- }
- catch (ThreadDeath e)
- {
- if (! interactive)
- throw e;
- else if (sigIntHandler == null || sigIntHandler.trace == null)
- e.printStackTrace(perr);
- else
- sigIntHandler.trace.printStackTrace(perr);
- Thread.interrupted();
- }
- catch (Error e)
- {
- throw e;
- }
- catch (Throwable e)
- {
- if (! interactive)
- return e;
- printError(e, messages, perr);
- }
- finally
- {
- if (oldIntHandler != null)
- Signals.unregister("INT", oldIntHandler);
- }
- }
- }
- finally
- {
- Environment.restoreCurrent(saveEnv);
- if (out != null)
- ctx.consumer = saveConsumer;
- Language.restoreCurrent(saveLanguage);
- }
- return null;
- }
- static java.lang.reflect.Method getJLineParserMethod(InPort in) {
- Class cls = in.getClass();
- try {
- if (cls.getName().equals("gnu.kawa.io.JLineInPort")) {
- cls = Class.forName("gnu.kawa.io.JLineInPort$KawaParsedLine");
- return cls.getDeclaredMethod("parse",
- Language.class, Lexer.class);
- }
- } catch (Throwable ex) {
- }
- return null;
- }
- public static void printError (Throwable ex, SourceMessages messages,
- OutPort perr)
- {
- if (ex instanceof WrongArguments)
- {
- WrongArguments e = (WrongArguments) ex;
- messages.printAll(perr, Compilation.maxErrors());
- if (e.usage != null)
- perr.println("usage: "+e.usage);
- e.printStackTrace(perr);
- }
- /*
- else if (ex instanceof java.io.IOException)
- {
- messages.printAll(perr, Compilation.maxErrors());
- String msg = new SourceError(inp, 'e', "").toString();
- msg = msg.substring(0, msg.length() - 2);
- perr.println(msg + " (or later): caught IOException");
- ex.printStackTrace(perr);
- }
- */
- else
- {
- SyntaxException se;
- if (ex instanceof SyntaxException
- && (se = (SyntaxException) ex).getMessages() == messages)
- {
- se.printAll(perr, Compilation.maxErrors());
- se.clear();
- }
- else
- {
- messages.printAll(perr, Compilation.maxErrors());
- ex.printStackTrace(perr);
- }
- }
- perr.flush();
- }
- public final static CompiledModule checkCompiledZip (InputStream fs, Path path, Environment env, Language language)
- throws IOException
- {
- try
- {
- fs.mark(5);
- boolean isZip = (fs.read() == 'P' && fs.read() == 'K'
- && fs.read() == '\003' && fs.read() == '\004');
- fs.reset();
- if (! isZip)
- return null;
- }
- catch (IOException ex)
- {
- return null;
- }
- fs.close ();
- Environment orig_env = Environment.getCurrent();
- String name = path.toString();
- try
- {
- if (env != orig_env)
- Environment.setCurrent(env);
- File zfile = path.toFile();
- if (zfile == null)
- throw new RuntimeException ("load: "+name+" - not a file path");
- if (!zfile.exists ())
- throw new RuntimeException ("load: "+name+" - not found");
- if (!zfile.canRead ())
- throw new RuntimeException ("load: "+name+" - not readable");
- ZipLoader loader = new ZipLoader (name);
- Class clas = loader.loadAllClasses();
- return CompiledModule.make(clas, language);
- }
- catch (java.io.IOException ex)
- {
- throw new WrappedException ("load: "+name+" - "+ex.toString (), ex);
- }
- finally
- {
- if (env != orig_env)
- Environment.setCurrent(orig_env);
- }
- }
- static InPort openFile(InputStream fs, Path path) throws IOException {
- Object conv = OutPort.charEncoding.get(null);
- InPort src;
- if (conv == null || conv == Boolean.TRUE)
- return BinaryInPort.openHeuristicFile(fs, path);
- else
- return InPort.openFile(fs, path, conv);
- }
- /** Run a named source file, compiled .zip, or class.
- * We try in order if {@code fname} names a compiled zip file,
- * or names some other file (in which case it is assumed to be source),
- * or is the name of a class in the classpath.
- * @param lineByLine Should we read and evaluate a source file line-by-line
- * (i.e. read and evaluate each line before reading the next one),
- * or should be read and compile the whole file as a module before
- * running it? Only used when parsing a source file.
- * @param skipLines If reading a source file, the number of initial
- * lines to skip before beginning parsing.
- * @return True on success, false on failure.
- */
- public static boolean runFileOrClass (String fname,
- boolean lineByLine, int skipLines)
- {
- Language language = Language.getDefaultLanguage();
- try
- {
- Path path;
- InputStream fs;
- if (fname.equals("-"))
- {
- path = Path.valueOf("/dev/stdin");
- fs = System.in;
- }
- else
- {
- path = Path.valueOf(fname);
- fs = path.openInputStream();
- }
- try
- {
- Environment env = Environment.getCurrent();
- return runFile(fs, path, env, lineByLine, skipLines);
- }
- catch (Error e)
- {
- throw e;
- }
- catch (Throwable e)
- {
- e.printStackTrace(System.err);
- return false;
- }
- }
- catch (Error e)
- {
- throw e;
- }
- catch (Throwable e)
- {
- Class clas;
- try
- {
- clas = Class.forName(fname);
- }
- catch (Exception ex)
- {
- System.err.println("Cannot read file "+e.getMessage());
- return false;
- }
- try
- {
- runClass(clas, Environment.getCurrent());
- return true;
- }
- catch (Error ex)
- {
- throw ex;
- }
- catch (Throwable ex)
- {
- //ExitCalled.check(e);
- ex.printStackTrace();
- return false;
- }
- }
- }
- public static void runClass(Class clas, Environment env) throws Throwable {
- CompiledModule cmodule = CompiledModule.make(clas, Language.getDefaultLanguage());
- cmodule.evalModule(env, OutPort.outDefault());
- }
- public static final boolean runFile(InputStream fs, Path path,
- Environment env,
- boolean lineByLine, int skipLines)
- throws Throwable {
- if (! (fs instanceof BufferedInputStream)
- && ! (fs instanceof NBufferedInputStream))
- fs = new NBufferedInputStream(fs);
- Language language = Language.getDefaultLanguage();
- Path savePath = (Path) currentLoadPath.get();
- try {
- currentLoadPath.set(path);
- CompiledModule cmodule = checkCompiledZip(fs, path, env, language);
- if (cmodule == null) {
- InPort src = openFile(fs, path);
- while (--skipLines >= 0)
- src.skipRestOfLine();
- try {
- SourceMessages messages = new SourceMessages();
- URL url = path.toURL();
- OutPort perr = OutPort.errDefault();
- if (lineByLine) {
- boolean print = ModuleBody.getMainPrintValues();
- Consumer out
- = (print ? getOutputConsumer(OutPort.outDefault())
- : new VoidConsumer());
- Throwable ex
- = run(language, env, src, out, perr, url, messages);
- if (ex instanceof SyntaxException
- && ((SyntaxException) ex).getMessages() == messages) {
- messages.printAll(perr, Compilation.maxErrors());
- perr.flush();
- return false;
- }
- if (ex != null)
- throw ex;
- } else {
- cmodule = compileSource(src, env, url, language,
- messages, perr);
- if (cmodule == null)
- return false;
- }
- } finally {
- src.close();
- }
- }
- if (cmodule != null)
- cmodule.evalModule(env, OutPort.outDefault());
- } finally {
- currentLoadPath.set(savePath);
- }
- return true;
- }
- /** Parse and compile a module from the given port.
- * Return null on error, which gets reported to {@code messages}.
- */
- static CompiledModule compileSource(InPort port, Environment env, URL url,
- Language language,
- SourceMessages messages, OutPort perr)
- throws SyntaxException, IOException {
- ModuleManager manager = ModuleManager.getInstance();
- ModuleInfo minfo = manager.findWithSourcePath(port.getName());
- Lexer lexer = language.getLexer(port, messages);
- try {
- Compilation comp
- = language.parse(lexer, Language.PARSE_IMMEDIATE, minfo);
- CallContext ctx = CallContext.getInstance();
- //ctx.values = Values.noArgs;
- Object inst = ModuleExp.evalModule1(env, comp, url, null);
- messages.printAll(perr, Compilation.maxErrors());
- perr.flush();
- if (inst == null || messages.seenErrors())
- return null;
- return new CompiledModule(comp.getModule(), inst, language);
- }
- catch (Error ex) {
- throw ex;
- }
- catch (Throwable ex) {
- if (! (ex instanceof SyntaxException)
- || ((SyntaxException) ex).getMessages() != messages) {
- lexer.error('e', "unexpected exception while compiling: "+ex);
- messages.printAll(perr, Compilation.maxErrors());
- ex.printStackTrace(perr);
- }
- else
- messages.printAll(perr, Compilation.maxErrors());
- return null;
- }
- }
- public static final Procedure1 defaultPrompter = new Prompter();
- static class Prompter extends Procedure1 {
- public Object apply1 (Object arg) {
- return ((TtyInPort) arg).defaultPrompt();
- }
- }
- static class SigIntHandler implements Runnable {
- public Thread thread;
- public Error trace;
- public SigIntHandler(Thread thread) { this.thread = thread; }
- public SigIntHandler() { this.thread = Thread.currentThread(); }
- public void run() {
- Error ex = new Error("user interrupt of "+thread);
- ex.setStackTrace(thread.getStackTrace());
- this.trace = ex;
- thread.stop();
- }
- };
- }
|