source: branches/0.25.x/abcl/src/org/armedbear/lisp/Stream.java

Last change on this file was 13178, checked in by ehuelsmann, 15 years ago

Fix problem found by Blake McBride? while running SCONE.

Note: the problem was that SCONE tries to bind a symbol
named 1s2f, which was interpreted as a number by ABCL.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 87.0 KB
Line 
1/*
2 * Stream.java
3 *
4 * Copyright (C) 2003-2007 Peter Graves
5 * $Id: Stream.java 13178 2011-01-22 22:55:38Z ehuelsmann $
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 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.io.BufferedInputStream;
39import java.io.BufferedOutputStream;
40import java.io.IOException;
41import java.io.InputStream;
42import java.io.OutputStream;
43import java.io.OutputStreamWriter;
44import java.io.PrintWriter;
45import java.io.PushbackReader;
46import java.io.Reader;
47import java.io.StringWriter;
48import java.io.Writer;
49import java.math.BigInteger;
50import java.nio.charset.Charset;
51import java.util.BitSet;
52
53import org.armedbear.lisp.util.DecodingReader;
54
55/** The stream class
56 *
57 * A base class for all Lisp built-in streams.
58 *
59 */
60public class Stream extends StructureObject {
61    protected LispObject elementType;
62    protected boolean isInputStream;
63    protected boolean isOutputStream;
64    protected boolean isCharacterStream;
65    protected boolean isBinaryStream;
66
67    private boolean pastEnd = false;
68    private boolean interactive;
69    private boolean open = true;
70
71    // Character input.
72    protected PushbackReader reader;
73    protected int offset;
74    protected int lineNumber;
75
76    // Character output.
77    private Writer writer;
78
79    /** The number of characters on the current line of output
80     *
81     * Used to determine whether additional line feeds are
82     * required when calling FRESH-LINE
83     */
84    protected int charPos;
85
86    public enum EolStyle {
87        RAW,
88        CR,
89        CRLF,
90        LF
91    }
92
93    static final protected Symbol keywordDefault = internKeyword("DEFAULT");
94
95    static final private Symbol keywordCodePage = internKeyword("CODE-PAGE");
96    static final private Symbol keywordID = internKeyword("ID");
97
98    static final private Symbol keywordEolStyle = internKeyword("EOL-STYLE");
99    static final private Symbol keywordCR = internKeyword("CR");
100    static final private Symbol keywordLF = internKeyword("LF");
101    static final private Symbol keywordCRLF = internKeyword("CRLF");
102    static final private Symbol keywordRAW = internKeyword("RAW");
103
104    public final static EolStyle platformEolStyle = Utilities.isPlatformWindows ? EolStyle.CRLF : EolStyle.LF;
105
106    protected EolStyle eolStyle = platformEolStyle;
107    protected char eolChar = (eolStyle == EolStyle.CR) ? '\r' : '\n';
108    protected LispObject externalFormat = keywordDefault;
109    protected String encoding = null;
110    protected char lastChar = 0;
111
112    // Binary input.
113    private InputStream in;
114
115    // Binary output.
116    private OutputStream out;
117
118    protected Stream(Symbol structureClass) {
119        super(structureClass);
120    }
121
122    public Stream(Symbol structureClass, InputStream stream) {
123        this(structureClass);
124        initAsBinaryInputStream(stream);
125    }
126
127    public Stream(Symbol structureClass, Reader r) {
128        this(structureClass);
129        initAsCharacterInputStream(r);
130    }
131
132    public Stream(Symbol structureClass, OutputStream stream) {
133        this(structureClass);
134        initAsBinaryOutputStream(stream);
135    }
136
137    public Stream(Symbol structureClass, Writer w) {
138        this(structureClass);
139        initAsCharacterOutputStream(w);
140    }
141
142    public Stream(Symbol structureClass, InputStream inputStream, LispObject elementType) {
143        this(structureClass, inputStream, elementType, keywordDefault);
144    }
145
146
147    // Input stream constructors.
148    public Stream(Symbol structureClass, InputStream inputStream,
149                  LispObject elementType, LispObject format) {
150        this(structureClass);
151        this.elementType = elementType;
152        setExternalFormat(format);
153
154        if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
155            Reader r =
156                new DecodingReader(inputStream, 4096,
157                                   (encoding == null)
158                                   ? Charset.defaultCharset()
159                                   : Charset.forName(encoding));
160            initAsCharacterInputStream(r);
161        } else {
162            isBinaryStream = true;
163            InputStream stream = new BufferedInputStream(inputStream);
164            initAsBinaryInputStream(stream);
165        }
166    }
167
168    public Stream(Symbol structureClass, InputStream inputStream, LispObject elementType, boolean interactive) {
169        this(structureClass, inputStream, elementType);
170        setInteractive(interactive);
171    }
172
173    public Stream(Symbol structureClass, OutputStream outputStream, LispObject elementType) {
174        this(structureClass, outputStream, elementType, keywordDefault);
175    }
176
177    // Output stream constructors.
178    public Stream(Symbol structureClass, OutputStream outputStream, LispObject elementType, LispObject format) {
179        this(structureClass);
180        this.elementType = elementType;
181        setExternalFormat(format);
182        if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
183            Writer w =
184                (encoding == null) ?
185                new OutputStreamWriter(outputStream)
186                : new OutputStreamWriter(outputStream,
187                                         Charset.forName(encoding).newEncoder());
188            initAsCharacterOutputStream(w);
189        } else {
190            OutputStream stream = new BufferedOutputStream(outputStream);
191            initAsBinaryOutputStream(stream);
192        }
193    }
194
195    public Stream(Symbol structureClass, OutputStream outputStream,
196                  LispObject elementType,
197                  boolean interactive) {
198        this(structureClass, outputStream, elementType);
199        setInteractive(interactive);
200    }
201
202    protected void initAsCharacterInputStream(Reader reader) {
203        if (! (reader instanceof PushbackReader))
204            this.reader = new PushbackReader(reader, 5);
205        else
206            this.reader = (PushbackReader)reader;
207
208        isInputStream = true;
209        isCharacterStream = true;
210    }
211
212    protected void initAsBinaryInputStream(InputStream in) {
213        this.in = in;
214        isInputStream = true;
215        isBinaryStream = true;
216    }
217
218    protected void initAsCharacterOutputStream(Writer writer) {
219        this.writer = writer;
220        isOutputStream = true;
221        isCharacterStream = true;
222    }
223
224    protected void initAsBinaryOutputStream(OutputStream out) {
225        this.out = out;
226        isOutputStream = true;
227        isBinaryStream = true;
228    }
229
230    public boolean isInputStream() {
231        return isInputStream;
232    }
233
234    public boolean isOutputStream() {
235        return isOutputStream;
236    }
237
238    public boolean isCharacterInputStream() {
239        return isCharacterStream && isInputStream;
240    }
241
242    public boolean isBinaryInputStream() {
243        return isBinaryStream && isInputStream;
244    }
245
246    public boolean isCharacterOutputStream() {
247        return isCharacterStream && isOutputStream;
248    }
249
250    public boolean isBinaryOutputStream() {
251        return isBinaryStream && isOutputStream;
252    }
253
254    public boolean isInteractive() {
255        return interactive;
256    }
257
258    public void setInteractive(boolean b) {
259        interactive = b;
260    }
261
262    public LispObject getExternalFormat() {
263        return externalFormat;
264    }
265
266    public String getEncoding() {
267        return encoding;
268    }
269
270    public void setExternalFormat(LispObject format) {
271        // make sure we encode any remaining buffers with the current format
272        finishOutput();
273
274        if (format == keywordDefault) {
275            encoding = null;
276            eolStyle = platformEolStyle;
277            eolChar = (eolStyle == EolStyle.CR) ? '\r' : '\n';
278            externalFormat = format;
279            return;
280        }
281
282        LispObject enc;
283        boolean encIsCp = false;
284
285        if (format instanceof Cons) {
286            // meaning a non-empty list
287            enc = format.car();
288            if (enc == keywordCodePage) {
289                encIsCp = true;
290
291                enc = getf(format.cdr(), keywordID, null);
292            }
293
294            LispObject eol = getf(format.cdr(), keywordEolStyle, keywordRAW);
295            if (eol == keywordCR)
296                eolStyle = EolStyle.CR;
297            else if (eol == keywordLF)
298                eolStyle = EolStyle.LF;
299            else if (eol == keywordCRLF)
300                eolStyle = EolStyle.CRLF;
301            else if (eol != keywordRAW)
302                ; //###FIXME: raise an error
303
304        } else
305            enc = format;
306
307        if (enc.numberp())
308            encoding = enc.toString();
309        else if (enc instanceof AbstractString)
310            encoding = enc.getStringValue();
311        else if (enc == keywordDefault)
312            // This allows the user to use the encoding determined by
313            // Java to be the default for the current environment
314            // while still being able to set other stream options
315            // (e.g. :EOL-STYLE)
316            encoding = null;
317        else if (enc instanceof Symbol)
318            encoding = ((Symbol)enc).getName();
319        else
320            ; //###FIXME: raise an error!
321
322        if (encIsCp)
323            encoding = "Cp" + encoding;
324
325        eolChar = (eolStyle == EolStyle.CR) ? '\r' : '\n';
326        externalFormat = format;
327
328        if (reader != null
329                && reader instanceof DecodingReader)
330            ((DecodingReader)reader).setCharset(Charset.forName(encoding));
331    }
332
333    public boolean isOpen() {
334        return open;
335    }
336
337    public void setOpen(boolean b) {
338        open = b;
339    }
340   
341    @Override
342    public LispObject typeOf() {
343        return Symbol.SYSTEM_STREAM;
344    }
345
346    @Override
347    public LispObject classOf() {
348        return BuiltInClass.SYSTEM_STREAM;
349    }
350
351    @Override
352    public LispObject typep(LispObject typeSpecifier) {
353        if (typeSpecifier == Symbol.SYSTEM_STREAM)
354            return T;
355        if (typeSpecifier == Symbol.STREAM)
356            return T;
357        if (typeSpecifier == BuiltInClass.STREAM)
358            return T;
359        return super.typep(typeSpecifier);
360    }
361   
362    public LispObject getElementType() {
363        return elementType;
364    }
365
366    // Character input.
367    public int getOffset() {
368        return offset;
369    }
370
371    // Character input.
372    public final int getLineNumber() {
373        return lineNumber;
374    }
375
376    protected void setWriter(Writer writer) {
377        this.writer = writer;
378    }
379
380    // Character output.
381    public int getCharPos() {
382        return charPos;
383    }
384
385    // Character output.
386    public void setCharPos(int n) {
387        charPos = n;
388    }
389
390    /** Class to abstract readtable access
391     *
392     * Many of the functions below (used to) exist in 2 variants.
393     * One with hardcoded access to the FaslReadtable, the other
394     * with hardcoded access to the *readtable* variable.
395     *
396     * In order to prevent code duplication,
397     * this class abstracts access.
398     */
399    public static abstract class ReadtableAccessor {
400      /** Given the thread passed, return the applicable readtable. */
401      public abstract Readtable rt(LispThread thread);
402    }
403
404   /** pre-instantiated readtable accessor for the *readtable*. */
405   public static ReadtableAccessor currentReadtable
406        = new ReadtableAccessor()
407    {
408      public Readtable rt(LispThread thread)
409      {
410        return
411          (Readtable)Symbol.CURRENT_READTABLE.symbolValue(thread);
412      }
413    };
414
415    /** pre-instantiated readtable accessor for the fasl readtable. */
416    public static ReadtableAccessor faslReadtable
417        = new ReadtableAccessor()
418    {
419      public Readtable rt(LispThread thread)
420      {
421        return FaslReadtable.getInstance();
422      }
423    };
424
425
426    public LispObject read(boolean eofError, LispObject eofValue,
427                           boolean recursive, LispThread thread,
428                           ReadtableAccessor rta)
429    {
430        LispObject result = readPreservingWhitespace(eofError, eofValue,
431                                                     recursive, thread, rta);
432        if (result != eofValue && !recursive) {
433            try {
434                if (_charReady()) {
435                    int n = _readChar();
436                    if (n >= 0) {
437                        char c = (char) n; // ### BUG: Codepoint conversion
438                        Readtable rt = rta.rt(thread);
439                        if (!rt.isWhitespace(c))
440                            _unreadChar(c);
441                    }
442                }
443            } catch (IOException e) {
444                return error(new StreamError(this, e));
445            }
446        }
447        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
448            return NIL;
449        else
450            return result;
451    }
452
453    // ### *sharp-equal-alist*
454    // internal symbol
455    private static final Symbol _SHARP_EQUAL_ALIST_ =
456        internSpecial("*SHARP-EQUAL-ALIST*", PACKAGE_SYS, NIL);
457
458    public LispObject readPreservingWhitespace(boolean eofError,
459                                               LispObject eofValue,
460                                               boolean recursive,
461                                               LispThread thread,
462                                               ReadtableAccessor rta)
463
464    {
465        if (recursive) {
466            final Readtable rt = rta.rt(thread);
467            while (true) {
468                int n = -1;
469                try {
470                    n = _readChar();
471                } catch (IOException e) {
472                    Debug.trace(e);
473                    error(new StreamError(this, e));
474                }
475                if (n < 0) {
476                    if (eofError)
477                        return error(new EndOfFile(this));
478                    else
479                        return eofValue;
480                }
481                char c = (char) n; // ### BUG: Codepoint conversion
482                if (rt.isWhitespace(c))
483                    continue;
484                LispObject result = processChar(thread, c, rt);
485                if (result != null)
486                    return result;
487            }
488        } else {
489            final SpecialBindingsMark mark = thread.markSpecialBindings();
490            thread.bindSpecial(_SHARP_EQUAL_ALIST_, NIL);
491            try {
492                return readPreservingWhitespace(eofError, eofValue, true,
493                                                thread, rta);
494            } finally {
495                thread.resetSpecialBindings(mark);
496            }
497        }
498    }
499
500    /** Dispatch macro function if 'c' has one associated,
501     * read a token otherwise.
502     *
503     * When the macro function returns zero values, this function
504     * returns null or the token or returned value otherwise.
505     */
506    private final LispObject processChar(LispThread thread,
507                                         char c, Readtable rt)
508    {
509        final LispObject handler = rt.getReaderMacroFunction(c);
510        LispObject value;
511
512        if (handler instanceof ReaderMacroFunction) {
513            thread._values = null;
514            value = ((ReaderMacroFunction)handler).execute(this, c);
515        }
516        else if (handler != null && handler != NIL) {
517            thread._values = null;
518            value = handler.execute(this, LispCharacter.getInstance(c));
519        }
520        else
521            return readToken(c, rt);
522
523        // If we're looking at zero return values, set 'value' to null
524        if (value == NIL) {
525            LispObject[] values = thread._values;
526            if (values != null && values.length == 0) {
527                value = null;
528                thread._values = null; // reset 'no values' indicator
529            }
530        }
531        return value;
532    }
533
534    public LispObject readPathname(ReadtableAccessor rta) {
535        LispObject obj = read(true, NIL, false,
536                              LispThread.currentThread(), rta);
537        if (obj instanceof AbstractString) {
538            return Pathname.parseNamestring((AbstractString)obj);
539        }
540        if (obj.listp())
541            return Pathname.makePathname(obj);
542        return error(new TypeError("#p requires a string argument."));
543    }
544
545    public LispObject readSymbol() {
546        final Readtable rt =
547            (Readtable) Symbol.CURRENT_READTABLE.symbolValue(LispThread.currentThread());
548        StringBuilder sb = new StringBuilder();
549        _readToken(sb, rt);
550        return new Symbol(sb.toString());
551    }
552
553    public LispObject readSymbol(Readtable rt) {
554        StringBuilder sb = new StringBuilder();
555        _readToken(sb, rt);
556        return new Symbol(sb.toString());
557    }
558
559    public LispObject readStructure(ReadtableAccessor rta) {
560        final LispThread thread = LispThread.currentThread();
561        LispObject obj = read(true, NIL, true, thread, rta);
562        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
563            return NIL;
564        if (obj.listp()) {
565            Symbol structure = checkSymbol(obj.car());
566            LispClass c = LispClass.findClass(structure);
567            if (!(c instanceof StructureClass))
568                return error(new ReaderError(structure.getName() +
569                                             " is not a defined structure type.",
570                                             this));
571            LispObject args = obj.cdr();
572            Symbol DEFSTRUCT_DEFAULT_CONSTRUCTOR =
573                PACKAGE_SYS.intern("DEFSTRUCT-DEFAULT-CONSTRUCTOR");
574            LispObject constructor =
575                DEFSTRUCT_DEFAULT_CONSTRUCTOR.getSymbolFunctionOrDie().execute(structure);
576            final int length = args.length();
577            if ((length % 2) != 0)
578                return error(new ReaderError("Odd number of keyword arguments following #S: " +
579                                             obj.writeToString(),
580                                             this));
581            LispObject[] array = new LispObject[length];
582            LispObject rest = args;
583            for (int i = 0; i < length; i += 2) {
584                LispObject key = rest.car();
585                if (key instanceof Symbol && ((Symbol)key).getPackage() == PACKAGE_KEYWORD) {
586                    array[i] = key;
587                } else {
588                    array[i] = PACKAGE_KEYWORD.intern(javaString(key));
589                }
590                array[i + 1] = rest.cadr();
591                rest = rest.cddr();
592            }
593            return funcall(constructor.getSymbolFunctionOrDie(), array,
594                           thread);
595        }
596        return error(new ReaderError("Non-list following #S: " +
597                                     obj.writeToString(),
598                                     this));
599    }
600
601    public LispObject readString(char terminator, ReadtableAccessor rta)
602    {
603      final LispThread thread = LispThread.currentThread();
604      final Readtable rt = rta.rt(thread);
605      StringBuilder sb = new StringBuilder();
606      try
607      {
608        while (true) {
609          int n = _readChar();
610          if (n < 0)
611            return error(new EndOfFile(this));
612
613          char c = (char) n; // ### BUG: Codepoint conversion
614          if (rt.getSyntaxType(c) == Readtable.SYNTAX_TYPE_SINGLE_ESCAPE) {
615            // Single escape.
616            n = _readChar();
617            if (n < 0)
618              return error(new EndOfFile(this));
619
620            sb.append((char)n); // ### BUG: Codepoint conversion
621            continue;
622          }
623          if (c == terminator)
624            break;
625          // Default.
626          sb.append(c);
627        }
628      }
629      catch (java.io.IOException e)
630      {
631        //error(new EndOfFile(stream));
632        return new SimpleString(sb);
633      }
634      return new SimpleString(sb);
635    }
636
637    public LispObject readList(boolean requireProperList,
638                               ReadtableAccessor rta)
639    {
640        final LispThread thread = LispThread.currentThread();
641        Cons first = null;
642        Cons last = null;
643        Readtable rt;
644        try {
645            while (true) {
646                rt = rta.rt(thread);
647                char c = flushWhitespace(rt);
648                if (c == ')') {
649                    return first == null ? NIL : first;
650                }
651                if (c == '.') {
652                    int n = _readChar();
653                    if (n < 0)
654                        return error(new EndOfFile(this));
655                    char nextChar = (char) n; // ### BUG: Codepoint conversion
656                    if (isTokenDelimiter(nextChar, rt)) {
657                        if (last == null) {
658                            if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
659                                return NIL;
660                            else
661                                return error(new ReaderError("Nothing appears before . in list.",
662                                                             this));
663                        }
664                        _unreadChar(nextChar);
665                        LispObject obj = read(true, NIL, true, thread, rta);
666                        if (requireProperList) {
667                            if (!obj.listp())
668                                error(new ReaderError("The value " +
669                                                      obj.writeToString() +
670                                                      " is not of type " +
671                                                      Symbol.LIST.writeToString() + ".",
672                                                      this));
673                        }
674                        last.cdr = obj;
675                        continue;
676                    }
677                    // normal token beginning with '.'
678                    _unreadChar(nextChar);
679                }
680
681                LispObject obj = processChar(thread, c, rt);
682                if (obj == null)
683                    continue;
684
685
686                if (first == null) {
687                    first = new Cons(obj);
688                    last = first;
689                } else {
690                    Cons newCons = new Cons(obj);
691                    last.cdr = newCons;
692                    last = newCons;
693                }
694            }
695        } catch (IOException e) {
696            error(new StreamError(this, e));
697            return null;
698        }
699    }
700
701    private static final boolean isTokenDelimiter(char c, Readtable rt)
702
703    {
704        switch (c) {
705        case '"':
706        case '\'':
707        case '(':
708        case ')':
709        case ',':
710        case ';':
711        case '`':
712            return true;
713        default:
714            return rt.isWhitespace(c);
715        }
716    }
717
718    public LispObject readDispatchChar(char dispChar,
719                                       ReadtableAccessor rta)
720    {
721        int numArg = -1;
722        char c = 0;
723        try {
724            while (true) {
725                int n = _readChar();
726                if (n < 0)
727                    return error(new EndOfFile(this));
728                c = (char) n; // ### BUG: Codepoint conversion
729                if (c < '0' || c > '9')
730                    break;
731                if (numArg < 0)
732                    numArg = 0;
733                numArg = numArg * 10 + c - '0';
734            }
735        } catch (IOException e) {
736            error(new StreamError(this, e));
737        }
738        final LispThread thread = LispThread.currentThread();
739        final Readtable rt = rta.rt(thread);
740        LispObject fun = rt.getDispatchMacroCharacter(dispChar, c);
741        if (fun != NIL) {
742            LispObject result;
743
744            thread._values = null;
745            if (fun instanceof DispatchMacroFunction)
746                return ((DispatchMacroFunction)fun).execute(this, c, numArg);
747            else
748                return
749                    thread.execute(fun, this, LispCharacter.getInstance(c),
750                       (numArg < 0) ? NIL : Fixnum.getInstance(numArg));
751        }
752
753        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
754            return null;
755
756        return error(new ReaderError("No dispatch function defined for #\\" + c,
757                                     this));
758    }
759
760    public LispObject readSharpLeftParen(char c, int n, 
761                                         ReadtableAccessor rta) 
762    {
763        final LispThread thread = LispThread.currentThread();
764        LispObject list = readList(true, rta);
765        if (_BACKQUOTE_COUNT_.symbolValue(thread).zerop()) {
766            if (n >= 0) {
767                LispObject[] array = new LispObject[n];
768                for (int i = 0; i < n; i++) {
769                    array[i] = list.car();
770                    if (list.cdr() != NIL)
771                        list = list.cdr();
772                }
773                return new SimpleVector(array);
774            } else
775                return new SimpleVector(list);
776        }
777        return new Cons(_BQ_VECTOR_FLAG_.symbolValue(thread), list);
778    }
779
780    public LispObject readSharpStar(char ignored, int n, 
781                                    ReadtableAccessor rta) 
782    {
783        final LispThread thread = LispThread.currentThread();
784        final Readtable rt = rta.rt(thread);
785
786        final boolean suppress =
787            (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL);
788        StringBuilder sb = new StringBuilder();
789        try 
790            {
791    while (true) {
792                    int ch = _readChar();
793                    if (ch < 0)
794                        break;
795                    char c = (char) ch;
796                    if (c == '0' || c == '1')
797                        sb.append(c);
798                    else {
799                        int syntaxType = rt.getSyntaxType(c);
800                        if (syntaxType == Readtable.SYNTAX_TYPE_WHITESPACE ||
801                            syntaxType == Readtable.SYNTAX_TYPE_TERMINATING_MACRO) {
802                            _unreadChar(c);
803                            break;
804                        } else if (!suppress) {
805                            String name = LispCharacter.charToName(c);
806                            if (name == null)
807                                name = "#\\" + c;
808                            error(new ReaderError("Illegal element for bit-vector: " + name,
809                                                  this));
810                        }
811                    }
812    }
813            }
814        catch (java.io.IOException e)
815            {
816    error(new ReaderError("IO error: ",
817              this));
818    return NIL;
819            }
820       
821        if (suppress)
822            return NIL;
823        if (n >= 0) {
824            // n was supplied.
825            final int length = sb.length();
826            if (length == 0) {
827                if (n > 0)
828                    return error(new ReaderError("No element specified for bit vector of length " +
829                                                 n + '.',
830                                                 this));
831            }
832            if (n > length) {
833                final char c = sb.charAt(length - 1);
834                for (int i = length; i < n; i++)
835                    sb.append(c);
836            } else if (n < length) {
837                return error(new ReaderError("Bit vector is longer than specified length: #" +
838                                             n + '*' + sb.toString(),
839                                             this));
840            }
841        }
842        return new SimpleBitVector(sb.toString());
843    }
844
845
846    public LispObject readSharpDot(char c, int n, 
847                                   ReadtableAccessor rta) 
848    {
849        final LispThread thread = LispThread.currentThread();
850        if (Symbol.READ_EVAL.symbolValue(thread) == NIL)
851            return error(new ReaderError("Can't read #. when *READ-EVAL* is NIL.",
852                                         this));
853        else
854            return eval(read(true, NIL, true, thread, rta),
855                        new Environment(), thread);
856    }
857
858    public LispObject readCharacterLiteral(Readtable rt, LispThread thread)
859
860    {
861        try {
862            int n = _readChar();
863            if (n < 0)
864                return error(new EndOfFile(this));
865            char c = (char) n; // ### BUG: Codepoint conversion
866            StringBuilder sb = new StringBuilder(String.valueOf(c));
867            while (true) {
868                n = _readChar();
869                if (n < 0)
870                    break;
871                c = (char) n; // ### BUG: Codepoint conversion
872                if (rt.isWhitespace(c))
873                    break;
874                if (c == '(' || c == ')') {
875                    _unreadChar(c);
876                    break;
877                }
878                sb.append(c);
879            }
880            if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
881                return NIL;
882            if (sb.length() == 1)
883                return LispCharacter.getInstance(sb.charAt(0));
884            String token = sb.toString();
885            n = LispCharacter.nameToChar(token);
886            if (n >= 0)
887                return LispCharacter.getInstance((char)n); // ### BUG: Codepoint conversion
888            return error(new LispError("Unrecognized character name: \"" + token + '"'));
889        } catch (IOException e) {
890            return error(new StreamError(this, e));
891        }
892    }
893
894    public void skipBalancedComment() {
895        try {
896            while (true) {
897                int n = _readChar();
898                if (n < 0)
899                    return;
900                if (n == '|') {
901                    n = _readChar();
902                    if (n == '#')
903                        return;
904                    else
905                        _unreadChar(n);
906                } else if (n == '#') {
907                    n = _readChar();
908                    if (n == '|')
909                        skipBalancedComment(); // Nested comment. Recurse!
910                    else
911                        _unreadChar(n);
912                }
913            }
914        } catch (IOException e) {
915            error(new StreamError(this, e));
916        }
917    }
918
919    public LispObject readArray(int rank, ReadtableAccessor rta) {
920        final LispThread thread = LispThread.currentThread();
921        LispObject obj = read(true, NIL, true, thread, rta);
922        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
923            return NIL;
924        switch (rank) {
925        case -1:
926            return error(new ReaderError("No dimensions argument to #A.", this));
927        case 0:
928            return new ZeroRankArray(T, obj, false);
929        case 1: {
930            if (obj.listp() || obj instanceof AbstractVector)
931                return new SimpleVector(obj);
932            return error(new ReaderError(obj.writeToString() + " is not a sequence.",
933                                         this));
934        }
935        default:
936            return new SimpleArray_T(rank, obj);
937        }
938    }
939
940    public LispObject readComplex(ReadtableAccessor rta) {
941        final LispThread thread = LispThread.currentThread();
942        LispObject obj = read(true, NIL, true, thread, rta);
943        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
944            return NIL;
945        if (obj instanceof Cons && obj.length() == 2)
946            return Complex.getInstance(obj.car(), obj.cadr());
947        // Error.
948        StringBuilder sb = new StringBuilder("Invalid complex number format");
949        if (this instanceof FileStream) {
950            Pathname p = ((FileStream)this).getPathname();
951            if (p != null) {
952                String namestring = p.getNamestring();
953                if (namestring != null) {
954                    sb.append(" in #P\"");
955                    sb.append(namestring);
956                    sb.append('"');
957                }
958            }
959            sb.append(" at offset ");
960            sb.append(_getFilePosition());
961        }
962        sb.append(": #C");
963        sb.append(obj.writeToString());
964        return error(new ReaderError(sb.toString(), this));
965    }
966
967    private String readMultipleEscape(Readtable rt) {
968        StringBuilder sb = new StringBuilder();
969        try {
970            while (true) {
971                int n = _readChar();
972                if (n < 0)
973                    return serror(new EndOfFile(this));
974
975                char c = (char) n; // ### BUG: Codepoint conversion
976                byte syntaxType = rt.getSyntaxType(c);
977                if (syntaxType == Readtable.SYNTAX_TYPE_SINGLE_ESCAPE) {
978                    n = _readChar();
979                    if (n < 0)
980                        return serror(new EndOfFile(this));
981
982                    sb.append((char)n); // ### BUG: Codepoint conversion
983                    continue;
984                }
985                if (syntaxType == Readtable.SYNTAX_TYPE_MULTIPLE_ESCAPE)
986                    break;
987                sb.append(c);
988            }
989        } catch (IOException e) {
990            return serror(new StreamError(this, e));
991        }
992        return sb.toString();
993    }
994
995    private static final int findUnescapedSingleColon(String s, BitSet flags) {
996        if (flags == null)
997            return s.indexOf(':');
998        final int limit = s.length();
999        for (int i = 0; i < limit; i++) {
1000            if (s.charAt(i) == ':' && !flags.get(i)) {
1001                return i;
1002            }
1003        }
1004        return -1;
1005    }
1006
1007    private static final int findUnescapedDoubleColon(String s, BitSet flags) {
1008        if (flags == null)
1009            return s.indexOf("::");
1010        final int limit = s.length() - 1;
1011        for (int i = 0; i < limit; i++) {
1012            if (s.charAt(i) == ':' && !flags.get(i)) {
1013                if (s.charAt(i + 1) == ':' && !flags.get(i + 1)) {
1014                    return i;
1015                }
1016            }
1017        }
1018        return -1;
1019    }
1020
1021    private final LispObject readToken(char c, Readtable rt)
1022
1023    {
1024        StringBuilder sb = new StringBuilder(String.valueOf(c));
1025        final LispThread thread = LispThread.currentThread();
1026        BitSet flags = _readToken(sb, rt);
1027        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
1028            return NIL;
1029        final LispObject readtableCase = rt.getReadtableCase();
1030        final String token;
1031        if (readtableCase == Keyword.INVERT)
1032            token = invert(sb.toString(), flags);
1033        else
1034            token = sb.toString();
1035        final int length = token.length();
1036        if (length > 0) {
1037            final char firstChar = token.charAt(0);
1038            if (flags == null) {
1039                if (firstChar == '.') {
1040                    // Section 2.3.3: "If a token consists solely of dots (with
1041                    // no escape characters), then an error of type READER-
1042                    // ERROR is signaled, except in one circumstance: if the
1043                    // token is a single dot and appears in a situation where
1044                    // dotted pair notation permits a dot, then it is accepted
1045                    // as part of such syntax and no error is signaled."
1046                    boolean ok = false;
1047                    for (int i = length; i-- > 1;) {
1048                        if (token.charAt(i) != '.') {
1049                            ok = true;
1050                            break;
1051                        }
1052                    }
1053                    if (!ok) {
1054                        final String message;
1055                        if (length > 1)
1056                            message = "Too many dots.";
1057                        else
1058                            message = "Dot context error.";
1059                        return error(new ReaderError(message, this));
1060                    }
1061                }
1062                final int radix = getReadBase(thread);
1063                if ("+-.0123456789".indexOf(firstChar) >= 0) {
1064                    LispObject number = makeNumber(token, length, radix);
1065                    if (number != null)
1066                        return number;
1067                } else if (Character.digit(firstChar, radix) >= 0) {
1068                    LispObject number = makeNumber(token, length, radix);
1069                    if (number != null)
1070                        return number;
1071                }
1072            }
1073            if (firstChar == ':')
1074                if (flags == null || !flags.get(0))
1075                    return PACKAGE_KEYWORD.intern(token.substring(1));
1076            int index = findUnescapedDoubleColon(token, flags);
1077            if (index > 0) {
1078                String packageName = token.substring(0, index);
1079                String symbolName = token.substring(index + 2);
1080                Package pkg = Packages.findPackage(packageName);
1081                if (pkg == null)
1082                    return error(new LispError("Package \"" + packageName +
1083                                               "\" not found."));
1084                return pkg.intern(symbolName);
1085            }
1086            index = findUnescapedSingleColon(token, flags);
1087            if (index > 0) {
1088                final String packageName = token.substring(0, index);
1089                Package pkg = Packages.findPackage(packageName);
1090                if (pkg == null)
1091                    return error(new PackageError("Package \"" + packageName +
1092                                                  "\" not found."));
1093                final String symbolName = token.substring(index + 1);
1094                final SimpleString s = new SimpleString(symbolName);
1095                Symbol symbol = pkg.findExternalSymbol(s);
1096                if (symbol != null)
1097                    return symbol;
1098                // Error!
1099                if (pkg.findInternalSymbol(s) != null)
1100                    return error(new ReaderError("The symbol \"" + symbolName +
1101                                                 "\" is not external in package " +
1102                                                 packageName + '.',
1103                                                 this));
1104                else
1105                    return error(new ReaderError("The symbol \"" + symbolName +
1106                                                 "\" was not found in package " +
1107                                                 packageName + '.',
1108                                                 this));
1109            }
1110        }
1111        // Intern token in current package.
1112        return ((Package)Symbol._PACKAGE_.symbolValue(thread)).intern(new SimpleString(token));
1113    }
1114
1115    private final BitSet _readToken(StringBuilder sb, Readtable rt)
1116
1117    {
1118        BitSet flags = null;
1119        final LispObject readtableCase = rt.getReadtableCase();
1120        if (sb.length() > 0) {
1121            Debug.assertTrue(sb.length() == 1);
1122            char c = sb.charAt(0);
1123            byte syntaxType = rt.getSyntaxType(c);
1124            if (syntaxType == Readtable.SYNTAX_TYPE_SINGLE_ESCAPE) {
1125                int n = -1;
1126                try {
1127                    n = _readChar();
1128                } catch (IOException e) {
1129                    error(new StreamError(this, e));
1130                    return flags;
1131                }
1132                if (n < 0) {
1133                    error(new EndOfFile(this));
1134                    return null; // Not reached
1135                }
1136
1137                sb.setCharAt(0, (char) n); // ### BUG: Codepoint conversion
1138                flags = new BitSet(1);
1139                flags.set(0);
1140            } else if (syntaxType == Readtable.SYNTAX_TYPE_MULTIPLE_ESCAPE) {
1141                sb.setLength(0);
1142                sb.append(readMultipleEscape(rt));
1143                flags = new BitSet(sb.length());
1144                flags.set(0, sb.length());
1145            } else if (rt.isInvalid(c)) {
1146                rt.checkInvalid(c, this); // Signals a reader-error.
1147            } else if (readtableCase == Keyword.UPCASE) {
1148                sb.setCharAt(0, LispCharacter.toUpperCase(c));
1149            } else if (readtableCase == Keyword.DOWNCASE) {
1150                sb.setCharAt(0, LispCharacter.toLowerCase(c));
1151            }
1152        }
1153        try {
1154            while (true) {
1155                int n = _readChar();
1156                if (n < 0)
1157                    break;
1158                char c = (char) n; // ### BUG: Codepoint conversion
1159                if (rt.isWhitespace(c)) {
1160                    _unreadChar(n);
1161                    break;
1162                }
1163                byte syntaxType = rt.getSyntaxType(c);
1164                if (syntaxType == Readtable.SYNTAX_TYPE_TERMINATING_MACRO) {
1165                    _unreadChar(c);
1166                    break;
1167                }
1168                rt.checkInvalid(c, this);
1169                if (syntaxType == Readtable.SYNTAX_TYPE_SINGLE_ESCAPE) {
1170                    n = _readChar();
1171                    if (n < 0)
1172                        break;
1173                    sb.append((char)n); // ### BUG: Codepoint conversion
1174                    if (flags == null)
1175                        flags = new BitSet(sb.length());
1176                    flags.set(sb.length() - 1);
1177                    continue;
1178                }
1179                if (syntaxType == Readtable.SYNTAX_TYPE_MULTIPLE_ESCAPE) {
1180                    int begin = sb.length();
1181                    sb.append(readMultipleEscape(rt));
1182                    int end = sb.length();
1183                    if (flags == null)
1184                        flags = new BitSet(sb.length());
1185                    flags.set(begin, end);
1186                    continue;
1187                }
1188                if (readtableCase == Keyword.UPCASE)
1189                    c = LispCharacter.toUpperCase(c);
1190                else if (readtableCase == Keyword.DOWNCASE)
1191                    c = LispCharacter.toLowerCase(c);
1192                sb.append(c);
1193            }
1194        } catch (IOException e) {
1195            error(new StreamError(this, e));
1196            return flags;
1197        }
1198
1199        return flags;
1200    }
1201
1202    public static final String invert(String s, BitSet flags) {
1203        // Section 23.1.2: "When the readtable case is :INVERT, then if all of
1204        // the unescaped letters in the extended token are of the same case,
1205        // those (unescaped) letters are converted to the opposite case."
1206        final int limit = s.length();
1207        final int LOWER = 1;
1208        final int UPPER = 2;
1209        int state = 0;
1210        for (int i = 0; i < limit; i++) {
1211            // We only care about unescaped characters.
1212            if (flags != null && flags.get(i))
1213                continue;
1214            char c = s.charAt(i);
1215            if (Character.isUpperCase(c)) {
1216                if (state == LOWER)
1217                    return s; // Mixed case.
1218                state = UPPER;
1219            }
1220            if (Character.isLowerCase(c)) {
1221                if (state == UPPER)
1222                    return s; // Mixed case.
1223                state = LOWER;
1224            }
1225        }
1226        StringBuilder sb = new StringBuilder(limit);
1227        for (int i = 0; i < limit; i++) {
1228            char c = s.charAt(i);
1229            if (flags != null && flags.get(i)) // Escaped.
1230                sb.append(c);
1231            else if (Character.isUpperCase(c))
1232                sb.append(Character.toLowerCase(c));
1233            else if (Character.isLowerCase(c))
1234                sb.append(Character.toUpperCase(c));
1235            else
1236                sb.append(c);
1237        }
1238        return sb.toString();
1239    }
1240
1241    private static final int getReadBase(LispThread thread)
1242
1243    {
1244        final int readBase;
1245        final LispObject readBaseObject = Symbol.READ_BASE.symbolValue(thread);
1246        if (readBaseObject instanceof Fixnum) {
1247            readBase = ((Fixnum)readBaseObject).value;
1248        } else
1249            // The value of *READ-BASE* is not a Fixnum.
1250            return ierror(new LispError("The value of *READ-BASE* is not " +
1251                                        "of type '(INTEGER 2 36)."));
1252
1253        if (readBase < 2 || readBase > 36)
1254            return ierror(new LispError("The value of *READ-BASE* is not " +
1255                                        "of type '(INTEGER 2 36)."));
1256
1257        return readBase;
1258    }
1259
1260    private final LispObject makeNumber(String token, int length, int radix)
1261    {
1262        if (length == 0)
1263            return null;
1264        if (token.indexOf('/') >= 0)
1265            return makeRatio(token, radix);
1266        if (token.charAt(length - 1) == '.') {
1267            radix = 10;
1268            token = token.substring(0, --length);
1269        }
1270        boolean numeric = true;
1271        if (radix == 10) {
1272            for (int i = length; i-- > 0;) {
1273                char c = token.charAt(i);
1274                if (c < '0' || c > '9') {
1275                    if (i > 0 || (c != '-' && c != '+')) {
1276                        numeric = false;
1277                        break;
1278                    }
1279                }
1280            }
1281        } else {
1282            for (int i = length; i-- > 0;) {
1283                char c = token.charAt(i);
1284                if (Character.digit(c, radix) < 0) {
1285                    if (i > 0 || (c != '-' && c != '+')) {
1286                        numeric = false;
1287                        break;
1288                    }
1289                }
1290            }
1291        }
1292        if (!numeric) // Can't be an integer.
1293            return makeFloat(token, length);
1294        if (token.charAt(0) == '+')
1295            token = token.substring(1);
1296        try {
1297            int n = Integer.parseInt(token, radix);
1298            return (n >= 0 && n <= 255) ? Fixnum.constants[n] : Fixnum.getInstance(n);
1299        } catch (NumberFormatException e) {}
1300        // parseInt() failed.
1301        try {
1302            return Bignum.getInstance(token, radix);
1303        } catch (NumberFormatException e) {}
1304        // Not a number.
1305        return null;
1306    }
1307
1308    private final LispObject makeRatio(String token, int radix)
1309
1310    {
1311        final int index = token.indexOf('/');
1312        if (index < 0)
1313            return null;
1314        try {
1315            BigInteger numerator =
1316                new BigInteger(token.substring(0, index), radix);
1317            BigInteger denominator =
1318                new BigInteger(token.substring(index + 1), radix);
1319            // Check the denominator here, before calling number(), so we can
1320            // signal a READER-ERROR, as required by ANSI, instead of DIVISION-
1321            // BY-ZERO.
1322            if (denominator.signum() == 0)
1323                error(new ReaderError("Division by zero.", this));
1324            return number(numerator, denominator);
1325        } catch (NumberFormatException e) {
1326            return null;
1327        }
1328    }
1329
1330    private static final LispObject makeFloat(final String token,
1331            final int length)
1332    {
1333        if (length == 0)
1334            return null;
1335        StringBuilder sb = new StringBuilder();
1336        int i = 0;
1337        boolean maybe = false;
1338        char marker = 0;
1339        char c = token.charAt(i);
1340        if (c == '-' || c == '+') {
1341            sb.append(c);
1342            ++i;
1343        }
1344        while (i < length) {
1345            c = token.charAt(i);
1346            if (c == '.' || (c >= '0' && c <= '9')) {
1347                if (c == '.')
1348                    maybe = true;
1349                sb.append(c);
1350                ++i;
1351            } else
1352                break;
1353        }
1354        if (i < length) {
1355            c = token.charAt(i);
1356            if ("esfdlESFDL".indexOf(c) >= 0) {
1357                // Exponent marker.
1358                maybe = true;
1359                marker = LispCharacter.toUpperCase(c);
1360                if (marker == 'S')
1361                    marker = 'F';
1362                else if (marker == 'L')
1363                    marker = 'D';
1364                else if (marker == 'E') {
1365                    LispObject format = Symbol.READ_DEFAULT_FLOAT_FORMAT.symbolValue();
1366                    if (format == Symbol.SINGLE_FLOAT || format == Symbol.SHORT_FLOAT)
1367                        marker = 'F';
1368                    else
1369                        marker = 'D';
1370                }
1371                sb.append('E');
1372                ++i;
1373            }
1374        }
1375        if (!maybe)
1376            return null;
1377        // Append rest of token.
1378        sb.append(token.substring(i));
1379        c = sb.charAt(sb.length()-1);
1380        if (! ('0' <= c && c <= '9'))
1381            // we need to check that the last item is a number:
1382            // the Double.parseDouble routine accepts numbers ending in 'D'
1383            // like 1e2d. The same is true for Float.parseFloat and the 'F'
1384            // character. However, these are not valid Lisp floats.
1385            return null;
1386        try {
1387            if (marker == 0) {
1388                LispObject format = Symbol.READ_DEFAULT_FLOAT_FORMAT.symbolValue();
1389                if (format == Symbol.SINGLE_FLOAT || format == Symbol.SHORT_FLOAT)
1390                    marker = 'F';
1391                else
1392                    marker = 'D';
1393            }
1394            if (marker == 'D')
1395                return new DoubleFloat(Double.parseDouble(sb.toString()));
1396            else
1397                return new SingleFloat(Float.parseFloat(sb.toString()));
1398        } catch (NumberFormatException e) {
1399            return null;
1400        }
1401    }
1402
1403    public LispObject readRadix(int radix, ReadtableAccessor rta) {
1404        StringBuilder sb = new StringBuilder();
1405        final LispThread thread = LispThread.currentThread();
1406        final Readtable rt = rta.rt(thread);
1407        boolean escaped = (_readToken(sb, rt) != null);
1408        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
1409            return NIL;
1410        if (escaped)
1411            return error(new ReaderError("Illegal syntax for number.", this));
1412        String s = sb.toString();
1413        if (s.indexOf('/') >= 0)
1414            return makeRatio(s, radix);
1415        // Integer.parseInt() below handles a prefixed '-' character correctly, but
1416        // does not accept a prefixed '+' character, so we skip over it here
1417        if (s.charAt(0) == '+')
1418            s = s.substring(1);
1419        try {
1420            int n = Integer.parseInt(s, radix);
1421            return (n >= 0 && n <= 255) ? Fixnum.constants[n] : Fixnum.getInstance(n);
1422        } catch (NumberFormatException e) {}
1423        // parseInt() failed.
1424        try {
1425            return Bignum.getInstance(s, radix);
1426        } catch (NumberFormatException e) {}
1427        // Not a number.
1428        return error(new LispError());
1429    }
1430
1431    private char flushWhitespace(Readtable rt) {
1432        try {
1433            while (true) {
1434                int n = _readChar();
1435                if (n < 0)
1436                    return (char)ierror(new EndOfFile(this));
1437
1438                char c = (char) n; // ### BUG: Codepoint conversion
1439                if (!rt.isWhitespace(c))
1440                    return c;
1441            }
1442        } catch (IOException e) {
1443            error(new StreamError(this, e));
1444            return 0;
1445        }
1446    }
1447
1448    public LispObject readDelimitedList(char delimiter)
1449
1450    {
1451        final LispThread thread = LispThread.currentThread();
1452        LispObject result = NIL;
1453        while (true) {
1454            Readtable rt = (Readtable) Symbol.CURRENT_READTABLE.symbolValue(thread);
1455            char c = flushWhitespace(rt);
1456            if (c == delimiter)
1457                break;
1458
1459            LispObject obj = processChar(thread, c, rt);
1460            if (obj != null)
1461                result = new Cons(obj, result);
1462        }
1463        if (Symbol.READ_SUPPRESS.symbolValue(thread) != NIL)
1464            return NIL;
1465        else
1466            return result.nreverse();
1467    }
1468
1469    // read-line &optional stream eof-error-p eof-value recursive-p
1470    // => line, missing-newline-p
1471    // recursive-p is ignored
1472    public LispObject readLine(boolean eofError, LispObject eofValue)
1473
1474    {
1475        final LispThread thread = LispThread.currentThread();
1476        StringBuilder sb = new StringBuilder();
1477        try {
1478            while (true) {
1479                int n = _readChar();
1480                if (n < 0) {
1481                    if (sb.length() == 0) {
1482                        if (eofError)
1483                            return error(new EndOfFile(this));
1484                        return thread.setValues(eofValue, T);
1485                    }
1486                    return thread.setValues(new SimpleString(sb), T);
1487                }
1488                if (n == '\n')
1489                    return thread.setValues(new SimpleString(sb), NIL);
1490                else
1491                    sb.append((char)n); // ### BUG: Codepoint conversion
1492            }
1493        } catch (IOException e) {
1494            return error(new StreamError(this, e));
1495        }
1496    }
1497
1498    // read-char &optional stream eof-error-p eof-value recursive-p => char
1499    // recursive-p is ignored
1500    public LispObject readChar() {
1501        try {
1502            int n = _readChar();
1503            if (n < 0)
1504                return error(new EndOfFile(this));
1505            return LispCharacter.getInstance((char)n); // ### BUG: Codepoint conversion
1506        } catch (IOException e) {
1507            return error(new StreamError(this, e));
1508        }
1509
1510    }
1511
1512    public LispObject readChar(boolean eofError, LispObject eofValue)
1513
1514    {
1515        try {
1516            int n = _readChar();
1517            if (n < 0) {
1518                if (eofError)
1519                    return error(new EndOfFile(this));
1520                else
1521                    return eofValue;
1522            }
1523            return LispCharacter.getInstance((char)n); // ### BUG: Codepoint conversion
1524        } catch (IOException e) {
1525            return error(new StreamError(this, e));
1526        }
1527    }
1528
1529    // read-char-no-hang &optional stream eof-error-p eof-value recursive-p => char
1530    // recursive-p is ignored
1531    public LispObject readCharNoHang(boolean eofError, LispObject eofValue)
1532
1533    {
1534        try {
1535            return _charReady() ? readChar(eofError, eofValue) : NIL;
1536        } catch (IOException e) {
1537            return error(new StreamError(this, e));
1538        }
1539    }
1540
1541
1542    // unread-char character &optional input-stream => nil
1543    public LispObject unreadChar(LispCharacter c) {
1544        try {
1545            _unreadChar(c.value);
1546            return NIL;
1547        } catch (IOException e) {
1548            return error(new StreamError(this, e));
1549        }
1550    }
1551
1552    public LispObject finishOutput() {
1553        _finishOutput();
1554        return NIL;
1555    }
1556
1557    // clear-input &optional input-stream => nil
1558    public LispObject clearInput() {
1559        _clearInput();
1560        return NIL;
1561    }
1562
1563    public LispObject getFilePosition() {
1564        long pos = _getFilePosition();
1565        return pos >= 0 ? number(pos) : NIL;
1566    }
1567
1568    public LispObject setFilePosition(LispObject arg) {
1569        return _setFilePosition(arg) ? T : NIL;
1570    }
1571
1572    // close stream &key abort => result
1573    // Must return true if stream was open, otherwise implementation-dependent.
1574    public LispObject close(LispObject abort) {
1575        _close();
1576        return T;
1577    }
1578
1579    @Override
1580    public String toString() {
1581        return unreadableString("STREAM");
1582    }
1583
1584    // read-byte stream &optional eof-error-p eof-value => byte
1585    // Reads an 8-bit byte.
1586    public LispObject readByte(boolean eofError, LispObject eofValue)
1587
1588    {
1589        int n = _readByte();
1590        if (n < 0) {
1591            if (eofError)
1592                return error(new EndOfFile(this));
1593            else
1594                return eofValue;
1595        }
1596        return Fixnum.constants[n];
1597    }
1598
1599    public LispObject terpri() {
1600        _writeChar('\n');
1601        return NIL;
1602    }
1603
1604    public LispObject freshLine() {
1605        if (charPos == 0)
1606            return NIL;
1607        _writeChar('\n');
1608        return T;
1609    }
1610
1611    public void print(char c) {
1612        _writeChar(c);
1613    }
1614
1615    // PRIN1 produces output suitable for input to READ.
1616    // Binds *PRINT-ESCAPE* to true.
1617    public void prin1(LispObject obj) {
1618        LispThread thread = LispThread.currentThread();
1619        final SpecialBindingsMark mark = thread.markSpecialBindings();
1620        thread.bindSpecial(Symbol.PRINT_ESCAPE, T);
1621        try {
1622            _writeString(obj.writeToString());
1623        } finally {
1624            thread.resetSpecialBindings(mark);
1625        }
1626    }
1627
1628    public LispObject listen() {
1629        if (pastEnd)
1630            return NIL;
1631        try {
1632            if (! _charReady())
1633                return NIL;
1634
1635            int n = _readChar();
1636            if (n < 0)
1637                return NIL;
1638
1639            _unreadChar(n);
1640
1641            return T;
1642        } catch (IOException e) {
1643            return error(new StreamError(this, e));
1644        }
1645    }
1646
1647    public LispObject fileLength() {
1648        return type_error(this, Symbol.FILE_STREAM);
1649    }
1650
1651    public LispObject fileStringLength(LispObject arg) {
1652        if (arg instanceof LispCharacter) {
1653            if (Utilities.isPlatformWindows) {
1654                if (((LispCharacter)arg).value == '\n')
1655                    return Fixnum.TWO;
1656            }
1657            return Fixnum.ONE;
1658        }
1659        if (arg instanceof AbstractString) {
1660            if (Utilities.isPlatformWindows) {
1661                int fileStringLength = 0;
1662                char[] chars = ((AbstractString)arg).getStringChars();
1663                for (int i = chars.length; i-- > 0;) {
1664                    if (chars[i] == '\n')
1665                        fileStringLength += 2;
1666                    else
1667                        ++fileStringLength;
1668                }
1669                return number(fileStringLength);
1670
1671            }
1672            return number(arg.length());
1673        }
1674        return error(new TypeError(arg.writeToString() +
1675                                   " is neither a string nor a character."));
1676    }
1677
1678    /** Reads a character off an underlying stream
1679     *
1680     * @return a character, or -1 at end-of-file
1681     */
1682    protected int _readChar() throws IOException {
1683        if (reader == null)
1684            streamNotCharacterInputStream();
1685
1686        int n = reader.read();
1687
1688        if (n < 0) {
1689            pastEnd = true;
1690            return -1;
1691        }
1692
1693        ++offset;
1694        if (n == '\r' && eolStyle == EolStyle.CRLF) {
1695            n = _readChar();
1696            if (n != '\n') {
1697                _unreadChar(n);
1698                return '\r';
1699            } else
1700                return '\n';
1701        }
1702
1703        if (n == eolChar) {
1704            ++lineNumber;
1705            return '\n';
1706        }
1707
1708        return n;
1709    }
1710
1711    /** Puts a character back into the (underlying) stream
1712     *
1713     * @param n
1714     */
1715    protected void _unreadChar(int n) throws IOException {
1716        if (reader == null)
1717            streamNotCharacterInputStream();
1718
1719        --offset;
1720        if (n == '\n') {
1721            n = eolChar;
1722            --lineNumber;
1723        }
1724
1725        reader.unread(n);
1726        pastEnd = false;
1727    }
1728
1729
1730    /** Returns a boolean indicating input readily available
1731     *
1732     * @return true if a character is available
1733     */
1734    protected boolean _charReady() throws IOException {
1735        if (reader == null)
1736            streamNotCharacterInputStream();
1737        return reader.ready();
1738    }
1739
1740    /** Writes a character into the underlying stream,
1741     * updating charPos while doing so
1742     *
1743     * @param c
1744     */
1745    public void _writeChar(char c) {
1746        try {
1747            if (c == '\n') {
1748                if (eolStyle == EolStyle.CRLF && lastChar != '\r')
1749                    writer.write('\r');
1750
1751                writer.write(eolChar);
1752                lastChar = eolChar;
1753                writer.flush();
1754                charPos = 0;
1755            } else {
1756                writer.write(c);
1757                lastChar = c;
1758                ++charPos;
1759            }
1760        } catch (NullPointerException e) {
1761            // writer is null
1762            streamNotCharacterOutputStream();
1763        } catch (IOException e) {
1764            error(new StreamError(this, e));
1765        }
1766    }
1767
1768    /** Writes a series of characters in the underlying stream,
1769     * updating charPos while doing so
1770     *
1771     * @param chars
1772     * @param start
1773     * @param end
1774     */
1775    public void _writeChars(char[] chars, int start, int end)
1776
1777    {
1778        try {
1779            if (eolStyle != EolStyle.RAW) {
1780                for (int i = start; i < end; i++)
1781                    //###FIXME: the number of writes can be greatly reduced by
1782                    // writing the space between newlines as chunks.
1783                    _writeChar(chars[i]);
1784                return;
1785            }
1786
1787            writer.write(chars, start, end - start);
1788            if (start < end)
1789                lastChar = chars[end-1];
1790
1791            int index = -1;
1792            for (int i = end; i-- > start;) {
1793                if (chars[i] == '\n') {
1794                    index = i;
1795                    break;
1796                }
1797            }
1798            if (index < 0) {
1799                // No newline.
1800                charPos += (end - start);
1801            } else {
1802                charPos = end - (index + 1);
1803                writer.flush();
1804            }
1805        } catch (NullPointerException e) {
1806            if (writer == null)
1807                streamNotCharacterOutputStream();
1808            else
1809                throw e;
1810        } catch (IOException e) {
1811            error(new StreamError(this, e));
1812        }
1813    }
1814
1815    /** Writes a string to the underlying stream,
1816     * updating charPos while doing so
1817     *
1818     * @param s
1819     */
1820    public void _writeString(String s) {
1821        try {
1822            _writeChars(s.toCharArray(), 0, s.length());
1823        } catch (NullPointerException e) {
1824            if (writer == null)
1825                streamNotCharacterOutputStream();
1826            else
1827                throw e;
1828        }
1829    }
1830
1831    /** Writes a string to the underlying stream, appending
1832     * a new line and updating charPos while doing so
1833     *
1834     * @param s
1835     */
1836    public void _writeLine(String s) {
1837        try {
1838            _writeString(s);
1839            _writeChar('\n');
1840        } catch (NullPointerException e) {
1841            // writer is null
1842            streamNotCharacterOutputStream();
1843        }
1844    }
1845
1846    // Reads an 8-bit byte.
1847    /** Reads an 8-bit byte off the underlying stream
1848     *
1849     * @return
1850     */
1851    public int _readByte() {
1852        try {
1853            int n = in.read();
1854            if (n < 0)
1855                pastEnd = true;
1856
1857            return n; // Reads an 8-bit byte.
1858        } catch (IOException e) {
1859            return ierror(new StreamError(this, e));
1860        }
1861    }
1862
1863    // Writes an 8-bit byte.
1864    /** Writes an 8-bit byte off the underlying stream
1865     *
1866     * @param n
1867     */
1868    public void _writeByte(int n) {
1869        try {
1870            out.write(n); // Writes an 8-bit byte.
1871        } catch (NullPointerException e) {
1872            // out is null
1873            streamNotBinaryOutputStream();
1874        } catch (IOException e) {
1875            error(new StreamError(this, e));
1876        }
1877    }
1878
1879    /** Flushes any buffered output in the (underlying) stream
1880     *
1881     */
1882    public void _finishOutput() {
1883        try {
1884            if (writer != null)
1885                writer.flush();
1886            if (out != null)
1887                out.flush();
1888        } catch (IOException e) {
1889            error(new StreamError(this, e));
1890        }
1891    }
1892
1893    /** Reads all input from the underlying stream,
1894     * until _charReady() indicates no more input to be available
1895     *
1896     */
1897    public void _clearInput() {
1898        if (reader != null) {
1899            int c = 0;
1900            try {
1901                while (_charReady() && (c >= 0))
1902                    c = _readChar();
1903            } catch (IOException e) {
1904                error(new StreamError(this, e));
1905            }
1906        } else if (in != null) {
1907            try {
1908                int n = 0;
1909                while (in.available() > 0)
1910                    n = in.read();
1911
1912                if (n < 0)
1913                    pastEnd = true;
1914            } catch (IOException e) {
1915                error(new StreamError(this, e));
1916            }
1917        }
1918    }
1919
1920    /** Returns a (non-negative) file position integer or a negative value
1921     * if the position cannot be determined.
1922     *
1923     * @return non-negative value as a position spec
1924     * @return negative value for 'unspecified'
1925     */
1926    protected long _getFilePosition() {
1927        return -1;
1928    }
1929
1930    /** Sets the file position based on a position designator passed in arg
1931     *
1932     * @param arg File position specifier as described in the CLHS
1933     * @return true on success, false on failure
1934     */
1935    protected boolean _setFilePosition(LispObject arg) {
1936        return false;
1937    }
1938
1939    /** Closes the stream and underlying streams
1940     *
1941     */
1942    public void _close() {
1943        try {
1944            if (reader != null)
1945                reader.close();
1946            if (in != null)
1947                in.close();
1948            if (writer != null)
1949                writer.close();
1950            if (out != null)
1951                out.close();
1952            setOpen(false);
1953        } catch (IOException e) {
1954            error(new StreamError(this, e));
1955        }
1956    }
1957
1958    public void printStackTrace(Throwable t) {
1959        StringWriter sw = new StringWriter();
1960        PrintWriter pw = new PrintWriter(sw);
1961        t.printStackTrace(pw);
1962        try {
1963            writer.write(sw.toString());
1964            writer.write('\n');
1965            lastChar = '\n';
1966            writer.flush();
1967            charPos = 0;
1968        } catch (IOException e) {
1969            error(new StreamError(this, e));
1970        }
1971    }
1972
1973    protected LispObject streamNotInputStream() {
1974        return error(new StreamError(this, writeToString() + " is not an input stream."));
1975    }
1976
1977    protected LispObject streamNotCharacterInputStream() {
1978        return error(new StreamError(this, writeToString() + " is not a character input stream."));
1979    }
1980
1981    protected LispObject streamNotOutputStream() {
1982        return error(new StreamError(this, writeToString() + " is not an output stream."));
1983    }
1984
1985    protected LispObject streamNotBinaryOutputStream() {
1986        return error(new StreamError(this, writeToString() + " is not a binary output stream."));
1987    }
1988
1989    protected LispObject streamNotCharacterOutputStream() {
1990        return error(new StreamError(this, writeToString() + " is not a character output stream."));
1991    }
1992
1993    // ### %stream-write-char character output-stream => character
1994    // OUTPUT-STREAM must be a real stream, not an output stream designator!
1995    private static final Primitive _WRITE_CHAR =
1996        new Primitive("%stream-write-char", PACKAGE_SYS, true,
1997    "character output-stream") {
1998        @Override
1999        public LispObject execute(LispObject first, LispObject second)
2000
2001        {
2002            checkStream(second)._writeChar(LispCharacter.getValue(first));
2003            return first;
2004        }
2005    };
2006
2007    // ### %write-char character output-stream => character
2008    private static final Primitive _STREAM_WRITE_CHAR =
2009        new Primitive("%write-char", PACKAGE_SYS, false,
2010    "character output-stream") {
2011        @Override
2012        public LispObject execute(LispObject first, LispObject second)
2013
2014        {
2015            final char c = LispCharacter.getValue(first);
2016            if (second == T)
2017                second = Symbol.TERMINAL_IO.symbolValue();
2018            else if (second == NIL)
2019                second = Symbol.STANDARD_OUTPUT.symbolValue();
2020            final Stream stream = checkStream(second);
2021            stream._writeChar(c);
2022            return first;
2023        }
2024    };
2025
2026    // ### %write-string string output-stream start end => string
2027    private static final Primitive _WRITE_STRING =
2028        new Primitive("%write-string", PACKAGE_SYS, false,
2029    "string output-stream start end") {
2030        @Override
2031        public LispObject execute(LispObject first, LispObject second,
2032                                  LispObject third, LispObject fourth)
2033
2034        {
2035            final AbstractString s = checkString(first);
2036            char[] chars = s.chars();
2037            final Stream out = outSynonymOf(second);
2038            final int start = Fixnum.getValue(third);
2039            final int end;
2040            if (fourth == NIL)
2041                end = chars.length;
2042            else {
2043                end = Fixnum.getValue(fourth);
2044            }
2045            checkBounds(start, end, chars.length);
2046            out._writeChars(chars, start, end);
2047            return first;
2048        }
2049    };
2050
2051    // ### %finish-output output-stream => nil
2052    private static final Primitive _FINISH_OUTPUT =
2053    new Primitive("%finish-output", PACKAGE_SYS, false, "output-stream") {
2054        @Override
2055        public LispObject execute(LispObject arg) {
2056            return finishOutput(arg);
2057        }
2058    };
2059
2060    // ### %force-output output-stream => nil
2061    private static final Primitive _FORCE_OUTPUT =
2062    new Primitive("%force-output", PACKAGE_SYS, false, "output-stream") {
2063        @Override
2064        public LispObject execute(LispObject arg) {
2065            return finishOutput(arg);
2066        }
2067    };
2068
2069    static final LispObject finishOutput(LispObject arg)
2070
2071    {
2072        final LispObject out;
2073        if (arg == T)
2074            out = Symbol.TERMINAL_IO.symbolValue();
2075        else if (arg == NIL)
2076            out = Symbol.STANDARD_OUTPUT.symbolValue();
2077        else
2078            out = arg;
2079        return checkStream(out).finishOutput();
2080    }
2081
2082    // ### clear-input &optional input-stream => nil
2083    private static final Primitive CLEAR_INPUT =
2084    new Primitive(Symbol.CLEAR_INPUT, "&optional input-stream") {
2085        @Override
2086        public LispObject execute(LispObject[] args) {
2087            if (args.length > 1)
2088                return error(new WrongNumberOfArgumentsException(this));
2089            final Stream in;
2090            if (args.length == 0)
2091                in = checkCharacterInputStream(Symbol.STANDARD_INPUT.symbolValue());
2092            else
2093                in = inSynonymOf(args[0]);
2094            in.clearInput();
2095            return NIL;
2096        }
2097    };
2098
2099    // ### %clear-output output-stream => nil
2100    // "If any of these operations does not make sense for output-stream, then
2101    // it does nothing."
2102    private static final Primitive _CLEAR_OUTPUT =
2103    new Primitive("%clear-output", PACKAGE_SYS, false, "output-stream") {
2104        @Override
2105        public LispObject execute(LispObject arg) {
2106            if (arg == T) // *TERMINAL-IO*
2107                return NIL;
2108            if (arg == NIL) // *STANDARD-OUTPUT*
2109                return NIL;
2110            if (arg instanceof Stream)
2111                return NIL;
2112            return type_error(arg, Symbol.STREAM);
2113        }
2114    };
2115
2116    // ### close stream &key abort => result
2117    private static final Primitive CLOSE =
2118    new Primitive(Symbol.CLOSE, "stream &key abort") {
2119        @Override
2120        public LispObject execute(LispObject arg) {
2121            return checkStream(arg).close(NIL);
2122        }
2123
2124        @Override
2125        public LispObject execute(LispObject first, LispObject second,
2126                                  LispObject third)
2127
2128        {
2129            final Stream stream = checkStream(first);
2130            if (second == Keyword.ABORT)
2131                return stream.close(third);
2132            return error(new ProgramError("Unrecognized keyword argument " +
2133                                          second.writeToString() + "."));
2134        }
2135    };
2136
2137    // ### out-synonym-of stream-designator => stream
2138    private static final Primitive OUT_SYNONYM_OF =
2139    new Primitive("out-synonym-of", PACKAGE_SYS, true, "stream-designator") {
2140        @Override
2141        public LispObject execute (LispObject arg) {
2142            if (arg instanceof Stream)
2143                return arg;
2144            if (arg == T)
2145                return Symbol.TERMINAL_IO.symbolValue();
2146            if (arg == NIL)
2147                return Symbol.STANDARD_OUTPUT.symbolValue();
2148            return arg;
2149        }
2150    };
2151
2152    // ### write-8-bits
2153    // write-8-bits byte stream => nil
2154    private static final Primitive WRITE_8_BITS =
2155    new Primitive("write-8-bits", PACKAGE_SYS, true, "byte stream") {
2156        @Override
2157        public LispObject execute (LispObject first, LispObject second)
2158
2159        {
2160            int n = Fixnum.getValue(first);
2161            if (n < 0 || n > 255)
2162                return type_error(first, UNSIGNED_BYTE_8);
2163            checkStream(second)._writeByte(n);
2164            return NIL;
2165        }
2166    };
2167
2168    // ### read-8-bits
2169    // read-8-bits stream &optional eof-error-p eof-value => byte
2170    private static final Primitive READ_8_BITS =
2171        new Primitive("read-8-bits", PACKAGE_SYS, true,
2172    "stream &optional eof-error-p eof-value") {
2173        @Override
2174        public LispObject execute (LispObject first, LispObject second,
2175                                   LispObject third)
2176
2177        {
2178            return checkBinaryInputStream(first).readByte((second != NIL),
2179                    third);
2180        }
2181
2182        @Override
2183        public LispObject execute (LispObject[] args) {
2184            int length = args.length;
2185            if (length < 1 || length > 3)
2186                return error(new WrongNumberOfArgumentsException(this));
2187            final Stream in = checkBinaryInputStream(args[0]);
2188            boolean eofError = length > 1 ? (args[1] != NIL) : true;
2189            LispObject eofValue = length > 2 ? args[2] : NIL;
2190            return in.readByte(eofError, eofValue);
2191        }
2192    };
2193
2194    // ### read-line &optional input-stream eof-error-p eof-value recursive-p
2195    // => line, missing-newline-p
2196    private static final Primitive READ_LINE =
2197        new Primitive(Symbol.READ_LINE,
2198    "&optional input-stream eof-error-p eof-value recursive-p") {
2199        @Override
2200        public LispObject execute() {
2201            final LispObject obj = Symbol.STANDARD_INPUT.symbolValue();
2202            final Stream stream = checkStream(obj);
2203            return stream.readLine(true, NIL);
2204        }
2205        @Override
2206        public LispObject execute(LispObject arg) {
2207            if (arg == T)
2208                arg = Symbol.TERMINAL_IO.symbolValue();
2209            else if (arg == NIL)
2210                arg = Symbol.STANDARD_INPUT.symbolValue();
2211            final Stream stream = checkStream(arg);
2212            return stream.readLine(true, NIL);
2213        }
2214        @Override
2215        public LispObject execute(LispObject first, LispObject second)
2216
2217        {
2218            if (first == T)
2219                first = Symbol.TERMINAL_IO.symbolValue();
2220            else if (first == NIL)
2221                first = Symbol.STANDARD_INPUT.symbolValue();
2222            final Stream stream = checkStream(first);
2223            return stream.readLine(second != NIL, NIL);
2224        }
2225        @Override
2226        public LispObject execute(LispObject first, LispObject second,
2227                                  LispObject third)
2228
2229        {
2230            if (first == T)
2231                first = Symbol.TERMINAL_IO.symbolValue();
2232            else if (first == NIL)
2233                first = Symbol.STANDARD_INPUT.symbolValue();
2234            final Stream stream = checkStream(first);
2235            return stream.readLine(second != NIL, third);
2236        }
2237        @Override
2238        public LispObject execute(LispObject first, LispObject second,
2239                                  LispObject third, LispObject fourth)
2240
2241        {
2242            // recursive-p is ignored
2243            if (first == T)
2244                first = Symbol.TERMINAL_IO.symbolValue();
2245            else if (first == NIL)
2246                first = Symbol.STANDARD_INPUT.symbolValue();
2247            final Stream stream = checkStream(first);
2248            return stream.readLine(second != NIL, third);
2249        }
2250    };
2251
2252    // ### %read-from-string string eof-error-p eof-value start end preserve-whitespace
2253    // => object, position
2254    private static final Primitive _READ_FROM_STRING =
2255    new Primitive("%read-from-string", PACKAGE_SYS, false) {
2256        @Override
2257        public LispObject execute(LispObject first, LispObject second,
2258                                  LispObject third, LispObject fourth,
2259                                  LispObject fifth, LispObject sixth)
2260
2261        {
2262            String s = first.getStringValue();
2263            boolean eofError = (second != NIL);
2264            boolean preserveWhitespace = (sixth != NIL);
2265            final int startIndex;
2266            if (fourth != NIL)
2267                startIndex = Fixnum.getValue(fourth);
2268            else
2269                startIndex = 0;
2270            final int endIndex;
2271            if (fifth != NIL)
2272                endIndex = Fixnum.getValue(fifth);
2273            else
2274                endIndex = s.length();
2275            StringInputStream in =
2276                new StringInputStream(s, startIndex, endIndex);
2277            final LispThread thread = LispThread.currentThread();
2278            LispObject result;
2279            if (preserveWhitespace)
2280                result = in.readPreservingWhitespace(eofError, third, false,
2281                                                     thread, currentReadtable);
2282            else
2283                result = in.read(eofError, third, false, thread, currentReadtable);
2284            return thread.setValues(result, Fixnum.getInstance(in.getOffset()));
2285        }
2286    };
2287
2288    // ### read &optional input-stream eof-error-p eof-value recursive-p => object
2289    private static final Primitive READ =
2290        new Primitive(Symbol.READ,
2291    "&optional input-stream eof-error-p eof-value recursive-p") {
2292        @Override
2293        public LispObject execute() {
2294            final LispThread thread = LispThread.currentThread();
2295            final LispObject obj = Symbol.STANDARD_INPUT.symbolValue(thread);
2296            final Stream stream = checkStream(obj);
2297            return stream.read(true, NIL, false, thread, currentReadtable);
2298        }
2299        @Override
2300        public LispObject execute(LispObject arg) {
2301            final LispThread thread = LispThread.currentThread();
2302            if (arg == T)
2303                arg = Symbol.TERMINAL_IO.symbolValue(thread);
2304            else if (arg == NIL)
2305                arg = Symbol.STANDARD_INPUT.symbolValue(thread);
2306            final Stream stream = checkStream(arg);
2307            return stream.read(true, NIL, false, thread, currentReadtable);
2308        }
2309        @Override
2310        public LispObject execute(LispObject first, LispObject second)
2311
2312        {
2313            final LispThread thread = LispThread.currentThread();
2314            if (first == T)
2315                first = Symbol.TERMINAL_IO.symbolValue(thread);
2316            else if (first == NIL)
2317                first = Symbol.STANDARD_INPUT.symbolValue(thread);
2318            final Stream stream = checkStream(first);
2319            return stream.read(second != NIL, NIL, false, thread, currentReadtable);
2320        }
2321        @Override
2322        public LispObject execute(LispObject first, LispObject second,
2323                                  LispObject third)
2324
2325        {
2326            final LispThread thread = LispThread.currentThread();
2327            if (first == T)
2328                first = Symbol.TERMINAL_IO.symbolValue(thread);
2329            else if (first == NIL)
2330                first = Symbol.STANDARD_INPUT.symbolValue(thread);
2331            final Stream stream = checkStream(first);
2332            return stream.read(second != NIL, third, false, thread, currentReadtable);
2333        }
2334        @Override
2335        public LispObject execute(LispObject first, LispObject second,
2336                                  LispObject third, LispObject fourth)
2337
2338        {
2339            final LispThread thread = LispThread.currentThread();
2340            if (first == T)
2341                first = Symbol.TERMINAL_IO.symbolValue(thread);
2342            else if (first == NIL)
2343                first = Symbol.STANDARD_INPUT.symbolValue(thread);
2344            final Stream stream = checkStream(first);
2345            return stream.read(second != NIL, third, fourth != NIL,
2346                               thread, currentReadtable);
2347        }
2348    };
2349
2350    // ### read-preserving-whitespace
2351    // &optional input-stream eof-error-p eof-value recursive-p => object
2352    private static final Primitive READ_PRESERVING_WHITESPACE =
2353        new Primitive(Symbol.READ_PRESERVING_WHITESPACE,
2354    "&optional input-stream eof-error-p eof-value recursive-p") {
2355        @Override
2356        public LispObject execute(LispObject[] args) {
2357            int length = args.length;
2358            if (length > 4)
2359                return error(new WrongNumberOfArgumentsException(this));
2360            Stream stream =
2361                length > 0 ? inSynonymOf(args[0]) : getStandardInput();
2362            boolean eofError = length > 1 ? (args[1] != NIL) : true;
2363            LispObject eofValue = length > 2 ? args[2] : NIL;
2364            boolean recursive = length > 3 ? (args[3] != NIL) : false;
2365            return stream.readPreservingWhitespace(eofError, eofValue,
2366                                                   recursive,
2367                                                   LispThread.currentThread(),
2368                                                   currentReadtable);
2369        }
2370    };
2371
2372    // ### read-char &optional input-stream eof-error-p eof-value recursive-p
2373    // => char
2374    private static final Primitive READ_CHAR =
2375        new Primitive(Symbol.READ_CHAR,
2376    "&optional input-stream eof-error-p eof-value recursive-p") {
2377        @Override
2378        public LispObject execute() {
2379            return checkCharacterInputStream(Symbol.STANDARD_INPUT.symbolValue()).readChar();
2380        }
2381        @Override
2382        public LispObject execute(LispObject arg) {
2383            return inSynonymOf(arg).readChar();
2384        }
2385        @Override
2386        public LispObject execute(LispObject first, LispObject second)
2387
2388        {
2389            return inSynonymOf(first).readChar(second != NIL, NIL);
2390        }
2391        @Override
2392        public LispObject execute(LispObject first, LispObject second,
2393                                  LispObject third)
2394
2395        {
2396            return inSynonymOf(first).readChar(second != NIL, third);
2397        }
2398        @Override
2399        public LispObject execute(LispObject first, LispObject second,
2400                                  LispObject third, LispObject fourth)
2401
2402        {
2403            return inSynonymOf(first).readChar(second != NIL, third);
2404        }
2405    };
2406
2407    // ### read-char-no-hang &optional input-stream eof-error-p eof-value
2408    // recursive-p => char
2409    private static final Primitive READ_CHAR_NO_HANG =
2410    new Primitive("read-char-no-hang", "&optional input-stream eof-error-p eof-value recursive-p") {
2411
2412        @Override
2413        public LispObject execute(LispObject[] args) {
2414            int length = args.length;
2415            if (length > 4)
2416                error(new WrongNumberOfArgumentsException(this));
2417            Stream stream =
2418                length > 0 ? inSynonymOf(args[0]) : getStandardInput();
2419            boolean eofError = length > 1 ? (args[1] != NIL) : true;
2420            LispObject eofValue = length > 2 ? args[2] : NIL;
2421            // recursive-p is ignored
2422            // boolean recursive = length > 3 ? (args[3] != NIL) : false;
2423            return stream.readCharNoHang(eofError, eofValue);
2424        }
2425    };
2426
2427    // ### read-delimited-list char &optional input-stream recursive-p => list
2428    private static final Primitive READ_DELIMITED_LIST =
2429    new Primitive("read-delimited-list", "char &optional input-stream recursive-p") {
2430
2431        @Override
2432        public LispObject execute(LispObject[] args) {
2433            int length = args.length;
2434            if (length < 1 || length > 3)
2435                error(new WrongNumberOfArgumentsException(this));
2436            char c = LispCharacter.getValue(args[0]);
2437            Stream stream =
2438                length > 1 ? inSynonymOf(args[1]) : getStandardInput();
2439            return stream.readDelimitedList(c);
2440        }
2441    };
2442
2443
2444    // ### unread-char character &optional input-stream => nil
2445    private static final Primitive UNREAD_CHAR =
2446    new Primitive(Symbol.UNREAD_CHAR, "character &optional input-stream") {
2447        @Override
2448        public LispObject execute(LispObject arg) {
2449            return getStandardInput().unreadChar(checkCharacter(arg));
2450        }
2451        @Override
2452        public LispObject execute(LispObject first, LispObject second)
2453
2454        {
2455            Stream stream = inSynonymOf(second);
2456            return stream.unreadChar(checkCharacter(first));
2457        }
2458    };
2459
2460    // ### write-vector-unsigned-byte-8
2461    private static final Primitive WRITE_VECTOR_UNSIGNED_BYTE_8 =
2462        new Primitive("write-vector-unsigned-byte-8", PACKAGE_SYS, true,
2463    "vector stream start end") {
2464        @Override
2465        public LispObject execute(LispObject first, LispObject second,
2466                                  LispObject third, LispObject fourth)
2467
2468        {
2469            final AbstractVector v = checkVector(first);
2470            final Stream stream = checkStream(second);
2471            int start = Fixnum.getValue(third);
2472            int end = Fixnum.getValue(fourth);
2473            for (int i = start; i < end; i++)
2474                stream._writeByte(v.aref(i));
2475            return v;
2476        }
2477    };
2478
2479    // ### read-vector-unsigned-byte-8 vector stream start end => position
2480    private static final Primitive READ_VECTOR_UNSIGNED_BYTE_8 =
2481        new Primitive("read-vector-unsigned-byte-8", PACKAGE_SYS, true,
2482    "vector stream start end") {
2483        @Override
2484        public LispObject execute(LispObject first, LispObject second,
2485                                  LispObject third, LispObject fourth)
2486
2487        {
2488            AbstractVector v = checkVector(first);
2489            Stream stream = checkBinaryInputStream(second);
2490            int start = Fixnum.getValue(third);
2491            int end = Fixnum.getValue(fourth);
2492            if (!v.getElementType().equal(UNSIGNED_BYTE_8))
2493                return type_error(first, list(Symbol.VECTOR,
2494                                              UNSIGNED_BYTE_8));
2495            for (int i = start; i < end; i++) {
2496                int n = stream._readByte();
2497                if (n < 0) {
2498                    // End of file.
2499                    return Fixnum.getInstance(i);
2500                }
2501                v.aset(i, n);
2502            }
2503            return fourth;
2504        }
2505    };
2506
2507    // ### file-position
2508    private static final Primitive FILE_POSITION =
2509    new Primitive("file-position", "stream &optional position-spec") {
2510        @Override
2511        public LispObject execute(LispObject arg) {
2512            return checkStream(arg).getFilePosition();
2513        }
2514        @Override
2515        public LispObject execute(LispObject first, LispObject second)
2516
2517        {
2518            return checkStream(first).setFilePosition(second);
2519        }
2520    };
2521
2522    // ### stream-line-number
2523    private static final Primitive STREAM_LINE_NUMBER =
2524    new Primitive("stream-line-number", PACKAGE_SYS, false, "stream") {
2525        @Override
2526        public LispObject execute(LispObject arg) {
2527            return Fixnum.getInstance(checkStream(arg).getLineNumber() + 1);
2528        }
2529    };
2530
2531    // ### stream-offset
2532    private static final Primitive STREAM_OFFSET =
2533    new Primitive("stream-offset", PACKAGE_SYS, false, "stream") {
2534        @Override
2535        public LispObject execute(LispObject arg) {
2536            return number(checkStream(arg).getOffset());
2537        }
2538    };
2539
2540    // ### stream-charpos stream => position
2541    private static final Primitive STREAM_CHARPOS =
2542    new Primitive("stream-charpos", PACKAGE_SYS, false) {
2543        @Override
2544        public LispObject execute(LispObject arg) {
2545            Stream stream = checkCharacterOutputStream(arg);
2546            return Fixnum.getInstance(stream.getCharPos());
2547        }
2548    };
2549
2550    // ### stream-%set-charpos stream newval => newval
2551    private static final Primitive STREAM_SET_CHARPOS =
2552    new Primitive("stream-%set-charpos", PACKAGE_SYS, false) {
2553        @Override
2554        public LispObject execute(LispObject first, LispObject second)
2555
2556        {
2557            Stream stream = checkCharacterOutputStream(first);
2558            stream.setCharPos(Fixnum.getValue(second));
2559            return second;
2560        }
2561    };
2562
2563    public InputStream getWrappedInputStream() {
2564  return in;
2565    }
2566
2567    public OutputStream getWrappedOutputStream() {
2568  return out;
2569    }
2570
2571    public Writer getWrappedWriter() {
2572  return writer;
2573    }
2574
2575    public PushbackReader getWrappedReader() {
2576  return reader;
2577    }
2578
2579}
Note: See TracBrowser for help on using the repository browser.