view 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
line wrap: on
line source
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 **/
11 package clojure.lang;
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.*;
23 public class LispReader{
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!");
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\\.]*)\\.");
60 //symbol->gensymbol
61 static Var GENSYM_ENV = Var.create(null);
62 //sorted-map num->gensymbol
63 static Var ARG_ENV = Var.create(null);
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();
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 }
97 static boolean isWhitespace(int ch){
98 return Character.isWhitespace(ch) || ch == ',';
99 }
101 static void unread(PushbackReader r, int ch) throws IOException{
102 if(ch != -1)
103 r.unread(ch);
104 }
106 public static class ReaderException extends Exception{
107 final int line;
109 public ReaderException(int line, Throwable cause){
110 super(cause);
111 this.line = line;
112 }
113 }
115 static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive)
116 throws Exception{
118 try
119 {
120 for(; ;)
121 {
122 int ch = r.read();
124 while(isWhitespace(ch))
125 ch = r.read();
127 if(ch == -1)
128 {
129 if(eofIsError)
130 throw new Exception("EOF while reading");
131 return eofValue;
132 }
134 if(Character.isDigit(ch))
135 {
136 Object n = readNumber(r, (char) ch);
137 if(RT.suppressRead())
138 return null;
139 return n;
140 }
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 }
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 }
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 }
184 static private String readToken(PushbackReader r, char initch) throws Exception{
185 StringBuilder sb = new StringBuilder();
186 sb.append(initch);
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 }
200 static private Object readNumber(PushbackReader r, char initch) throws Exception{
201 StringBuilder sb = new StringBuilder();
202 sb.append(initch);
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 }
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 }
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 }
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 }
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;
282 ret = matchSymbol(s);
283 if(ret != null)
284 return ret;
286 throw new Exception("Invalid token: " + s);
287 }
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 }
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 }
362 static private IFn getMacro(int ch){
363 if(ch < macros.length)
364 return macros[ch];
365 return null;
366 }
368 static private boolean isMacro(int ch){
369 return (ch < macros.length && macros[ch] != null);
370 }
372 static private boolean isTerminatingMacro(int ch){
373 return (ch != '#' && ch < macros.length && macros[ch] != null);
374 }
376 public static class RegexReader extends AFn{
377 static StringReader stringrdr = new StringReader();
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 }
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;
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 }
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 }
472 }
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 }
482 public static class WrappingReader extends AFn{
483 final Symbol sym;
485 public WrappingReader(Symbol sym){
486 this.sym = sym;
487 }
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 }
495 }
497 public static class DeprecatedWrappingReader extends AFn{
498 final Symbol sym;
499 final String macro;
501 public DeprecatedWrappingReader(Symbol sym, String macro){
502 this.sym = sym;
503 this.macro = macro;
504 }
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 }
515 }
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 }
531 /*
532 static class DerefReader extends AFn{
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 }
552 }
553 */
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 }
567 static Symbol garg(int n){
568 return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID() + "#");
569 }
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);
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 }
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 }
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 }
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");
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 }
681 }
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));
691 Object form = read(r, true, null, true);
692 return syntaxQuote(form);
693 }
694 finally
695 {
696 Var.popThreadBindings();
697 }
698 }
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);
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 }
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 }
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 }
820 }
822 static boolean isUnquoteSplicing(Object form){
823 return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE_SPLICING);
824 }
826 static boolean isUnquote(Object form){
827 return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE);
828 }
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 }
849 }
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 }
892 }
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 }
911 }
913 static class CtorReader extends AFn{
914 static final Symbol cls = Symbol.create("class");
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 }
942 }
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 }
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 }
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 //}
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 }
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());
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));
1019 public static class UnmatchedDelimiterReader extends AFn{
1020 public Object invoke(Object reader, Object rightdelim) throws Exception{
1021 throw new Exception("Unmatched delimiter: " + rightdelim);
1026 public static class UnreadableReader extends AFn{
1027 public Object invoke(Object reader, Object leftangle) throws Exception{
1028 throw new Exception("Unreadable form");
1032 public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive) throws Exception{
1033 ArrayList a = new ArrayList();
1035 for(; ;)
1037 int ch = r.read();
1039 while(isWhitespace(ch))
1040 ch = r.read();
1042 if(ch == -1)
1043 throw new Exception("EOF while reading");
1045 if(ch == delim)
1046 break;
1048 IFn macroFn = getMacro(ch);
1049 if(macroFn != null)
1051 Object mret = macroFn.invoke(r, (char) ch);
1052 //no op macros return the reader
1053 if(mret != r)
1054 a.add(mret);
1056 else
1058 unread(r, ch);
1060 Object o = read(r, true, null, isRecursive);
1061 if(o != r)
1062 a.add(o);
1067 return a;
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));
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
1084 for(; ;)
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();
1095 catch(Exception e)
1097 e.printStackTrace();
1100 */