KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > lisp > Stream


1 /*
2  * Stream.java
3  *
4  * Copyright (C) 2003-2004 Peter Graves
5  * $Id: Stream.java,v 1.82 2004/09/18 20:27:28 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.lisp;
23
24 import java.io.BufferedInputStream JavaDoc;
25 import java.io.BufferedOutputStream JavaDoc;
26 import java.io.BufferedReader JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.io.InputStreamReader JavaDoc;
30 import java.io.OutputStream JavaDoc;
31 import java.io.OutputStreamWriter JavaDoc;
32 import java.io.PrintWriter JavaDoc;
33 import java.io.PushbackReader JavaDoc;
34 import java.io.StringWriter JavaDoc;
35 import java.io.Writer JavaDoc;
36 import java.math.BigInteger JavaDoc;
37 import java.util.BitSet JavaDoc;
38
39 public class Stream extends LispObject
40 {
41     protected LispObject elementType;
42     protected boolean isInputStream;
43     protected boolean isOutputStream;
44     protected boolean isCharacterStream;
45     protected boolean isBinaryStream;
46
47     private boolean interactive;
48     private boolean open = true;
49
50     // Character input.
51
private PushbackReader JavaDoc reader;
52     protected int offset;
53     protected int lineNumber;
54
55     // Character output.
56
private Writer JavaDoc writer;
57
58     // The number of characters on the current line of output (-1 if unknown).
59
protected int charPos;
60
61     // Binary input.
62
private BufferedInputStream JavaDoc in;
63
64     // Binary output.
65
private BufferedOutputStream JavaDoc out;
66
67     protected Stream()
68     {
69     }
70
71     // Input stream constructors.
72
public Stream(InputStream JavaDoc inputStream, LispObject elementType)
73     {
74         this.elementType = elementType;
75         if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
76             isCharacterStream = true;
77             InputStreamReader JavaDoc inputStreamReader;
78             try {
79                 inputStreamReader =
80                     new InputStreamReader JavaDoc(inputStream, "ISO-8859-1");
81             }
82             catch (java.io.UnsupportedEncodingException JavaDoc e) {
83                 Debug.trace(e);
84                 inputStreamReader =
85                     new InputStreamReader JavaDoc(inputStream);
86             }
87             reader = new PushbackReader JavaDoc(new BufferedReader JavaDoc(inputStreamReader),
88                                         2);
89         } else {
90             isBinaryStream = true;
91             in = new BufferedInputStream JavaDoc(inputStream);
92         }
93         isInputStream = true;
94         isOutputStream = false;
95     }
96
97     public Stream(InputStream JavaDoc inputStream, LispObject elementType, boolean interactive)
98     {
99         this(inputStream, elementType);
100         setInteractive(interactive);
101     }
102
103     // Output stream constructors.
104
public Stream(OutputStream JavaDoc outputStream, LispObject elementType)
105     {
106         this.elementType = elementType;
107         if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
108             isCharacterStream = true;
109             try {
110                 writer = new OutputStreamWriter JavaDoc(outputStream, "ISO-8859-1");
111             }
112             catch (java.io.UnsupportedEncodingException JavaDoc e) {
113                 Debug.trace(e);
114                 writer = new OutputStreamWriter JavaDoc(outputStream);
115             }
116         } else {
117             isBinaryStream = true;
118             out = new BufferedOutputStream JavaDoc(outputStream);
119         }
120         isInputStream = false;
121         isOutputStream = true;
122     }
123
124     public Stream(OutputStream JavaDoc outputStream, LispObject elementType,
125                   boolean interactive)
126     {
127         this(outputStream, elementType);
128         setInteractive(interactive);
129     }
130
131     public boolean isInputStream() throws ConditionThrowable
132     {
133         return isInputStream;
134     }
135
136     public boolean isOutputStream() throws ConditionThrowable
137     {
138         return isOutputStream;
139     }
140
141     public boolean isCharacterInputStream() throws ConditionThrowable
142     {
143         return isCharacterStream && isInputStream;
144     }
145
146     public boolean isBinaryInputStream() throws ConditionThrowable
147     {
148         return isBinaryStream && isInputStream;
149     }
150
151     public boolean isCharacterOutputStream() throws ConditionThrowable
152     {
153         return isCharacterStream && isOutputStream;
154     }
155
156     public boolean isBinaryOutputStream() throws ConditionThrowable
157     {
158         return isBinaryStream && isOutputStream;
159     }
160
161     public boolean isInteractive()
162     {
163         return interactive;
164     }
165
166     public void setInteractive(boolean b)
167     {
168         interactive = b;
169     }
170
171     public boolean isOpen()
172     {
173         return open;
174     }
175
176     public void setOpen(boolean b)
177     {
178         open = b;
179     }
180
181     public LispObject typeOf()
182     {
183         return Symbol.STREAM;
184     }
185
186     public LispClass classOf()
187     {
188         return BuiltInClass.STREAM;
189     }
190
191     public LispObject typep(LispObject typeSpecifier) throws ConditionThrowable
192     {
193         if (typeSpecifier == Symbol.STREAM)
194             return T;
195         if (typeSpecifier == BuiltInClass.STREAM)
196             return T;
197         return super.typep(typeSpecifier);
198     }
199
200     public LispObject getElementType() throws ConditionThrowable
201     {
202         return elementType;
203     }
204
205     // Character input.
206
public int getOffset()
207     {
208         return offset;
209     }
210
211     // Character input.
212
public int getLineNumber()
213     {
214         return lineNumber;
215     }
216
217     protected void setWriter(Writer JavaDoc writer)
218     {
219         this.writer = writer;
220     }
221
222     // Character output.
223
public int getCharPos()
224     {
225         return charPos;
226     }
227
228     // Character output.
229
public void setCharPos(int n)
230     {
231         charPos = n;
232     }
233
234     public LispObject read(boolean eofError, LispObject eofValue,
235                            boolean recursive)
236         throws ConditionThrowable
237     {
238         LispObject result = readPreservingWhitespace(eofError, eofValue,
239                                                      recursive);
240         if (result != eofValue && !recursive) {
241             if (_charReady()) {
242                 int n = _readChar();
243                 if (n >= 0) {
244                     char c = (char) n;
245                     if (!currentReadtable().isWhitespace(c))
246                         _unreadChar(c);
247                 }
248             }
249         }
250         if (_READ_SUPPRESS_.symbolValueNoThrow() != NIL)
251             return NIL;
252         else
253             return result;
254     }
255
256     public LispObject readPreservingWhitespace(boolean eofError,
257                                                LispObject eofValue,
258                                                boolean recursive)
259         throws ConditionThrowable
260     {
261         final Readtable rt = currentReadtable();
262         while (true) {
263             int n = _readChar();
264             if (n < 0) {
265                 if (eofError)
266                     return signal(new EndOfFile(this));
267                 else
268                     return eofValue;
269             }
270             char c = (char) n;
271             if (rt.isWhitespace(c))
272                 continue;
273             LispObject result = processChar(c);
274             if (result != null)
275                 return result;
276         }
277     }
278
279     private LispObject processChar(char c) throws ConditionThrowable
280     {
281         LispObject handler = currentReadtable().getReaderMacroFunction(c);
282         if (handler instanceof ReaderMacroFunction)
283             return ((ReaderMacroFunction)handler).execute(this, c);
284         if (handler != null && handler != NIL)
285             return handler.execute(this, LispCharacter.getInstance(c));
286         return readToken(c);
287     }
288
289     public LispObject readPathname() throws ConditionThrowable
290     {
291         LispObject obj = read(true, NIL, false);
292         if (obj instanceof AbstractString)
293             return new Pathname(obj.getStringValue());
294         if (obj.listp())
295             return Pathname.makePathname(obj);
296         return signal(new TypeError("#p requires a string or list argument."));
297     }
298
299     public LispObject readSymbol() throws ConditionThrowable
300     {
301         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
302         _readToken(sb, LispThread.currentThread());
303         return new Symbol(sb.toString());
304     }
305
306     public LispObject readStructure() throws ConditionThrowable
307     {
308         LispObject obj = read(true, NIL, false);
309         if (_READ_SUPPRESS_.symbolValueNoThrow() != NIL)
310             return NIL;
311         if (obj.listp()) {
312             Symbol structure = checkSymbol(obj.car());
313             LispClass c = LispClass.findClass(structure);
314             if (!(c instanceof StructureClass)) {
315                 return signal(new ReaderError(structure.getName() +
316                                               " is not a defined structure type."));
317             }
318             LispObject args = obj.cdr();
319             Package JavaDoc pkg = checkPackage(structure.getPackage());
320             Symbol constructor = pkg.intern("MAKE-" + structure.getName());
321             return funcall(constructor.getSymbolFunctionOrDie(),
322                            args.copyToArray(), LispThread.currentThread());
323         }
324         return signal(new ReaderError("Non-list following #S: " + obj));
325     }
326
327     public LispObject readList() throws ConditionThrowable
328     {
329         Cons first = null;
330         Cons last = null;
331         while (true) {
332             char c = flushWhitespace();
333             if (c == ')') {
334                 return first == null ? NIL : first;
335             }
336             if (c == '.') {
337                 int n = _readChar();
338                 if (n < 0)
339                     return signal(new EndOfFile(this));
340                 char nextChar = (char) n;
341                 if (nextChar == ',') {
342                     LispObject obj = readComma();
343                     last.setCdr(obj);
344                     continue;
345                 } else if (isTokenDelimiter(nextChar)) {
346                     if (last == null)
347                         return signal(new ReaderError("Nothing appears before . in list."));
348                     LispObject obj = read(true, NIL, true);
349                     last.setCdr(obj);
350                     continue;
351                 } else {
352                     // normal token beginning with '.'
353
_unreadChar(nextChar);
354                 }
355             }
356             LispObject obj = processChar(c);
357             if (obj == null) {
358                 // A comment.
359
continue;
360             }
361             if (first == null) {
362                 first = new Cons(obj);
363                 last = first;
364             } else {
365                 Cons newCons = new Cons(obj);
366                 last.setCdr(newCons);
367                 last = newCons;
368             }
369         }
370     }
371
372     private static final boolean isTokenDelimiter(char c)
373         throws ConditionThrowable
374     {
375         switch (c) {
376             case '"':
377             case '\'':
378             case '(':
379             case ')':
380             case ',':
381             case ';':
382             case '`':
383                 return true;
384             default:
385                 return currentReadtable().isWhitespace(c);
386         }
387     }
388
389     public LispObject readComma() throws ConditionThrowable
390     {
391         int n = _readChar();
392         if (n < 0)
393             return signal(new EndOfFile(this));
394         char c = (char) n;
395         switch (c) {
396             case '@':
397                 return new Cons(Symbol.COMMA_ATSIGN,
398                                 new Cons(read(true, NIL, true), NIL));
399             case '.':
400                 return new Cons(Symbol.COMMA_DOT,
401                                 new Cons(read(true, NIL, true), NIL));
402             default:
403                 _unreadChar(c);
404                 return new Cons(Symbol.COMMA,
405                                 new Cons(read(true, NIL, true), NIL));
406         }
407     }
408
409     public LispObject readDispatchChar(char ignored) throws ConditionThrowable
410     {
411         int numArg = -1;
412         char c;
413         while (true) {
414             int n = _readChar();
415             if (n < 0)
416                 return signal(new EndOfFile(this));
417             c = (char) n;
418             if (c < '0' || c > '9')
419                 break;
420             if (numArg < 0)
421                 numArg = 0;
422             numArg = numArg * 10 + c - '0';
423         }
424         LispObject fun =
425             currentReadtable().getDispatchMacroCharacter('#', c);
426         if (fun instanceof DispatchMacroFunction)
427             return ((DispatchMacroFunction)fun).execute(this, c, numArg);
428         if (fun != NIL) {
429             final LispThread thread = LispThread.currentThread();
430             LispObject result = funcall3(fun,
431                                          this,
432                                          LispCharacter.getInstance(c),
433                                          (numArg < 0) ? NIL : new Fixnum(numArg),
434                                          thread);
435             LispObject[] values = thread.getValues();
436             if (values != null && values.length == 0)
437                 result = null;
438             thread.clearValues();
439             return result;
440         }
441         return null;
442     }
443
444     public LispObject readCharacterLiteral() throws ConditionThrowable
445     {
446         int n = _readChar();
447         if (n < 0)
448             return signal(new EndOfFile(this));
449         char c = (char) n;
450         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
451         sb.append(c);
452         Readtable rt = currentReadtable();
453         while (true) {
454             n = _readChar();
455             if (n < 0)
456                 break;
457             c = (char) n;
458             if (rt.isWhitespace(c))
459                 break;
460             if (c == '(' || c == ')') {
461                 _unreadChar(c);
462                 break;
463             }
464             sb.append(c);
465         }
466         if (_READ_SUPPRESS_.symbolValueNoThrow() != NIL)
467             return NIL;
468         String JavaDoc token = sb.toString();
469         if (token.length() == 1)
470             return LispCharacter.getInstance(token.charAt(0));
471         n = LispCharacter.nameToChar(token);
472         if (n >= 0)
473             return LispCharacter.getInstance((char)n);
474         return signal(new LispError("Unrecognized character name: \"" + token + '"'));
475     }
476
477     public void skipBalancedComment() throws ConditionThrowable
478     {
479         while (true) {
480             int n = _readChar();
481             if (n < 0)
482                 return;
483             if (n == '|') {
484                 n = _readChar();
485                 if (n == '#')
486                     return;
487                 else
488                     _unreadChar(n);
489             } else if (n == '#') {
490                 n = _readChar();
491                 if (n == '|')
492                     skipBalancedComment(); // Nested comment. Recurse!
493
else
494                     _unreadChar(n);
495             }
496         }
497     }
498
499     public LispObject readBitVector() throws ConditionThrowable
500     {
501         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
502         while (true) {
503             int n = _readChar();
504             if (n < 0)
505                 break;
506             char c = (char) n;
507             if (c == '0' || c == '1')
508                 sb.append(c);
509             else {
510                 _unreadChar(c);
511                 break;
512             }
513         }
514         return new SimpleBitVector(sb.toString());
515     }
516
517     public LispObject readArray(int rank) throws ConditionThrowable
518     {
519         LispObject obj = read(true, NIL, true);
520         switch (rank) {
521             case -1:
522                 return signal(new ReaderError("No dimensions argument to #A."));
523             case 0:
524                 return new ZeroRankArray(T, obj, false);
525             case 1:
526                 return new SimpleVector(obj);
527             default:
528                 return new SimpleArray(rank, obj);
529         }
530     }
531
532     public LispObject readComplex() throws ConditionThrowable
533     {
534         LispObject obj = read(true, NIL, true);
535         if (_READ_SUPPRESS_.symbolValue() != NIL)
536             return NIL;
537         if (obj instanceof Cons && obj.length() == 2)
538             return Complex.getInstance(obj.car(), obj.cadr());
539         // Error.
540
StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Invalid complex number format");
541         if (this instanceof FileStream) {
542             Pathname p = ((FileStream)this).getPathname();
543             if (p != null) {
544                 String JavaDoc namestring = p.getNamestring();
545                 if (namestring != null) {
546                     sb.append(" in #P\"");
547                     sb.append(namestring);
548                     sb.append('"');
549                 }
550             }
551             sb.append(" at offset ");
552             sb.append(_getFilePosition());
553         }
554         sb.append(": #C");
555         sb.append(obj.writeToString());
556         return signal(new ReaderError(sb.toString()));
557     }
558
559     private String JavaDoc readMultipleEscape() throws ConditionThrowable
560     {
561         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
562         while (true) {
563             int n = _readChar();
564             if (n < 0)
565                 break;
566             char c = (char) n;
567             if (c == '\\') {
568                 n = _readChar();
569                 if (n < 0) {
570                     signal(new EndOfFile(this));
571                     // Not reached.
572
return null;
573                 }
574                 sb.append((char)n);
575                 continue;
576             }
577             if (c == '|')
578                 break;
579             sb.append(c);
580         }
581         return sb.toString();
582     }
583
584     private static final int findUnescapedSingleColon(String JavaDoc s, BitSet JavaDoc flags)
585     {
586         if (flags == null)
587             return s.indexOf(':');
588         final int limit = s.length();
589         for (int i = 0; i < limit; i++) {
590             if (s.charAt(i) == ':' && !flags.get(i)) {
591                 return i;
592             }
593         }
594         return -1;
595     }
596
597     private static final int findUnescapedDoubleColon(String JavaDoc s, BitSet JavaDoc flags)
598     {
599         if (flags == null)
600             return s.indexOf("::");
601         final int limit = s.length() - 1;
602         for (int i = 0; i < limit; i++) {
603             if (s.charAt(i) == ':' && !flags.get(i)) {
604                 if (s.charAt(i + 1) == ':' && !flags.get(i + 1)) {
605                     return i;
606                 }
607             }
608         }
609         return -1;
610     }
611
612     private final LispObject readToken(char c) throws ConditionThrowable
613     {
614         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
615         sb.append(c);
616         final LispThread thread = LispThread.currentThread();
617         BitSet JavaDoc flags = _readToken(sb, thread);
618         boolean escaped = (flags != null);
619         if (_READ_SUPPRESS_.symbolValue(thread) != NIL)
620             return NIL;
621         final String JavaDoc token = sb.toString();
622         final int length = token.length();
623         if (length > 0) {
624             final char firstChar = token.charAt(0);
625             if (flags == null) {
626                 if (firstChar == '.') {
627                     // Section 2.3.3: "If a token consists solely of dots (with
628
// no escape characters), then an error of type READER-
629
// ERROR is signaled, except in one circumstance: if the
630
// token is a single dot and appears in a situation where
631
// dotted pair notation permits a dot, then it is accepted
632
// as part of such syntax and no error is signaled."
633
boolean ok = false;
634                     for (int i = length; i-- > 1;) {
635                         if (token.charAt(i) != '.') {
636                             ok = true;
637                             break;
638                         }
639                     }
640                     if (!ok) {
641                         final String JavaDoc message;
642                         if (length > 1)
643                             message = "Too many dots.";
644                         else
645                             message = "Dot context error.";
646                         return signal(new ReaderError(message));
647                     }
648                 }
649                 final int radix = getReadBase(thread);
650                 if ("-+0123456789".indexOf(firstChar) >= 0) {
651                     LispObject number = makeNumber(token, length, radix);
652                     if (number != null)
653                         return number;
654                 } else if (Character.digit(firstChar, radix) >= 0) {
655                     LispObject number = makeNumber(token, length, radix);
656                     if (number != null)
657                         return number;
658                 }
659             }
660             if (firstChar == ':')
661                 return PACKAGE_KEYWORD.intern(token.substring(1));
662             int index = findUnescapedDoubleColon(token, flags);
663             if (index > 0) {
664                 String JavaDoc packageName = token.substring(0, index);
665                 String JavaDoc symbolName = token.substring(index + 2);
666                 Package JavaDoc pkg = Packages.findPackage(packageName);
667                 if (pkg == null)
668                     return signal(new LispError("Package \"" + packageName +
669                                                 "\" not found."));
670                 return pkg.intern(symbolName);
671             }
672             index = findUnescapedSingleColon(token, flags);
673             if (index > 0) {
674                 String JavaDoc packageName = token.substring(0, index);
675                 String JavaDoc symbolName = token.substring(index + 1);
676                 Package JavaDoc pkg = Packages.findPackage(packageName);
677                 if (pkg == null)
678                     return signal(new PackageError("Package \"" + packageName +
679                                                    "\" not found."));
680                 Symbol symbol = pkg.findExternalSymbol(symbolName);
681                 if (symbol != null)
682                     return symbol;
683                 // Error!
684
if (pkg.findInternalSymbol(symbolName) != null)
685                     return signal(new LispError("The symbol \"" + symbolName +
686                                                 "\" is not external in package " +
687                                                 packageName + '.'));
688                 else
689                     return signal(new LispError("The symbol \"" + symbolName +
690                                                 "\" was not found in package " +
691                                                 packageName + '.'));
692             }
693         }
694         // Intern token in current package.
695
return ((Package JavaDoc)_PACKAGE_.symbolValue(thread)).intern(token);
696     }
697
698     private final BitSet JavaDoc _readToken(StringBuffer JavaDoc sb, LispThread thread)
699         throws ConditionThrowable
700     {
701         BitSet JavaDoc flags = null;
702         final Readtable rt = currentReadtable(thread);
703         final LispObject readtableCase = rt.getReadtableCase();
704         if (sb.length() > 0) {
705             Debug.assertTrue(sb.length() == 1);
706             char c = sb.charAt(0);
707             if (c == '|') {
708                 sb.setLength(0);
709                 sb.append(readMultipleEscape());
710                 flags = new BitSet JavaDoc(sb.length());
711                 for (int i = sb.length(); i-- > 0;)
712                     flags.set(i);
713             } else if (c == '\\') {
714                 int n = _readChar();
715                 if (n < 0) {
716                     signal(new EndOfFile(this));
717                     // Not reached.
718
return flags;
719                 }
720                 sb.setCharAt(0, (char) n);
721                 flags = new BitSet JavaDoc(1);
722                 flags.set(0);
723             } else if (readtableCase == Keyword.UPCASE) {
724                 sb.setCharAt(0, Utilities.toUpperCase(c));
725             } else if (readtableCase == Keyword.DOWNCASE) {
726                 sb.setCharAt(0, Utilities.toLowerCase(c));
727             }
728         }
729       loop:
730         while (true) {
731             int n = _readChar();
732             if (n < 0)
733                 break;
734             char c = (char) n;
735             if (rt.isWhitespace(c))
736                 break;
737             if (rt.getAttribute(c) == Readtable.ATTR_TERMINATING_MACRO) {
738                 _unreadChar(c);
739                 break;
740             }
741             switch (c) {
742                 case '\\':
743                     n = _readChar();
744                     if (n < 0)
745                         break loop;
746                     sb.append((char)n);
747                     if (flags == null)
748                         flags = new BitSet JavaDoc(sb.length());
749                     flags.set(sb.length() - 1);
750                     break;
751                 case '|': {
752                     int begin = sb.length();
753                     sb.append(readMultipleEscape());
754                     int end = sb.length();
755                     if (flags == null)
756                         flags = new BitSet JavaDoc(sb.length());
757                     for (int i = begin; i < end; i++)
758                         flags.set(i);
759                     break;
760                 }
761                 default:
762                     if (readtableCase == Keyword.UPCASE)
763                         c = Utilities.toUpperCase(c);
764                     else if (readtableCase == Keyword.DOWNCASE)
765                         c = Utilities.toLowerCase(c);
766                     sb.append(c);
767             }
768         }
769         if (readtableCase == Keyword.INVERT) {
770             // FIXME Preserve case of escaped characters!
771
String JavaDoc s = invert(sb.toString());
772             sb.setLength(0);
773             sb.append(s);
774         }
775         return flags;
776     }
777
778     private static final int getReadBase(LispThread thread)
779         throws ConditionThrowable
780     {
781         final int readBase;
782         try {
783             readBase = ((Fixnum)_READ_BASE_.symbolValue(thread)).value;
784         }
785         catch (ClassCastException JavaDoc e) {
786             // The value of *READ-BASE* is not a Fixnum.
787
signal(new LispError("The value of *READ-BASE* is not of type '(INTEGER 2 36)."));
788             // Not reached.
789
return 10;
790         }
791         if (readBase < 2 || readBase > 36) {
792             signal(new LispError("The value of *READ-BASE* is not of type '(INTEGER 2 36)."));
793             // Not reached.
794
return 10;
795         }
796         return readBase;
797     }
798
799     private static final LispObject makeNumber(String JavaDoc token,
800                                                int length,
801                                                int radix)
802         throws ConditionThrowable
803     {
804         if (length == 0)
805             return null;
806         if (token.indexOf('/') >= 0)
807             return makeRatio(token, radix);
808         if (token.charAt(length - 1) == '.') {
809             radix = 10;
810             token = token.substring(0, --length);
811         }
812         boolean numeric = true;
813         if (radix == 10) {
814             for (int i = length; i-- > 0;) {
815                 char c = token.charAt(i);
816                 if (c < '0' || c > '9') {
817                     if (i > 0 || (c != '-' && c != '+')) {
818                         numeric = false;
819                         break;
820                     }
821                 }
822             }
823         } else {
824             for (int i = length; i-- > 0;) {
825                 char c = token.charAt(i);
826                 if (Character.digit(c, radix) < 0) {
827                     if (i > 0 || (c != '-' && c != '+')) {
828                         numeric = false;
829                         break;
830                     }
831                 }
832             }
833         }
834         if (!numeric) // Can't be an integer.
835
return makeFloat(token, length);
836         if (token.charAt(0) == '+')
837             token = token.substring(1);
838         try {
839             return new Fixnum(Integer.parseInt(token, radix));
840         }
841         catch (NumberFormatException JavaDoc e) {}
842         // parseInt() failed.
843
try {
844             return new Bignum(new BigInteger JavaDoc(token, radix));
845         }
846         catch (NumberFormatException JavaDoc e) {}
847         // Not a number.
848
return null;
849     }
850
851     private static final LispObject makeRatio(String JavaDoc token, int radix)
852         throws ConditionThrowable
853     {
854         final int index = token.indexOf('/');
855         if (index < 0)
856             return null;
857         try {
858             BigInteger JavaDoc numerator =
859                 new BigInteger JavaDoc(token.substring(0, index), radix);
860             BigInteger JavaDoc denominator =
861                 new BigInteger JavaDoc(token.substring(index + 1), radix);
862             return number(numerator, denominator);
863         }
864         catch (NumberFormatException JavaDoc e) {}
865         return null;
866     }
867
868     private static final LispObject makeFloat(final String JavaDoc token,
869                                               final int length)
870         throws ConditionThrowable
871     {
872         if (length == 0)
873             return null;
874         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
875         int i = 0;
876         boolean maybe = false;
877         char c = token.charAt(i);
878         if (c == '-' || c == '+') {
879             sb.append(c);
880             ++i;
881         }
882         while (i < length) {
883             c = token.charAt(i);
884             if (c == '.' || (c >= '0' && c <= '9')) {
885                 if (c == '.')
886                     maybe = true;
887                 sb.append(c);
888                 ++i;
889             } else
890                 break;
891         }
892         if (i < length) {
893             if ("esfdlESFDL".indexOf(token.charAt(i)) >= 0) {
894                 // Exponent marker.
895
maybe = true;
896                 sb.append('E');
897                 ++i;
898             }
899         }
900         if (!maybe)
901             return null;
902         // Append rest of token.
903
sb.append(token.substring(i));
904         try {
905             return new LispFloat(Double.parseDouble(sb.toString()));
906         }
907         catch (NumberFormatException JavaDoc e) {
908             return null;
909         }
910     }
911
912     public LispObject readRadix(int radix) throws ConditionThrowable
913     {
914         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
915         boolean escaped = (_readToken(sb, LispThread.currentThread()) != null);
916         if (escaped)
917             return signal(new ReaderError("Illegal syntax for number."));
918         String JavaDoc s = sb.toString();
919         if (s.indexOf('/') >= 0)
920             return makeRatio(s, radix);
921         try {
922             return new Fixnum(Integer.parseInt(s, radix));
923         }
924         catch (NumberFormatException JavaDoc e) {}
925         // parseInt() failed.
926
try {
927             return new Bignum(new BigInteger JavaDoc(s, radix));
928         }
929         catch (NumberFormatException JavaDoc e) {}
930         // Not a number.
931
return signal(new LispError());
932     }
933
934     private char flushWhitespace() throws ConditionThrowable
935     {
936         final Readtable rt = currentReadtable();
937         while (true) {
938             int n = _readChar();
939             if (n < 0) {
940                 signal(new EndOfFile(this));
941                 // Not reached.
942
return 0;
943             }
944             char c = (char) n;
945             if (!rt.isWhitespace(c))
946                 return c;
947         }
948     }
949
950     public LispObject readDelimitedList(char delimiter)
951         throws ConditionThrowable
952     {
953         LispObject result = NIL;
954         while (true) {
955             char c = flushWhitespace();
956             if (c == delimiter)
957                 break;
958             LispObject obj = processChar(c);
959             if (obj != null)
960                 result = new Cons(obj, result);
961         }
962         return result.nreverse();
963     }
964
965     // read-line &optional stream eof-error-p eof-value recursive-p
966
// => line, missing-newline-p
967
// recursive-p is ignored
968
public LispObject readLine(boolean eofError, LispObject eofValue)
969         throws ConditionThrowable
970     {
971         final LispThread thread = LispThread.currentThread();
972         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
973         while (true) {
974             int n = _readChar();
975             if (n < 0) {
976                 if (sb.length() == 0) {
977                     if (eofError)
978                         return signal(new EndOfFile(this));
979                     return thread.setValues(eofValue, T);
980                 }
981                 return thread.setValues(new SimpleString(sb), T);
982             }
983             if (n == '\n')
984                 return thread.setValues(new SimpleString(sb), NIL);
985             else
986                 sb.append((char)n);
987         }
988     }
989
990     // read-char &optional stream eof-error-p eof-value recursive-p => char
991
// recursive-p is ignored
992
public LispObject readChar(boolean eofError, LispObject eofValue)
993         throws ConditionThrowable
994     {
995         int n = _readChar();
996         if (n < 0) {
997             if (eofError)
998                 return signal(new EndOfFile(this));
999             else
1000                return eofValue;
1001        }
1002        return LispCharacter.getInstance((char)n);
1003    }
1004
1005    // read-char-no-hang &optional stream eof-error-p eof-value recursive-p => char
1006
// recursive-p is ignored
1007
public LispObject readCharNoHang(boolean eofError, LispObject eofValue)
1008        throws ConditionThrowable
1009    {
1010        return _charReady() ? readChar(eofError, eofValue) : NIL;
1011    }
1012
1013    // unread-char character &optional input-stream => nil
1014
public LispObject unreadChar(LispCharacter c) throws ConditionThrowable
1015    {
1016        _unreadChar(c.getValue());
1017        return NIL;
1018    }
1019
1020    public LispObject finishOutput() throws ConditionThrowable
1021    {
1022        _finishOutput();
1023        return NIL;
1024    }
1025
1026    // clear-input &optional input-stream => nil
1027
public LispObject clearInput() throws ConditionThrowable
1028    {
1029        _clearInput();
1030        return NIL;
1031    }
1032
1033    public LispObject getFilePosition() throws ConditionThrowable
1034    {
1035        long pos = _getFilePosition();
1036        return pos >= 0 ? number(pos) : NIL;
1037    }
1038
1039    public LispObject setFilePosition(LispObject arg) throws ConditionThrowable
1040    {
1041        return _setFilePosition(arg) ? T : NIL;
1042    }
1043
1044    // close stream &key abort => result
1045
// Must return true if stream was open, otherwise implementation-dependent.
1046
public LispObject close(LispObject abort) throws ConditionThrowable
1047    {
1048        _close();
1049        return T;
1050    }
1051
1052    public String JavaDoc toString()
1053    {
1054        return unreadableString("STREAM");
1055    }
1056
1057    // read-byte stream &optional eof-error-p eof-value => byte
1058
// Reads an 8-bit byte.
1059
public LispObject readByte(boolean eofError, LispObject eofValue)
1060        throws ConditionThrowable
1061    {
1062        int n = _readByte();
1063        if (n < 0) {
1064            if (eofError)
1065                return signal(new EndOfFile(this));
1066            else
1067                return eofValue;
1068        }
1069        return new Fixnum(n);
1070    }
1071
1072    public LispObject terpri() throws ConditionThrowable
1073    {
1074        _writeChar('\n');
1075        return NIL;
1076    }
1077
1078    public LispObject freshLine() throws ConditionThrowable
1079    {
1080        if (charPos == 0)
1081            return NIL;
1082        _writeChar('\n');
1083        return T;
1084    }
1085
1086    public void print(char c) throws ConditionThrowable
1087    {
1088        _writeChar(c);
1089    }
1090
1091    // PRIN1 produces output suitable for input to READ.
1092
// Binds *PRINT-ESCAPE* to true.
1093
public void prin1(LispObject obj) throws ConditionThrowable
1094    {
1095        LispThread thread = LispThread.currentThread();
1096        Environment oldDynEnv = thread.getDynamicEnvironment();
1097        thread.bindSpecial(_PRINT_ESCAPE_, T);
1098        String JavaDoc s = obj.writeToString();
1099        thread.setDynamicEnvironment(oldDynEnv);
1100        _writeString(s);
1101    }
1102
1103    public LispObject listen() throws ConditionThrowable
1104    {
1105        return _charReady() ? T : NIL;
1106    }
1107
1108    public LispObject fileLength() throws ConditionThrowable
1109    {
1110        return signal(new TypeError("Stream is not associated with a file."));
1111    }
1112
1113    public LispObject fileStringLength(LispObject arg) throws ConditionThrowable
1114    {
1115        if (arg instanceof LispCharacter)
1116            return Fixnum.ONE;
1117        else if (arg instanceof AbstractString)
1118            return number(arg.length());
1119        else
1120            return signal(new TypeError(String.valueOf(arg) +
1121                                        " is neither a string nor a character."));
1122    }
1123
1124    // Returns -1 at end of file.
1125
protected int _readChar() throws ConditionThrowable
1126    {
1127        try {
1128            int n = reader.read();
1129            ++offset;
1130            if (n == '\r') {
1131                if (interactive && Utilities.isPlatformWindows())
1132                    return _readChar();
1133            }
1134            if (n == '\n')
1135                ++lineNumber;
1136            return n;
1137        }
1138        catch (IOException JavaDoc e) {
1139            signal(new StreamError(this, e));
1140            // Not reached.
1141
return -1;
1142        }
1143    }
1144
1145    protected void _unreadChar(int n) throws ConditionThrowable
1146    {
1147        try {
1148            reader.unread(n);
1149            --offset;
1150            if (n == '\n')
1151                --lineNumber;
1152        }
1153        catch (IOException JavaDoc e) {
1154            signal(new StreamError(this, e));
1155        }
1156    }
1157
1158    protected boolean _charReady() throws ConditionThrowable
1159    {
1160        try {
1161            return reader.ready();
1162        }
1163        catch (IOException JavaDoc e) {
1164            signal(new StreamError(this, e));
1165            // Not reached.
1166
return false;
1167        }
1168    }
1169
1170    public void _writeChar(char c) throws ConditionThrowable
1171    {
1172        try {
1173            writer.write(c);
1174            if (c == '\n') {
1175                writer.flush();
1176                charPos = 0;
1177            } else
1178                ++charPos;
1179        }
1180        catch (IOException JavaDoc e) {
1181            signal(new StreamError(this, e));
1182        }
1183    }
1184
1185    public void _writeChars(char[] chars, int start, int end)
1186        throws ConditionThrowable
1187    {
1188        try {
1189            writer.write(chars, start, end - start);
1190            int index = -1;
1191            for (int i = end; i-- > start;) {
1192                if (chars[i] == '\n') {
1193                    index = i;
1194                    break;
1195                }
1196            }
1197            if (index < 0) {
1198                // No newline.
1199
charPos += (end - start);
1200            } else {
1201                charPos = end - (index + 1);
1202                writer.flush();
1203            }
1204        }
1205        catch (IOException JavaDoc e) {
1206            signal(new StreamError(this, e));
1207        }
1208    }
1209
1210    public void _writeString(String JavaDoc s) throws ConditionThrowable
1211    {
1212        try {
1213            writer.write(s);
1214            int index = s.lastIndexOf('\n');
1215            if (index < 0)
1216                charPos += s.length();
1217            else {
1218                charPos = s.length() - (index + 1);
1219                writer.flush();
1220            }
1221        }
1222        catch (IOException JavaDoc e) {
1223            signal(new StreamError(this, e));
1224        }
1225    }
1226
1227    public void _writeLine(String JavaDoc s) throws ConditionThrowable
1228    {
1229        try {
1230            writer.write(s);
1231            writer.write('\n');
1232            writer.flush();
1233            charPos = 0;
1234        }
1235        catch (IOException JavaDoc e) {
1236            signal(new StreamError(this, e));
1237        }
1238    }
1239
1240    // Reads an 8-bit byte.
1241
public int _readByte() throws ConditionThrowable
1242    {
1243        try {
1244            return in.read(); // Reads an 8-bit byte.
1245
}
1246        catch (IOException JavaDoc e) {
1247            signal(new StreamError(this, e));
1248            // Not reached.
1249
return -1;
1250        }
1251    }
1252
1253    // Writes an 8-bit byte.
1254
public void _writeByte(int n) throws ConditionThrowable
1255    {
1256        try {
1257            out.write(n); // Writes an 8-bit byte.
1258
}
1259        catch (IOException JavaDoc e) {
1260            signal(new StreamError(this, e));
1261        }
1262    }
1263
1264    public void _finishOutput() throws ConditionThrowable
1265    {
1266        try {
1267            if (writer != null)
1268                writer.flush();
1269            if (out != null)
1270                out.flush();
1271        }
1272        catch (IOException JavaDoc e) {
1273            signal(new StreamError(this, e));
1274        }
1275    }
1276
1277    public void _clearInput() throws ConditionThrowable
1278    {
1279        if (reader != null) {
1280            while (_charReady())
1281                _readChar();
1282        } else if (in != null) {
1283            try {
1284                while (in.available() > 0)
1285                    in.read();
1286            }
1287            catch (IOException JavaDoc e) {
1288                signal(new StreamError(this, e));
1289            }
1290        }
1291    }
1292
1293    protected long _getFilePosition() throws ConditionThrowable
1294    {
1295        return -1;
1296    }
1297
1298    protected boolean _setFilePosition(LispObject arg) throws ConditionThrowable
1299    {
1300        return false;
1301    }
1302
1303    public void _close() throws ConditionThrowable
1304    {
1305        try {
1306            if (reader != null)
1307                reader.close();
1308            if (in != null)
1309                in.close();
1310            if (writer != null)
1311                writer.close();
1312            if (out != null)
1313                out.close();
1314            setOpen(false);
1315        }
1316        catch (IOException JavaDoc e) {
1317            signal(new StreamError(this, e));
1318        }
1319    }
1320
1321    public void printStackTrace(Throwable JavaDoc t) throws ConditionThrowable
1322    {
1323        StringWriter JavaDoc sw = new StringWriter JavaDoc();
1324        PrintWriter JavaDoc pw = new PrintWriter JavaDoc(sw);
1325        t.printStackTrace(pw);
1326        try {
1327            writer.write(sw.toString());
1328            writer.write('\n');
1329            writer.flush();
1330            charPos = 0;
1331        }
1332        catch (IOException JavaDoc e) {
1333            signal(new StreamError(this, e));
1334        }
1335    }
1336
1337    // ### file-position
1338
private static final Primitive FILE_POSITION =
1339        new Primitive("file-position", "stream &optional position-spec")
1340    {
1341        public LispObject execute(LispObject arg) throws ConditionThrowable
1342        {
1343            return checkStream(arg).getFilePosition();
1344        }
1345        public LispObject execute(LispObject first, LispObject second)
1346            throws ConditionThrowable
1347        {
1348            return checkStream(first).setFilePosition(second);
1349        }
1350    };
1351
1352    // ### stream-line-number
1353
private static final Primitive1 STREAM_LINE_NUMBER =
1354        new Primitive1("stream-line-number", PACKAGE_SYS, false, "stream")
1355    {
1356        public LispObject execute(LispObject arg) throws ConditionThrowable
1357        {
1358            Stream stream = checkStream(arg);
1359            return number(stream.getLineNumber() + 1);
1360        }
1361    };
1362
1363    // ### stream-offset
1364
private static final Primitive1 STREAM_OFFSET =
1365        new Primitive1("stream-offset", PACKAGE_SYS, false, "stream")
1366    {
1367        public LispObject execute(LispObject arg) throws ConditionThrowable
1368        {
1369            Stream stream = checkStream(arg);
1370            return number(stream.getOffset());
1371        }
1372    };
1373}
1374
Popular Tags