0001: /*
0002: * Stream.java
0003: *
0004: * Copyright (C) 2003-2004 Peter Graves
0005: * $Id: Stream.java,v 1.82 2004/09/18 20:27:28 piso Exp $
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: */
0021:
0022: package org.armedbear.lisp;
0023:
0024: import java.io.BufferedInputStream;
0025: import java.io.BufferedOutputStream;
0026: import java.io.BufferedReader;
0027: import java.io.IOException;
0028: import java.io.InputStream;
0029: import java.io.InputStreamReader;
0030: import java.io.OutputStream;
0031: import java.io.OutputStreamWriter;
0032: import java.io.PrintWriter;
0033: import java.io.PushbackReader;
0034: import java.io.StringWriter;
0035: import java.io.Writer;
0036: import java.math.BigInteger;
0037: import java.util.BitSet;
0038:
0039: public class Stream extends LispObject {
0040: protected LispObject elementType;
0041: protected boolean isInputStream;
0042: protected boolean isOutputStream;
0043: protected boolean isCharacterStream;
0044: protected boolean isBinaryStream;
0045:
0046: private boolean interactive;
0047: private boolean open = true;
0048:
0049: // Character input.
0050: private PushbackReader reader;
0051: protected int offset;
0052: protected int lineNumber;
0053:
0054: // Character output.
0055: private Writer writer;
0056:
0057: // The number of characters on the current line of output (-1 if unknown).
0058: protected int charPos;
0059:
0060: // Binary input.
0061: private BufferedInputStream in;
0062:
0063: // Binary output.
0064: private BufferedOutputStream out;
0065:
0066: protected Stream() {
0067: }
0068:
0069: // Input stream constructors.
0070: public Stream(InputStream inputStream, LispObject elementType) {
0071: this .elementType = elementType;
0072: if (elementType == Symbol.CHARACTER
0073: || elementType == Symbol.BASE_CHAR) {
0074: isCharacterStream = true;
0075: InputStreamReader inputStreamReader;
0076: try {
0077: inputStreamReader = new InputStreamReader(inputStream,
0078: "ISO-8859-1");
0079: } catch (java.io.UnsupportedEncodingException e) {
0080: Debug.trace(e);
0081: inputStreamReader = new InputStreamReader(inputStream);
0082: }
0083: reader = new PushbackReader(new BufferedReader(
0084: inputStreamReader), 2);
0085: } else {
0086: isBinaryStream = true;
0087: in = new BufferedInputStream(inputStream);
0088: }
0089: isInputStream = true;
0090: isOutputStream = false;
0091: }
0092:
0093: public Stream(InputStream inputStream, LispObject elementType,
0094: boolean interactive) {
0095: this (inputStream, elementType);
0096: setInteractive(interactive);
0097: }
0098:
0099: // Output stream constructors.
0100: public Stream(OutputStream outputStream, LispObject elementType) {
0101: this .elementType = elementType;
0102: if (elementType == Symbol.CHARACTER
0103: || elementType == Symbol.BASE_CHAR) {
0104: isCharacterStream = true;
0105: try {
0106: writer = new OutputStreamWriter(outputStream,
0107: "ISO-8859-1");
0108: } catch (java.io.UnsupportedEncodingException e) {
0109: Debug.trace(e);
0110: writer = new OutputStreamWriter(outputStream);
0111: }
0112: } else {
0113: isBinaryStream = true;
0114: out = new BufferedOutputStream(outputStream);
0115: }
0116: isInputStream = false;
0117: isOutputStream = true;
0118: }
0119:
0120: public Stream(OutputStream outputStream, LispObject elementType,
0121: boolean interactive) {
0122: this (outputStream, elementType);
0123: setInteractive(interactive);
0124: }
0125:
0126: public boolean isInputStream() throws ConditionThrowable {
0127: return isInputStream;
0128: }
0129:
0130: public boolean isOutputStream() throws ConditionThrowable {
0131: return isOutputStream;
0132: }
0133:
0134: public boolean isCharacterInputStream() throws ConditionThrowable {
0135: return isCharacterStream && isInputStream;
0136: }
0137:
0138: public boolean isBinaryInputStream() throws ConditionThrowable {
0139: return isBinaryStream && isInputStream;
0140: }
0141:
0142: public boolean isCharacterOutputStream() throws ConditionThrowable {
0143: return isCharacterStream && isOutputStream;
0144: }
0145:
0146: public boolean isBinaryOutputStream() throws ConditionThrowable {
0147: return isBinaryStream && isOutputStream;
0148: }
0149:
0150: public boolean isInteractive() {
0151: return interactive;
0152: }
0153:
0154: public void setInteractive(boolean b) {
0155: interactive = b;
0156: }
0157:
0158: public boolean isOpen() {
0159: return open;
0160: }
0161:
0162: public void setOpen(boolean b) {
0163: open = b;
0164: }
0165:
0166: public LispObject typeOf() {
0167: return Symbol.STREAM;
0168: }
0169:
0170: public LispClass classOf() {
0171: return BuiltInClass.STREAM;
0172: }
0173:
0174: public LispObject typep(LispObject typeSpecifier)
0175: throws ConditionThrowable {
0176: if (typeSpecifier == Symbol.STREAM)
0177: return T;
0178: if (typeSpecifier == BuiltInClass.STREAM)
0179: return T;
0180: return super .typep(typeSpecifier);
0181: }
0182:
0183: public LispObject getElementType() throws ConditionThrowable {
0184: return elementType;
0185: }
0186:
0187: // Character input.
0188: public int getOffset() {
0189: return offset;
0190: }
0191:
0192: // Character input.
0193: public int getLineNumber() {
0194: return lineNumber;
0195: }
0196:
0197: protected void setWriter(Writer writer) {
0198: this .writer = writer;
0199: }
0200:
0201: // Character output.
0202: public int getCharPos() {
0203: return charPos;
0204: }
0205:
0206: // Character output.
0207: public void setCharPos(int n) {
0208: charPos = n;
0209: }
0210:
0211: public LispObject read(boolean eofError, LispObject eofValue,
0212: boolean recursive) throws ConditionThrowable {
0213: LispObject result = readPreservingWhitespace(eofError,
0214: eofValue, recursive);
0215: if (result != eofValue && !recursive) {
0216: if (_charReady()) {
0217: int n = _readChar();
0218: if (n >= 0) {
0219: char c = (char) n;
0220: if (!currentReadtable().isWhitespace(c))
0221: _unreadChar(c);
0222: }
0223: }
0224: }
0225: if (_READ_SUPPRESS_.symbolValueNoThrow() != NIL)
0226: return NIL;
0227: else
0228: return result;
0229: }
0230:
0231: public LispObject readPreservingWhitespace(boolean eofError,
0232: LispObject eofValue, boolean recursive)
0233: throws ConditionThrowable {
0234: final Readtable rt = currentReadtable();
0235: while (true) {
0236: int n = _readChar();
0237: if (n < 0) {
0238: if (eofError)
0239: return signal(new EndOfFile(this ));
0240: else
0241: return eofValue;
0242: }
0243: char c = (char) n;
0244: if (rt.isWhitespace(c))
0245: continue;
0246: LispObject result = processChar(c);
0247: if (result != null)
0248: return result;
0249: }
0250: }
0251:
0252: private LispObject processChar(char c) throws ConditionThrowable {
0253: LispObject handler = currentReadtable().getReaderMacroFunction(
0254: c);
0255: if (handler instanceof ReaderMacroFunction)
0256: return ((ReaderMacroFunction) handler).execute(this , c);
0257: if (handler != null && handler != NIL)
0258: return handler.execute(this , LispCharacter.getInstance(c));
0259: return readToken(c);
0260: }
0261:
0262: public LispObject readPathname() throws ConditionThrowable {
0263: LispObject obj = read(true, NIL, false);
0264: if (obj instanceof AbstractString)
0265: return new Pathname(obj.getStringValue());
0266: if (obj.listp())
0267: return Pathname.makePathname(obj);
0268: return signal(new TypeError(
0269: "#p requires a string or list argument."));
0270: }
0271:
0272: public LispObject readSymbol() throws ConditionThrowable {
0273: StringBuffer sb = new StringBuffer();
0274: _readToken(sb, LispThread.currentThread());
0275: return new Symbol(sb.toString());
0276: }
0277:
0278: public LispObject readStructure() throws ConditionThrowable {
0279: LispObject obj = read(true, NIL, false);
0280: if (_READ_SUPPRESS_.symbolValueNoThrow() != NIL)
0281: return NIL;
0282: if (obj.listp()) {
0283: Symbol structure = checkSymbol(obj.car());
0284: LispClass c = LispClass.findClass(structure);
0285: if (!(c instanceof StructureClass)) {
0286: return signal(new ReaderError(structure.getName()
0287: + " is not a defined structure type."));
0288: }
0289: LispObject args = obj.cdr();
0290: Package pkg = checkPackage(structure.getPackage());
0291: Symbol constructor = pkg.intern("MAKE-"
0292: + structure.getName());
0293: return funcall(constructor.getSymbolFunctionOrDie(), args
0294: .copyToArray(), LispThread.currentThread());
0295: }
0296: return signal(new ReaderError("Non-list following #S: " + obj));
0297: }
0298:
0299: public LispObject readList() throws ConditionThrowable {
0300: Cons first = null;
0301: Cons last = null;
0302: while (true) {
0303: char c = flushWhitespace();
0304: if (c == ')') {
0305: return first == null ? NIL : first;
0306: }
0307: if (c == '.') {
0308: int n = _readChar();
0309: if (n < 0)
0310: return signal(new EndOfFile(this ));
0311: char nextChar = (char) n;
0312: if (nextChar == ',') {
0313: LispObject obj = readComma();
0314: last.setCdr(obj);
0315: continue;
0316: } else if (isTokenDelimiter(nextChar)) {
0317: if (last == null)
0318: return signal(new ReaderError(
0319: "Nothing appears before . in list."));
0320: LispObject obj = read(true, NIL, true);
0321: last.setCdr(obj);
0322: continue;
0323: } else {
0324: // normal token beginning with '.'
0325: _unreadChar(nextChar);
0326: }
0327: }
0328: LispObject obj = processChar(c);
0329: if (obj == null) {
0330: // A comment.
0331: continue;
0332: }
0333: if (first == null) {
0334: first = new Cons(obj);
0335: last = first;
0336: } else {
0337: Cons newCons = new Cons(obj);
0338: last.setCdr(newCons);
0339: last = newCons;
0340: }
0341: }
0342: }
0343:
0344: private static final boolean isTokenDelimiter(char c)
0345: throws ConditionThrowable {
0346: switch (c) {
0347: case '"':
0348: case '\'':
0349: case '(':
0350: case ')':
0351: case ',':
0352: case ';':
0353: case '`':
0354: return true;
0355: default:
0356: return currentReadtable().isWhitespace(c);
0357: }
0358: }
0359:
0360: public LispObject readComma() throws ConditionThrowable {
0361: int n = _readChar();
0362: if (n < 0)
0363: return signal(new EndOfFile(this ));
0364: char c = (char) n;
0365: switch (c) {
0366: case '@':
0367: return new Cons(Symbol.COMMA_ATSIGN, new Cons(read(true,
0368: NIL, true), NIL));
0369: case '.':
0370: return new Cons(Symbol.COMMA_DOT, new Cons(read(true, NIL,
0371: true), NIL));
0372: default:
0373: _unreadChar(c);
0374: return new Cons(Symbol.COMMA, new Cons(
0375: read(true, NIL, true), NIL));
0376: }
0377: }
0378:
0379: public LispObject readDispatchChar(char ignored)
0380: throws ConditionThrowable {
0381: int numArg = -1;
0382: char c;
0383: while (true) {
0384: int n = _readChar();
0385: if (n < 0)
0386: return signal(new EndOfFile(this ));
0387: c = (char) n;
0388: if (c < '0' || c > '9')
0389: break;
0390: if (numArg < 0)
0391: numArg = 0;
0392: numArg = numArg * 10 + c - '0';
0393: }
0394: LispObject fun = currentReadtable().getDispatchMacroCharacter(
0395: '#', c);
0396: if (fun instanceof DispatchMacroFunction)
0397: return ((DispatchMacroFunction) fun).execute(this , c,
0398: numArg);
0399: if (fun != NIL) {
0400: final LispThread thread = LispThread.currentThread();
0401: LispObject result = funcall3(fun, this , LispCharacter
0402: .getInstance(c), (numArg < 0) ? NIL : new Fixnum(
0403: numArg), thread);
0404: LispObject[] values = thread.getValues();
0405: if (values != null && values.length == 0)
0406: result = null;
0407: thread.clearValues();
0408: return result;
0409: }
0410: return null;
0411: }
0412:
0413: public LispObject readCharacterLiteral() throws ConditionThrowable {
0414: int n = _readChar();
0415: if (n < 0)
0416: return signal(new EndOfFile(this ));
0417: char c = (char) n;
0418: StringBuffer sb = new StringBuffer();
0419: sb.append(c);
0420: Readtable rt = currentReadtable();
0421: while (true) {
0422: n = _readChar();
0423: if (n < 0)
0424: break;
0425: c = (char) n;
0426: if (rt.isWhitespace(c))
0427: break;
0428: if (c == '(' || c == ')') {
0429: _unreadChar(c);
0430: break;
0431: }
0432: sb.append(c);
0433: }
0434: if (_READ_SUPPRESS_.symbolValueNoThrow() != NIL)
0435: return NIL;
0436: String token = sb.toString();
0437: if (token.length() == 1)
0438: return LispCharacter.getInstance(token.charAt(0));
0439: n = LispCharacter.nameToChar(token);
0440: if (n >= 0)
0441: return LispCharacter.getInstance((char) n);
0442: return signal(new LispError("Unrecognized character name: \""
0443: + token + '"'));
0444: }
0445:
0446: public void skipBalancedComment() throws ConditionThrowable {
0447: while (true) {
0448: int n = _readChar();
0449: if (n < 0)
0450: return;
0451: if (n == '|') {
0452: n = _readChar();
0453: if (n == '#')
0454: return;
0455: else
0456: _unreadChar(n);
0457: } else if (n == '#') {
0458: n = _readChar();
0459: if (n == '|')
0460: skipBalancedComment(); // Nested comment. Recurse!
0461: else
0462: _unreadChar(n);
0463: }
0464: }
0465: }
0466:
0467: public LispObject readBitVector() throws ConditionThrowable {
0468: StringBuffer sb = new StringBuffer();
0469: while (true) {
0470: int n = _readChar();
0471: if (n < 0)
0472: break;
0473: char c = (char) n;
0474: if (c == '0' || c == '1')
0475: sb.append(c);
0476: else {
0477: _unreadChar(c);
0478: break;
0479: }
0480: }
0481: return new SimpleBitVector(sb.toString());
0482: }
0483:
0484: public LispObject readArray(int rank) throws ConditionThrowable {
0485: LispObject obj = read(true, NIL, true);
0486: switch (rank) {
0487: case -1:
0488: return signal(new ReaderError(
0489: "No dimensions argument to #A."));
0490: case 0:
0491: return new ZeroRankArray(T, obj, false);
0492: case 1:
0493: return new SimpleVector(obj);
0494: default:
0495: return new SimpleArray(rank, obj);
0496: }
0497: }
0498:
0499: public LispObject readComplex() throws ConditionThrowable {
0500: LispObject obj = read(true, NIL, true);
0501: if (_READ_SUPPRESS_.symbolValue() != NIL)
0502: return NIL;
0503: if (obj instanceof Cons && obj.length() == 2)
0504: return Complex.getInstance(obj.car(), obj.cadr());
0505: // Error.
0506: StringBuffer sb = new StringBuffer(
0507: "Invalid complex number format");
0508: if (this instanceof FileStream) {
0509: Pathname p = ((FileStream) this ).getPathname();
0510: if (p != null) {
0511: String namestring = p.getNamestring();
0512: if (namestring != null) {
0513: sb.append(" in #P\"");
0514: sb.append(namestring);
0515: sb.append('"');
0516: }
0517: }
0518: sb.append(" at offset ");
0519: sb.append(_getFilePosition());
0520: }
0521: sb.append(": #C");
0522: sb.append(obj.writeToString());
0523: return signal(new ReaderError(sb.toString()));
0524: }
0525:
0526: private String readMultipleEscape() throws ConditionThrowable {
0527: StringBuffer sb = new StringBuffer();
0528: while (true) {
0529: int n = _readChar();
0530: if (n < 0)
0531: break;
0532: char c = (char) n;
0533: if (c == '\\') {
0534: n = _readChar();
0535: if (n < 0) {
0536: signal(new EndOfFile(this ));
0537: // Not reached.
0538: return null;
0539: }
0540: sb.append((char) n);
0541: continue;
0542: }
0543: if (c == '|')
0544: break;
0545: sb.append(c);
0546: }
0547: return sb.toString();
0548: }
0549:
0550: private static final int findUnescapedSingleColon(String s,
0551: BitSet flags) {
0552: if (flags == null)
0553: return s.indexOf(':');
0554: final int limit = s.length();
0555: for (int i = 0; i < limit; i++) {
0556: if (s.charAt(i) == ':' && !flags.get(i)) {
0557: return i;
0558: }
0559: }
0560: return -1;
0561: }
0562:
0563: private static final int findUnescapedDoubleColon(String s,
0564: BitSet flags) {
0565: if (flags == null)
0566: return s.indexOf("::");
0567: final int limit = s.length() - 1;
0568: for (int i = 0; i < limit; i++) {
0569: if (s.charAt(i) == ':' && !flags.get(i)) {
0570: if (s.charAt(i + 1) == ':' && !flags.get(i + 1)) {
0571: return i;
0572: }
0573: }
0574: }
0575: return -1;
0576: }
0577:
0578: private final LispObject readToken(char c)
0579: throws ConditionThrowable {
0580: StringBuffer sb = new StringBuffer();
0581: sb.append(c);
0582: final LispThread thread = LispThread.currentThread();
0583: BitSet flags = _readToken(sb, thread);
0584: boolean escaped = (flags != null);
0585: if (_READ_SUPPRESS_.symbolValue(thread) != NIL)
0586: return NIL;
0587: final String token = sb.toString();
0588: final int length = token.length();
0589: if (length > 0) {
0590: final char firstChar = token.charAt(0);
0591: if (flags == null) {
0592: if (firstChar == '.') {
0593: // Section 2.3.3: "If a token consists solely of dots (with
0594: // no escape characters), then an error of type READER-
0595: // ERROR is signaled, except in one circumstance: if the
0596: // token is a single dot and appears in a situation where
0597: // dotted pair notation permits a dot, then it is accepted
0598: // as part of such syntax and no error is signaled."
0599: boolean ok = false;
0600: for (int i = length; i-- > 1;) {
0601: if (token.charAt(i) != '.') {
0602: ok = true;
0603: break;
0604: }
0605: }
0606: if (!ok) {
0607: final String message;
0608: if (length > 1)
0609: message = "Too many dots.";
0610: else
0611: message = "Dot context error.";
0612: return signal(new ReaderError(message));
0613: }
0614: }
0615: final int radix = getReadBase(thread);
0616: if ("-+0123456789".indexOf(firstChar) >= 0) {
0617: LispObject number = makeNumber(token, length, radix);
0618: if (number != null)
0619: return number;
0620: } else if (Character.digit(firstChar, radix) >= 0) {
0621: LispObject number = makeNumber(token, length, radix);
0622: if (number != null)
0623: return number;
0624: }
0625: }
0626: if (firstChar == ':')
0627: return PACKAGE_KEYWORD.intern(token.substring(1));
0628: int index = findUnescapedDoubleColon(token, flags);
0629: if (index > 0) {
0630: String packageName = token.substring(0, index);
0631: String symbolName = token.substring(index + 2);
0632: Package pkg = Packages.findPackage(packageName);
0633: if (pkg == null)
0634: return signal(new LispError("Package \""
0635: + packageName + "\" not found."));
0636: return pkg.intern(symbolName);
0637: }
0638: index = findUnescapedSingleColon(token, flags);
0639: if (index > 0) {
0640: String packageName = token.substring(0, index);
0641: String symbolName = token.substring(index + 1);
0642: Package pkg = Packages.findPackage(packageName);
0643: if (pkg == null)
0644: return signal(new PackageError("Package \""
0645: + packageName + "\" not found."));
0646: Symbol symbol = pkg.findExternalSymbol(symbolName);
0647: if (symbol != null)
0648: return symbol;
0649: // Error!
0650: if (pkg.findInternalSymbol(symbolName) != null)
0651: return signal(new LispError("The symbol \""
0652: + symbolName
0653: + "\" is not external in package "
0654: + packageName + '.'));
0655: else
0656: return signal(new LispError("The symbol \""
0657: + symbolName
0658: + "\" was not found in package "
0659: + packageName + '.'));
0660: }
0661: }
0662: // Intern token in current package.
0663: return ((Package) _PACKAGE_.symbolValue(thread)).intern(token);
0664: }
0665:
0666: private final BitSet _readToken(StringBuffer sb, LispThread thread)
0667: throws ConditionThrowable {
0668: BitSet flags = null;
0669: final Readtable rt = currentReadtable(thread);
0670: final LispObject readtableCase = rt.getReadtableCase();
0671: if (sb.length() > 0) {
0672: Debug.assertTrue(sb.length() == 1);
0673: char c = sb.charAt(0);
0674: if (c == '|') {
0675: sb.setLength(0);
0676: sb.append(readMultipleEscape());
0677: flags = new BitSet(sb.length());
0678: for (int i = sb.length(); i-- > 0;)
0679: flags.set(i);
0680: } else if (c == '\\') {
0681: int n = _readChar();
0682: if (n < 0) {
0683: signal(new EndOfFile(this ));
0684: // Not reached.
0685: return flags;
0686: }
0687: sb.setCharAt(0, (char) n);
0688: flags = new BitSet(1);
0689: flags.set(0);
0690: } else if (readtableCase == Keyword.UPCASE) {
0691: sb.setCharAt(0, Utilities.toUpperCase(c));
0692: } else if (readtableCase == Keyword.DOWNCASE) {
0693: sb.setCharAt(0, Utilities.toLowerCase(c));
0694: }
0695: }
0696: loop: while (true) {
0697: int n = _readChar();
0698: if (n < 0)
0699: break;
0700: char c = (char) n;
0701: if (rt.isWhitespace(c))
0702: break;
0703: if (rt.getAttribute(c) == Readtable.ATTR_TERMINATING_MACRO) {
0704: _unreadChar(c);
0705: break;
0706: }
0707: switch (c) {
0708: case '\\':
0709: n = _readChar();
0710: if (n < 0)
0711: break loop;
0712: sb.append((char) n);
0713: if (flags == null)
0714: flags = new BitSet(sb.length());
0715: flags.set(sb.length() - 1);
0716: break;
0717: case '|': {
0718: int begin = sb.length();
0719: sb.append(readMultipleEscape());
0720: int end = sb.length();
0721: if (flags == null)
0722: flags = new BitSet(sb.length());
0723: for (int i = begin; i < end; i++)
0724: flags.set(i);
0725: break;
0726: }
0727: default:
0728: if (readtableCase == Keyword.UPCASE)
0729: c = Utilities.toUpperCase(c);
0730: else if (readtableCase == Keyword.DOWNCASE)
0731: c = Utilities.toLowerCase(c);
0732: sb.append(c);
0733: }
0734: }
0735: if (readtableCase == Keyword.INVERT) {
0736: // FIXME Preserve case of escaped characters!
0737: String s = invert(sb.toString());
0738: sb.setLength(0);
0739: sb.append(s);
0740: }
0741: return flags;
0742: }
0743:
0744: private static final int getReadBase(LispThread thread)
0745: throws ConditionThrowable {
0746: final int readBase;
0747: try {
0748: readBase = ((Fixnum) _READ_BASE_.symbolValue(thread)).value;
0749: } catch (ClassCastException e) {
0750: // The value of *READ-BASE* is not a Fixnum.
0751: signal(new LispError(
0752: "The value of *READ-BASE* is not of type '(INTEGER 2 36)."));
0753: // Not reached.
0754: return 10;
0755: }
0756: if (readBase < 2 || readBase > 36) {
0757: signal(new LispError(
0758: "The value of *READ-BASE* is not of type '(INTEGER 2 36)."));
0759: // Not reached.
0760: return 10;
0761: }
0762: return readBase;
0763: }
0764:
0765: private static final LispObject makeNumber(String token,
0766: int length, int radix) throws ConditionThrowable {
0767: if (length == 0)
0768: return null;
0769: if (token.indexOf('/') >= 0)
0770: return makeRatio(token, radix);
0771: if (token.charAt(length - 1) == '.') {
0772: radix = 10;
0773: token = token.substring(0, --length);
0774: }
0775: boolean numeric = true;
0776: if (radix == 10) {
0777: for (int i = length; i-- > 0;) {
0778: char c = token.charAt(i);
0779: if (c < '0' || c > '9') {
0780: if (i > 0 || (c != '-' && c != '+')) {
0781: numeric = false;
0782: break;
0783: }
0784: }
0785: }
0786: } else {
0787: for (int i = length; i-- > 0;) {
0788: char c = token.charAt(i);
0789: if (Character.digit(c, radix) < 0) {
0790: if (i > 0 || (c != '-' && c != '+')) {
0791: numeric = false;
0792: break;
0793: }
0794: }
0795: }
0796: }
0797: if (!numeric) // Can't be an integer.
0798: return makeFloat(token, length);
0799: if (token.charAt(0) == '+')
0800: token = token.substring(1);
0801: try {
0802: return new Fixnum(Integer.parseInt(token, radix));
0803: } catch (NumberFormatException e) {
0804: }
0805: // parseInt() failed.
0806: try {
0807: return new Bignum(new BigInteger(token, radix));
0808: } catch (NumberFormatException e) {
0809: }
0810: // Not a number.
0811: return null;
0812: }
0813:
0814: private static final LispObject makeRatio(String token, int radix)
0815: throws ConditionThrowable {
0816: final int index = token.indexOf('/');
0817: if (index < 0)
0818: return null;
0819: try {
0820: BigInteger numerator = new BigInteger(token.substring(0,
0821: index), radix);
0822: BigInteger denominator = new BigInteger(token
0823: .substring(index + 1), radix);
0824: return number(numerator, denominator);
0825: } catch (NumberFormatException e) {
0826: }
0827: return null;
0828: }
0829:
0830: private static final LispObject makeFloat(final String token,
0831: final int length) throws ConditionThrowable {
0832: if (length == 0)
0833: return null;
0834: StringBuffer sb = new StringBuffer();
0835: int i = 0;
0836: boolean maybe = false;
0837: char c = token.charAt(i);
0838: if (c == '-' || c == '+') {
0839: sb.append(c);
0840: ++i;
0841: }
0842: while (i < length) {
0843: c = token.charAt(i);
0844: if (c == '.' || (c >= '0' && c <= '9')) {
0845: if (c == '.')
0846: maybe = true;
0847: sb.append(c);
0848: ++i;
0849: } else
0850: break;
0851: }
0852: if (i < length) {
0853: if ("esfdlESFDL".indexOf(token.charAt(i)) >= 0) {
0854: // Exponent marker.
0855: maybe = true;
0856: sb.append('E');
0857: ++i;
0858: }
0859: }
0860: if (!maybe)
0861: return null;
0862: // Append rest of token.
0863: sb.append(token.substring(i));
0864: try {
0865: return new LispFloat(Double.parseDouble(sb.toString()));
0866: } catch (NumberFormatException e) {
0867: return null;
0868: }
0869: }
0870:
0871: public LispObject readRadix(int radix) throws ConditionThrowable {
0872: StringBuffer sb = new StringBuffer();
0873: boolean escaped = (_readToken(sb, LispThread.currentThread()) != null);
0874: if (escaped)
0875: return signal(new ReaderError("Illegal syntax for number."));
0876: String s = sb.toString();
0877: if (s.indexOf('/') >= 0)
0878: return makeRatio(s, radix);
0879: try {
0880: return new Fixnum(Integer.parseInt(s, radix));
0881: } catch (NumberFormatException e) {
0882: }
0883: // parseInt() failed.
0884: try {
0885: return new Bignum(new BigInteger(s, radix));
0886: } catch (NumberFormatException e) {
0887: }
0888: // Not a number.
0889: return signal(new LispError());
0890: }
0891:
0892: private char flushWhitespace() throws ConditionThrowable {
0893: final Readtable rt = currentReadtable();
0894: while (true) {
0895: int n = _readChar();
0896: if (n < 0) {
0897: signal(new EndOfFile(this ));
0898: // Not reached.
0899: return 0;
0900: }
0901: char c = (char) n;
0902: if (!rt.isWhitespace(c))
0903: return c;
0904: }
0905: }
0906:
0907: public LispObject readDelimitedList(char delimiter)
0908: throws ConditionThrowable {
0909: LispObject result = NIL;
0910: while (true) {
0911: char c = flushWhitespace();
0912: if (c == delimiter)
0913: break;
0914: LispObject obj = processChar(c);
0915: if (obj != null)
0916: result = new Cons(obj, result);
0917: }
0918: return result.nreverse();
0919: }
0920:
0921: // read-line &optional stream eof-error-p eof-value recursive-p
0922: // => line, missing-newline-p
0923: // recursive-p is ignored
0924: public LispObject readLine(boolean eofError, LispObject eofValue)
0925: throws ConditionThrowable {
0926: final LispThread thread = LispThread.currentThread();
0927: StringBuffer sb = new StringBuffer();
0928: while (true) {
0929: int n = _readChar();
0930: if (n < 0) {
0931: if (sb.length() == 0) {
0932: if (eofError)
0933: return signal(new EndOfFile(this ));
0934: return thread.setValues(eofValue, T);
0935: }
0936: return thread.setValues(new SimpleString(sb), T);
0937: }
0938: if (n == '\n')
0939: return thread.setValues(new SimpleString(sb), NIL);
0940: else
0941: sb.append((char) n);
0942: }
0943: }
0944:
0945: // read-char &optional stream eof-error-p eof-value recursive-p => char
0946: // recursive-p is ignored
0947: public LispObject readChar(boolean eofError, LispObject eofValue)
0948: throws ConditionThrowable {
0949: int n = _readChar();
0950: if (n < 0) {
0951: if (eofError)
0952: return signal(new EndOfFile(this ));
0953: else
0954: return eofValue;
0955: }
0956: return LispCharacter.getInstance((char) n);
0957: }
0958:
0959: // read-char-no-hang &optional stream eof-error-p eof-value recursive-p => char
0960: // recursive-p is ignored
0961: public LispObject readCharNoHang(boolean eofError,
0962: LispObject eofValue) throws ConditionThrowable {
0963: return _charReady() ? readChar(eofError, eofValue) : NIL;
0964: }
0965:
0966: // unread-char character &optional input-stream => nil
0967: public LispObject unreadChar(LispCharacter c)
0968: throws ConditionThrowable {
0969: _unreadChar(c.getValue());
0970: return NIL;
0971: }
0972:
0973: public LispObject finishOutput() throws ConditionThrowable {
0974: _finishOutput();
0975: return NIL;
0976: }
0977:
0978: // clear-input &optional input-stream => nil
0979: public LispObject clearInput() throws ConditionThrowable {
0980: _clearInput();
0981: return NIL;
0982: }
0983:
0984: public LispObject getFilePosition() throws ConditionThrowable {
0985: long pos = _getFilePosition();
0986: return pos >= 0 ? number(pos) : NIL;
0987: }
0988:
0989: public LispObject setFilePosition(LispObject arg)
0990: throws ConditionThrowable {
0991: return _setFilePosition(arg) ? T : NIL;
0992: }
0993:
0994: // close stream &key abort => result
0995: // Must return true if stream was open, otherwise implementation-dependent.
0996: public LispObject close(LispObject abort) throws ConditionThrowable {
0997: _close();
0998: return T;
0999: }
1000:
1001: public String toString() {
1002: return unreadableString("STREAM");
1003: }
1004:
1005: // read-byte stream &optional eof-error-p eof-value => byte
1006: // Reads an 8-bit byte.
1007: public LispObject readByte(boolean eofError, LispObject eofValue)
1008: throws ConditionThrowable {
1009: int n = _readByte();
1010: if (n < 0) {
1011: if (eofError)
1012: return signal(new EndOfFile(this ));
1013: else
1014: return eofValue;
1015: }
1016: return new Fixnum(n);
1017: }
1018:
1019: public LispObject terpri() throws ConditionThrowable {
1020: _writeChar('\n');
1021: return NIL;
1022: }
1023:
1024: public LispObject freshLine() throws ConditionThrowable {
1025: if (charPos == 0)
1026: return NIL;
1027: _writeChar('\n');
1028: return T;
1029: }
1030:
1031: public void print(char c) throws ConditionThrowable {
1032: _writeChar(c);
1033: }
1034:
1035: // PRIN1 produces output suitable for input to READ.
1036: // Binds *PRINT-ESCAPE* to true.
1037: public void prin1(LispObject obj) throws ConditionThrowable {
1038: LispThread thread = LispThread.currentThread();
1039: Environment oldDynEnv = thread.getDynamicEnvironment();
1040: thread.bindSpecial(_PRINT_ESCAPE_, T);
1041: String s = obj.writeToString();
1042: thread.setDynamicEnvironment(oldDynEnv);
1043: _writeString(s);
1044: }
1045:
1046: public LispObject listen() throws ConditionThrowable {
1047: return _charReady() ? T : NIL;
1048: }
1049:
1050: public LispObject fileLength() throws ConditionThrowable {
1051: return signal(new TypeError(
1052: "Stream is not associated with a file."));
1053: }
1054:
1055: public LispObject fileStringLength(LispObject arg)
1056: throws ConditionThrowable {
1057: if (arg instanceof LispCharacter)
1058: return Fixnum.ONE;
1059: else if (arg instanceof AbstractString)
1060: return number(arg.length());
1061: else
1062: return signal(new TypeError(String.valueOf(arg)
1063: + " is neither a string nor a character."));
1064: }
1065:
1066: // Returns -1 at end of file.
1067: protected int _readChar() throws ConditionThrowable {
1068: try {
1069: int n = reader.read();
1070: ++offset;
1071: if (n == '\r') {
1072: if (interactive && Utilities.isPlatformWindows())
1073: return _readChar();
1074: }
1075: if (n == '\n')
1076: ++lineNumber;
1077: return n;
1078: } catch (IOException e) {
1079: signal(new StreamError(this , e));
1080: // Not reached.
1081: return -1;
1082: }
1083: }
1084:
1085: protected void _unreadChar(int n) throws ConditionThrowable {
1086: try {
1087: reader.unread(n);
1088: --offset;
1089: if (n == '\n')
1090: --lineNumber;
1091: } catch (IOException e) {
1092: signal(new StreamError(this , e));
1093: }
1094: }
1095:
1096: protected boolean _charReady() throws ConditionThrowable {
1097: try {
1098: return reader.ready();
1099: } catch (IOException e) {
1100: signal(new StreamError(this , e));
1101: // Not reached.
1102: return false;
1103: }
1104: }
1105:
1106: public void _writeChar(char c) throws ConditionThrowable {
1107: try {
1108: writer.write(c);
1109: if (c == '\n') {
1110: writer.flush();
1111: charPos = 0;
1112: } else
1113: ++charPos;
1114: } catch (IOException e) {
1115: signal(new StreamError(this , e));
1116: }
1117: }
1118:
1119: public void _writeChars(char[] chars, int start, int end)
1120: throws ConditionThrowable {
1121: try {
1122: writer.write(chars, start, end - start);
1123: int index = -1;
1124: for (int i = end; i-- > start;) {
1125: if (chars[i] == '\n') {
1126: index = i;
1127: break;
1128: }
1129: }
1130: if (index < 0) {
1131: // No newline.
1132: charPos += (end - start);
1133: } else {
1134: charPos = end - (index + 1);
1135: writer.flush();
1136: }
1137: } catch (IOException e) {
1138: signal(new StreamError(this , e));
1139: }
1140: }
1141:
1142: public void _writeString(String s) throws ConditionThrowable {
1143: try {
1144: writer.write(s);
1145: int index = s.lastIndexOf('\n');
1146: if (index < 0)
1147: charPos += s.length();
1148: else {
1149: charPos = s.length() - (index + 1);
1150: writer.flush();
1151: }
1152: } catch (IOException e) {
1153: signal(new StreamError(this , e));
1154: }
1155: }
1156:
1157: public void _writeLine(String s) throws ConditionThrowable {
1158: try {
1159: writer.write(s);
1160: writer.write('\n');
1161: writer.flush();
1162: charPos = 0;
1163: } catch (IOException e) {
1164: signal(new StreamError(this , e));
1165: }
1166: }
1167:
1168: // Reads an 8-bit byte.
1169: public int _readByte() throws ConditionThrowable {
1170: try {
1171: return in.read(); // Reads an 8-bit byte.
1172: } catch (IOException e) {
1173: signal(new StreamError(this , e));
1174: // Not reached.
1175: return -1;
1176: }
1177: }
1178:
1179: // Writes an 8-bit byte.
1180: public void _writeByte(int n) throws ConditionThrowable {
1181: try {
1182: out.write(n); // Writes an 8-bit byte.
1183: } catch (IOException e) {
1184: signal(new StreamError(this , e));
1185: }
1186: }
1187:
1188: public void _finishOutput() throws ConditionThrowable {
1189: try {
1190: if (writer != null)
1191: writer.flush();
1192: if (out != null)
1193: out.flush();
1194: } catch (IOException e) {
1195: signal(new StreamError(this , e));
1196: }
1197: }
1198:
1199: public void _clearInput() throws ConditionThrowable {
1200: if (reader != null) {
1201: while (_charReady())
1202: _readChar();
1203: } else if (in != null) {
1204: try {
1205: while (in.available() > 0)
1206: in.read();
1207: } catch (IOException e) {
1208: signal(new StreamError(this , e));
1209: }
1210: }
1211: }
1212:
1213: protected long _getFilePosition() throws ConditionThrowable {
1214: return -1;
1215: }
1216:
1217: protected boolean _setFilePosition(LispObject arg)
1218: throws ConditionThrowable {
1219: return false;
1220: }
1221:
1222: public void _close() throws ConditionThrowable {
1223: try {
1224: if (reader != null)
1225: reader.close();
1226: if (in != null)
1227: in.close();
1228: if (writer != null)
1229: writer.close();
1230: if (out != null)
1231: out.close();
1232: setOpen(false);
1233: } catch (IOException e) {
1234: signal(new StreamError(this , e));
1235: }
1236: }
1237:
1238: public void printStackTrace(Throwable t) throws ConditionThrowable {
1239: StringWriter sw = new StringWriter();
1240: PrintWriter pw = new PrintWriter(sw);
1241: t.printStackTrace(pw);
1242: try {
1243: writer.write(sw.toString());
1244: writer.write('\n');
1245: writer.flush();
1246: charPos = 0;
1247: } catch (IOException e) {
1248: signal(new StreamError(this , e));
1249: }
1250: }
1251:
1252: // ### file-position
1253: private static final Primitive FILE_POSITION = new Primitive(
1254: "file-position", "stream &optional position-spec") {
1255: public LispObject execute(LispObject arg)
1256: throws ConditionThrowable {
1257: return checkStream(arg).getFilePosition();
1258: }
1259:
1260: public LispObject execute(LispObject first, LispObject second)
1261: throws ConditionThrowable {
1262: return checkStream(first).setFilePosition(second);
1263: }
1264: };
1265:
1266: // ### stream-line-number
1267: private static final Primitive1 STREAM_LINE_NUMBER = new Primitive1(
1268: "stream-line-number", PACKAGE_SYS, false, "stream") {
1269: public LispObject execute(LispObject arg)
1270: throws ConditionThrowable {
1271: Stream stream = checkStream(arg);
1272: return number(stream.getLineNumber() + 1);
1273: }
1274: };
1275:
1276: // ### stream-offset
1277: private static final Primitive1 STREAM_OFFSET = new Primitive1(
1278: "stream-offset", PACKAGE_SYS, false, "stream") {
1279: public LispObject execute(LispObject arg)
1280: throws ConditionThrowable {
1281: Stream stream = checkStream(arg);
1282: return number(stream.getOffset());
1283: }
1284: };
1285: }
|