rlm@10
|
1 /**
|
rlm@10
|
2 * Copyright (c) Rich Hickey. All rights reserved.
|
rlm@10
|
3 * The use and distribution terms for this software are covered by the
|
rlm@10
|
4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
5 * which can be found in the file epl-v10.html at the root of this distribution.
|
rlm@10
|
6 * By using this software in any fashion, you are agreeing to be bound by
|
rlm@10
|
7 * the terms of this license.
|
rlm@10
|
8 * You must not remove this notice, or any other, from this software.
|
rlm@10
|
9 **/
|
rlm@10
|
10
|
rlm@10
|
11 package clojure.lang;
|
rlm@10
|
12
|
rlm@10
|
13 import java.io.*;
|
rlm@10
|
14 import java.util.regex.Pattern;
|
rlm@10
|
15 import java.util.regex.Matcher;
|
rlm@10
|
16 import java.util.ArrayList;
|
rlm@10
|
17 import java.util.List;
|
rlm@10
|
18 import java.util.Map;
|
rlm@10
|
19 import java.math.BigInteger;
|
rlm@10
|
20 import java.math.BigDecimal;
|
rlm@10
|
21 import java.lang.*;
|
rlm@10
|
22
|
rlm@10
|
23 public class LispReader{
|
rlm@10
|
24
|
rlm@10
|
25 static final Symbol QUOTE = Symbol.create("quote");
|
rlm@10
|
26 static final Symbol THE_VAR = Symbol.create("var");
|
rlm@10
|
27 //static Symbol SYNTAX_QUOTE = Symbol.create(null, "syntax-quote");
|
rlm@10
|
28 static Symbol UNQUOTE = Symbol.create("clojure.core", "unquote");
|
rlm@10
|
29 static Symbol UNQUOTE_SPLICING = Symbol.create("clojure.core", "unquote-splicing");
|
rlm@10
|
30 static Symbol CONCAT = Symbol.create("clojure.core", "concat");
|
rlm@10
|
31 static Symbol SEQ = Symbol.create("clojure.core", "seq");
|
rlm@10
|
32 static Symbol LIST = Symbol.create("clojure.core", "list");
|
rlm@10
|
33 static Symbol APPLY = Symbol.create("clojure.core", "apply");
|
rlm@10
|
34 static Symbol HASHMAP = Symbol.create("clojure.core", "hash-map");
|
rlm@10
|
35 static Symbol HASHSET = Symbol.create("clojure.core", "hash-set");
|
rlm@10
|
36 static Symbol VECTOR = Symbol.create("clojure.core", "vector");
|
rlm@10
|
37 static Symbol WITH_META = Symbol.create("clojure.core", "with-meta");
|
rlm@10
|
38 static Symbol META = Symbol.create("clojure.core", "meta");
|
rlm@10
|
39 static Symbol DEREF = Symbol.create("clojure.core", "deref");
|
rlm@10
|
40 //static Symbol DEREF_BANG = Symbol.create("clojure.core", "deref!");
|
rlm@10
|
41
|
rlm@10
|
42 static IFn[] macros = new IFn[256];
|
rlm@10
|
43 static IFn[] dispatchMacros = new IFn[256];
|
rlm@10
|
44 //static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^:/]][^:/]*/)?[\\D&&[^:/]][^:/]*");
|
rlm@10
|
45 static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?([\\D&&[^/]][^/]*)");
|
rlm@10
|
46 //static Pattern varPat = Pattern.compile("([\\D&&[^:\\.]][^:\\.]*):([\\D&&[^:\\.]][^:\\.]*)");
|
rlm@10
|
47 //static Pattern intPat = Pattern.compile("[-+]?[0-9]+\\.?");
|
rlm@10
|
48 static Pattern intPat =
|
rlm@10
|
49 Pattern.compile(
|
rlm@10
|
50 "([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)");
|
rlm@10
|
51 static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
|
rlm@10
|
52 static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
|
rlm@10
|
53 static final Symbol SLASH = Symbol.create("/");
|
rlm@10
|
54 static final Symbol CLOJURE_SLASH = Symbol.create("clojure.core","/");
|
rlm@10
|
55 //static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*");
|
rlm@10
|
56 //static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
|
rlm@10
|
57 //static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
|
rlm@10
|
58 //static Pattern classNamePat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.");
|
rlm@10
|
59
|
rlm@10
|
60 //symbol->gensymbol
|
rlm@10
|
61 static Var GENSYM_ENV = Var.create(null);
|
rlm@10
|
62 //sorted-map num->gensymbol
|
rlm@10
|
63 static Var ARG_ENV = Var.create(null);
|
rlm@10
|
64
|
rlm@10
|
65 static
|
rlm@10
|
66 {
|
rlm@10
|
67 macros['"'] = new StringReader();
|
rlm@10
|
68 macros[';'] = new CommentReader();
|
rlm@10
|
69 macros['\''] = new WrappingReader(QUOTE);
|
rlm@10
|
70 macros['@'] = new WrappingReader(DEREF);//new DerefReader();
|
rlm@10
|
71 macros['^'] = new MetaReader();
|
rlm@10
|
72 macros['`'] = new SyntaxQuoteReader();
|
rlm@10
|
73 macros['~'] = new UnquoteReader();
|
rlm@10
|
74 macros['('] = new ListReader();
|
rlm@10
|
75 macros[')'] = new UnmatchedDelimiterReader();
|
rlm@10
|
76 macros['['] = new VectorReader();
|
rlm@10
|
77 macros[']'] = new UnmatchedDelimiterReader();
|
rlm@10
|
78 macros['{'] = new MapReader();
|
rlm@10
|
79 macros['}'] = new UnmatchedDelimiterReader();
|
rlm@10
|
80 // macros['|'] = new ArgVectorReader();
|
rlm@10
|
81 macros['\\'] = new CharacterReader();
|
rlm@10
|
82 macros['%'] = new ArgReader();
|
rlm@10
|
83 macros['#'] = new DispatchReader();
|
rlm@10
|
84
|
rlm@10
|
85
|
rlm@10
|
86 dispatchMacros['^'] = new MetaReader();
|
rlm@10
|
87 dispatchMacros['\''] = new VarReader();
|
rlm@10
|
88 dispatchMacros['"'] = new RegexReader();
|
rlm@10
|
89 dispatchMacros['('] = new FnReader();
|
rlm@10
|
90 dispatchMacros['{'] = new SetReader();
|
rlm@10
|
91 dispatchMacros['='] = new EvalReader();
|
rlm@10
|
92 dispatchMacros['!'] = new CommentReader();
|
rlm@10
|
93 dispatchMacros['<'] = new UnreadableReader();
|
rlm@10
|
94 dispatchMacros['_'] = new DiscardReader();
|
rlm@10
|
95 }
|
rlm@10
|
96
|
rlm@10
|
97 static boolean isWhitespace(int ch){
|
rlm@10
|
98 return Character.isWhitespace(ch) || ch == ',';
|
rlm@10
|
99 }
|
rlm@10
|
100
|
rlm@10
|
101 static void unread(PushbackReader r, int ch) throws IOException{
|
rlm@10
|
102 if(ch != -1)
|
rlm@10
|
103 r.unread(ch);
|
rlm@10
|
104 }
|
rlm@10
|
105
|
rlm@10
|
106 public static class ReaderException extends Exception{
|
rlm@10
|
107 final int line;
|
rlm@10
|
108
|
rlm@10
|
109 public ReaderException(int line, Throwable cause){
|
rlm@10
|
110 super(cause);
|
rlm@10
|
111 this.line = line;
|
rlm@10
|
112 }
|
rlm@10
|
113 }
|
rlm@10
|
114
|
rlm@10
|
115 static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive)
|
rlm@10
|
116 throws Exception{
|
rlm@10
|
117
|
rlm@10
|
118 try
|
rlm@10
|
119 {
|
rlm@10
|
120 for(; ;)
|
rlm@10
|
121 {
|
rlm@10
|
122 int ch = r.read();
|
rlm@10
|
123
|
rlm@10
|
124 while(isWhitespace(ch))
|
rlm@10
|
125 ch = r.read();
|
rlm@10
|
126
|
rlm@10
|
127 if(ch == -1)
|
rlm@10
|
128 {
|
rlm@10
|
129 if(eofIsError)
|
rlm@10
|
130 throw new Exception("EOF while reading");
|
rlm@10
|
131 return eofValue;
|
rlm@10
|
132 }
|
rlm@10
|
133
|
rlm@10
|
134 if(Character.isDigit(ch))
|
rlm@10
|
135 {
|
rlm@10
|
136 Object n = readNumber(r, (char) ch);
|
rlm@10
|
137 if(RT.suppressRead())
|
rlm@10
|
138 return null;
|
rlm@10
|
139 return n;
|
rlm@10
|
140 }
|
rlm@10
|
141
|
rlm@10
|
142 IFn macroFn = getMacro(ch);
|
rlm@10
|
143 if(macroFn != null)
|
rlm@10
|
144 {
|
rlm@10
|
145 Object ret = macroFn.invoke(r, (char) ch);
|
rlm@10
|
146 if(RT.suppressRead())
|
rlm@10
|
147 return null;
|
rlm@10
|
148 //no op macros return the reader
|
rlm@10
|
149 if(ret == r)
|
rlm@10
|
150 continue;
|
rlm@10
|
151 return ret;
|
rlm@10
|
152 }
|
rlm@10
|
153
|
rlm@10
|
154 if(ch == '+' || ch == '-')
|
rlm@10
|
155 {
|
rlm@10
|
156 int ch2 = r.read();
|
rlm@10
|
157 if(Character.isDigit(ch2))
|
rlm@10
|
158 {
|
rlm@10
|
159 unread(r, ch2);
|
rlm@10
|
160 Object n = readNumber(r, (char) ch);
|
rlm@10
|
161 if(RT.suppressRead())
|
rlm@10
|
162 return null;
|
rlm@10
|
163 return n;
|
rlm@10
|
164 }
|
rlm@10
|
165 unread(r, ch2);
|
rlm@10
|
166 }
|
rlm@10
|
167
|
rlm@10
|
168 String token = readToken(r, (char) ch);
|
rlm@10
|
169 if(RT.suppressRead())
|
rlm@10
|
170 return null;
|
rlm@10
|
171 return interpretToken(token);
|
rlm@10
|
172 }
|
rlm@10
|
173 }
|
rlm@10
|
174 catch(Exception e)
|
rlm@10
|
175 {
|
rlm@10
|
176 if(isRecursive || !(r instanceof LineNumberingPushbackReader))
|
rlm@10
|
177 throw e;
|
rlm@10
|
178 LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
|
rlm@10
|
179 //throw new Exception(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
|
rlm@10
|
180 throw new ReaderException(rdr.getLineNumber(), e);
|
rlm@10
|
181 }
|
rlm@10
|
182 }
|
rlm@10
|
183
|
rlm@10
|
184 static private String readToken(PushbackReader r, char initch) throws Exception{
|
rlm@10
|
185 StringBuilder sb = new StringBuilder();
|
rlm@10
|
186 sb.append(initch);
|
rlm@10
|
187
|
rlm@10
|
188 for(; ;)
|
rlm@10
|
189 {
|
rlm@10
|
190 int ch = r.read();
|
rlm@10
|
191 if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
|
rlm@10
|
192 {
|
rlm@10
|
193 unread(r, ch);
|
rlm@10
|
194 return sb.toString();
|
rlm@10
|
195 }
|
rlm@10
|
196 sb.append((char) ch);
|
rlm@10
|
197 }
|
rlm@10
|
198 }
|
rlm@10
|
199
|
rlm@10
|
200 static private Object readNumber(PushbackReader r, char initch) throws Exception{
|
rlm@10
|
201 StringBuilder sb = new StringBuilder();
|
rlm@10
|
202 sb.append(initch);
|
rlm@10
|
203
|
rlm@10
|
204 for(; ;)
|
rlm@10
|
205 {
|
rlm@10
|
206 int ch = r.read();
|
rlm@10
|
207 if(ch == -1 || isWhitespace(ch) || isMacro(ch))
|
rlm@10
|
208 {
|
rlm@10
|
209 unread(r, ch);
|
rlm@10
|
210 break;
|
rlm@10
|
211 }
|
rlm@10
|
212 sb.append((char) ch);
|
rlm@10
|
213 }
|
rlm@10
|
214
|
rlm@10
|
215 String s = sb.toString();
|
rlm@10
|
216 Object n = matchNumber(s);
|
rlm@10
|
217 if(n == null)
|
rlm@10
|
218 throw new NumberFormatException("Invalid number: " + s);
|
rlm@10
|
219 return n;
|
rlm@10
|
220 }
|
rlm@10
|
221
|
rlm@10
|
222 static private int readUnicodeChar(String token, int offset, int length, int base) throws Exception{
|
rlm@10
|
223 if(token.length() != offset + length)
|
rlm@10
|
224 throw new IllegalArgumentException("Invalid unicode character: \\" + token);
|
rlm@10
|
225 int uc = 0;
|
rlm@10
|
226 for(int i = offset; i < offset + length; ++i)
|
rlm@10
|
227 {
|
rlm@10
|
228 int d = Character.digit(token.charAt(i), base);
|
rlm@10
|
229 if(d == -1)
|
rlm@10
|
230 throw new IllegalArgumentException("Invalid digit: " + (char) d);
|
rlm@10
|
231 uc = uc * base + d;
|
rlm@10
|
232 }
|
rlm@10
|
233 return (char) uc;
|
rlm@10
|
234 }
|
rlm@10
|
235
|
rlm@10
|
236 static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) throws Exception{
|
rlm@10
|
237 int uc = Character.digit(initch, base);
|
rlm@10
|
238 if(uc == -1)
|
rlm@10
|
239 throw new IllegalArgumentException("Invalid digit: " + initch);
|
rlm@10
|
240 int i = 1;
|
rlm@10
|
241 for(; i < length; ++i)
|
rlm@10
|
242 {
|
rlm@10
|
243 int ch = r.read();
|
rlm@10
|
244 if(ch == -1 || isWhitespace(ch) || isMacro(ch))
|
rlm@10
|
245 {
|
rlm@10
|
246 unread(r, ch);
|
rlm@10
|
247 break;
|
rlm@10
|
248 }
|
rlm@10
|
249 int d = Character.digit(ch, base);
|
rlm@10
|
250 if(d == -1)
|
rlm@10
|
251 throw new IllegalArgumentException("Invalid digit: " + (char) ch);
|
rlm@10
|
252 uc = uc * base + d;
|
rlm@10
|
253 }
|
rlm@10
|
254 if(i != length && exact)
|
rlm@10
|
255 throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length);
|
rlm@10
|
256 return uc;
|
rlm@10
|
257 }
|
rlm@10
|
258
|
rlm@10
|
259 static private Object interpretToken(String s) throws Exception{
|
rlm@10
|
260 if(s.equals("nil"))
|
rlm@10
|
261 {
|
rlm@10
|
262 return null;
|
rlm@10
|
263 }
|
rlm@10
|
264 else if(s.equals("true"))
|
rlm@10
|
265 {
|
rlm@10
|
266 return RT.T;
|
rlm@10
|
267 }
|
rlm@10
|
268 else if(s.equals("false"))
|
rlm@10
|
269 {
|
rlm@10
|
270 return RT.F;
|
rlm@10
|
271 }
|
rlm@10
|
272 else if(s.equals("/"))
|
rlm@10
|
273 {
|
rlm@10
|
274 return SLASH;
|
rlm@10
|
275 }
|
rlm@10
|
276 else if(s.equals("clojure.core//"))
|
rlm@10
|
277 {
|
rlm@10
|
278 return CLOJURE_SLASH;
|
rlm@10
|
279 }
|
rlm@10
|
280 Object ret = null;
|
rlm@10
|
281
|
rlm@10
|
282 ret = matchSymbol(s);
|
rlm@10
|
283 if(ret != null)
|
rlm@10
|
284 return ret;
|
rlm@10
|
285
|
rlm@10
|
286 throw new Exception("Invalid token: " + s);
|
rlm@10
|
287 }
|
rlm@10
|
288
|
rlm@10
|
289
|
rlm@10
|
290 private static Object matchSymbol(String s){
|
rlm@10
|
291 Matcher m = symbolPat.matcher(s);
|
rlm@10
|
292 if(m.matches())
|
rlm@10
|
293 {
|
rlm@10
|
294 int gc = m.groupCount();
|
rlm@10
|
295 String ns = m.group(1);
|
rlm@10
|
296 String name = m.group(2);
|
rlm@10
|
297 if(ns != null && ns.endsWith(":/")
|
rlm@10
|
298 || name.endsWith(":")
|
rlm@10
|
299 || s.indexOf("::", 1) != -1)
|
rlm@10
|
300 return null;
|
rlm@10
|
301 if(s.startsWith("::"))
|
rlm@10
|
302 {
|
rlm@10
|
303 Symbol ks = Symbol.intern(s.substring(2));
|
rlm@10
|
304 Namespace kns;
|
rlm@10
|
305 if(ks.ns != null)
|
rlm@10
|
306 kns = Compiler.namespaceFor(ks);
|
rlm@10
|
307 else
|
rlm@10
|
308 kns = Compiler.currentNS();
|
rlm@10
|
309 //auto-resolving keyword
|
rlm@10
|
310 if (kns != null)
|
rlm@10
|
311 return Keyword.intern(kns.name.name,ks.name);
|
rlm@10
|
312 else
|
rlm@10
|
313 return null;
|
rlm@10
|
314 }
|
rlm@10
|
315 boolean isKeyword = s.charAt(0) == ':';
|
rlm@10
|
316 Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
|
rlm@10
|
317 if(isKeyword)
|
rlm@10
|
318 return Keyword.intern(sym);
|
rlm@10
|
319 return sym;
|
rlm@10
|
320 }
|
rlm@10
|
321 return null;
|
rlm@10
|
322 }
|
rlm@10
|
323
|
rlm@10
|
324
|
rlm@10
|
325 private static Object matchNumber(String s){
|
rlm@10
|
326 Matcher m = intPat.matcher(s);
|
rlm@10
|
327 if(m.matches())
|
rlm@10
|
328 {
|
rlm@10
|
329 if(m.group(2) != null)
|
rlm@10
|
330 return 0;
|
rlm@10
|
331 boolean negate = (m.group(1).equals("-"));
|
rlm@10
|
332 String n;
|
rlm@10
|
333 int radix = 10;
|
rlm@10
|
334 if((n = m.group(3)) != null)
|
rlm@10
|
335 radix = 10;
|
rlm@10
|
336 else if((n = m.group(4)) != null)
|
rlm@10
|
337 radix = 16;
|
rlm@10
|
338 else if((n = m.group(5)) != null)
|
rlm@10
|
339 radix = 8;
|
rlm@10
|
340 else if((n = m.group(7)) != null)
|
rlm@10
|
341 radix = Integer.parseInt(m.group(6));
|
rlm@10
|
342 if(n == null)
|
rlm@10
|
343 return null;
|
rlm@10
|
344 BigInteger bn = new BigInteger(n, radix);
|
rlm@10
|
345 return Numbers.reduce(negate ? bn.negate() : bn);
|
rlm@10
|
346 }
|
rlm@10
|
347 m = floatPat.matcher(s);
|
rlm@10
|
348 if(m.matches())
|
rlm@10
|
349 {
|
rlm@10
|
350 if(m.group(4) != null)
|
rlm@10
|
351 return new BigDecimal(m.group(1));
|
rlm@10
|
352 return Double.parseDouble(s);
|
rlm@10
|
353 }
|
rlm@10
|
354 m = ratioPat.matcher(s);
|
rlm@10
|
355 if(m.matches())
|
rlm@10
|
356 {
|
rlm@10
|
357 return Numbers.divide(new BigInteger(m.group(1)), new BigInteger(m.group(2)));
|
rlm@10
|
358 }
|
rlm@10
|
359 return null;
|
rlm@10
|
360 }
|
rlm@10
|
361
|
rlm@10
|
362 static private IFn getMacro(int ch){
|
rlm@10
|
363 if(ch < macros.length)
|
rlm@10
|
364 return macros[ch];
|
rlm@10
|
365 return null;
|
rlm@10
|
366 }
|
rlm@10
|
367
|
rlm@10
|
368 static private boolean isMacro(int ch){
|
rlm@10
|
369 return (ch < macros.length && macros[ch] != null);
|
rlm@10
|
370 }
|
rlm@10
|
371
|
rlm@10
|
372 static private boolean isTerminatingMacro(int ch){
|
rlm@10
|
373 return (ch != '#' && ch < macros.length && macros[ch] != null);
|
rlm@10
|
374 }
|
rlm@10
|
375
|
rlm@10
|
376 public static class RegexReader extends AFn{
|
rlm@10
|
377 static StringReader stringrdr = new StringReader();
|
rlm@10
|
378
|
rlm@10
|
379 public Object invoke(Object reader, Object doublequote) throws Exception{
|
rlm@10
|
380 StringBuilder sb = new StringBuilder();
|
rlm@10
|
381 Reader r = (Reader) reader;
|
rlm@10
|
382 for(int ch = r.read(); ch != '"'; ch = r.read())
|
rlm@10
|
383 {
|
rlm@10
|
384 if(ch == -1)
|
rlm@10
|
385 throw new Exception("EOF while reading regex");
|
rlm@10
|
386 sb.append( (char) ch );
|
rlm@10
|
387 if(ch == '\\') //escape
|
rlm@10
|
388 {
|
rlm@10
|
389 ch = r.read();
|
rlm@10
|
390 if(ch == -1)
|
rlm@10
|
391 throw new Exception("EOF while reading regex");
|
rlm@10
|
392 sb.append( (char) ch ) ;
|
rlm@10
|
393 }
|
rlm@10
|
394 }
|
rlm@10
|
395 return Pattern.compile(sb.toString());
|
rlm@10
|
396 }
|
rlm@10
|
397 }
|
rlm@10
|
398
|
rlm@10
|
399 public static class StringReader extends AFn{
|
rlm@10
|
400 public Object invoke(Object reader, Object doublequote) throws Exception{
|
rlm@10
|
401 StringBuilder sb = new StringBuilder();
|
rlm@10
|
402 Reader r = (Reader) reader;
|
rlm@10
|
403
|
rlm@10
|
404 for(int ch = r.read(); ch != '"'; ch = r.read())
|
rlm@10
|
405 {
|
rlm@10
|
406 if(ch == -1)
|
rlm@10
|
407 throw new Exception("EOF while reading string");
|
rlm@10
|
408 if(ch == '\\') //escape
|
rlm@10
|
409 {
|
rlm@10
|
410 ch = r.read();
|
rlm@10
|
411 if(ch == -1)
|
rlm@10
|
412 throw new Exception("EOF while reading string");
|
rlm@10
|
413 switch(ch)
|
rlm@10
|
414 {
|
rlm@10
|
415 case 't':
|
rlm@10
|
416 ch = '\t';
|
rlm@10
|
417 break;
|
rlm@10
|
418 case 'r':
|
rlm@10
|
419 ch = '\r';
|
rlm@10
|
420 break;
|
rlm@10
|
421 case 'n':
|
rlm@10
|
422 ch = '\n';
|
rlm@10
|
423 break;
|
rlm@10
|
424 case '\\':
|
rlm@10
|
425 break;
|
rlm@10
|
426 case '"':
|
rlm@10
|
427 break;
|
rlm@10
|
428 case 'b':
|
rlm@10
|
429 ch = '\b';
|
rlm@10
|
430 break;
|
rlm@10
|
431 case 'f':
|
rlm@10
|
432 ch = '\f';
|
rlm@10
|
433 break;
|
rlm@10
|
434 case 'u':
|
rlm@10
|
435 {
|
rlm@10
|
436 ch = r.read();
|
rlm@10
|
437 if (Character.digit(ch, 16) == -1)
|
rlm@10
|
438 throw new Exception("Invalid unicode escape: \\u" + (char) ch);
|
rlm@10
|
439 ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
|
rlm@10
|
440 break;
|
rlm@10
|
441 }
|
rlm@10
|
442 default:
|
rlm@10
|
443 {
|
rlm@10
|
444 if(Character.isDigit(ch))
|
rlm@10
|
445 {
|
rlm@10
|
446 ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false);
|
rlm@10
|
447 if(ch > 0377)
|
rlm@10
|
448 throw new Exception("Octal escape sequence must be in range [0, 377].");
|
rlm@10
|
449 }
|
rlm@10
|
450 else
|
rlm@10
|
451 throw new Exception("Unsupported escape character: \\" + (char) ch);
|
rlm@10
|
452 }
|
rlm@10
|
453 }
|
rlm@10
|
454 }
|
rlm@10
|
455 sb.append((char) ch);
|
rlm@10
|
456 }
|
rlm@10
|
457 return sb.toString();
|
rlm@10
|
458 }
|
rlm@10
|
459 }
|
rlm@10
|
460
|
rlm@10
|
461 public static class CommentReader extends AFn{
|
rlm@10
|
462 public Object invoke(Object reader, Object semicolon) throws Exception{
|
rlm@10
|
463 Reader r = (Reader) reader;
|
rlm@10
|
464 int ch;
|
rlm@10
|
465 do
|
rlm@10
|
466 {
|
rlm@10
|
467 ch = r.read();
|
rlm@10
|
468 } while(ch != -1 && ch != '\n' && ch != '\r');
|
rlm@10
|
469 return r;
|
rlm@10
|
470 }
|
rlm@10
|
471
|
rlm@10
|
472 }
|
rlm@10
|
473
|
rlm@10
|
474 public static class DiscardReader extends AFn{
|
rlm@10
|
475 public Object invoke(Object reader, Object underscore) throws Exception{
|
rlm@10
|
476 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
477 read(r, true, null, true);
|
rlm@10
|
478 return r;
|
rlm@10
|
479 }
|
rlm@10
|
480 }
|
rlm@10
|
481
|
rlm@10
|
482 public static class WrappingReader extends AFn{
|
rlm@10
|
483 final Symbol sym;
|
rlm@10
|
484
|
rlm@10
|
485 public WrappingReader(Symbol sym){
|
rlm@10
|
486 this.sym = sym;
|
rlm@10
|
487 }
|
rlm@10
|
488
|
rlm@10
|
489 public Object invoke(Object reader, Object quote) throws Exception{
|
rlm@10
|
490 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
491 Object o = read(r, true, null, true);
|
rlm@10
|
492 return RT.list(sym, o);
|
rlm@10
|
493 }
|
rlm@10
|
494
|
rlm@10
|
495 }
|
rlm@10
|
496
|
rlm@10
|
497 public static class DeprecatedWrappingReader extends AFn{
|
rlm@10
|
498 final Symbol sym;
|
rlm@10
|
499 final String macro;
|
rlm@10
|
500
|
rlm@10
|
501 public DeprecatedWrappingReader(Symbol sym, String macro){
|
rlm@10
|
502 this.sym = sym;
|
rlm@10
|
503 this.macro = macro;
|
rlm@10
|
504 }
|
rlm@10
|
505
|
rlm@10
|
506 public Object invoke(Object reader, Object quote) throws Exception{
|
rlm@10
|
507 System.out.println("WARNING: reader macro " + macro +
|
rlm@10
|
508 " is deprecated; use " + sym.getName() +
|
rlm@10
|
509 " instead");
|
rlm@10
|
510 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
511 Object o = read(r, true, null, true);
|
rlm@10
|
512 return RT.list(sym, o);
|
rlm@10
|
513 }
|
rlm@10
|
514
|
rlm@10
|
515 }
|
rlm@10
|
516
|
rlm@10
|
517 public static class VarReader extends AFn{
|
rlm@10
|
518 public Object invoke(Object reader, Object quote) throws Exception{
|
rlm@10
|
519 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
520 Object o = read(r, true, null, true);
|
rlm@10
|
521 // if(o instanceof Symbol)
|
rlm@10
|
522 // {
|
rlm@10
|
523 // Object v = Compiler.maybeResolveIn(Compiler.currentNS(), (Symbol) o);
|
rlm@10
|
524 // if(v instanceof Var)
|
rlm@10
|
525 // return v;
|
rlm@10
|
526 // }
|
rlm@10
|
527 return RT.list(THE_VAR, o);
|
rlm@10
|
528 }
|
rlm@10
|
529 }
|
rlm@10
|
530
|
rlm@10
|
531 /*
|
rlm@10
|
532 static class DerefReader extends AFn{
|
rlm@10
|
533
|
rlm@10
|
534 public Object invoke(Object reader, Object quote) throws Exception{
|
rlm@10
|
535 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
536 int ch = r.read();
|
rlm@10
|
537 if(ch == -1)
|
rlm@10
|
538 throw new Exception("EOF while reading character");
|
rlm@10
|
539 if(ch == '!')
|
rlm@10
|
540 {
|
rlm@10
|
541 Object o = read(r, true, null, true);
|
rlm@10
|
542 return RT.list(DEREF_BANG, o);
|
rlm@10
|
543 }
|
rlm@10
|
544 else
|
rlm@10
|
545 {
|
rlm@10
|
546 r.unread(ch);
|
rlm@10
|
547 Object o = read(r, true, null, true);
|
rlm@10
|
548 return RT.list(DEREF, o);
|
rlm@10
|
549 }
|
rlm@10
|
550 }
|
rlm@10
|
551
|
rlm@10
|
552 }
|
rlm@10
|
553 */
|
rlm@10
|
554
|
rlm@10
|
555 public static class DispatchReader extends AFn{
|
rlm@10
|
556 public Object invoke(Object reader, Object hash) throws Exception{
|
rlm@10
|
557 int ch = ((Reader) reader).read();
|
rlm@10
|
558 if(ch == -1)
|
rlm@10
|
559 throw new Exception("EOF while reading character");
|
rlm@10
|
560 IFn fn = dispatchMacros[ch];
|
rlm@10
|
561 if(fn == null)
|
rlm@10
|
562 throw new Exception(String.format("No dispatch macro for: %c", (char) ch));
|
rlm@10
|
563 return fn.invoke(reader, ch);
|
rlm@10
|
564 }
|
rlm@10
|
565 }
|
rlm@10
|
566
|
rlm@10
|
567 static Symbol garg(int n){
|
rlm@10
|
568 return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID() + "#");
|
rlm@10
|
569 }
|
rlm@10
|
570
|
rlm@10
|
571 public static class FnReader extends AFn{
|
rlm@10
|
572 public Object invoke(Object reader, Object lparen) throws Exception{
|
rlm@10
|
573 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
574 if(ARG_ENV.deref() != null)
|
rlm@10
|
575 throw new IllegalStateException("Nested #()s are not allowed");
|
rlm@10
|
576 try
|
rlm@10
|
577 {
|
rlm@10
|
578 Var.pushThreadBindings(
|
rlm@10
|
579 RT.map(ARG_ENV, PersistentTreeMap.EMPTY));
|
rlm@10
|
580 r.unread('(');
|
rlm@10
|
581 Object form = read(r, true, null, true);
|
rlm@10
|
582
|
rlm@10
|
583 PersistentVector args = PersistentVector.EMPTY;
|
rlm@10
|
584 PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref();
|
rlm@10
|
585 ISeq rargs = argsyms.rseq();
|
rlm@10
|
586 if(rargs != null)
|
rlm@10
|
587 {
|
rlm@10
|
588 int higharg = (Integer) ((Map.Entry) rargs.first()).getKey();
|
rlm@10
|
589 if(higharg > 0)
|
rlm@10
|
590 {
|
rlm@10
|
591 for(int i = 1; i <= higharg; ++i)
|
rlm@10
|
592 {
|
rlm@10
|
593 Object sym = argsyms.valAt(i);
|
rlm@10
|
594 if(sym == null)
|
rlm@10
|
595 sym = garg(i);
|
rlm@10
|
596 args = args.cons(sym);
|
rlm@10
|
597 }
|
rlm@10
|
598 }
|
rlm@10
|
599 Object restsym = argsyms.valAt(-1);
|
rlm@10
|
600 if(restsym != null)
|
rlm@10
|
601 {
|
rlm@10
|
602 args = args.cons(Compiler._AMP_);
|
rlm@10
|
603 args = args.cons(restsym);
|
rlm@10
|
604 }
|
rlm@10
|
605 }
|
rlm@10
|
606 return RT.list(Compiler.FN, args, form);
|
rlm@10
|
607 }
|
rlm@10
|
608 finally
|
rlm@10
|
609 {
|
rlm@10
|
610 Var.popThreadBindings();
|
rlm@10
|
611 }
|
rlm@10
|
612 }
|
rlm@10
|
613 }
|
rlm@10
|
614
|
rlm@10
|
615 static Symbol registerArg(int n){
|
rlm@10
|
616 PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref();
|
rlm@10
|
617 if(argsyms == null)
|
rlm@10
|
618 {
|
rlm@10
|
619 throw new IllegalStateException("arg literal not in #()");
|
rlm@10
|
620 }
|
rlm@10
|
621 Symbol ret = (Symbol) argsyms.valAt(n);
|
rlm@10
|
622 if(ret == null)
|
rlm@10
|
623 {
|
rlm@10
|
624 ret = garg(n);
|
rlm@10
|
625 ARG_ENV.set(argsyms.assoc(n, ret));
|
rlm@10
|
626 }
|
rlm@10
|
627 return ret;
|
rlm@10
|
628 }
|
rlm@10
|
629
|
rlm@10
|
630 static class ArgReader extends AFn{
|
rlm@10
|
631 public Object invoke(Object reader, Object pct) throws Exception{
|
rlm@10
|
632 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
633 if(ARG_ENV.deref() == null)
|
rlm@10
|
634 {
|
rlm@10
|
635 return interpretToken(readToken(r, '%'));
|
rlm@10
|
636 }
|
rlm@10
|
637 int ch = r.read();
|
rlm@10
|
638 unread(r, ch);
|
rlm@10
|
639 //% alone is first arg
|
rlm@10
|
640 if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
|
rlm@10
|
641 {
|
rlm@10
|
642 return registerArg(1);
|
rlm@10
|
643 }
|
rlm@10
|
644 Object n = read(r, true, null, true);
|
rlm@10
|
645 if(n.equals(Compiler._AMP_))
|
rlm@10
|
646 return registerArg(-1);
|
rlm@10
|
647 if(!(n instanceof Number))
|
rlm@10
|
648 throw new IllegalStateException("arg literal must be %, %& or %integer");
|
rlm@10
|
649 return registerArg(((Number) n).intValue());
|
rlm@10
|
650 }
|
rlm@10
|
651 }
|
rlm@10
|
652
|
rlm@10
|
653 public static class MetaReader extends AFn{
|
rlm@10
|
654 public Object invoke(Object reader, Object caret) throws Exception{
|
rlm@10
|
655 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
656 int line = -1;
|
rlm@10
|
657 if(r instanceof LineNumberingPushbackReader)
|
rlm@10
|
658 line = ((LineNumberingPushbackReader) r).getLineNumber();
|
rlm@10
|
659 Object meta = read(r, true, null, true);
|
rlm@10
|
660 if(meta instanceof Symbol || meta instanceof Keyword || meta instanceof String)
|
rlm@10
|
661 meta = RT.map(RT.TAG_KEY, meta);
|
rlm@10
|
662 else if(!(meta instanceof IPersistentMap))
|
rlm@10
|
663 throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
|
rlm@10
|
664
|
rlm@10
|
665 Object o = read(r, true, null, true);
|
rlm@10
|
666 if(o instanceof IMeta)
|
rlm@10
|
667 {
|
rlm@10
|
668 if(line != -1 && o instanceof ISeq)
|
rlm@10
|
669 meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line);
|
rlm@10
|
670 if(o instanceof IReference)
|
rlm@10
|
671 {
|
rlm@10
|
672 ((IReference)o).resetMeta((IPersistentMap) meta);
|
rlm@10
|
673 return o;
|
rlm@10
|
674 }
|
rlm@10
|
675 return ((IObj) o).withMeta((IPersistentMap) meta);
|
rlm@10
|
676 }
|
rlm@10
|
677 else
|
rlm@10
|
678 throw new IllegalArgumentException("Metadata can only be applied to IMetas");
|
rlm@10
|
679 }
|
rlm@10
|
680
|
rlm@10
|
681 }
|
rlm@10
|
682
|
rlm@10
|
683 public static class SyntaxQuoteReader extends AFn{
|
rlm@10
|
684 public Object invoke(Object reader, Object backquote) throws Exception{
|
rlm@10
|
685 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
686 try
|
rlm@10
|
687 {
|
rlm@10
|
688 Var.pushThreadBindings(
|
rlm@10
|
689 RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
|
rlm@10
|
690
|
rlm@10
|
691 Object form = read(r, true, null, true);
|
rlm@10
|
692 return syntaxQuote(form);
|
rlm@10
|
693 }
|
rlm@10
|
694 finally
|
rlm@10
|
695 {
|
rlm@10
|
696 Var.popThreadBindings();
|
rlm@10
|
697 }
|
rlm@10
|
698 }
|
rlm@10
|
699
|
rlm@10
|
700 static Object syntaxQuote(Object form) throws Exception{
|
rlm@10
|
701 Object ret;
|
rlm@10
|
702 if(Compiler.isSpecial(form))
|
rlm@10
|
703 ret = RT.list(Compiler.QUOTE, form);
|
rlm@10
|
704 else if(form instanceof Symbol)
|
rlm@10
|
705 {
|
rlm@10
|
706 Symbol sym = (Symbol) form;
|
rlm@10
|
707 if(sym.ns == null && sym.name.endsWith("#"))
|
rlm@10
|
708 {
|
rlm@10
|
709 IPersistentMap gmap = (IPersistentMap) GENSYM_ENV.deref();
|
rlm@10
|
710 if(gmap == null)
|
rlm@10
|
711 throw new IllegalStateException("Gensym literal not in syntax-quote");
|
rlm@10
|
712 Symbol gs = (Symbol) gmap.valAt(sym);
|
rlm@10
|
713 if(gs == null)
|
rlm@10
|
714 GENSYM_ENV.set(gmap.assoc(sym, gs = Symbol.intern(null,
|
rlm@10
|
715 sym.name.substring(0, sym.name.length() - 1)
|
rlm@10
|
716 + "__" + RT.nextID() + "__auto__")));
|
rlm@10
|
717 sym = gs;
|
rlm@10
|
718 }
|
rlm@10
|
719 else if(sym.ns == null && sym.name.endsWith("."))
|
rlm@10
|
720 {
|
rlm@10
|
721 Symbol csym = Symbol.intern(null, sym.name.substring(0, sym.name.length() - 1));
|
rlm@10
|
722 csym = Compiler.resolveSymbol(csym);
|
rlm@10
|
723 sym = Symbol.intern(null, csym.name.concat("."));
|
rlm@10
|
724 }
|
rlm@10
|
725 else if(sym.ns == null && sym.name.startsWith("."))
|
rlm@10
|
726 {
|
rlm@10
|
727 // Simply quote method names.
|
rlm@10
|
728 }
|
rlm@10
|
729 else
|
rlm@10
|
730 {
|
rlm@10
|
731 Object maybeClass = null;
|
rlm@10
|
732 if(sym.ns != null)
|
rlm@10
|
733 maybeClass = Compiler.currentNS().getMapping(
|
rlm@10
|
734 Symbol.intern(null, sym.ns));
|
rlm@10
|
735 if(maybeClass instanceof Class)
|
rlm@10
|
736 {
|
rlm@10
|
737 // Classname/foo -> package.qualified.Classname/foo
|
rlm@10
|
738 sym = Symbol.intern(
|
rlm@10
|
739 ((Class)maybeClass).getName(), sym.name);
|
rlm@10
|
740 }
|
rlm@10
|
741 else
|
rlm@10
|
742 sym = Compiler.resolveSymbol(sym);
|
rlm@10
|
743 }
|
rlm@10
|
744 ret = RT.list(Compiler.QUOTE, sym);
|
rlm@10
|
745 }
|
rlm@10
|
746 else if(isUnquote(form))
|
rlm@10
|
747 return RT.second(form);
|
rlm@10
|
748 else if(isUnquoteSplicing(form))
|
rlm@10
|
749 throw new IllegalStateException("splice not in list");
|
rlm@10
|
750 else if(form instanceof IPersistentCollection)
|
rlm@10
|
751 {
|
rlm@10
|
752 if(form instanceof IPersistentMap)
|
rlm@10
|
753 {
|
rlm@10
|
754 IPersistentVector keyvals = flattenMap(form);
|
rlm@10
|
755 ret = RT.list(APPLY, HASHMAP, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(keyvals.seq()))));
|
rlm@10
|
756 }
|
rlm@10
|
757 else if(form instanceof IPersistentVector)
|
rlm@10
|
758 {
|
rlm@10
|
759 ret = RT.list(APPLY, VECTOR, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentVector) form).seq()))));
|
rlm@10
|
760 }
|
rlm@10
|
761 else if(form instanceof IPersistentSet)
|
rlm@10
|
762 {
|
rlm@10
|
763 ret = RT.list(APPLY, HASHSET, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentSet) form).seq()))));
|
rlm@10
|
764 }
|
rlm@10
|
765 else if(form instanceof ISeq || form instanceof IPersistentList)
|
rlm@10
|
766 {
|
rlm@10
|
767 ISeq seq = RT.seq(form);
|
rlm@10
|
768 if(seq == null)
|
rlm@10
|
769 ret = RT.cons(LIST,null);
|
rlm@10
|
770 else
|
rlm@10
|
771 ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));
|
rlm@10
|
772 }
|
rlm@10
|
773 else
|
rlm@10
|
774 throw new UnsupportedOperationException("Unknown Collection type");
|
rlm@10
|
775 }
|
rlm@10
|
776 else if(form instanceof Keyword
|
rlm@10
|
777 || form instanceof Number
|
rlm@10
|
778 || form instanceof Character
|
rlm@10
|
779 || form instanceof String)
|
rlm@10
|
780 ret = form;
|
rlm@10
|
781 else
|
rlm@10
|
782 ret = RT.list(Compiler.QUOTE, form);
|
rlm@10
|
783
|
rlm@10
|
784 if(form instanceof IObj && RT.meta(form) != null)
|
rlm@10
|
785 {
|
rlm@10
|
786 //filter line numbers
|
rlm@10
|
787 IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY);
|
rlm@10
|
788 if(newMeta.count() > 0)
|
rlm@10
|
789 return RT.list(WITH_META, ret, syntaxQuote(((IObj) form).meta()));
|
rlm@10
|
790 }
|
rlm@10
|
791 return ret;
|
rlm@10
|
792 }
|
rlm@10
|
793
|
rlm@10
|
794 private static ISeq sqExpandList(ISeq seq) throws Exception{
|
rlm@10
|
795 PersistentVector ret = PersistentVector.EMPTY;
|
rlm@10
|
796 for(; seq != null; seq = seq.next())
|
rlm@10
|
797 {
|
rlm@10
|
798 Object item = seq.first();
|
rlm@10
|
799 if(isUnquote(item))
|
rlm@10
|
800 ret = ret.cons(RT.list(LIST, RT.second(item)));
|
rlm@10
|
801 else if(isUnquoteSplicing(item))
|
rlm@10
|
802 ret = ret.cons(RT.second(item));
|
rlm@10
|
803 else
|
rlm@10
|
804 ret = ret.cons(RT.list(LIST, syntaxQuote(item)));
|
rlm@10
|
805 }
|
rlm@10
|
806 return ret.seq();
|
rlm@10
|
807 }
|
rlm@10
|
808
|
rlm@10
|
809 private static IPersistentVector flattenMap(Object form){
|
rlm@10
|
810 IPersistentVector keyvals = PersistentVector.EMPTY;
|
rlm@10
|
811 for(ISeq s = RT.seq(form); s != null; s = s.next())
|
rlm@10
|
812 {
|
rlm@10
|
813 IMapEntry e = (IMapEntry) s.first();
|
rlm@10
|
814 keyvals = (IPersistentVector) keyvals.cons(e.key());
|
rlm@10
|
815 keyvals = (IPersistentVector) keyvals.cons(e.val());
|
rlm@10
|
816 }
|
rlm@10
|
817 return keyvals;
|
rlm@10
|
818 }
|
rlm@10
|
819
|
rlm@10
|
820 }
|
rlm@10
|
821
|
rlm@10
|
822 static boolean isUnquoteSplicing(Object form){
|
rlm@10
|
823 return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE_SPLICING);
|
rlm@10
|
824 }
|
rlm@10
|
825
|
rlm@10
|
826 static boolean isUnquote(Object form){
|
rlm@10
|
827 return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE);
|
rlm@10
|
828 }
|
rlm@10
|
829
|
rlm@10
|
830 static class UnquoteReader extends AFn{
|
rlm@10
|
831 public Object invoke(Object reader, Object comma) throws Exception{
|
rlm@10
|
832 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
833 int ch = r.read();
|
rlm@10
|
834 if(ch == -1)
|
rlm@10
|
835 throw new Exception("EOF while reading character");
|
rlm@10
|
836 if(ch == '@')
|
rlm@10
|
837 {
|
rlm@10
|
838 Object o = read(r, true, null, true);
|
rlm@10
|
839 return RT.list(UNQUOTE_SPLICING, o);
|
rlm@10
|
840 }
|
rlm@10
|
841 else
|
rlm@10
|
842 {
|
rlm@10
|
843 unread(r, ch);
|
rlm@10
|
844 Object o = read(r, true, null, true);
|
rlm@10
|
845 return RT.list(UNQUOTE, o);
|
rlm@10
|
846 }
|
rlm@10
|
847 }
|
rlm@10
|
848
|
rlm@10
|
849 }
|
rlm@10
|
850
|
rlm@10
|
851 public static class CharacterReader extends AFn{
|
rlm@10
|
852 public Object invoke(Object reader, Object backslash) throws Exception{
|
rlm@10
|
853 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
854 int ch = r.read();
|
rlm@10
|
855 if(ch == -1)
|
rlm@10
|
856 throw new Exception("EOF while reading character");
|
rlm@10
|
857 String token = readToken(r, (char) ch);
|
rlm@10
|
858 if(token.length() == 1)
|
rlm@10
|
859 return Character.valueOf(token.charAt(0));
|
rlm@10
|
860 else if(token.equals("newline"))
|
rlm@10
|
861 return '\n';
|
rlm@10
|
862 else if(token.equals("space"))
|
rlm@10
|
863 return ' ';
|
rlm@10
|
864 else if(token.equals("tab"))
|
rlm@10
|
865 return '\t';
|
rlm@10
|
866 else if(token.equals("backspace"))
|
rlm@10
|
867 return '\b';
|
rlm@10
|
868 else if(token.equals("formfeed"))
|
rlm@10
|
869 return '\f';
|
rlm@10
|
870 else if(token.equals("return"))
|
rlm@10
|
871 return '\r';
|
rlm@10
|
872 else if(token.startsWith("u"))
|
rlm@10
|
873 {
|
rlm@10
|
874 char c = (char) readUnicodeChar(token, 1, 4, 16);
|
rlm@10
|
875 if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
|
rlm@10
|
876 throw new Exception("Invalid character constant: \\u" + Integer.toString(c, 16));
|
rlm@10
|
877 return c;
|
rlm@10
|
878 }
|
rlm@10
|
879 else if(token.startsWith("o"))
|
rlm@10
|
880 {
|
rlm@10
|
881 int len = token.length() - 1;
|
rlm@10
|
882 if(len > 3)
|
rlm@10
|
883 throw new Exception("Invalid octal escape sequence length: " + len);
|
rlm@10
|
884 int uc = readUnicodeChar(token, 1, len, 8);
|
rlm@10
|
885 if(uc > 0377)
|
rlm@10
|
886 throw new Exception("Octal escape sequence must be in range [0, 377].");
|
rlm@10
|
887 return (char) uc;
|
rlm@10
|
888 }
|
rlm@10
|
889 throw new Exception("Unsupported character: \\" + token);
|
rlm@10
|
890 }
|
rlm@10
|
891
|
rlm@10
|
892 }
|
rlm@10
|
893
|
rlm@10
|
894 public static class ListReader extends AFn{
|
rlm@10
|
895 public Object invoke(Object reader, Object leftparen) throws Exception{
|
rlm@10
|
896 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
897 int line = -1;
|
rlm@10
|
898 if(r instanceof LineNumberingPushbackReader)
|
rlm@10
|
899 line = ((LineNumberingPushbackReader) r).getLineNumber();
|
rlm@10
|
900 List list = readDelimitedList(')', r, true);
|
rlm@10
|
901 if(list.isEmpty())
|
rlm@10
|
902 return PersistentList.EMPTY;
|
rlm@10
|
903 IObj s = (IObj) PersistentList.create(list);
|
rlm@10
|
904 // IObj s = (IObj) RT.seq(list);
|
rlm@10
|
905 if(line != -1)
|
rlm@10
|
906 return s.withMeta(RT.map(RT.LINE_KEY, line));
|
rlm@10
|
907 else
|
rlm@10
|
908 return s;
|
rlm@10
|
909 }
|
rlm@10
|
910
|
rlm@10
|
911 }
|
rlm@10
|
912
|
rlm@10
|
913 static class CtorReader extends AFn{
|
rlm@10
|
914 static final Symbol cls = Symbol.create("class");
|
rlm@10
|
915
|
rlm@10
|
916 public Object invoke(Object reader, Object leftangle) throws Exception{
|
rlm@10
|
917 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
918 // #<class classname>
|
rlm@10
|
919 // #<classname args*>
|
rlm@10
|
920 // #<classname/staticMethod args*>
|
rlm@10
|
921 List list = readDelimitedList('>', r, true);
|
rlm@10
|
922 if(list.isEmpty())
|
rlm@10
|
923 throw new Exception("Must supply 'class', classname or classname/staticMethod");
|
rlm@10
|
924 Symbol s = (Symbol) list.get(0);
|
rlm@10
|
925 Object[] args = list.subList(1, list.size()).toArray();
|
rlm@10
|
926 if(s.equals(cls))
|
rlm@10
|
927 {
|
rlm@10
|
928 return RT.classForName(args[0].toString());
|
rlm@10
|
929 }
|
rlm@10
|
930 else if(s.ns != null) //static method
|
rlm@10
|
931 {
|
rlm@10
|
932 String classname = s.ns;
|
rlm@10
|
933 String method = s.name;
|
rlm@10
|
934 return Reflector.invokeStaticMethod(classname, method, args);
|
rlm@10
|
935 }
|
rlm@10
|
936 else
|
rlm@10
|
937 {
|
rlm@10
|
938 return Reflector.invokeConstructor(RT.classForName(s.name), args);
|
rlm@10
|
939 }
|
rlm@10
|
940 }
|
rlm@10
|
941
|
rlm@10
|
942 }
|
rlm@10
|
943
|
rlm@10
|
944 public static class EvalReader extends AFn{
|
rlm@10
|
945 public Object invoke(Object reader, Object eq) throws Exception{
|
rlm@10
|
946 if (!RT.booleanCast(RT.READEVAL.deref()))
|
rlm@10
|
947 {
|
rlm@10
|
948 throw new Exception("EvalReader not allowed when *read-eval* is false.");
|
rlm@10
|
949 }
|
rlm@10
|
950
|
rlm@10
|
951 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
952 Object o = read(r, true, null, true);
|
rlm@10
|
953 if(o instanceof Symbol)
|
rlm@10
|
954 {
|
rlm@10
|
955 return RT.classForName(o.toString());
|
rlm@10
|
956 }
|
rlm@10
|
957 else if(o instanceof IPersistentList)
|
rlm@10
|
958 {
|
rlm@10
|
959 Symbol fs = (Symbol) RT.first(o);
|
rlm@10
|
960 if(fs.equals(THE_VAR))
|
rlm@10
|
961 {
|
rlm@10
|
962 Symbol vs = (Symbol) RT.second(o);
|
rlm@10
|
963 return RT.var(vs.ns, vs.name); //Compiler.resolve((Symbol) RT.second(o),true);
|
rlm@10
|
964 }
|
rlm@10
|
965 if(fs.name.endsWith("."))
|
rlm@10
|
966 {
|
rlm@10
|
967 Object[] args = RT.toArray(RT.next(o));
|
rlm@10
|
968 return Reflector.invokeConstructor(RT.classForName(fs.name.substring(0, fs.name.length() - 1)), args);
|
rlm@10
|
969 }
|
rlm@10
|
970 if(Compiler.namesStaticMember(fs))
|
rlm@10
|
971 {
|
rlm@10
|
972 Object[] args = RT.toArray(RT.next(o));
|
rlm@10
|
973 return Reflector.invokeStaticMethod(fs.ns, fs.name, args);
|
rlm@10
|
974 }
|
rlm@10
|
975 Object v = Compiler.maybeResolveIn(Compiler.currentNS(), fs);
|
rlm@10
|
976 if(v instanceof Var)
|
rlm@10
|
977 {
|
rlm@10
|
978 return ((IFn) v).applyTo(RT.next(o));
|
rlm@10
|
979 }
|
rlm@10
|
980 throw new Exception("Can't resolve " + fs);
|
rlm@10
|
981 }
|
rlm@10
|
982 else
|
rlm@10
|
983 throw new IllegalArgumentException("Unsupported #= form");
|
rlm@10
|
984 }
|
rlm@10
|
985 }
|
rlm@10
|
986
|
rlm@10
|
987 //static class ArgVectorReader extends AFn{
|
rlm@10
|
988 // public Object invoke(Object reader, Object leftparen) throws Exception{
|
rlm@10
|
989 // PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
990 // return ArgVector.create(readDelimitedList('|', r, true));
|
rlm@10
|
991 // }
|
rlm@10
|
992 //
|
rlm@10
|
993 //}
|
rlm@10
|
994
|
rlm@10
|
995 public static class VectorReader extends AFn{
|
rlm@10
|
996 public Object invoke(Object reader, Object leftparen) throws Exception{
|
rlm@10
|
997 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
998 return LazilyPersistentVector.create(readDelimitedList(']', r, true));
|
rlm@10
|
999 }
|
rlm@10
|
1000
|
rlm@10
|
1001 }
|
rlm@10
|
1002
|
rlm@10
|
1003 public static class MapReader extends AFn{
|
rlm@10
|
1004 public Object invoke(Object reader, Object leftparen) throws Exception{
|
rlm@10
|
1005 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
1006 return RT.map(readDelimitedList('}', r, true).toArray());
|
rlm@10
|
1007 }
|
rlm@10
|
1008
|
rlm@10
|
1009 }
|
rlm@10
|
1010
|
rlm@10
|
1011 public static class SetReader extends AFn{
|
rlm@10
|
1012 public Object invoke(Object reader, Object leftbracket) throws Exception{
|
rlm@10
|
1013 PushbackReader r = (PushbackReader) reader;
|
rlm@10
|
1014 return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true));
|
rlm@10
|
1015 }
|
rlm@10
|
1016
|
rlm@10
|
1017 }
|
rlm@10
|
1018
|
rlm@10
|
1019 public static class UnmatchedDelimiterReader extends AFn{
|
rlm@10
|
1020 public Object invoke(Object reader, Object rightdelim) throws Exception{
|
rlm@10
|
1021 throw new Exception("Unmatched delimiter: " + rightdelim);
|
rlm@10
|
1022 }
|
rlm@10
|
1023
|
rlm@10
|
1024 }
|
rlm@10
|
1025
|
rlm@10
|
1026 public static class UnreadableReader extends AFn{
|
rlm@10
|
1027 public Object invoke(Object reader, Object leftangle) throws Exception{
|
rlm@10
|
1028 throw new Exception("Unreadable form");
|
rlm@10
|
1029 }
|
rlm@10
|
1030 }
|
rlm@10
|
1031
|
rlm@10
|
1032 public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive) throws Exception{
|
rlm@10
|
1033 ArrayList a = new ArrayList();
|
rlm@10
|
1034
|
rlm@10
|
1035 for(; ;)
|
rlm@10
|
1036 {
|
rlm@10
|
1037 int ch = r.read();
|
rlm@10
|
1038
|
rlm@10
|
1039 while(isWhitespace(ch))
|
rlm@10
|
1040 ch = r.read();
|
rlm@10
|
1041
|
rlm@10
|
1042 if(ch == -1)
|
rlm@10
|
1043 throw new Exception("EOF while reading");
|
rlm@10
|
1044
|
rlm@10
|
1045 if(ch == delim)
|
rlm@10
|
1046 break;
|
rlm@10
|
1047
|
rlm@10
|
1048 IFn macroFn = getMacro(ch);
|
rlm@10
|
1049 if(macroFn != null)
|
rlm@10
|
1050 {
|
rlm@10
|
1051 Object mret = macroFn.invoke(r, (char) ch);
|
rlm@10
|
1052 //no op macros return the reader
|
rlm@10
|
1053 if(mret != r)
|
rlm@10
|
1054 a.add(mret);
|
rlm@10
|
1055 }
|
rlm@10
|
1056 else
|
rlm@10
|
1057 {
|
rlm@10
|
1058 unread(r, ch);
|
rlm@10
|
1059
|
rlm@10
|
1060 Object o = read(r, true, null, isRecursive);
|
rlm@10
|
1061 if(o != r)
|
rlm@10
|
1062 a.add(o);
|
rlm@10
|
1063 }
|
rlm@10
|
1064 }
|
rlm@10
|
1065
|
rlm@10
|
1066
|
rlm@10
|
1067 return a;
|
rlm@10
|
1068 }
|
rlm@10
|
1069
|
rlm@10
|
1070 /*
|
rlm@10
|
1071 public static void main(String[] args) throws Exception{
|
rlm@10
|
1072 //RT.init();
|
rlm@10
|
1073 PushbackReader rdr = new PushbackReader( new java.io.StringReader( "(+ 21 21)" ) );
|
rlm@10
|
1074 Object input = LispReader.read(rdr, false, new Object(), false );
|
rlm@10
|
1075 System.out.println(Compiler.eval(input));
|
rlm@10
|
1076 }
|
rlm@10
|
1077
|
rlm@10
|
1078 public static void main(String[] args){
|
rlm@10
|
1079 LineNumberingPushbackReader r = new LineNumberingPushbackReader(new InputStreamReader(System.in));
|
rlm@10
|
1080 OutputStreamWriter w = new OutputStreamWriter(System.out);
|
rlm@10
|
1081 Object ret = null;
|
rlm@10
|
1082 try
|
rlm@10
|
1083 {
|
rlm@10
|
1084 for(; ;)
|
rlm@10
|
1085 {
|
rlm@10
|
1086 ret = LispReader.read(r, true, null, false);
|
rlm@10
|
1087 RT.print(ret, w);
|
rlm@10
|
1088 w.write('\n');
|
rlm@10
|
1089 if(ret != null)
|
rlm@10
|
1090 w.write(ret.getClass().toString());
|
rlm@10
|
1091 w.write('\n');
|
rlm@10
|
1092 w.flush();
|
rlm@10
|
1093 }
|
rlm@10
|
1094 }
|
rlm@10
|
1095 catch(Exception e)
|
rlm@10
|
1096 {
|
rlm@10
|
1097 e.printStackTrace();
|
rlm@10
|
1098 }
|
rlm@10
|
1099 }
|
rlm@10
|
1100 */
|
rlm@10
|
1101
|
rlm@10
|
1102 }
|
rlm@10
|
1103
|