package silk; import java.io.*; import java.io.FileNotFoundException; import java.net.URL; import java.net.MalformedURLException; import java.applet.Applet; import java.util.Hashtable; import java.util.Vector; /** This class represents a Scheme interpreter. * See http://www.norvig.com/SILK.html for more documentation. * This is version 2.0. * @author Peter Norvig, Copyright 1998, http://www.norvig.com/license.html * @author Modified by Timothy J. Hickey, http://www.cs.brandeis.edu/~tim **/ public class Scheme extends SchemeUtils { InputPort input; PrintWriter output; public Applet applet; public URL documentbase = null; public GlobalEnvironment environment = new GlobalEnvironment(null, 2000,this); /** Create a Scheme interpreter, the parameters are an applet (possibly null) if Scheme is called from an applet, a documentbase (specifying where to find source files for loading) an array of files to be loaded into the interpreter. **/ public Scheme(Applet applet, URL documentbase) { // new InputStreamReader is to force going to the InputPort that // doesn't do BufferedReader, which breaks under emacs for Rusty! // InputPort input = new InputPort(System.in); this(applet, documentbase, new InputPort(new InputStreamReader(System.in)), new PrintWriter(System.out, true)); } public String[] crack(String s, int delim, int start, Vector v) { return null; } public Scheme(Applet applet, URL documentbase, InputPort input, PrintWriter output) { this.applet = applet; this.documentbase = documentbase; this.input = input; this.output = output; this.output.println("SILK version 3.0, Halloween, 1999\n"); environment.define(Symbol.THISINTERPRETER,this); Primitive.installR5Primitives(environment); Primitive.installSilkPrimitives(environment); Code.installR5Syntax(environment); Code.installSilkSyntax(environment); environment.define(Symbol.THISENVIRONMENT,environment); try { this.output.print("Loading primitives "); long start = System.currentTimeMillis(); this.load(new InputPort(SchemePrimitives.CODE)); this.output.println(" in " + (System.currentTimeMillis() - start) + " ms."); } catch (SchemeException e) { e.printSchemeStackTrace(this.output); } catch (RuntimeException e) { this.output.println("Error in primitives "+ e); e.printStackTrace(this.output); } // Turn debugging on by default during user interaction Code.debugging = true; // If not in an applet eval the scheme.eval property. if (this.applet == null) { String toeval = System.getProperty("scheme.eval"); this.output.println("scheme.eval: " + toeval); if (!(toeval == null)) this.evalString(toeval); } } /** Create a Scheme interpreter and load an array of files into it. * Also load SchemePrimitives.CODE. **/ public Scheme() { this(null,null); } public Applet getApplet() { return this.applet; } public URL getDocumentbase() { return this.documentbase; } public GlobalEnvironment getEnvironment() { return this.environment; } /** Create a new Scheme interpreter, passing in the command line args * as files to load, and then enter a read eval write loop. **/ public static void main(String[] files) { InputPort input = new InputPort(new InputStreamReader(System.in)); PrintWriter output = new PrintWriter(System.out, true); Scheme interp = new Scheme(null, null, input, output); boolean exited = false; try { interp.loadFiles(files); } catch(ExitException e) { exited = true; } if (!exited) interp.readEvalWriteLoop("> "); input.close(); output.close(); System.exit(0); } /** Prompt, read, eval, and write the result. * Also sets up a catch for any RuntimeExceptions encountered. **/ public Object readEvalWriteLoop(String prompt) { return readEvalWriteLoop(prompt, this.input, this.output); } public Object readEvalWriteLoop(String prompt, InputPort input, PrintWriter output) { Object x; Engine eng = new Engine(); Object result = null; this.input = input; this.output = output; while(true) { try { output.print(prompt); output.flush(); x = input.read(); if (x == EOF) return EOF; write(eng.eval(x, environment), output, true); output.println(); output.flush(); } catch (ExitException e) { return e.getValue(); } catch (SchemeException e) { e.printSchemeStackTrace(output); } catch (RuntimeException e) { e.printStackTrace(output); }} } /** Load from files **/ public void loadFiles(String[] files) { long start = System.currentTimeMillis(); if (files != null) { try { for (int i = 0; i < (files == null ? 0 : files.length); i++) this.loadFile(files[i]); output.println("Files loaded in " + (System.currentTimeMillis() - start) + " ms."); } catch (ExitException e) { throw e; } catch (SchemeException e) { e.printSchemeStackTrace(this.output); } catch (Exception e) { e.printStackTrace(this.output); }}} /** Eval all the expressions in a file. Calls load(openInputPort(name)). **/ public Object loadFile(String name) { InputPort ip = openInputPort(name); if (ip == null) return error("File not found: " + name); else { this.load(ip); return FALSE; }} /** Open a URL that is either absolute or relative the the documentbase if Scheme is run from an Applet. **/ public InputPort openURL(String name) { URL u = null; try { u = new URL(this.documentbase, name); } catch (MalformedURLException e) { if (this.documentbase != null) try { u = new URL(name); } catch (MalformedURLException e1) { return null; }} if (u == null) return null; else { InputPort i = null; try { i = new InputPort(u.openStream()); } catch (IOException e3) {return null; } return i;}} /** Open a resource that some class loader knows about. **/ public InputPort openResource(String name) { InputStream s = JavaArgs.getLoader().getResourceAsStream(name); if (s == null) return null; else return new InputPort(s);} /** Search direct, then global variable scheme.roots for File named name. Returns null if running in an applet. **/ public String fullPath(String name) { if (applet != null) return null; else { File it1 = new File(name); if (it1.exists()) return it1.getAbsolutePath(); Object roots = this.getGlobal("scheme.roots"); if (roots == Environment.UNDEFINED) return null; while (roots != null) { File it2 = new File(stringify(first(roots), false).toString(), name); if (it2.exists()) return it2.toString(); roots = rest(roots); } return null; }} /** Open a file using the above two methods. **/ public InputPort openFile(String name) { String path = this.fullPath(name); if (path == null) return null; try { return new InputPort(new FileInputStream(path)); } catch (FileNotFoundException e) { return null; }} /** Open a port trying all the open methods. First file, then resource, then url. This lets a file override a file in the jar file. **/ public InputPort openInputPort(String name) { InputPort ip1 = openFile(name); if (!(ip1 == null)) return ip1; else { InputPort ip2 = openResource(name); if (!(ip2 == null)) return ip2; else { InputPort ip3 = openURL(name); if (!(ip3 == null)) return ip3; else return null; }}} /** The following methods provides convenient access to Scheme from Java.**/ /** Get the value of the global variable named name. **/ public Object getGlobal (String name) { return this.environment.get(Symbol.intern(name)); } /** Set the value of the global variable named name to value. **/ public void setGlobal (String name, Object value) { this.environment.define(Symbol.intern(name), value); } /** Eval all the expressions coming from an InputPort. **/ public Object load(InputPort in) { Environment environment = this.environment; Engine eng = new Engine(); Object x = null; for(;;) { if ((x = in.read()) == EOF) return TRUE; eng.eval(x, environment); }} /** Eval all expressions read from the String. **/ public Object load(String s) { return this.load(new InputPort(new StringReader(s))); } /** Evaluate an s-expression **/ public Object eval(Object expression) { return (new Engine()).eval(expression, this.environment); } /** Evaluate a String or Object. **/ public Object evalString(String s) { return this.eval((new InputPort(s)).read()); } /** read from the string, s **/ public Object readString(String s) { return (new InputPort(s)).read(); } /** Apply a Scheme Procedure to a list of arguments. **/ public static Object apply(Procedure p, Pair args) { return (new Engine()).apply(p, args); } }