import java.io.*; import java.math.BigInteger; /** * This is the abstract base class for the SchemeObject hierarchy. **/ public abstract class SchemeObject { /** * Dealing with the static type system of Java is not convenient * when manipulating SchemeObjects because Scheme itself is * denamically typed. To make manipulating Scheme objects and * lists in particular more convenient we make all SchemeObjects * provide a getCar() and getCdr() * method. If the object is not a SchemePair then * these methods should through a ClassCastException. As such, * the following are equivalent: *
     * SchemeObject o = ...;
     *
     * System.out.println(o.getCar());
     * 
* and *
     * SchemeObject o = ...;
     *
     * Scheme.out.println(((SchemePair)o).getCar());
     * 
* @return The car of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair **/ public SchemeObject getCar() { throw new ClassCastException("Took car of " + this); } /** * Dealing with the static type system of Java is not convenient * when manipulating SchemeObjects because Scheme itself is * denamically typed. To make manipulating Scheme objects and * lists in particular more convenient we make all SchemeObjects * provide a getCar() and getCdr() * method. If the object is not a SchemePair then * these methods should through a ClassCastException. As such, * the following are equivalent: *
     * SchemeObject o = ...;
     *
     * System.out.println(o.getCdr());
     * 
* and *
     * SchemeObject o = ...;
     *
     * Scheme.out.println(((SchemePair)o).getCdr());
     * 
* @return The cdr of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair **/ public SchemeObject getCdr() { throw new RuntimeException("Took cdr of " + this); } /** * This calls getCar() and/or getCdr() * @return The car of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject car() { return getCar(); } /** * This calls getCar() and/or getCdr() * @return The cdr of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject cdr() { return getCdr(); } /** * This calls getCar() and/or getCdr() * @return The cadr of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject cadr() { return getCdr().getCar(); } /** * This calls getCar() and/or getCdr() * @return The caddr of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject caddr() { return getCdr().getCdr().getCar(); } /** * This calls getCar() and/or getCdr() * @return The cddr of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject cddr() { return getCdr().getCdr(); } /** * This calls getCar() and/or getCdr() * @return The cadddr of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject cadddr() { return getCdr().getCdr().getCdr().getCar(); } /** * This calls getCar() and/or getCdr() * @return The cdar of the SchemePair. * @throws ClassCastException if this object does not override this method. * @see SchemePair * @see #getCar() * @see #getCdr() **/ public SchemeObject cdar() { return getCar().getCdr(); } /** * This reads in a char from input and throws an IOException on EOF. * @param in InputStream to read from. * @return Character read. * @throws EOFException on EOF. * @throws IOException on other IO read error. * @see InputStream **/ private static char readChar(InputStream in) throws IOException { int ch = in.read(); if (ch < 0) throw new EOFException(); return ((char) ch); } /** * This reads in a SchemeObject from the String. * @param s String to read a SchemeObject from. * @see #read(InputStream inputStream) * @see StringInputStream */ public static SchemeObject read(String s) throws IOException,EndOfSchemeListException { return read(new PushbackInputStream(new StringInputStream(s))); } /** * This reads in a SchemeObject from the * InputStream that is passed in. Subsequent calls * to read with this InputStream are only guaranteed * to work correctly if this InputStream is in fact * a PushbackInputStream. To get a * PushbackInputStream * from an InputStream (for example System.in) * use new PushbackInputStream(System.in). * * @return SchemeObject represting what was read in. * * @param inputStream The InputStream to read the * SchemeObject from. * * @throws IOException if an there is an error reading from the * stream. * * @throws EndOfSchemeListException if there is an unexpected end * of a list expression. For example: ")". * * @see java.io.InputStream * @see java.io.PushbackInputStream **/ public static SchemeObject read(InputStream inputStream) throws IOException, EndOfSchemeListException { char ch; // The read() methods below need a PushbackInputStream because // they need to put characters already read back into the // InputStream. PushbackInputStream in; if (inputStream instanceof PushbackInputStream) in = (PushbackInputStream)inputStream; else in = new PushbackInputStream(inputStream,1); // skip whitespace. for(ch = readChar(in); Character.isWhitespace(ch); ch = readChar(in)) ; if (ch == '(') // beginning of a list return readList(in); if (ch == '"') // beginning of a string. return readString(in); if (ch == '\'') // quoted expression. { SchemeObject o = read(in); return new SchemePair(SchemeSymbol.getSymbol("quote"),new SchemePair(o,SchemeNull.getNull())); } if (ch == '#') // beginning of boolean or character { // (characters are not supported) String s = readSymbol(in); if ("t".equalsIgnoreCase(s)) return SchemeBoolean.getTrue(); if ("f".equalsIgnoreCase(s)) return SchemeBoolean.getFalse(); // if (s.charAt(0) == '\\') // return SchemeChar.getChar(s.charAt(1)); return SchemeSymbol.getSymbol("#" + s); } if (ch == ')') // unexpected ')', error! throw new EndOfSchemeListException(); if (ch == ';') // comment, ignore rest of line. { // skip line for (ch = (char)readChar(in); ch != '\n'; ch = (char)readChar(in)) ; return read(in); } else // some sort of symbol or number. { in.unread(ch); // pushback the character read. String s = readSymbol(in); /* * parse symbol as symbol or number. */ if (s != ".") { try { return new SchemeInteger(new BigInteger(s)); } catch (NumberFormatException e) {} try { return new SchemeDouble(Double.parseDouble(s)); } catch (NumberFormatException e) {} } return SchemeSymbol.getSymbol(s); } } /** * This reads in a list expression assuming that the initial '(' * has already been read. It returns a SchemeList * that is either SchemePair or SchemeNull. * * @return SchemeList representing the partial list that was read in. * * @param in The PushbackInputStream to read the * scheme list from. * * @throws IOException if an there is an error reading from the * stream. * * @throws EndOfSchemeListException if there is an unexpected end * of a list expression. For example: "1 2 ')". **/ private static SchemeList readList(PushbackInputStream in) throws IOException, EndOfSchemeListException { try { SchemeObject o = read(in); SchemeObject p = readList(in); if (p != SchemeNull.getNull() && SchemeSymbol.getSymbol(".") == p.getCar()) p = p.cadr(); return new SchemePair(o,p); } catch(EndOfSchemeListException e) { return SchemeNull.getNull(); } } /** * This reads in a string expression assuming that the initial '"' * has already been read. It returns a SchemeString. * * @return SchemeString representing the string that was read in. * * @param in The PushbackInputStream to read the * scheme string from. * * @throws IOException if an there is an error reading from the * stream. **/ private static SchemeString readString(PushbackInputStream in) throws IOException { StringBuffer sb = new StringBuffer(); for (char ch = readChar(in); ch != '"'; ch = readChar(in)) { if (ch == '\\') { ch = readChar(in); switch (ch) { case 'n': ch = '\n'; break; case 't': ch = '\t'; break; case 'r': ch = '\r'; break; } sb.append(ch); } else sb.append(ch); } return new SchemeString(sb.toString()); } /** * This reads in a symbol expression and returns it as a * String. * * @return String representing the symbol that was read in. This * may be a number. If so, it should be converted to a * SchemeNumber * * @param in The PushbackInputStream to read the * scheme string from. * * @throws IOException if an there is an error reading from the * stream. **/ private static String readSymbol(PushbackInputStream in) throws IOException { StringBuffer sb = new StringBuffer(); for(char ch = readChar(in); !Character.isWhitespace(ch); ch = readChar(in)) { if (ch == ')' || ch == '(') { in.unread(ch); break; } sb.append(ch); } return sb.toString(); } }