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