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

Last change on this file was 12255, checked in by ehuelsmann, 16 years ago

Rename ConditionThrowable? to ControlTransfer? and remove

try/catch blocks which don't have anything to do with
non-local transfer of control.

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