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

Last change on this file was 13508, checked in by Mark Evenson, 14 years ago

Fix #148: READTABLE-CASE :invert doesn't work for symbols.

A slightly modified version of the patch provided by Ole Arnedt with a
test.

  • 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 13508 2011-08-16 09:49:25Z mevenson $
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        return readSymbol(rt);
549    }
550
551    public LispObject readSymbol(Readtable rt) {
552        final StringBuilder sb = new StringBuilder();
553        final BitSet flags = _readToken(sb, rt);
554        return new Symbol(rt.getReadtableCase() == Keyword.INVERT
555                          ? invert(sb.toString(), flags)
556                          : 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.princToString(),
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.princToString(),
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.princToString() +
670                                                      " is not of type " +
671                                                      Symbol.LIST.princToString() + ".",
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.princToString() + " 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.printObject());
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    // read-byte stream &optional eof-error-p eof-value => byte
1580    // Reads an 8-bit byte.
1581    public LispObject readByte(boolean eofError, LispObject eofValue)
1582
1583    {
1584        int n = _readByte();
1585        if (n < 0) {
1586            if (eofError)
1587                return error(new EndOfFile(this));
1588            else
1589                return eofValue;
1590        }
1591        return Fixnum.constants[n];
1592    }
1593
1594    public LispObject terpri() {
1595        _writeChar('\n');
1596        return NIL;
1597    }
1598
1599    public LispObject freshLine() {
1600        if (charPos == 0)
1601            return NIL;
1602        _writeChar('\n');
1603        return T;
1604    }
1605
1606    public void print(char c) {
1607        _writeChar(c);
1608    }
1609
1610    // PRIN1 produces output suitable for input to READ.
1611    // Binds *PRINT-ESCAPE* to true.
1612    public void prin1(LispObject obj) {
1613        LispThread thread = LispThread.currentThread();
1614        final SpecialBindingsMark mark = thread.markSpecialBindings();
1615        thread.bindSpecial(Symbol.PRINT_ESCAPE, T);
1616        try {
1617            _writeString(obj.printObject());
1618        } finally {
1619            thread.resetSpecialBindings(mark);
1620        }
1621    }
1622
1623    public LispObject listen() {
1624        if (pastEnd)
1625            return NIL;
1626        try {
1627            if (! _charReady())
1628                return NIL;
1629
1630            int n = _readChar();
1631            if (n < 0)
1632                return NIL;
1633
1634            _unreadChar(n);
1635
1636            return T;
1637        } catch (IOException e) {
1638            return error(new StreamError(this, e));
1639        }
1640    }
1641
1642    public LispObject fileLength() {
1643        return type_error(this, Symbol.FILE_STREAM);
1644    }
1645
1646    public LispObject fileStringLength(LispObject arg) {
1647        if (arg instanceof LispCharacter) {
1648            if (Utilities.isPlatformWindows) {
1649                if (((LispCharacter)arg).value == '\n')
1650                    return Fixnum.TWO;
1651            }
1652            return Fixnum.ONE;
1653        }
1654        if (arg instanceof AbstractString) {
1655            if (Utilities.isPlatformWindows) {
1656                int fileStringLength = 0;
1657                char[] chars = ((AbstractString)arg).getStringChars();
1658                for (int i = chars.length; i-- > 0;) {
1659                    if (chars[i] == '\n')
1660                        fileStringLength += 2;
1661                    else
1662                        ++fileStringLength;
1663                }
1664                return number(fileStringLength);
1665
1666            }
1667            return number(arg.length());
1668        }
1669        return error(new TypeError(arg.princToString() +
1670                                   " is neither a string nor a character."));
1671    }
1672
1673    /** Reads a character off an underlying stream
1674     *
1675     * @return a character, or -1 at end-of-file
1676     */
1677    protected int _readChar() throws IOException {
1678        if (reader == null)
1679            streamNotCharacterInputStream();
1680
1681        int n = reader.read();
1682
1683        if (n < 0) {
1684            pastEnd = true;
1685            return -1;
1686        }
1687
1688        ++offset;
1689        if (n == '\r' && eolStyle == EolStyle.CRLF) {
1690            n = _readChar();
1691            if (n != '\n') {
1692                _unreadChar(n);
1693                return '\r';
1694            } else
1695                return '\n';
1696        }
1697
1698        if (n == eolChar) {
1699            ++lineNumber;
1700            return '\n';
1701        }
1702
1703        return n;
1704    }
1705
1706    /** Puts a character back into the (underlying) stream
1707     *
1708     * @param n
1709     */
1710    protected void _unreadChar(int n) throws IOException {
1711        if (reader == null)
1712            streamNotCharacterInputStream();
1713
1714        --offset;
1715        if (n == '\n') {
1716            n = eolChar;
1717            --lineNumber;
1718        }
1719
1720        reader.unread(n);
1721        pastEnd = false;
1722    }
1723
1724
1725    /** Returns a boolean indicating input readily available
1726     *
1727     * @return true if a character is available
1728     */
1729    protected boolean _charReady() throws IOException {
1730        if (reader == null)
1731            streamNotCharacterInputStream();
1732        return reader.ready();
1733    }
1734
1735    /** Writes a character into the underlying stream,
1736     * updating charPos while doing so
1737     *
1738     * @param c
1739     */
1740    public void _writeChar(char c) {
1741        try {
1742            if (c == '\n') {
1743                if (eolStyle == EolStyle.CRLF && lastChar != '\r')
1744                    writer.write('\r');
1745
1746                writer.write(eolChar);
1747                lastChar = eolChar;
1748                writer.flush();
1749                charPos = 0;
1750            } else {
1751                writer.write(c);
1752                lastChar = c;
1753                ++charPos;
1754            }
1755        } catch (NullPointerException e) {
1756            // writer is null
1757            streamNotCharacterOutputStream();
1758        } catch (IOException e) {
1759            error(new StreamError(this, e));
1760        }
1761    }
1762
1763    /** Writes a series of characters in the underlying stream,
1764     * updating charPos while doing so
1765     *
1766     * @param chars
1767     * @param start
1768     * @param end
1769     */
1770    public void _writeChars(char[] chars, int start, int end)
1771
1772    {
1773        try {
1774            if (eolStyle != EolStyle.RAW) {
1775                for (int i = start; i < end; i++)
1776                    //###FIXME: the number of writes can be greatly reduced by
1777                    // writing the space between newlines as chunks.
1778                    _writeChar(chars[i]);
1779                return;
1780            }
1781
1782            writer.write(chars, start, end - start);
1783            if (start < end)
1784                lastChar = chars[end-1];
1785
1786            int index = -1;
1787            for (int i = end; i-- > start;) {
1788                if (chars[i] == '\n') {
1789                    index = i;
1790                    break;
1791                }
1792            }
1793            if (index < 0) {
1794                // No newline.
1795                charPos += (end - start);
1796            } else {
1797                charPos = end - (index + 1);
1798                writer.flush();
1799            }
1800        } catch (NullPointerException e) {
1801            if (writer == null)
1802                streamNotCharacterOutputStream();
1803            else
1804                throw e;
1805        } catch (IOException e) {
1806            error(new StreamError(this, e));
1807        }
1808    }
1809
1810    /** Writes a string to the underlying stream,
1811     * updating charPos while doing so
1812     *
1813     * @param s
1814     */
1815    public void _writeString(String s) {
1816        try {
1817            _writeChars(s.toCharArray(), 0, s.length());
1818        } catch (NullPointerException e) {
1819            if (writer == null)
1820                streamNotCharacterOutputStream();
1821            else
1822                throw e;
1823        }
1824    }
1825
1826    /** Writes a string to the underlying stream, appending
1827     * a new line and updating charPos while doing so
1828     *
1829     * @param s
1830     */
1831    public void _writeLine(String s) {
1832        try {
1833            _writeString(s);
1834            _writeChar('\n');
1835        } catch (NullPointerException e) {
1836            // writer is null
1837            streamNotCharacterOutputStream();
1838        }
1839    }
1840
1841    // Reads an 8-bit byte.
1842    /** Reads an 8-bit byte off the underlying stream
1843     *
1844     * @return
1845     */
1846    public int _readByte() {
1847        try {
1848            int n = in.read();
1849            if (n < 0)
1850                pastEnd = true;
1851
1852            return n; // Reads an 8-bit byte.
1853        } catch (IOException e) {
1854            return ierror(new StreamError(this, e));
1855        }
1856    }
1857
1858    // Writes an 8-bit byte.
1859    /** Writes an 8-bit byte off the underlying stream
1860     *
1861     * @param n
1862     */
1863    public void _writeByte(int n) {
1864        try {
1865            out.write(n); // Writes an 8-bit byte.
1866        } catch (NullPointerException e) {
1867            // out is null
1868            streamNotBinaryOutputStream();
1869        } catch (IOException e) {
1870            error(new StreamError(this, e));
1871        }
1872    }
1873
1874    /** Flushes any buffered output in the (underlying) stream
1875     *
1876     */
1877    public void _finishOutput() {
1878        try {
1879            if (writer != null)
1880                writer.flush();
1881            if (out != null)
1882                out.flush();
1883        } catch (IOException e) {
1884            error(new StreamError(this, e));
1885        }
1886    }
1887
1888    /** Reads all input from the underlying stream,
1889     * until _charReady() indicates no more input to be available
1890     *
1891     */
1892    public void _clearInput() {
1893        if (reader != null) {
1894            int c = 0;
1895            try {
1896                while (_charReady() && (c >= 0))
1897                    c = _readChar();
1898            } catch (IOException e) {
1899                error(new StreamError(this, e));
1900            }
1901        } else if (in != null) {
1902            try {
1903                int n = 0;
1904                while (in.available() > 0)
1905                    n = in.read();
1906
1907                if (n < 0)
1908                    pastEnd = true;
1909            } catch (IOException e) {
1910                error(new StreamError(this, e));
1911            }
1912        }
1913    }
1914
1915    /** Returns a (non-negative) file position integer or a negative value
1916     * if the position cannot be determined.
1917     *
1918     * @return non-negative value as a position spec
1919     * @return negative value for 'unspecified'
1920     */
1921    protected long _getFilePosition() {
1922        return -1;
1923    }
1924
1925    /** Sets the file position based on a position designator passed in arg
1926     *
1927     * @param arg File position specifier as described in the CLHS
1928     * @return true on success, false on failure
1929     */
1930    protected boolean _setFilePosition(LispObject arg) {
1931        return false;
1932    }
1933
1934    /** Closes the stream and underlying streams
1935     *
1936     */
1937    public void _close() {
1938        try {
1939            if (reader != null)
1940                reader.close();
1941            if (in != null)
1942                in.close();
1943            if (writer != null)
1944                writer.close();
1945            if (out != null)
1946                out.close();
1947            setOpen(false);
1948        } catch (IOException e) {
1949            error(new StreamError(this, e));
1950        }
1951    }
1952
1953    public void printStackTrace(Throwable t) {
1954        StringWriter sw = new StringWriter();
1955        PrintWriter pw = new PrintWriter(sw);
1956        t.printStackTrace(pw);
1957        try {
1958            writer.write(sw.toString());
1959            writer.write('\n');
1960            lastChar = '\n';
1961            writer.flush();
1962            charPos = 0;
1963        } catch (IOException e) {
1964            error(new StreamError(this, e));
1965        }
1966    }
1967
1968    protected LispObject streamNotInputStream() {
1969        return error(new StreamError(this, princToString() + " is not an input stream."));
1970    }
1971
1972    protected LispObject streamNotCharacterInputStream() {
1973        return error(new StreamError(this, princToString() + " is not a character input stream."));
1974    }
1975
1976    protected LispObject streamNotOutputStream() {
1977        return error(new StreamError(this, princToString() + " is not an output stream."));
1978    }
1979
1980    protected LispObject streamNotBinaryOutputStream() {
1981        return error(new StreamError(this, princToString() + " is not a binary output stream."));
1982    }
1983
1984    protected LispObject streamNotCharacterOutputStream() {
1985        return error(new StreamError(this, princToString() + " is not a character output stream."));
1986    }
1987
1988    // ### %stream-write-char character output-stream => character
1989    // OUTPUT-STREAM must be a real stream, not an output stream designator!
1990    private static final Primitive _WRITE_CHAR =
1991        new Primitive("%stream-write-char", PACKAGE_SYS, true,
1992    "character output-stream") {
1993        @Override
1994        public LispObject execute(LispObject first, LispObject second)
1995
1996        {
1997            checkStream(second)._writeChar(LispCharacter.getValue(first));
1998            return first;
1999        }
2000    };
2001
2002    // ### %write-char character output-stream => character
2003    private static final Primitive _STREAM_WRITE_CHAR =
2004        new Primitive("%write-char", PACKAGE_SYS, false,
2005    "character output-stream") {
2006        @Override
2007        public LispObject execute(LispObject first, LispObject second)
2008
2009        {
2010            final char c = LispCharacter.getValue(first);
2011            if (second == T)
2012                second = Symbol.TERMINAL_IO.symbolValue();
2013            else if (second == NIL)
2014                second = Symbol.STANDARD_OUTPUT.symbolValue();
2015            final Stream stream = checkStream(second);
2016            stream._writeChar(c);
2017            return first;
2018        }
2019    };
2020
2021    // ### %write-string string output-stream start end => string
2022    private static final Primitive _WRITE_STRING =
2023        new Primitive("%write-string", PACKAGE_SYS, false,
2024    "string output-stream start end") {
2025        @Override
2026        public LispObject execute(LispObject first, LispObject second,
2027                                  LispObject third, LispObject fourth)
2028
2029        {
2030            final AbstractString s = checkString(first);
2031            char[] chars = s.chars();
2032            final Stream out = outSynonymOf(second);
2033            final int start = Fixnum.getValue(third);
2034            final int end;
2035            if (fourth == NIL)
2036                end = chars.length;
2037            else {
2038                end = Fixnum.getValue(fourth);
2039            }
2040            checkBounds(start, end, chars.length);
2041            out._writeChars(chars, start, end);
2042            return first;
2043        }
2044    };
2045
2046    // ### %finish-output output-stream => nil
2047    private static final Primitive _FINISH_OUTPUT =
2048    new Primitive("%finish-output", PACKAGE_SYS, false, "output-stream") {
2049        @Override
2050        public LispObject execute(LispObject arg) {
2051            return finishOutput(arg);
2052        }
2053    };
2054
2055    // ### %force-output output-stream => nil
2056    private static final Primitive _FORCE_OUTPUT =
2057    new Primitive("%force-output", PACKAGE_SYS, false, "output-stream") {
2058        @Override
2059        public LispObject execute(LispObject arg) {
2060            return finishOutput(arg);
2061        }
2062    };
2063
2064    static final LispObject finishOutput(LispObject arg)
2065
2066    {
2067        final LispObject out;
2068        if (arg == T)
2069            out = Symbol.TERMINAL_IO.symbolValue();
2070        else if (arg == NIL)
2071            out = Symbol.STANDARD_OUTPUT.symbolValue();
2072        else
2073            out = arg;
2074        return checkStream(out).finishOutput();
2075    }
2076
2077    // ### clear-input &optional input-stream => nil
2078    private static final Primitive CLEAR_INPUT =
2079    new Primitive(Symbol.CLEAR_INPUT, "&optional input-stream") {
2080        @Override
2081        public LispObject execute(LispObject[] args) {
2082            if (args.length > 1)
2083                return error(new WrongNumberOfArgumentsException(this, -1, 1));
2084            final Stream in;
2085            if (args.length == 0)
2086                in = checkCharacterInputStream(Symbol.STANDARD_INPUT.symbolValue());
2087            else
2088                in = inSynonymOf(args[0]);
2089            in.clearInput();
2090            return NIL;
2091        }
2092    };
2093
2094    // ### %clear-output output-stream => nil
2095    // "If any of these operations does not make sense for output-stream, then
2096    // it does nothing."
2097    private static final Primitive _CLEAR_OUTPUT =
2098    new Primitive("%clear-output", PACKAGE_SYS, false, "output-stream") {
2099        @Override
2100        public LispObject execute(LispObject arg) {
2101            if (arg == T) // *TERMINAL-IO*
2102                return NIL;
2103            if (arg == NIL) // *STANDARD-OUTPUT*
2104                return NIL;
2105            if (arg instanceof Stream)
2106                return NIL;
2107            return type_error(arg, Symbol.STREAM);
2108        }
2109    };
2110
2111    // ### close stream &key abort => result
2112    private static final Primitive CLOSE =
2113    new Primitive(Symbol.CLOSE, "stream &key abort") {
2114        @Override
2115        public LispObject execute(LispObject arg) {
2116            return checkStream(arg).close(NIL);
2117        }
2118
2119        @Override
2120        public LispObject execute(LispObject first, LispObject second,
2121                                  LispObject third)
2122
2123        {
2124            final Stream stream = checkStream(first);
2125            if (second == Keyword.ABORT)
2126                return stream.close(third);
2127            return error(new ProgramError("Unrecognized keyword argument " +
2128                                          second.princToString() + "."));
2129        }
2130    };
2131
2132    // ### out-synonym-of stream-designator => stream
2133    private static final Primitive OUT_SYNONYM_OF =
2134    new Primitive("out-synonym-of", PACKAGE_SYS, true, "stream-designator") {
2135        @Override
2136        public LispObject execute (LispObject arg) {
2137            if (arg instanceof Stream)
2138                return arg;
2139            if (arg == T)
2140                return Symbol.TERMINAL_IO.symbolValue();
2141            if (arg == NIL)
2142                return Symbol.STANDARD_OUTPUT.symbolValue();
2143            return arg;
2144        }
2145    };
2146
2147    // ### write-8-bits
2148    // write-8-bits byte stream => nil
2149    private static final Primitive WRITE_8_BITS =
2150    new Primitive("write-8-bits", PACKAGE_SYS, true, "byte stream") {
2151        @Override
2152        public LispObject execute (LispObject first, LispObject second)
2153
2154        {
2155            int n = Fixnum.getValue(first);
2156            if (n < 0 || n > 255)
2157                return type_error(first, UNSIGNED_BYTE_8);
2158            checkStream(second)._writeByte(n);
2159            return NIL;
2160        }
2161    };
2162
2163    // ### read-8-bits
2164    // read-8-bits stream &optional eof-error-p eof-value => byte
2165    private static final Primitive READ_8_BITS =
2166        new Primitive("read-8-bits", PACKAGE_SYS, true,
2167    "stream &optional eof-error-p eof-value") {
2168        @Override
2169        public LispObject execute (LispObject first, LispObject second,
2170                                   LispObject third)
2171
2172        {
2173            return checkBinaryInputStream(first).readByte((second != NIL),
2174                    third);
2175        }
2176
2177        @Override
2178        public LispObject execute (LispObject[] args) {
2179            int length = args.length;
2180            if (length < 1 || length > 3)
2181                return error(new WrongNumberOfArgumentsException(this, 1, 3));
2182            final Stream in = checkBinaryInputStream(args[0]);
2183            boolean eofError = length > 1 ? (args[1] != NIL) : true;
2184            LispObject eofValue = length > 2 ? args[2] : NIL;
2185            return in.readByte(eofError, eofValue);
2186        }
2187    };
2188
2189    // ### read-line &optional input-stream eof-error-p eof-value recursive-p
2190    // => line, missing-newline-p
2191    private static final Primitive READ_LINE =
2192        new Primitive(Symbol.READ_LINE,
2193    "&optional input-stream eof-error-p eof-value recursive-p") {
2194        @Override
2195        public LispObject execute() {
2196            final LispObject obj = Symbol.STANDARD_INPUT.symbolValue();
2197            final Stream stream = checkStream(obj);
2198            return stream.readLine(true, NIL);
2199        }
2200        @Override
2201        public LispObject execute(LispObject arg) {
2202            if (arg == T)
2203                arg = Symbol.TERMINAL_IO.symbolValue();
2204            else if (arg == NIL)
2205                arg = Symbol.STANDARD_INPUT.symbolValue();
2206            final Stream stream = checkStream(arg);
2207            return stream.readLine(true, NIL);
2208        }
2209        @Override
2210        public LispObject execute(LispObject first, LispObject second)
2211
2212        {
2213            if (first == T)
2214                first = Symbol.TERMINAL_IO.symbolValue();
2215            else if (first == NIL)
2216                first = Symbol.STANDARD_INPUT.symbolValue();
2217            final Stream stream = checkStream(first);
2218            return stream.readLine(second != NIL, NIL);
2219        }
2220        @Override
2221        public LispObject execute(LispObject first, LispObject second,
2222                                  LispObject third)
2223
2224        {
2225            if (first == T)
2226                first = Symbol.TERMINAL_IO.symbolValue();
2227            else if (first == NIL)
2228                first = Symbol.STANDARD_INPUT.symbolValue();
2229            final Stream stream = checkStream(first);
2230            return stream.readLine(second != NIL, third);
2231        }
2232        @Override
2233        public LispObject execute(LispObject first, LispObject second,
2234                                  LispObject third, LispObject fourth)
2235
2236        {
2237            // recursive-p is ignored
2238            if (first == T)
2239                first = Symbol.TERMINAL_IO.symbolValue();
2240            else if (first == NIL)
2241                first = Symbol.STANDARD_INPUT.symbolValue();
2242            final Stream stream = checkStream(first);
2243            return stream.readLine(second != NIL, third);
2244        }
2245    };
2246
2247    // ### %read-from-string string eof-error-p eof-value start end preserve-whitespace
2248    // => object, position
2249    private static final Primitive _READ_FROM_STRING =
2250    new Primitive("%read-from-string", PACKAGE_SYS, false) {
2251        @Override
2252        public LispObject execute(LispObject first, LispObject second,
2253                                  LispObject third, LispObject fourth,
2254                                  LispObject fifth, LispObject sixth)
2255
2256        {
2257            String s = first.getStringValue();
2258            boolean eofError = (second != NIL);
2259            boolean preserveWhitespace = (sixth != NIL);
2260            final int startIndex;
2261            if (fourth != NIL)
2262                startIndex = Fixnum.getValue(fourth);
2263            else
2264                startIndex = 0;
2265            final int endIndex;
2266            if (fifth != NIL)
2267                endIndex = Fixnum.getValue(fifth);
2268            else
2269                endIndex = s.length();
2270            StringInputStream in =
2271                new StringInputStream(s, startIndex, endIndex);
2272            final LispThread thread = LispThread.currentThread();
2273            LispObject result;
2274            if (preserveWhitespace)
2275                result = in.readPreservingWhitespace(eofError, third, false,
2276                                                     thread, currentReadtable);
2277            else
2278                result = in.read(eofError, third, false, thread, currentReadtable);
2279            return thread.setValues(result, Fixnum.getInstance(in.getOffset()));
2280        }
2281    };
2282
2283    // ### read &optional input-stream eof-error-p eof-value recursive-p => object
2284    private static final Primitive READ =
2285        new Primitive(Symbol.READ,
2286    "&optional input-stream eof-error-p eof-value recursive-p") {
2287        @Override
2288        public LispObject execute() {
2289            final LispThread thread = LispThread.currentThread();
2290            final LispObject obj = Symbol.STANDARD_INPUT.symbolValue(thread);
2291            final Stream stream = checkStream(obj);
2292            return stream.read(true, NIL, false, thread, currentReadtable);
2293        }
2294        @Override
2295        public LispObject execute(LispObject arg) {
2296            final LispThread thread = LispThread.currentThread();
2297            if (arg == T)
2298                arg = Symbol.TERMINAL_IO.symbolValue(thread);
2299            else if (arg == NIL)
2300                arg = Symbol.STANDARD_INPUT.symbolValue(thread);
2301            final Stream stream = checkStream(arg);
2302            return stream.read(true, NIL, false, thread, currentReadtable);
2303        }
2304        @Override
2305        public LispObject execute(LispObject first, LispObject second)
2306
2307        {
2308            final LispThread thread = LispThread.currentThread();
2309            if (first == T)
2310                first = Symbol.TERMINAL_IO.symbolValue(thread);
2311            else if (first == NIL)
2312                first = Symbol.STANDARD_INPUT.symbolValue(thread);
2313            final Stream stream = checkStream(first);
2314            return stream.read(second != NIL, NIL, false, thread, currentReadtable);
2315        }
2316        @Override
2317        public LispObject execute(LispObject first, LispObject second,
2318                                  LispObject third)
2319
2320        {
2321            final LispThread thread = LispThread.currentThread();
2322            if (first == T)
2323                first = Symbol.TERMINAL_IO.symbolValue(thread);
2324            else if (first == NIL)
2325                first = Symbol.STANDARD_INPUT.symbolValue(thread);
2326            final Stream stream = checkStream(first);
2327            return stream.read(second != NIL, third, false, thread, currentReadtable);
2328        }
2329        @Override
2330        public LispObject execute(LispObject first, LispObject second,
2331                                  LispObject third, LispObject fourth)
2332
2333        {
2334            final LispThread thread = LispThread.currentThread();
2335            if (first == T)
2336                first = Symbol.TERMINAL_IO.symbolValue(thread);
2337            else if (first == NIL)
2338                first = Symbol.STANDARD_INPUT.symbolValue(thread);
2339            final Stream stream = checkStream(first);
2340            return stream.read(second != NIL, third, fourth != NIL,
2341                               thread, currentReadtable);
2342        }
2343    };
2344
2345    // ### read-preserving-whitespace
2346    // &optional input-stream eof-error-p eof-value recursive-p => object
2347    private static final Primitive READ_PRESERVING_WHITESPACE =
2348        new Primitive(Symbol.READ_PRESERVING_WHITESPACE,
2349    "&optional input-stream eof-error-p eof-value recursive-p") {
2350        @Override
2351        public LispObject execute(LispObject[] args) {
2352            int length = args.length;
2353            if (length > 4)
2354                return error(new WrongNumberOfArgumentsException(this, -1, 4));
2355            Stream stream =
2356                length > 0 ? inSynonymOf(args[0]) : getStandardInput();
2357            boolean eofError = length > 1 ? (args[1] != NIL) : true;
2358            LispObject eofValue = length > 2 ? args[2] : NIL;
2359            boolean recursive = length > 3 ? (args[3] != NIL) : false;
2360            return stream.readPreservingWhitespace(eofError, eofValue,
2361                                                   recursive,
2362                                                   LispThread.currentThread(),
2363                                                   currentReadtable);
2364        }
2365    };
2366
2367    // ### read-char &optional input-stream eof-error-p eof-value recursive-p
2368    // => char
2369    private static final Primitive READ_CHAR =
2370        new Primitive(Symbol.READ_CHAR,
2371    "&optional input-stream eof-error-p eof-value recursive-p") {
2372        @Override
2373        public LispObject execute() {
2374            return checkCharacterInputStream(Symbol.STANDARD_INPUT.symbolValue()).readChar();
2375        }
2376        @Override
2377        public LispObject execute(LispObject arg) {
2378            return inSynonymOf(arg).readChar();
2379        }
2380        @Override
2381        public LispObject execute(LispObject first, LispObject second)
2382
2383        {
2384            return inSynonymOf(first).readChar(second != NIL, NIL);
2385        }
2386        @Override
2387        public LispObject execute(LispObject first, LispObject second,
2388                                  LispObject third)
2389
2390        {
2391            return inSynonymOf(first).readChar(second != NIL, third);
2392        }
2393        @Override
2394        public LispObject execute(LispObject first, LispObject second,
2395                                  LispObject third, LispObject fourth)
2396
2397        {
2398            return inSynonymOf(first).readChar(second != NIL, third);
2399        }
2400    };
2401
2402    // ### read-char-no-hang &optional input-stream eof-error-p eof-value
2403    // recursive-p => char
2404    private static final Primitive READ_CHAR_NO_HANG =
2405    new Primitive("read-char-no-hang", "&optional input-stream eof-error-p eof-value recursive-p") {
2406
2407        @Override
2408        public LispObject execute(LispObject[] args) {
2409            int length = args.length;
2410            if (length > 4)
2411                error(new WrongNumberOfArgumentsException(this, -1, 4));
2412            Stream stream =
2413                length > 0 ? inSynonymOf(args[0]) : getStandardInput();
2414            boolean eofError = length > 1 ? (args[1] != NIL) : true;
2415            LispObject eofValue = length > 2 ? args[2] : NIL;
2416            // recursive-p is ignored
2417            // boolean recursive = length > 3 ? (args[3] != NIL) : false;
2418            return stream.readCharNoHang(eofError, eofValue);
2419        }
2420    };
2421
2422    // ### read-delimited-list char &optional input-stream recursive-p => list
2423    private static final Primitive READ_DELIMITED_LIST =
2424    new Primitive("read-delimited-list", "char &optional input-stream recursive-p") {
2425
2426        @Override
2427        public LispObject execute(LispObject[] args) {
2428            int length = args.length;
2429            if (length < 1 || length > 3)
2430                error(new WrongNumberOfArgumentsException(this, 1, 3));
2431            char c = LispCharacter.getValue(args[0]);
2432            Stream stream =
2433                length > 1 ? inSynonymOf(args[1]) : getStandardInput();
2434            return stream.readDelimitedList(c);
2435        }
2436    };
2437
2438
2439    // ### unread-char character &optional input-stream => nil
2440    private static final Primitive UNREAD_CHAR =
2441    new Primitive(Symbol.UNREAD_CHAR, "character &optional input-stream") {
2442        @Override
2443        public LispObject execute(LispObject arg) {
2444            return getStandardInput().unreadChar(checkCharacter(arg));
2445        }
2446        @Override
2447        public LispObject execute(LispObject first, LispObject second)
2448
2449        {
2450            Stream stream = inSynonymOf(second);
2451            return stream.unreadChar(checkCharacter(first));
2452        }
2453    };
2454
2455    // ### write-vector-unsigned-byte-8
2456    private static final Primitive WRITE_VECTOR_UNSIGNED_BYTE_8 =
2457        new Primitive("write-vector-unsigned-byte-8", PACKAGE_SYS, true,
2458    "vector stream start end") {
2459        @Override
2460        public LispObject execute(LispObject first, LispObject second,
2461                                  LispObject third, LispObject fourth)
2462
2463        {
2464            final AbstractVector v = checkVector(first);
2465            final Stream stream = checkStream(second);
2466            int start = Fixnum.getValue(third);
2467            int end = Fixnum.getValue(fourth);
2468            for (int i = start; i < end; i++)
2469                stream._writeByte(v.aref(i));
2470            return v;
2471        }
2472    };
2473
2474    // ### read-vector-unsigned-byte-8 vector stream start end => position
2475    private static final Primitive READ_VECTOR_UNSIGNED_BYTE_8 =
2476        new Primitive("read-vector-unsigned-byte-8", PACKAGE_SYS, true,
2477    "vector stream start end") {
2478        @Override
2479        public LispObject execute(LispObject first, LispObject second,
2480                                  LispObject third, LispObject fourth)
2481
2482        {
2483            AbstractVector v = checkVector(first);
2484            Stream stream = checkBinaryInputStream(second);
2485            int start = Fixnum.getValue(third);
2486            int end = Fixnum.getValue(fourth);
2487            if (!v.getElementType().equal(UNSIGNED_BYTE_8))
2488                return type_error(first, list(Symbol.VECTOR,
2489                                              UNSIGNED_BYTE_8));
2490            for (int i = start; i < end; i++) {
2491                int n = stream._readByte();
2492                if (n < 0) {
2493                    // End of file.
2494                    return Fixnum.getInstance(i);
2495                }
2496                v.aset(i, n);
2497            }
2498            return fourth;
2499        }
2500    };
2501
2502    // ### file-position
2503    private static final Primitive FILE_POSITION =
2504    new Primitive("file-position", "stream &optional position-spec") {
2505        @Override
2506        public LispObject execute(LispObject arg) {
2507            return checkStream(arg).getFilePosition();
2508        }
2509        @Override
2510        public LispObject execute(LispObject first, LispObject second)
2511
2512        {
2513            return checkStream(first).setFilePosition(second);
2514        }
2515    };
2516
2517    // ### stream-line-number
2518    private static final Primitive STREAM_LINE_NUMBER =
2519    new Primitive("stream-line-number", PACKAGE_SYS, false, "stream") {
2520        @Override
2521        public LispObject execute(LispObject arg) {
2522            return Fixnum.getInstance(checkStream(arg).getLineNumber() + 1);
2523        }
2524    };
2525
2526    // ### stream-offset
2527    private static final Primitive STREAM_OFFSET =
2528    new Primitive("stream-offset", PACKAGE_SYS, false, "stream") {
2529        @Override
2530        public LispObject execute(LispObject arg) {
2531            return number(checkStream(arg).getOffset());
2532        }
2533    };
2534
2535    // ### stream-charpos stream => position
2536    private static final Primitive STREAM_CHARPOS =
2537    new Primitive("stream-charpos", PACKAGE_SYS, false) {
2538        @Override
2539        public LispObject execute(LispObject arg) {
2540            Stream stream = checkCharacterOutputStream(arg);
2541            return Fixnum.getInstance(stream.getCharPos());
2542        }
2543    };
2544
2545    // ### stream-%set-charpos stream newval => newval
2546    private static final Primitive STREAM_SET_CHARPOS =
2547    new Primitive("stream-%set-charpos", PACKAGE_SYS, false) {
2548        @Override
2549        public LispObject execute(LispObject first, LispObject second)
2550
2551        {
2552            Stream stream = checkCharacterOutputStream(first);
2553            stream.setCharPos(Fixnum.getValue(second));
2554            return second;
2555        }
2556    };
2557
2558    public InputStream getWrappedInputStream() {
2559  return in;
2560    }
2561
2562    public OutputStream getWrappedOutputStream() {
2563  return out;
2564    }
2565
2566    public Writer getWrappedWriter() {
2567  return writer;
2568    }
2569
2570    public PushbackReader getWrappedReader() {
2571  return reader;
2572    }
2573
2574}
Note: See TracBrowser for help on using the repository browser.