Mercurial > lasercutter
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 the4 * 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 by7 * 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->gensymbol61 static Var GENSYM_ENV = Var.create(null);62 //sorted-map num->gensymbol63 static Var ARG_ENV = Var.create(null);65 static66 {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 try119 {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 reader149 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 else308 kns = Compiler.currentNS();309 //auto-resolving keyword310 if (kns != null)311 return Keyword.intern(kns.name.name,ks.name);312 else313 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 == '\\') //escape388 {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 == '\\') //escape409 {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 else451 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 do466 {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 else545 {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 try577 {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 finally609 {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 arg640 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 else678 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 try687 {688 Var.pushThreadBindings(689 RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));691 Object form = read(r, true, null, true);692 return syntaxQuote(form);693 }694 finally695 {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 else730 {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/foo738 sym = Symbol.intern(739 ((Class)maybeClass).getName(), sym.name);740 }741 else742 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 else771 ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));772 }773 else774 throw new UnsupportedOperationException("Unknown Collection type");775 }776 else if(form instanceof Keyword777 || form instanceof Number778 || form instanceof Character779 || form instanceof String)780 ret = form;781 else782 ret = RT.list(Compiler.QUOTE, form);784 if(form instanceof IObj && RT.meta(form) != null)785 {786 //filter line numbers787 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 else804 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 else842 {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 else908 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 method931 {932 String classname = s.ns;933 String method = s.name;934 return Reflector.invokeStaticMethod(classname, method, args);935 }936 else937 {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 else983 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 }1001 }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 }1009 }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 }1017 }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 }1024 }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 }1032 public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive) throws Exception{1033 ArrayList a = new ArrayList();1035 for(; ;)1036 {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)1050 {1051 Object mret = macroFn.invoke(r, (char) ch);1052 //no op macros return the reader1053 if(mret != r)1054 a.add(mret);1055 }1056 else1057 {1058 unread(r, ch);1060 Object o = read(r, true, null, isRecursive);1061 if(o != r)1062 a.add(o);1063 }1064 }1067 return a;1068 }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 }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 try1083 {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 */1102 }