source: branches/1.3.1/src/org/armedbear/lisp/Stream.java

Last change on this file was 14466, checked in by rschlatte, 12 years ago

call type_error when possible

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