source: trunk/abcl/src/org/armedbear/lisp/Stream.java

Last change on this file was 15770, checked in by Mark Evenson, 4 months ago

Have coerceToPathname use Gray stream PATHNAME generic

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