source: branches/0.15.x/abcl/src/org/armedbear/lisp/Readtable.java

Last change on this file was 11754, checked in by vvoutilainen, 16 years ago

Convert using ClassCastException? to checking instanceof.
Performance tests show this approach to be faster.
Patch by Douglas R. Miles. I modified the patch to
remove tabs, so indentation may be slightly off in places.
That's something that we need to handle separately, abcl
doesn't have a clear indentation policy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.1 KB
Line 
1/*
2 * Readtable.java
3 *
4 * Copyright (C) 2003-2007 Peter Graves
5 * $Id: Readtable.java 11754 2009-04-12 10:53:39Z vvoutilainen $
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
36public class Readtable extends LispObject
37{
38  public static final byte SYNTAX_TYPE_CONSTITUENT           = 0;
39  public static final byte SYNTAX_TYPE_WHITESPACE            = 1;
40  public static final byte SYNTAX_TYPE_TERMINATING_MACRO     = 2;
41  public static final byte SYNTAX_TYPE_NON_TERMINATING_MACRO = 3;
42  public static final byte SYNTAX_TYPE_SINGLE_ESCAPE         = 4;
43  public static final byte SYNTAX_TYPE_MULTIPLE_ESCAPE       = 5;
44
45  protected final byte[]          syntax               = new byte[CHAR_MAX];
46  protected final LispObject[]    readerMacroFunctions = new LispObject[CHAR_MAX];
47  protected final DispatchTable[] dispatchTables       = new DispatchTable[CHAR_MAX];
48
49  protected LispObject readtableCase;
50
51  public Readtable()
52  {
53    initialize();
54  }
55
56  protected void initialize()
57  {
58    syntax[9]    = SYNTAX_TYPE_WHITESPACE; // tab
59    syntax[10]   = SYNTAX_TYPE_WHITESPACE; // linefeed
60    syntax[12]   = SYNTAX_TYPE_WHITESPACE; // form feed
61    syntax[13]   = SYNTAX_TYPE_WHITESPACE; // return
62    syntax[' ']  = SYNTAX_TYPE_WHITESPACE;
63
64    syntax['"']  = SYNTAX_TYPE_TERMINATING_MACRO;
65    syntax['\''] = SYNTAX_TYPE_TERMINATING_MACRO;
66    syntax['(']  = SYNTAX_TYPE_TERMINATING_MACRO;
67    syntax[')']  = SYNTAX_TYPE_TERMINATING_MACRO;
68    syntax[',']  = SYNTAX_TYPE_TERMINATING_MACRO;
69    syntax[';']  = SYNTAX_TYPE_TERMINATING_MACRO;
70    syntax['`']  = SYNTAX_TYPE_TERMINATING_MACRO;
71
72    syntax['#']  = SYNTAX_TYPE_NON_TERMINATING_MACRO;
73
74    syntax['\\'] = SYNTAX_TYPE_SINGLE_ESCAPE;
75    syntax['|']  = SYNTAX_TYPE_MULTIPLE_ESCAPE;
76
77    readerMacroFunctions[';']  = LispReader.READ_COMMENT;
78    readerMacroFunctions['"']  = LispReader.READ_STRING;
79    readerMacroFunctions['(']  = LispReader.READ_LIST;
80    readerMacroFunctions[')']  = LispReader.READ_RIGHT_PAREN;
81    readerMacroFunctions['\''] = LispReader.READ_QUOTE;
82    readerMacroFunctions['#']  = LispReader.READ_DISPATCH_CHAR;
83
84    // BACKQUOTE-MACRO and COMMA-MACRO are defined in backquote.lisp.
85    readerMacroFunctions['`']  = Symbol.BACKQUOTE_MACRO;
86    readerMacroFunctions[',']  = Symbol.COMMA_MACRO;
87
88    DispatchTable dt = new DispatchTable();
89
90    dt.functions['(']  = LispReader.SHARP_LEFT_PAREN;
91    dt.functions['*']  = LispReader.SHARP_STAR;
92    dt.functions['.']  = LispReader.SHARP_DOT;
93    dt.functions[':']  = LispReader.SHARP_COLON;
94    dt.functions['A']  = LispReader.SHARP_A;
95    dt.functions['B']  = LispReader.SHARP_B;
96    dt.functions['C']  = LispReader.SHARP_C;
97    dt.functions['O']  = LispReader.SHARP_O;
98    dt.functions['P']  = LispReader.SHARP_P;
99    dt.functions['R']  = LispReader.SHARP_R;
100    dt.functions['S']  = LispReader.SHARP_S;
101    dt.functions['X']  = LispReader.SHARP_X;
102    dt.functions['\''] = LispReader.SHARP_QUOTE;
103    dt.functions['\\'] = LispReader.SHARP_BACKSLASH;
104    dt.functions['|']  = LispReader.SHARP_VERTICAL_BAR;
105    dt.functions[')']  = LispReader.SHARP_ILLEGAL;
106    dt.functions['<']  = LispReader.SHARP_ILLEGAL;
107    dt.functions[' ']  = LispReader.SHARP_ILLEGAL;
108    dt.functions[8]    = LispReader.SHARP_ILLEGAL; // backspace
109    dt.functions[9]    = LispReader.SHARP_ILLEGAL; // tab
110    dt.functions[10]   = LispReader.SHARP_ILLEGAL; // newline, linefeed
111    dt.functions[12]   = LispReader.SHARP_ILLEGAL; // page
112    dt.functions[13]   = LispReader.SHARP_ILLEGAL; // return
113
114    dispatchTables['#'] = dt;
115
116    readtableCase = Keyword.UPCASE;
117  }
118
119  public Readtable(LispObject obj) throws ConditionThrowable
120  {
121    Readtable rt;
122    if (obj == NIL)
123      rt = checkReadtable(STANDARD_READTABLE.symbolValue());
124    else
125      rt = checkReadtable(obj);
126    synchronized (rt)
127      {
128        System.arraycopy(rt.syntax, 0, syntax, 0, CHAR_MAX);
129        System.arraycopy(rt.readerMacroFunctions, 0, readerMacroFunctions, 0,
130                         CHAR_MAX);
131        // Deep copy.
132        for (int i = dispatchTables.length; i-- > 0;)
133          {
134            DispatchTable dt = rt.dispatchTables[i];
135            if (dt != null)
136              dispatchTables[i] = new DispatchTable(dt);
137          }
138        readtableCase = rt.readtableCase;
139      }
140  }
141
142  // FIXME synchronization
143  private static void copyReadtable(Readtable from, Readtable to)
144  {
145    System.arraycopy(from.syntax, 0, to.syntax, 0, CHAR_MAX);
146    System.arraycopy(from.readerMacroFunctions, 0, to.readerMacroFunctions, 0,
147                     CHAR_MAX);
148    for (int i = from.dispatchTables.length; i-- > 0;)
149      {
150        DispatchTable dt = from.dispatchTables[i];
151        if (dt != null)
152          to.dispatchTables[i] = new DispatchTable(dt);
153        else
154          to.dispatchTables[i] = null;
155      }
156    to.readtableCase = from.readtableCase;
157  }
158
159  @Override
160  public LispObject typeOf()
161  {
162    return Symbol.READTABLE;
163  }
164
165  @Override
166  public LispObject classOf()
167  {
168    return BuiltInClass.READTABLE;
169  }
170
171  @Override
172  public LispObject typep(LispObject type) throws ConditionThrowable
173  {
174    if (type == Symbol.READTABLE)
175      return T;
176    if (type == BuiltInClass.READTABLE)
177      return T;
178    return super.typep(type);
179  }
180
181  @Override
182  public String toString()
183  {
184    return unreadableString("READTABLE");
185  }
186
187  public LispObject getReadtableCase()
188  {
189    return readtableCase;
190  }
191
192  public boolean isWhitespace(char c)
193  {
194    if (c < CHAR_MAX)
195      return syntax[c] == SYNTAX_TYPE_WHITESPACE;
196    return false;
197  }
198
199  public byte getSyntaxType(char c)
200  {
201    if (c < CHAR_MAX)
202      return syntax[c];
203    return SYNTAX_TYPE_CONSTITUENT;
204  }
205
206  public boolean isInvalid(char c)
207  {
208    switch (c)
209      {
210      case 8:
211      case 9:
212      case 10:
213      case 12:
214      case 13:
215      case 32:
216      case 127:
217        return true;
218      default:
219        return false;
220      }
221  }
222
223  public void checkInvalid(char c, Stream stream) throws ConditionThrowable
224  {
225    // "... no mechanism is provided for changing the constituent trait of a
226    // character." (2.1.4.2)
227    if (isInvalid(c))
228      {
229        String name = LispCharacter.charToName(c);
230        FastStringBuffer sb = new FastStringBuffer("Invalid character");
231        if (name != null)
232          {
233            sb.append(" #\\");
234            sb.append(name);
235          }
236        error(new ReaderError(sb.toString(), stream));
237      }
238  }
239
240  public LispObject getReaderMacroFunction(char c)
241  {
242    if (c < CHAR_MAX)
243      return readerMacroFunctions[c];
244    else
245      return null;
246  }
247
248  private LispObject getMacroCharacter(char c) throws ConditionThrowable
249  {
250    LispObject function = getReaderMacroFunction(c);
251    LispObject non_terminating_p;
252    if (function != null)
253      {
254        if (syntax[c] == SYNTAX_TYPE_NON_TERMINATING_MACRO)
255          non_terminating_p = T;
256        else
257          non_terminating_p = NIL;
258      }
259    else
260      {
261        function = NIL;
262        non_terminating_p = NIL;
263      }
264    return LispThread.currentThread().setValues(function, non_terminating_p);
265  }
266
267  private void makeDispatchMacroCharacter(char dispChar, LispObject non_terminating_p)
268  {
269    byte syntaxType;
270    if (non_terminating_p != NIL)
271      syntaxType = SYNTAX_TYPE_NON_TERMINATING_MACRO;
272    else
273      syntaxType = SYNTAX_TYPE_TERMINATING_MACRO;
274    // FIXME synchronization
275    syntax[dispChar] = syntaxType;
276    readerMacroFunctions[dispChar] = LispReader.READ_DISPATCH_CHAR;
277    dispatchTables[dispChar] = new DispatchTable();
278  }
279
280  public LispObject getDispatchMacroCharacter(char dispChar, char subChar)
281    throws ConditionThrowable
282  {
283    DispatchTable dispatchTable = dispatchTables[dispChar];
284    if (dispatchTable == null)
285      {
286        LispCharacter c = LispCharacter.getInstance(dispChar);
287        return error(new LispError(c.writeToString() +
288                                    " is not a dispatch character."));
289      }
290    LispObject function =
291      dispatchTable.functions[LispCharacter.toUpperCase(subChar)];
292    return (function != null) ? function : NIL;
293  }
294
295  public void setDispatchMacroCharacter(char dispChar, char subChar,
296                                        LispObject function)
297    throws ConditionThrowable
298  {
299    DispatchTable dispatchTable = dispatchTables[dispChar];
300    if (dispatchTable == null)
301      {
302        LispCharacter c = LispCharacter.getInstance(dispChar);
303        error(new LispError(c.writeToString() +
304                             " is not a dispatch character."));
305      }
306    dispatchTable.functions[LispCharacter.toUpperCase(subChar)] = function;
307  }
308
309  protected static class DispatchTable
310  {
311    public LispObject[] functions = new LispObject[CHAR_MAX];
312
313    public DispatchTable()
314    {
315    }
316
317    public DispatchTable(DispatchTable dt)
318    {
319      for (int i = 0; i < functions.length; i++)
320        functions[i] = dt.functions[i];
321    }
322  }
323
324  // ### readtablep
325  private static final Primitive READTABLEP =
326    new Primitive("readtablep", "object")
327    {
328      @Override
329      public LispObject execute(LispObject arg)
330      {
331        return arg instanceof Readtable ? T : NIL;
332      }
333    };
334
335  // ### copy-readtable
336  private static final Primitive COPY_READTABLE =
337    new Primitive("copy-readtable", "&optional from-readtable to-readtable")
338    {
339      @Override
340      public LispObject execute() throws ConditionThrowable
341      {
342        return new Readtable(currentReadtable());
343      }
344
345      @Override
346      public LispObject execute(LispObject arg) throws ConditionThrowable
347      {
348        return new Readtable(arg);
349      }
350
351      @Override
352      public LispObject execute(LispObject first, LispObject second)
353        throws ConditionThrowable
354      {
355        Readtable from = designator_readtable(first);
356        if (second == NIL)
357          return new Readtable(from);
358        Readtable to = checkReadtable(second);
359        copyReadtable(from, to);
360        return to;
361      }
362    };
363
364  // ### get-macro-character char &optional readtable
365  // => function, non-terminating-p
366  private static final Primitive GET_MACRO_CHARACTER =
367    new Primitive("get-macro-character", "char &optional readtable")
368    {
369      @Override
370      public LispObject execute(LispObject arg) throws ConditionThrowable
371      {
372        char c = LispCharacter.getValue(arg);
373        Readtable rt = currentReadtable();
374        return rt.getMacroCharacter(c);
375      }
376
377      @Override
378      public LispObject execute(LispObject first, LispObject second)
379        throws ConditionThrowable
380      {
381        char c = LispCharacter.getValue(first);
382        Readtable rt = designator_readtable(second);
383        return rt.getMacroCharacter(c);
384      }
385    };
386
387  // ### set-macro-character char new-function &optional non-terminating-p readtable
388  // => t
389  private static final Primitive SET_MACRO_CHARACTER =
390    new Primitive("set-macro-character",
391                  "char new-function &optional non-terminating-p readtable")
392    {
393      @Override
394      public LispObject execute(LispObject first, LispObject second)
395        throws ConditionThrowable
396      {
397        return execute(first, second, NIL, currentReadtable());
398      }
399
400      @Override
401      public LispObject execute(LispObject first, LispObject second,
402                                LispObject third)
403        throws ConditionThrowable
404      {
405        return execute(first, second, third, currentReadtable());
406      }
407
408      @Override
409      public LispObject execute(LispObject first, LispObject second,
410                                LispObject third, LispObject fourth)
411        throws ConditionThrowable
412      {
413        char c = LispCharacter.getValue(first);
414        final LispObject designator;
415        if (second instanceof Function
416            || second instanceof StandardGenericFunction)
417          designator = second;
418        else if (second instanceof Symbol)
419          designator = second;
420        else
421          return error(new LispError(second.writeToString() +
422                                      " does not designate a function."));
423        byte syntaxType;
424        if (third != NIL)
425          syntaxType = SYNTAX_TYPE_NON_TERMINATING_MACRO;
426        else
427          syntaxType = SYNTAX_TYPE_TERMINATING_MACRO;
428        Readtable rt = designator_readtable(fourth);
429        // REVIEW synchronization
430        rt.syntax[c] = syntaxType;
431        rt.readerMacroFunctions[c] = designator;
432        return T;
433      }
434    };
435
436  // ### make-dispatch-macro-character char &optional non-terminating-p readtable
437  // => t
438  private static final Primitive MAKE_DISPATCH_MACRO_CHARACTER =
439    new Primitive("make-dispatch-macro-character",
440                  "char &optional non-terminating-p readtable")
441    {
442      @Override
443      public LispObject execute(LispObject[] args) throws ConditionThrowable
444      {
445        if (args.length < 1 || args.length > 3)
446          return error(new WrongNumberOfArgumentsException(this));
447        char dispChar = LispCharacter.getValue(args[0]);
448        LispObject non_terminating_p;
449        if (args.length > 1)
450          non_terminating_p = args[1];
451        else
452          non_terminating_p = NIL;
453        Readtable readtable;
454        if (args.length == 3)
455          readtable = checkReadtable(args[2]);
456        else
457          readtable = currentReadtable();
458        readtable.makeDispatchMacroCharacter(dispChar, non_terminating_p);
459        return T;
460      }
461    };
462
463  // ### get-dispatch-macro-character disp-char sub-char &optional readtable
464  // => function
465  private static final Primitive GET_DISPATCH_MACRO_CHARACTER =
466    new Primitive("get-dispatch-macro-character",
467                  "disp-char sub-char &optional readtable")
468    {
469      @Override
470      public LispObject execute(LispObject[] args) throws ConditionThrowable
471      {
472        if (args.length < 2 || args.length > 3)
473          return error(new WrongNumberOfArgumentsException(this));
474        char dispChar = LispCharacter.getValue(args[0]);
475        char subChar = LispCharacter.getValue(args[1]);
476        Readtable readtable;
477        if (args.length == 3)
478          readtable = designator_readtable(args[2]);
479        else
480          readtable = currentReadtable();
481        return readtable.getDispatchMacroCharacter(dispChar, subChar);
482      }
483    };
484
485  // ### set-dispatch-macro-character disp-char sub-char new-function &optional readtable
486  // => t
487  private static final Primitive SET_DISPATCH_MACRO_CHARACTER =
488    new Primitive("set-dispatch-macro-character",
489                  "disp-char sub-char new-function &optional readtable")
490    {
491      @Override
492      public LispObject execute(LispObject[] args) throws ConditionThrowable
493      {
494        if (args.length < 3 || args.length > 4)
495          return error(new WrongNumberOfArgumentsException(this));
496        char dispChar = LispCharacter.getValue(args[0]);
497        char subChar = LispCharacter.getValue(args[1]);
498        LispObject function = coerceToFunction(args[2]);
499        Readtable readtable;
500        if (args.length == 4)
501          readtable = designator_readtable(args[3]);
502        else
503          readtable = currentReadtable();
504        readtable.setDispatchMacroCharacter(dispChar, subChar, function);
505        return T;
506      }
507    };
508
509  // ### set-syntax-from-char to-char from-char &optional to-readtable from-readtable
510  // => t
511  private static final Primitive SET_SYNTAX_FROM_CHAR =
512    new Primitive("set-syntax-from-char",
513                  "to-char from-char &optional to-readtable from-readtable")
514    {
515      @Override
516      public LispObject execute(LispObject[] args) throws ConditionThrowable
517      {
518        if (args.length < 2 || args.length > 4)
519          return error(new WrongNumberOfArgumentsException(this));
520        char toChar = LispCharacter.getValue(args[0]);
521        char fromChar = LispCharacter.getValue(args[1]);
522        Readtable toReadtable;
523        if (args.length > 2)
524          toReadtable = checkReadtable(args[2]);
525        else
526          toReadtable = currentReadtable();
527        Readtable fromReadtable;
528        if (args.length > 3)
529          fromReadtable = designator_readtable(args[3]);
530        else
531          fromReadtable = checkReadtable(STANDARD_READTABLE.symbolValue());
532        // REVIEW synchronization
533        toReadtable.syntax[toChar] = fromReadtable.syntax[fromChar];
534        toReadtable.readerMacroFunctions[toChar] =
535          fromReadtable.readerMacroFunctions[fromChar];
536        // "If the character is a dispatching macro character, its entire
537        // dispatch table of reader macro functions is copied."
538        if (fromReadtable.dispatchTables[fromChar] != null)
539          {
540            toReadtable.dispatchTables[toChar] =
541              new DispatchTable(fromReadtable.dispatchTables[fromChar]);
542          }
543        return T;
544      }
545    };
546
547  // ### readtable-case readtable => mode
548  private static final Primitive READTABLE_CASE =
549    new Primitive("readtable-case", "readtable")
550    {
551      @Override
552      public LispObject execute(LispObject arg) throws ConditionThrowable
553      {
554          return checkReadtable(arg).readtableCase;
555      }
556    };
557
558  // ### %set-readtable-case readtable new-mode => new-mode
559  private static final Primitive _SET_READTABLE_CASE =
560    new Primitive("%set-readtable-case", PACKAGE_SYS, false,
561                  "readtable new-mode")
562    {
563      @Override
564      public LispObject execute(LispObject first, LispObject second)
565        throws ConditionThrowable
566      {
567            final Readtable readtable = checkReadtable(first);
568            if (second == Keyword.UPCASE || second == Keyword.DOWNCASE ||
569                second == Keyword.INVERT || second == Keyword.PRESERVE)
570              {
571                readtable.readtableCase = second;
572                return second;
573              }
574            return type_error(second, list(Symbol.MEMBER,
575                                                 Keyword.INVERT,
576                                                 Keyword.PRESERVE,
577                                                 Keyword.DOWNCASE,
578                                                 Keyword.UPCASE));
579      }
580    };
581}
Note: See TracBrowser for help on using the repository browser.