KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > lisp > Readtable


1 /*
2  * Readtable.java
3  *
4  * Copyright (C) 2003-2004 Peter Graves
5  * $Id: Readtable.java,v 1.32 2004/08/10 03:28:06 piso Exp $
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
22 package org.armedbear.lisp;
23
24 import java.util.ArrayList JavaDoc;
25
26 public final class Readtable extends LispObject
27 {
28     public static final byte ATTR_CONSTITUENT = 0;
29     public static final byte ATTR_WHITESPACE = 1;
30     public static final byte ATTR_TERMINATING_MACRO = 2;
31     public static final byte ATTR_NON_TERMINATING_MACRO = 3;
32     public static final byte ATTR_SINGLE_ESCAPE = 4;
33     public static final byte ATTR_MULTIPLE_ESCAPE = 5;
34     public static final byte ATTR_INVALID = 6;
35
36     private final byte[] attributes = new byte[CHAR_MAX];
37     private final LispObject[] readerMacroFunctions = new LispObject[CHAR_MAX];
38     private final DispatchTable[] dispatchTables = new DispatchTable[CHAR_MAX];
39
40     private LispObject readtableCase;
41
42     public Readtable()
43     {
44         attributes[9] = ATTR_WHITESPACE; // tab
45
attributes[10] = ATTR_WHITESPACE; // linefeed
46
attributes[12] = ATTR_WHITESPACE; // form feed
47
attributes[13] = ATTR_WHITESPACE; // return
48
attributes[' '] = ATTR_WHITESPACE;
49
50         attributes['"'] = ATTR_TERMINATING_MACRO;
51         attributes['\''] = ATTR_TERMINATING_MACRO;
52         attributes['('] = ATTR_TERMINATING_MACRO;
53         attributes[')'] = ATTR_TERMINATING_MACRO;
54         attributes[','] = ATTR_TERMINATING_MACRO;
55         attributes[';'] = ATTR_TERMINATING_MACRO;
56         attributes['`'] = ATTR_TERMINATING_MACRO;
57
58         attributes['#'] = ATTR_NON_TERMINATING_MACRO;
59
60         attributes['\\'] = ATTR_SINGLE_ESCAPE;
61         attributes['|'] = ATTR_MULTIPLE_ESCAPE;
62
63         readerMacroFunctions[';'] = LispReader.READ_COMMENT;
64         readerMacroFunctions['"'] = LispReader.READ_STRING;
65         readerMacroFunctions['('] = LispReader.READ_LIST;
66         readerMacroFunctions[')'] = LispReader.READ_RIGHT_PAREN;
67         readerMacroFunctions['\''] = LispReader.READ_QUOTE;
68         readerMacroFunctions['#'] = LispReader.READ_DISPATCH_CHAR;
69         readerMacroFunctions['`'] = LispReader.BACKQUOTE_MACRO;
70         readerMacroFunctions[','] = LispReader.COMMA_MACRO;
71
72         DispatchTable dt = new DispatchTable();
73         dt.functions['('] = LispReader.SHARP_LEFT_PAREN;
74         dt.functions['*'] = LispReader.SHARP_STAR;
75         dt.functions['.'] = LispReader.SHARP_DOT;
76         dt.functions[':'] = LispReader.SHARP_COLON;
77         dt.functions['A'] = LispReader.SHARP_A;
78         dt.functions['B'] = LispReader.SHARP_B;
79         dt.functions['C'] = LispReader.SHARP_C;
80         dt.functions['O'] = LispReader.SHARP_O;
81         dt.functions['P'] = LispReader.SHARP_P;
82         dt.functions['R'] = LispReader.SHARP_R;
83         dt.functions['S'] = LispReader.SHARP_S;
84         dt.functions['X'] = LispReader.SHARP_X;
85         dt.functions['\''] = LispReader.SHARP_QUOTE;
86         dt.functions['\\'] = LispReader.SHARP_BACKSLASH;
87         dt.functions['|'] = LispReader.SHARP_VERTICAL_BAR;
88         dt.functions[')'] = LispReader.SHARP_ILLEGAL;
89         dt.functions['<'] = LispReader.SHARP_ILLEGAL;
90         dt.functions[' '] = LispReader.SHARP_ILLEGAL;
91         dt.functions[8] = LispReader.SHARP_ILLEGAL; // backspace
92
dt.functions[9] = LispReader.SHARP_ILLEGAL; // tab
93
dt.functions[10] = LispReader.SHARP_ILLEGAL; // newline, linefeed
94
dt.functions[12] = LispReader.SHARP_ILLEGAL; // page
95
dt.functions[13] = LispReader.SHARP_ILLEGAL; // return
96
dispatchTables['#'] = dt;
97
98         readtableCase = Keyword.UPCASE;
99     }
100
101     public Readtable(LispObject obj) throws ConditionThrowable
102     {
103         Readtable rt;
104         if (obj == NIL)
105             rt = checkReadtable(_STANDARD_READTABLE_.symbolValue());
106         else
107             rt = checkReadtable(obj);
108         synchronized (rt) {
109             System.arraycopy(rt.attributes, 0, attributes, 0,
110                              CHAR_MAX);
111             System.arraycopy(rt.readerMacroFunctions, 0, readerMacroFunctions, 0,
112                              CHAR_MAX);
113             // Deep copy.
114
for (int i = dispatchTables.length; i-- > 0;) {
115                 DispatchTable dt = rt.dispatchTables[i];
116                 if (dt != null)
117                     dispatchTables[i] = new DispatchTable(dt);
118             }
119             readtableCase = rt.readtableCase;
120         }
121     }
122
123     // FIXME synchronization
124
private static void copyReadtable(Readtable from, Readtable to)
125     {
126         System.arraycopy(from.attributes, 0, to.attributes, 0,
127                          CHAR_MAX);
128         System.arraycopy(from.readerMacroFunctions, 0, to.readerMacroFunctions, 0,
129                          CHAR_MAX);
130         for (int i = from.dispatchTables.length; i-- > 0;) {
131             DispatchTable dt = from.dispatchTables[i];
132             if (dt != null)
133                 to.dispatchTables[i] = new DispatchTable(dt);
134             else
135                 to.dispatchTables[i] = null;
136         }
137         to.readtableCase = from.readtableCase;
138     }
139
140     public LispObject typeOf()
141     {
142         return Symbol.READTABLE;
143     }
144
145     public LispClass classOf()
146     {
147         return BuiltInClass.READTABLE;
148     }
149
150     public LispObject typep(LispObject type) throws ConditionThrowable
151     {
152         if (type == Symbol.READTABLE)
153             return T;
154         if (type == BuiltInClass.READTABLE)
155             return T;
156         return super.typep(type);
157     }
158
159     public String JavaDoc toString()
160     {
161         return unreadableString("READTABLE");
162     }
163
164     public LispObject getReadtableCase()
165     {
166         return readtableCase;
167     }
168
169     public boolean isWhitespace(char c)
170     {
171         if (c < CHAR_MAX)
172             return attributes[c] == ATTR_WHITESPACE;
173         return false;
174     }
175
176     public byte getAttribute(char c)
177     {
178         if (c < CHAR_MAX)
179             return attributes[c];
180         return ATTR_CONSTITUENT;
181     }
182
183     public LispObject getReaderMacroFunction(char c)
184     {
185         if (c < CHAR_MAX)
186             return readerMacroFunctions[c];
187         else
188             return null;
189     }
190
191     private LispObject getMacroCharacter(char c) throws ConditionThrowable
192     {
193         LispObject function = getReaderMacroFunction(c);
194         LispObject non_terminating_p;
195         if (function != null) {
196             byte attribute = attributes[c];
197             if (attribute == ATTR_NON_TERMINATING_MACRO)
198                 non_terminating_p = T;
199             else
200                 non_terminating_p = NIL;
201         } else {
202             function = NIL;
203             non_terminating_p = NIL;
204         }
205         return LispThread.currentThread().setValues(function, non_terminating_p);
206     }
207
208     private void makeDispatchMacroCharacter(char dispChar, LispObject non_terminating_p)
209     {
210         byte attribute;
211         if (non_terminating_p != NIL)
212             attribute = ATTR_NON_TERMINATING_MACRO;
213         else
214             attribute = ATTR_TERMINATING_MACRO;
215         // FIXME synchronization
216
attributes[dispChar] = attribute;
217         readerMacroFunctions[dispChar] = LispReader.READ_DISPATCH_CHAR;
218         dispatchTables[dispChar] = new DispatchTable();
219     }
220
221     public LispObject getDispatchMacroCharacter(char dispChar, char subChar)
222         throws ConditionThrowable
223     {
224         DispatchTable dispatchTable = dispatchTables[dispChar];
225         if (dispatchTable == null) {
226             LispCharacter c = LispCharacter.getInstance(dispChar);
227             return signal(new LispError(String.valueOf(c) + " is not a dispatch character."));
228         }
229         LispObject function =
230             dispatchTable.functions[Utilities.toUpperCase(subChar)];
231         return (function != null) ? function : NIL;
232     }
233
234     public void setDispatchMacroCharacter(char dispChar, char subChar,
235                                           LispObject function)
236         throws ConditionThrowable
237     {
238         DispatchTable dispatchTable = dispatchTables[dispChar];
239         if (dispatchTable == null) {
240             LispCharacter c = LispCharacter.getInstance(dispChar);
241             signal(new LispError(String.valueOf(c) + " is not a dispatch character."));
242         }
243         dispatchTable.functions[Utilities.toUpperCase(subChar)] = function;
244     }
245
246     private static class DispatchTable
247     {
248         public LispObject[] functions = new LispObject[CHAR_MAX];
249
250         public DispatchTable()
251         {
252         }
253
254         public DispatchTable(DispatchTable dt)
255         {
256             for (int i = 0; i < functions.length; i++)
257                 functions[i] = dt.functions[i];
258         }
259     }
260
261     // ### readtablep
262
private static final Primitive1 READTABLEP =
263         new Primitive1("readtablep", "object")
264     {
265         public LispObject execute(LispObject arg)
266         {
267             return arg instanceof Readtable ? T : NIL;
268         }
269     };
270
271     // ### *standard-readtable*
272
// internal symbol
273
public static final Symbol _STANDARD_READTABLE_ =
274         internSpecial("*STANDARD-READTABLE*", PACKAGE_SYS, new Readtable());
275
276     // ### copy-readtable
277
private static final Primitive COPY_READTABLE =
278         new Primitive("copy-readtable", "&optional from-readtable to-readtable")
279     {
280         public LispObject execute() throws ConditionThrowable
281         {
282             return new Readtable(currentReadtable());
283         }
284
285         public LispObject execute(LispObject arg) throws ConditionThrowable
286         {
287             return new Readtable(arg);
288         }
289
290         public LispObject execute(LispObject first, LispObject second)
291             throws ConditionThrowable
292         {
293             Readtable from;
294             if (first == NIL)
295                 from = checkReadtable(_STANDARD_READTABLE_.symbolValue());
296             else
297                 from = checkReadtable(first);
298             if (second == NIL)
299                 return new Readtable(from);
300             Readtable to = checkReadtable(second);
301             copyReadtable(from, to);
302             return to;
303         }
304     };
305
306     // ### get-macro-character char &optional readtable
307
// => function, non-terminating-p
308
private static final Primitive GET_MACRO_CHARACTER =
309         new Primitive("get-macro-character", "char &optional readtable")
310     {
311         public LispObject execute(LispObject arg) throws ConditionThrowable
312         {
313             char c = LispCharacter.getValue(arg);
314             Readtable rt = currentReadtable();
315             return rt.getMacroCharacter(c);
316         }
317
318         public LispObject execute(LispObject first, LispObject second)
319             throws ConditionThrowable
320         {
321             char c = LispCharacter.getValue(first);
322             Readtable rt;
323             if (second == NIL)
324                 rt = new Readtable(NIL);
325             else
326                 rt = checkReadtable(second);
327             return rt.getMacroCharacter(c);
328         }
329     };
330
331     // ### set-macro-character char new-function &optional non-terminating-p readtable
332
// => t
333
private static final Primitive SET_MACRO_CHARACTER =
334         new Primitive("set-macro-character",
335                       "char new-function &optional non-terminating-p readtable")
336     {
337         public LispObject execute(LispObject first, LispObject second)
338             throws ConditionThrowable
339         {
340             char c = LispCharacter.getValue(first);
341             Readtable rt = currentReadtable();
342             // FIXME synchronization
343
rt.attributes[c] = ATTR_TERMINATING_MACRO;
344             rt.readerMacroFunctions[c] = coerceToFunction(second);
345             return T;
346         }
347
348         public LispObject execute(LispObject first, LispObject second,
349                                   LispObject third)
350             throws ConditionThrowable
351         {
352             char c = LispCharacter.getValue(first);
353             Readtable rt = currentReadtable();
354             byte attribute;
355             if (third != NIL)
356                 attribute = ATTR_NON_TERMINATING_MACRO;
357             else
358                 attribute = ATTR_TERMINATING_MACRO;
359             // FIXME synchronization
360
rt.attributes[c] = attribute;
361             rt.readerMacroFunctions[c] = coerceToFunction(second);
362             return T;
363         }
364
365         public LispObject execute(LispObject[] args) throws ConditionThrowable
366         {
367             if (args.length != 4)
368                 return signal(new WrongNumberOfArgumentsException(this));
369             char c = LispCharacter.getValue(args[0]);
370             byte attribute;
371             if (args[2] != NIL)
372                 attribute = ATTR_NON_TERMINATING_MACRO;
373             else
374                 attribute = ATTR_TERMINATING_MACRO;
375             Readtable rt = checkReadtable(args[3]);
376             // FIXME synchronization
377
rt.attributes[c] = attribute;
378             rt.readerMacroFunctions[c] = coerceToFunction(args[1]);
379             return T;
380         }
381     };
382
383     // ### make-dispatch-macro-character char &optional non-terminating-p readtable
384
// => t
385
private static final Primitive MAKE_DISPATCH_MACRO_CHARACTER =
386         new Primitive("make-dispatch-macro-character",
387                       "char &optional non-terminating-p readtable")
388     {
389         public LispObject execute(LispObject[] args) throws ConditionThrowable
390         {
391             if (args.length < 1 || args.length > 3)
392                 return signal(new WrongNumberOfArgumentsException(this));
393             char dispChar = LispCharacter.getValue(args[0]);
394             LispObject non_terminating_p;
395             if (args.length > 1)
396                 non_terminating_p = args[1];
397             else
398                 non_terminating_p = NIL;
399             Readtable readtable;
400             if (args.length > 2)
401                 readtable = checkReadtable(args[2]);
402             else
403                 readtable = currentReadtable();
404             readtable.makeDispatchMacroCharacter(dispChar, non_terminating_p);
405             return T;
406         }
407     };
408
409     // ### get-dispatch-macro-character
410
// get-dispatch-macro-character disp-char sub-char &optional readtable
411
// => function
412
private static final Primitive GET_DISPATCH_MACRO_CHARACTER =
413         new Primitive("get-dispatch-macro-character",
414                       "disp-char sub-char &optional readtable")
415     {
416         public LispObject execute(LispObject[] args) throws ConditionThrowable
417         {
418             if (args.length < 2 || args.length > 3)
419                 return signal(new WrongNumberOfArgumentsException(this));
420             char dispChar = LispCharacter.getValue(args[0]);
421             char subChar = LispCharacter.getValue(args[1]);
422             Readtable readtable;
423             if (args.length == 3)
424                 readtable = checkReadtable(args[2]);
425             else
426                 readtable = currentReadtable();
427             return readtable.getDispatchMacroCharacter(dispChar, subChar);
428         }
429     };
430
431     // ### set-dispatch-macro-character
432
// set-dispatch-macro-character disp-char sub-char new-function &optional readtable
433
// => t
434
private static final Primitive SET_DISPATCH_MACRO_CHARACTER =
435         new Primitive("set-dispatch-macro-character",
436                       "disp-char sub-char new-function &optional readtable")
437     {
438         public LispObject execute(LispObject[] args) throws ConditionThrowable
439         {
440             if (args.length < 3 || args.length > 4)
441                 return signal(new WrongNumberOfArgumentsException(this));
442             char dispChar = LispCharacter.getValue(args[0]);
443             char subChar = LispCharacter.getValue(args[1]);
444             LispObject function = coerceToFunction(args[2]);
445             Readtable readtable;
446             if (args.length == 4)
447                 readtable = checkReadtable(args[3]);
448             else
449                 readtable = currentReadtable();
450             readtable.setDispatchMacroCharacter(dispChar, subChar, function);
451             return T;
452         }
453     };
454
455     // ### set-syntax-from-char
456
// to-char from-char &optional to-readtable from-readtable => t
457
private static final Primitive SET_SYNTAX_FROM_CHAR =
458         new Primitive("set-syntax-from-char",
459                       "to-char from-char &optional to-readtable from-readtable")
460     {
461         public LispObject execute(LispObject[] args) throws ConditionThrowable
462         {
463             if (args.length < 2 || args.length > 4)
464                 return signal(new WrongNumberOfArgumentsException(this));
465             char toChar = LispCharacter.getValue(args[0]);
466             char fromChar = LispCharacter.getValue(args[1]);
467             Readtable toReadtable;
468             if (args.length > 2)
469                 toReadtable = checkReadtable(args[2]);
470             else
471                 toReadtable = currentReadtable();
472             Readtable fromReadtable;
473             if (args.length > 3)
474                 fromReadtable = checkReadtable(args[3]);
475             else
476                 fromReadtable = new Readtable(NIL);
477             // FIXME synchronization
478
toReadtable.attributes[toChar] = fromReadtable.attributes[fromChar];
479             toReadtable.readerMacroFunctions[toChar] =
480                 fromReadtable.readerMacroFunctions[fromChar];
481             return T;
482         }
483     };
484
485     // ### readtable-case readtable => mode
486
private static final Primitive1 READTABLE_CASE =
487         new Primitive1("readtable-case", "readtable")
488     {
489         public LispObject execute(LispObject arg) throws ConditionThrowable
490         {
491             try {
492                 return ((Readtable)arg).readtableCase;
493             }
494             catch (ClassCastException JavaDoc e) {
495                 return signal(new TypeError(arg, Symbol.READTABLE));
496             }
497         }
498     };
499
500     // ### %set-readtable-case readtable new-mode => new-mode
501
private static final Primitive2 _SET_READTABLE_CASE =
502         new Primitive2("%set-readtable-case", PACKAGE_SYS, false,
503                        "readtable new-mode")
504     {
505         public LispObject execute(LispObject first, LispObject second)
506             throws ConditionThrowable
507         {
508             try {
509                 Readtable readtable = (Readtable) first;
510                 if (second == Keyword.UPCASE || second == Keyword.DOWNCASE ||
511                     second == Keyword.INVERT || second == Keyword.PRESERVE)
512                 {
513                     readtable.readtableCase = second;
514                     return second;
515                 }
516                 return signal(new TypeError(second, list5(Symbol.MEMBER,
517                                                           Keyword.INVERT,
518                                                           Keyword.PRESERVE,
519                                                           Keyword.DOWNCASE,
520                                                           Keyword.UPCASE)));
521             }
522             catch (ClassCastException JavaDoc e) {
523                 return signal(new TypeError(first, Symbol.READTABLE));
524             }
525         }
526     };
527 }
528
Popular Tags