001: /*
002: * CharacterInputStream.java
003: *
004: * Copyright (C) 2003 Peter Graves
005: * $Id: CharacterInputStream.java,v 1.6 2003/11/15 11:03:32 beedlem Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.lisp;
023:
024: import java.io.BufferedReader;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.io.PushbackReader;
029: import java.io.StringReader;
030: import java.math.BigInteger;
031:
032: public class CharacterInputStream extends LispInputStream {
033: private final PushbackReader reader;
034: protected int offset;
035: protected int lineNumber;
036:
037: protected CharacterInputStream() {
038: reader = null;
039: }
040:
041: public CharacterInputStream(InputStream in) {
042: reader = new PushbackReader(new BufferedReader(
043: new InputStreamReader(in)), 2);
044: }
045:
046: public int getOffset() {
047: return offset;
048: }
049:
050: public int getLineNumber() {
051: return lineNumber;
052: }
053:
054: public LispObject read(boolean eofError, LispObject eofValue,
055: boolean recursive) throws ConditionThrowable {
056: try {
057: LispObject result = readPreservingWhitespace(eofError,
058: eofValue, recursive);
059: if (result != eofValue && !recursive) {
060: if (ready()) {
061: int n = read();
062: if (n >= 0) {
063: char c = (char) n;
064: if (!Character.isWhitespace(c))
065: unread(c);
066: }
067: }
068: }
069: return _READ_SUPPRESS_.symbolValueNoThrow() != NIL ? NIL
070: : result;
071: } catch (IOException e) {
072: throw new ConditionThrowable(new StreamError(e));
073: }
074: }
075:
076: public LispObject readPreservingWhitespace(boolean eofError,
077: LispObject eofValue, boolean recursive)
078: throws ConditionThrowable {
079: while (true) {
080: int n;
081: try {
082: n = read();
083: } catch (IOException e) {
084: throw new ConditionThrowable(new StreamError(e));
085: }
086: if (n < 0) {
087: if (eofError)
088: throw new ConditionThrowable(new EndOfFile());
089: else
090: return eofValue;
091: }
092: char c = (char) n;
093: if (Character.isWhitespace(c))
094: continue;
095: LispObject result = processChar(c);
096: if (result != null)
097: return result;
098: }
099: }
100:
101: private LispObject processChar(char c) throws ConditionThrowable {
102: switch (c) {
103: case '"':
104: return readString();
105: case '\'':
106: return readQuote();
107: case '(':
108: return readList();
109: case ')':
110: return readRightParen();
111: case ';':
112: return readComment();
113: case ',':
114: return readComma();
115: case '`':
116: return readBackquote();
117: case '#':
118: return readSharp();
119: case '|':
120: return getCurrentPackage().intern(readMultipleEscape());
121: case ':':
122: return readKeyword();
123: default:
124: return readToken(c);
125: }
126: }
127:
128: private String _readString() throws ConditionThrowable {
129: try {
130: StringBuffer sb = new StringBuffer();
131: while (true) {
132: int n = read();
133: if (n < 0)
134: throw new ConditionThrowable(new EndOfFile());
135: char c = (char) n;
136: if (c == '\\') {
137: // Single escape.
138: n = read();
139: if (n < 0)
140: throw new ConditionThrowable(new EndOfFile());
141: sb.append((char) n);
142: continue;
143: }
144: if (c == '"')
145: break;
146: // Default.
147: sb.append(c);
148: }
149: return sb.toString();
150: } catch (IOException e) {
151: throw new ConditionThrowable(new StreamError(e));
152: }
153: }
154:
155: private LispString readString() throws ConditionThrowable {
156: return new LispString(_readString());
157: }
158:
159: private LispObject readPathname() throws ConditionThrowable {
160: int n;
161: try {
162: n = read();
163: } catch (IOException e) {
164: throw new ConditionThrowable(new StreamError(e));
165: }
166: if (n < 0)
167: throw new ConditionThrowable(new EndOfFile());
168: char nextChar = (char) n;
169: if (nextChar == '"')
170: return Pathname.parseNamestring(_readString());
171: throw new ConditionThrowable(new TypeError(
172: "#p requires a string argument"));
173: }
174:
175: private LispObject readQuote() throws ConditionThrowable {
176: return new Cons(Symbol.QUOTE, new Cons(read(true, NIL, true)));
177: }
178:
179: private LispObject readList() throws ConditionThrowable {
180: try {
181: Cons first = null;
182: Cons last = null;
183: while (true) {
184: char c = flushWhitespace();
185: if (c == ')') {
186: return first == null ? NIL : first;
187: }
188: if (c == '.') {
189: int n = read();
190: if (n < 0)
191: throw new ConditionThrowable(new EndOfFile());
192: char nextChar = (char) n;
193: if (isTokenDelimiter(nextChar)) {
194: if (last == null)
195: throw new ConditionThrowable(new LispError(
196: "nothing appears before . in list"));
197: LispObject obj = read(true, NIL, true);
198: last.setCdr(obj);
199: continue;
200: } else {
201: // normal token beginning with '.'
202: unread(nextChar);
203: }
204: }
205: LispObject obj = processChar(c);
206: if (obj == null) {
207: // A comment.
208: continue;
209: }
210: if (first == null) {
211: first = new Cons(obj);
212: last = first;
213: } else {
214: Cons newCons = new Cons(obj);
215: last.setCdr(newCons);
216: last = newCons;
217: }
218: }
219: } catch (IOException e) {
220: throw new ConditionThrowable(new StreamError(e));
221: }
222: }
223:
224: private boolean isTokenDelimiter(char c) {
225: switch (c) {
226: case '"':
227: case '\'':
228: case '(':
229: case ')':
230: case ',':
231: case ';':
232: case '`':
233: return true;
234: default:
235: return Character.isWhitespace(c);
236: }
237: }
238:
239: private LispObject readRightParen() throws ConditionThrowable {
240: throw new ConditionThrowable(new LispError(
241: "unmatched right parenthesis"));
242: }
243:
244: private LispObject readComment() throws ConditionThrowable {
245: try {
246: while (true) {
247: int n = read();
248: if (n < 0)
249: return null;
250: if (n == '\n')
251: return null;
252: }
253: } catch (IOException e) {
254: throw new ConditionThrowable(new StreamError(e));
255: }
256: }
257:
258: private LispObject readComma() throws ConditionThrowable {
259: try {
260: int n = read();
261: if (n < 0)
262: throw new ConditionThrowable(new EndOfFile());
263: char c = (char) n;
264: switch (c) {
265: case '@':
266: return new Cons(Symbol.COMMA_ATSIGN, new Cons(read(
267: true, NIL, true), NIL));
268: case '.':
269: return new Cons(Symbol.COMMA_DOT, new Cons(read(true,
270: NIL, true), NIL));
271: default:
272: unread(c);
273: return new Cons(Symbol.COMMA, new Cons(read(true, NIL,
274: true), NIL));
275: }
276: } catch (IOException e) {
277: throw new ConditionThrowable(new StreamError(e));
278: }
279: }
280:
281: private LispObject readBackquote() throws ConditionThrowable {
282: return new Cons(Symbol.BACKQUOTE, new Cons(
283: read(true, NIL, true)));
284: }
285:
286: private LispObject readSharp() throws ConditionThrowable {
287: try {
288: int numArg = 0;
289: char c;
290: while (true) {
291: int n = read();
292: if (n < 0)
293: throw new ConditionThrowable(new EndOfFile());
294: c = (char) n;
295: if (c < '0' || c > '9')
296: break;
297: numArg = numArg * 10 + c - '0';
298: }
299: LispObject fun = getCurrentReadtable()
300: .getDispatchMacroCharacter('#', c);
301: if (fun != NIL) {
302: LispObject[] args = new LispObject[3];
303: final LispThread thread = LispThread.currentThread();
304: LispObject result = funcall3(fun, this , LispCharacter
305: .getInstance(c), new Fixnum(numArg), thread);
306: LispObject[] values = thread.getValues();
307: if (values != null && values.length == 0)
308: return null; // Function returned no values.
309: return result;
310: }
311: switch (c) {
312: case '\'':
313: return new Cons(Symbol.FUNCTION, new Cons(read(true,
314: NIL, true)));
315: case '(':
316: return new Vector(readList());
317: case '\\':
318: return readCharacterLiteral();
319: case '+':
320: case '-':
321: return handleFeature(c);
322: case ':':
323: return readUninternedSymbol();
324: case '|':
325: skipBalancedComment();
326: return null;
327: case '.':
328: return eval(read(true, NIL, true), new Environment(),
329: LispThread.currentThread());
330: case '*':
331: return readBitVector();
332: case 'a':
333: case 'A':
334: return readArray(numArg);
335: case 'b':
336: case 'B':
337: return readBinary();
338: case 'c':
339: case 'C':
340: return readComplex();
341: case 'p':
342: case 'P':
343: return readPathname();
344: case 'x':
345: case 'X':
346: return readHex();
347: default:
348: //clearInput();
349: //throw new ConditionThrowable(new LispError("unsupported '#' macro character '" +
350: // c + '\'');
351: return null;
352: }
353: } catch (IOException e) {
354: throw new ConditionThrowable(new StreamError(e));
355: }
356: }
357:
358: private LispObject readCharacterLiteral() throws ConditionThrowable {
359: try {
360: int n = read();
361: if (n < 0)
362: throw new ConditionThrowable(new EndOfFile());
363: char c = (char) n;
364: StringBuffer sb = new StringBuffer();
365: sb.append(c);
366: while (true) {
367: n = read();
368: if (n < 0)
369: break;
370: c = (char) n;
371: if (Character.isWhitespace(c))
372: break;
373: if (c == '(' || c == ')') {
374: unread(c);
375: break;
376: }
377: sb.append(c);
378: }
379: String token = sb.toString();
380: if (token.length() == 1)
381: return LispCharacter.getInstance(token.charAt(0));
382: n = nameToChar(token);
383: if (n >= 0)
384: return LispCharacter.getInstance((char) n);
385: throw new ConditionThrowable(new LispError(
386: "unrecognized character name: " + token));
387: } catch (IOException e) {
388: throw new ConditionThrowable(new StreamError(e));
389: }
390: }
391:
392: // FIXME
393: private LispObject handleFeature(char c) throws ConditionThrowable {
394: LispObject feature = read(true, NIL, true);
395: LispObject form = read(true, NIL, true);
396: if (feature instanceof Symbol) {
397: if (((Symbol) feature).getName().equalsIgnoreCase(
398: "armedbear")) {
399: if (c == '+')
400: return form;
401: else
402: return null;
403: } else {
404: if (c == '+')
405: return null;
406: else
407: return form;
408: }
409: }
410: return null;
411: }
412:
413: private Symbol readUninternedSymbol() throws ConditionThrowable {
414: try {
415: int n = read();
416: if (n < 0)
417: throw new ConditionThrowable(new EndOfFile());
418: char c = (char) n;
419: StringBuffer sb = new StringBuffer();
420: if (c == '|') {
421: while (true) {
422: n = read();
423: if (n < 0)
424: throw new ConditionThrowable(new EndOfFile());
425: c = (char) n;
426: if (c == '\\') {
427: // Single escape.
428: n = read();
429: if (n < 0)
430: throw new ConditionThrowable(
431: new EndOfFile());
432: sb.append((char) n);
433: continue;
434: }
435: if (c == '|')
436: break;
437: sb.append(c);
438: }
439: } else {
440: sb.append(Utilities.toUpperCase(c));
441: while (true) {
442: n = read();
443: if (n < 0)
444: break;
445: c = (char) n;
446: if (c == '\\') {
447: // Single escape.
448: n = read();
449: if (n < 0)
450: throw new ConditionThrowable(
451: new EndOfFile());
452: sb.append((char) n);
453: continue;
454: }
455: if (Character.isWhitespace(c))
456: break;
457: if (c == '(' || c == ')') {
458: unread(c);
459: break;
460: }
461: sb.append(Utilities.toUpperCase(c));
462: }
463: }
464: return new Symbol(sb.toString());
465: } catch (IOException e) {
466: throw new ConditionThrowable(new StreamError(e));
467: }
468: }
469:
470: private void skipBalancedComment() throws ConditionThrowable {
471: try {
472: while (true) {
473: int n = read();
474: if (n < 0)
475: return;
476: if (n == '|') {
477: n = read();
478: if (n == '#')
479: return;
480: else
481: unread(n);
482: } else if (n == '#') {
483: n = read();
484: if (n == '|')
485: skipBalancedComment(); // Nested comment. Recurse!
486: else
487: unread(n);
488: }
489: }
490: } catch (IOException e) {
491: throw new ConditionThrowable(new StreamError(e));
492: }
493: }
494:
495: private LispObject readBitVector() throws ConditionThrowable {
496: try {
497: StringBuffer sb = new StringBuffer();
498: while (true) {
499: int n = read();
500: if (n < 0)
501: break;
502: char c = (char) n;
503: if (c == '0' || c == '1')
504: sb.append(c);
505: else {
506: unread(c);
507: break;
508: }
509: }
510: return new BitVector(sb.toString());
511: } catch (IOException e) {
512: throw new ConditionThrowable(new StreamError(e));
513: }
514: }
515:
516: private LispObject readArray(int rank) throws ConditionThrowable {
517: LispObject obj = read(true, NIL, true);
518: if (rank == 1)
519: return new Vector(obj);
520: return new Array(rank, obj);
521: }
522:
523: private LispObject readComplex() throws ConditionThrowable {
524: LispObject obj = read(true, NIL, true);
525: if (obj instanceof Cons && obj.length() == 2)
526: return Complex.getInstance(obj.car(), obj.cadr());
527: throw new ConditionThrowable(new LispError(
528: "invalid complex number format #C" + obj));
529: }
530:
531: private String readMultipleEscape() throws ConditionThrowable {
532: try {
533: StringBuffer sb = new StringBuffer();
534: while (true) {
535: int n = read();
536: if (n < 0)
537: break;
538: char c = (char) n;
539: if (c == '|')
540: break;
541: sb.append(c);
542: }
543: return sb.toString();
544: } catch (IOException e) {
545: throw new ConditionThrowable(new StreamError(e));
546: }
547: }
548:
549: private LispObject readKeyword() throws ConditionThrowable {
550: try {
551: StringBuffer sb = new StringBuffer();
552: while (true) {
553: int n = read();
554: if (n < 0)
555: break;
556: char c = (char) n;
557: if (Character.isWhitespace(c))
558: break;
559: if (c == '(' || c == ')') {
560: unread(c);
561: break;
562: }
563: if (c == '|') {
564: sb.append(readMultipleEscape());
565: continue;
566: }
567: sb.append(Utilities.toUpperCase(c));
568: }
569: return PACKAGE_KEYWORD.intern(sb.toString());
570: } catch (IOException e) {
571: throw new ConditionThrowable(new StreamError(e));
572: }
573: }
574:
575: private LispObject readToken(char firstChar)
576: throws ConditionThrowable {
577: try {
578: StringBuffer sb = new StringBuffer();
579: sb.append(Utilities.toUpperCase(firstChar));
580: while (true) {
581: int n = read();
582: if (n < 0)
583: return makeObject(sb.toString());
584: char c = (char) n;
585: if (Character.isWhitespace(c))
586: return makeObject(sb.toString());
587: switch (c) {
588: case '(':
589: case ')':
590: unread(c);
591: return makeObject(sb.toString());
592: default:
593: sb.append(Utilities.toUpperCase(c));
594: }
595: }
596: } catch (IOException e) {
597: throw new ConditionThrowable(new StreamError(e));
598: }
599: }
600:
601: private LispObject makeObject(String token)
602: throws ConditionThrowable {
603: final LispThread thread = LispThread.currentThread();
604: if (_READ_SUPPRESS_.symbolValueNoThrow(thread) != NIL)
605: return NIL;
606: char c = token.charAt(0);
607: if ("-+0123456789".indexOf(c) >= 0) {
608: LispObject number = makeNumber(token);
609: if (number != null)
610: return number;
611: }
612: if (token.equals("T"))
613: return T;
614: if (token.equals("NIL"))
615: return NIL;
616: if (c == ':')
617: return PACKAGE_KEYWORD.intern(token.substring(1));
618: int index = token.indexOf("::");
619: if (index > 0) {
620: String packageName = token.substring(0, index);
621: String symbolName = token.substring(index + 2);
622: Package pkg = Packages.findPackage(packageName);
623: if (pkg == null)
624: throw new ConditionThrowable(new LispError("package \""
625: + packageName + "\" not found"));
626: return pkg.intern(symbolName);
627: }
628: index = token.indexOf(':');
629: if (index > 0) {
630: String packageName = token.substring(0, index);
631: String symbolName = token.substring(index + 1);
632: Package pkg = Packages.findPackage(packageName);
633: if (pkg == null)
634: throw new ConditionThrowable(new PackageError(
635: "package \"" + packageName + "\" not found"));
636: Symbol symbol = pkg.findExternalSymbol(symbolName);
637: if (symbol != null)
638: return symbol;
639: // Error!
640: if (pkg.findInternalSymbol(symbolName) != null)
641: throw new ConditionThrowable(new LispError("symbol \""
642: + symbolName + "\" is not external in package "
643: + packageName));
644: else
645: throw new ConditionThrowable(new LispError("symbol \""
646: + symbolName + "\" not found in package "
647: + packageName));
648: }
649: // Intern token in current package.
650: return ((Package) _PACKAGE_.symbolValueNoThrow(thread))
651: .intern(token);
652: }
653:
654: private LispObject makeNumber(String token)
655: throws ConditionThrowable {
656: if (token.indexOf('/') >= 0)
657: return makeRatio(token);
658: if (token.endsWith("."))
659: token = token.substring(0, token.length() - 1);
660: LispObject number = makeFloat(token);
661: if (number != null)
662: return number;
663: // The first character was checked in makeObject().
664: for (int i = token.length(); i-- > 1;) {
665: char c = token.charAt(i);
666: if (c < '0' || c > '9')
667: return null;
668: }
669: try {
670: return new Fixnum(Integer.parseInt(token));
671: } catch (NumberFormatException e) {
672: }
673: // parseInt() failed.
674: try {
675: return new Bignum(new BigInteger(token));
676: } catch (NumberFormatException e) {
677: }
678: // Not a number.
679: return null;
680: }
681:
682: private LispObject makeRatio(String token)
683: throws ConditionThrowable {
684: final int index = token.indexOf('/');
685: if (index < 0)
686: return null;
687: try {
688: BigInteger numerator = new BigInteger(token.substring(0,
689: index));
690: BigInteger denominator = new BigInteger(token
691: .substring(index + 1));
692: return number(numerator, denominator);
693: } catch (NumberFormatException e) {
694: }
695: return null;
696: }
697:
698: private LispObject makeFloat(String token)
699: throws ConditionThrowable {
700: final int length = token.length();
701: if (length == 0)
702: return null;
703: StringBuffer sb = new StringBuffer();
704: int i = 0;
705: boolean maybe = false;
706: char c = token.charAt(i);
707: if (c == '-' || c == '+') {
708: sb.append(c);
709: ++i;
710: }
711: while (i < length) {
712: c = token.charAt(i);
713: if (c == '.' || (c >= '0' && c <= '9')) {
714: if (c == '.')
715: maybe = true;
716: sb.append(c);
717: ++i;
718: } else
719: break;
720: }
721: if (i < length) {
722: if ("esfdlESFDL".indexOf(token.charAt(i)) >= 0) {
723: // Exponent marker.
724: maybe = true;
725: sb.append('E');
726: ++i;
727: }
728: }
729: if (!maybe)
730: return null;
731: // Append rest of token.
732: sb.append(token.substring(i));
733: try {
734: return new LispFloat(Double.parseDouble(sb.toString()));
735: } catch (NumberFormatException e) {
736: return null;
737: }
738: }
739:
740: private LispObject readBinary() throws ConditionThrowable {
741: try {
742: StringBuffer sb = new StringBuffer();
743: while (true) {
744: int n = read();
745: if (n < 0)
746: break;
747: char c = (char) n;
748: if (c == '0' || c == '1')
749: sb.append(c);
750: else {
751: unread(c);
752: break;
753: }
754: }
755: String s = sb.toString();
756: try {
757: return new Fixnum(Integer.parseInt(s, 2));
758: } catch (NumberFormatException e) {
759: }
760: // parseInt() failed.
761: try {
762: return new Bignum(new BigInteger(s, 2));
763: } catch (NumberFormatException e) {
764: }
765: // Not a number.
766: throw new ConditionThrowable(new LispError());
767: } catch (IOException e) {
768: throw new ConditionThrowable(new StreamError(e));
769: }
770: }
771:
772: private LispObject readHex() throws ConditionThrowable {
773: try {
774: StringBuffer sb = new StringBuffer();
775: while (true) {
776: int n = read();
777: if (n < 0)
778: break;
779: char c = (char) n;
780: if (c >= '0' && c <= '9')
781: sb.append(c);
782: else if (c >= 'A' && c <= 'F')
783: sb.append(c);
784: else if (c >= 'a' && c <= 'f')
785: sb.append(c);
786: else {
787: unread(c);
788: break;
789: }
790: }
791: String s = sb.toString();
792: try {
793: return new Fixnum(Integer.parseInt(s, 16));
794: } catch (NumberFormatException e) {
795: }
796: // parseInt() failed.
797: try {
798: return new Bignum(new BigInteger(s, 16));
799: } catch (NumberFormatException e) {
800: }
801: // Not a number.
802: throw new ConditionThrowable(new LispError());
803: } catch (IOException e) {
804: throw new ConditionThrowable(new StreamError(e));
805: }
806: }
807:
808: private char flushWhitespace() throws ConditionThrowable {
809: try {
810: while (true) {
811: int n = read();
812: if (n < 0)
813: throw new ConditionThrowable(new EndOfFile());
814: char c = (char) n;
815: if (!Character.isWhitespace(c))
816: return c;
817: }
818: } catch (IOException e) {
819: throw new ConditionThrowable(new StreamError(e));
820: }
821: }
822:
823: // read-line &optional stream eof-error-p eof-value recursive-p
824: // => line, missing-newline-p
825: // recursive-p is ignored
826: public LispObject readLine(boolean eofError, LispObject eofValue)
827: throws ConditionThrowable {
828: StringBuffer sb = new StringBuffer();
829: while (true) {
830: try {
831: int n = read();
832: if (n < 0) {
833: if (sb.length() == 0) {
834: if (eofError)
835: throw new ConditionThrowable(
836: new EndOfFile());
837: return eofValue;
838: }
839: LispObject[] values = new LispObject[2];
840: values[0] = new LispString(sb.toString());
841: values[1] = T; // Missing newline.
842: LispThread.currentThread().setValues(values);
843: return values[0];
844: }
845: switch (n) {
846: case '\n': {
847: LispObject[] values = new LispObject[2];
848: values[0] = new LispString(sb.toString());
849: values[1] = NIL;
850: LispThread.currentThread().setValues(values);
851: return values[0];
852: }
853: default:
854: sb.append((char) n);
855: }
856: } catch (IOException e) {
857: throw new ConditionThrowable(new StreamError(e));
858: }
859: }
860: }
861:
862: // read-char &optional stream eof-error-p eof-value recursive-p => char
863: // recursive-p is ignored
864: public LispObject readChar(boolean eofError, LispObject eofValue)
865: throws ConditionThrowable {
866: int n;
867: try {
868: n = read();
869: } catch (IOException e) {
870: throw new ConditionThrowable(new StreamError(e));
871: }
872: if (n < 0) {
873: if (eofError)
874: throw new ConditionThrowable(new EndOfFile());
875: else
876: return eofValue;
877: }
878: return LispCharacter.getInstance((char) n);
879: }
880:
881: // unread-char character &optional input-stream => nil
882: public LispObject unreadChar(LispCharacter c)
883: throws ConditionThrowable {
884: try {
885: unread(c.getValue());
886: } catch (IOException e) {
887: throw new ConditionThrowable(new StreamError(e));
888: }
889: return NIL;
890: }
891:
892: // clear-input &optional input-stream => nil
893: public LispObject clearInput() throws ConditionThrowable {
894: try {
895: while (ready())
896: read();
897: } catch (IOException e) {
898: throw new ConditionThrowable(new StreamError(e));
899: }
900: return NIL;
901: }
902:
903: // close stream &key abort => result
904: // Must return true if stream was open, otherwise implementation-dependent.
905: public LispObject close(LispObject abort) throws ConditionThrowable {
906: try {
907: reader.close();
908: return T;
909: } catch (IOException e) {
910: throw new ConditionThrowable(new StreamError(e));
911: }
912: }
913:
914: protected int read() throws IOException {
915: int n = reader.read();
916: ++offset;
917: if (n == '\n')
918: ++lineNumber;
919: return n;
920: }
921:
922: protected void unread(int n) throws IOException {
923: reader.unread(n);
924: --offset;
925: if (n == '\n')
926: --lineNumber;
927: }
928:
929: protected boolean ready() throws IOException {
930: return reader.ready();
931: }
932:
933: public String toString() {
934: return unreadableString("STREAM [character input]");
935: }
936: }
|