/** * Copyright (c) Rich Hickey. All rights reserved. * The use and distribution terms for this software are covered by the * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) * which can be found in the file epl-v10.html at the root of this distribution. * By using this software in any fashion, you are agreeing to be bound by * the terms of this license. * You must not remove this notice, or any other, from this software. **/ package clojure.lang; import java.io.*; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.math.BigInteger; import java.math.BigDecimal; import java.lang.*; public class LispReader{ static final Symbol QUOTE = Symbol.create("quote"); static final Symbol THE_VAR = Symbol.create("var"); //static Symbol SYNTAX_QUOTE = Symbol.create(null, "syntax-quote"); static Symbol UNQUOTE = Symbol.create("clojure.core", "unquote"); static Symbol UNQUOTE_SPLICING = Symbol.create("clojure.core", "unquote-splicing"); static Symbol CONCAT = Symbol.create("clojure.core", "concat"); static Symbol SEQ = Symbol.create("clojure.core", "seq"); static Symbol LIST = Symbol.create("clojure.core", "list"); static Symbol APPLY = Symbol.create("clojure.core", "apply"); static Symbol HASHMAP = Symbol.create("clojure.core", "hash-map"); static Symbol HASHSET = Symbol.create("clojure.core", "hash-set"); static Symbol VECTOR = Symbol.create("clojure.core", "vector"); static Symbol WITH_META = Symbol.create("clojure.core", "with-meta"); static Symbol META = Symbol.create("clojure.core", "meta"); static Symbol DEREF = Symbol.create("clojure.core", "deref"); //static Symbol DEREF_BANG = Symbol.create("clojure.core", "deref!"); static IFn[] macros = new IFn[256]; static IFn[] dispatchMacros = new IFn[256]; //static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^:/]][^:/]*/)?[\\D&&[^:/]][^:/]*"); static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?([\\D&&[^/]][^/]*)"); //static Pattern varPat = Pattern.compile("([\\D&&[^:\\.]][^:\\.]*):([\\D&&[^:\\.]][^:\\.]*)"); //static Pattern intPat = Pattern.compile("[-+]?[0-9]+\\.?"); static Pattern intPat = Pattern.compile( "([-+]?)(?:(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]+)"); static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)"); static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?"); static final Symbol SLASH = Symbol.create("/"); static final Symbol CLOJURE_SLASH = Symbol.create("clojure.core","/"); //static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*"); //static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)"); //static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)"); //static Pattern classNamePat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\."); //symbol->gensymbol static Var GENSYM_ENV = Var.create(null); //sorted-map num->gensymbol static Var ARG_ENV = Var.create(null); static { macros['"'] = new StringReader(); macros[';'] = new CommentReader(); macros['\''] = new WrappingReader(QUOTE); macros['@'] = new WrappingReader(DEREF);//new DerefReader(); macros['^'] = new DeprecatedWrappingReader(META, "^"); macros['`'] = new SyntaxQuoteReader(); macros['~'] = new UnquoteReader(); macros['('] = new ListReader(); macros[')'] = new UnmatchedDelimiterReader(); macros['['] = new VectorReader(); macros[']'] = new UnmatchedDelimiterReader(); macros['{'] = new MapReader(); macros['}'] = new UnmatchedDelimiterReader(); // macros['|'] = new ArgVectorReader(); macros['\\'] = new CharacterReader(); macros['%'] = new ArgReader(); macros['#'] = new DispatchReader(); dispatchMacros['^'] = new MetaReader(); dispatchMacros['\''] = new VarReader(); dispatchMacros['"'] = new RegexReader(); dispatchMacros['('] = new FnReader(); dispatchMacros['{'] = new SetReader(); dispatchMacros['='] = new EvalReader(); dispatchMacros['!'] = new CommentReader(); dispatchMacros['<'] = new UnreadableReader(); dispatchMacros['_'] = new DiscardReader(); } static boolean isWhitespace(int ch){ return Character.isWhitespace(ch) || ch == ','; } static void unread(PushbackReader r, int ch) throws IOException{ if(ch != -1) r.unread(ch); } public static class ReaderException extends Exception{ final int line; public ReaderException(int line, Throwable cause){ super(cause); this.line = line; } } static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive) throws Exception{ try { for(; ;) { int ch = r.read(); while(isWhitespace(ch)) ch = r.read(); if(ch == -1) { if(eofIsError) throw new Exception("EOF while reading"); return eofValue; } if(Character.isDigit(ch)) { Object n = readNumber(r, (char) ch); if(RT.suppressRead()) return null; return n; } IFn macroFn = getMacro(ch); if(macroFn != null) { Object ret = macroFn.invoke(r, (char) ch); if(RT.suppressRead()) return null; //no op macros return the reader if(ret == r) continue; return ret; } if(ch == '+' || ch == '-') { int ch2 = r.read(); if(Character.isDigit(ch2)) { unread(r, ch2); Object n = readNumber(r, (char) ch); if(RT.suppressRead()) return null; return n; } unread(r, ch2); } String token = readToken(r, (char) ch); if(RT.suppressRead()) return null; return interpretToken(token); } } catch(Exception e) { if(isRecursive || !(r instanceof LineNumberingPushbackReader)) throw e; LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r; //throw new Exception(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e); throw new ReaderException(rdr.getLineNumber(), e); } } static private String readToken(PushbackReader r, char initch) throws Exception{ StringBuilder sb = new StringBuilder(); sb.append(initch); for(; ;) { int ch = r.read(); if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch)) { unread(r, ch); return sb.toString(); } sb.append((char) ch); } } static private Object readNumber(PushbackReader r, char initch) throws Exception{ StringBuilder sb = new StringBuilder(); sb.append(initch); for(; ;) { int ch = r.read(); if(ch == -1 || isWhitespace(ch) || isMacro(ch)) { unread(r, ch); break; } sb.append((char) ch); } String s = sb.toString(); Object n = matchNumber(s); if(n == null) throw new NumberFormatException("Invalid number: " + s); return n; } static private int readUnicodeChar(String token, int offset, int length, int base) throws Exception{ if(token.length() != offset + length) throw new IllegalArgumentException("Invalid unicode character: \\" + token); int uc = 0; for(int i = offset; i < offset + length; ++i) { int d = Character.digit(token.charAt(i), base); if(d == -1) throw new IllegalArgumentException("Invalid digit: " + (char) d); uc = uc * base + d; } return (char) uc; } static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) throws Exception{ int uc = Character.digit(initch, base); if(uc == -1) throw new IllegalArgumentException("Invalid digit: " + initch); int i = 1; for(; i < length; ++i) { int ch = r.read(); if(ch == -1 || isWhitespace(ch) || isMacro(ch)) { unread(r, ch); break; } int d = Character.digit(ch, base); if(d == -1) throw new IllegalArgumentException("Invalid digit: " + (char) ch); uc = uc * base + d; } if(i != length && exact) throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length); return uc; } static private Object interpretToken(String s) throws Exception{ if(s.equals("nil")) { return null; } else if(s.equals("true")) { return RT.T; } else if(s.equals("false")) { return RT.F; } else if(s.equals("/")) { return SLASH; } else if(s.equals("clojure.core//")) { return CLOJURE_SLASH; } Object ret = null; ret = matchSymbol(s); if(ret != null) return ret; throw new Exception("Invalid token: " + s); } private static Object matchSymbol(String s){ Matcher m = symbolPat.matcher(s); if(m.matches()) { int gc = m.groupCount(); String ns = m.group(1); String name = m.group(2); if(ns != null && ns.endsWith(":/") || name.endsWith(":") || s.indexOf("::", 1) != -1) return null; if(s.startsWith("::")) { Symbol ks = Symbol.intern(s.substring(2)); Namespace kns; if(ks.ns != null) kns = Compiler.namespaceFor(ks); else kns = Compiler.currentNS(); //auto-resolving keyword return Keyword.intern(kns.name.name,ks.name); } boolean isKeyword = s.charAt(0) == ':'; Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0)); if(isKeyword) return Keyword.intern(sym); return sym; } return null; } private static Object matchNumber(String s){ Matcher m = intPat.matcher(s); if(m.matches()) { if(m.group(2) != null) return 0; boolean negate = (m.group(1).equals("-")); String n; int radix = 10; if((n = m.group(3)) != null) radix = 10; else if((n = m.group(4)) != null) radix = 16; else if((n = m.group(5)) != null) radix = 8; else if((n = m.group(7)) != null) radix = Integer.parseInt(m.group(6)); if(n == null) return null; BigInteger bn = new BigInteger(n, radix); return Numbers.reduce(negate ? bn.negate() : bn); } m = floatPat.matcher(s); if(m.matches()) { if(m.group(4) != null) return new BigDecimal(m.group(1)); return Double.parseDouble(s); } m = ratioPat.matcher(s); if(m.matches()) { return Numbers.divide(new BigInteger(m.group(1)), new BigInteger(m.group(2))); } return null; } static private IFn getMacro(int ch){ if(ch < macros.length) return macros[ch]; return null; } static private boolean isMacro(int ch){ return (ch < macros.length && macros[ch] != null); } static private boolean isTerminatingMacro(int ch){ return (ch != '#' && ch < macros.length && macros[ch] != null); } public static class RegexReader extends AFn{ static StringReader stringrdr = new StringReader(); public Object invoke(Object reader, Object doublequote) throws Exception{ StringBuilder sb = new StringBuilder(); Reader r = (Reader) reader; for(int ch = r.read(); ch != '"'; ch = r.read()) { if(ch == -1) throw new Exception("EOF while reading regex"); sb.append( (char) ch ); if(ch == '\\') //escape { ch = r.read(); if(ch == -1) throw new Exception("EOF while reading regex"); sb.append( (char) ch ) ; } } return Pattern.compile(sb.toString()); } } public static class StringReader extends AFn{ public Object invoke(Object reader, Object doublequote) throws Exception{ StringBuilder sb = new StringBuilder(); Reader r = (Reader) reader; for(int ch = r.read(); ch != '"'; ch = r.read()) { if(ch == -1) throw new Exception("EOF while reading string"); if(ch == '\\') //escape { ch = r.read(); if(ch == -1) throw new Exception("EOF while reading string"); switch(ch) { case 't': ch = '\t'; break; case 'r': ch = '\r'; break; case 'n': ch = '\n'; break; case '\\': break; case '"': break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'u': { ch = r.read(); if (Character.digit(ch, 16) == -1) throw new Exception("Invalid unicode escape: \\u" + (char) ch); ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true); break; } default: { if(Character.isDigit(ch)) { ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false); if(ch > 0377) throw new Exception("Octal escape sequence must be in range [0, 377]."); } else throw new Exception("Unsupported escape character: \\" + (char) ch); } } } sb.append((char) ch); } return sb.toString(); } } public static class CommentReader extends AFn{ public Object invoke(Object reader, Object semicolon) throws Exception{ Reader r = (Reader) reader; int ch; do { ch = r.read(); } while(ch != -1 && ch != '\n' && ch != '\r'); return r; } } public static class DiscardReader extends AFn{ public Object invoke(Object reader, Object underscore) throws Exception{ PushbackReader r = (PushbackReader) reader; read(r, true, null, true); return r; } } public static class WrappingReader extends AFn{ final Symbol sym; public WrappingReader(Symbol sym){ this.sym = sym; } public Object invoke(Object reader, Object quote) throws Exception{ PushbackReader r = (PushbackReader) reader; Object o = read(r, true, null, true); return RT.list(sym, o); } } public static class DeprecatedWrappingReader extends AFn{ final Symbol sym; final String macro; public DeprecatedWrappingReader(Symbol sym, String macro){ this.sym = sym; this.macro = macro; } public Object invoke(Object reader, Object quote) throws Exception{ System.out.println("WARNING: reader macro " + macro + " is deprecated; use " + sym.getName() + " instead"); PushbackReader r = (PushbackReader) reader; Object o = read(r, true, null, true); return RT.list(sym, o); } } public static class VarReader extends AFn{ public Object invoke(Object reader, Object quote) throws Exception{ PushbackReader r = (PushbackReader) reader; Object o = read(r, true, null, true); // if(o instanceof Symbol) // { // Object v = Compiler.maybeResolveIn(Compiler.currentNS(), (Symbol) o); // if(v instanceof Var) // return v; // } return RT.list(THE_VAR, o); } } /* static class DerefReader extends AFn{ public Object invoke(Object reader, Object quote) throws Exception{ PushbackReader r = (PushbackReader) reader; int ch = r.read(); if(ch == -1) throw new Exception("EOF while reading character"); if(ch == '!') { Object o = read(r, true, null, true); return RT.list(DEREF_BANG, o); } else { r.unread(ch); Object o = read(r, true, null, true); return RT.list(DEREF, o); } } } */ public static class DispatchReader extends AFn{ public Object invoke(Object reader, Object hash) throws Exception{ int ch = ((Reader) reader).read(); if(ch == -1) throw new Exception("EOF while reading character"); IFn fn = dispatchMacros[ch]; if(fn == null) throw new Exception(String.format("No dispatch macro for: %c", (char) ch)); return fn.invoke(reader, ch); } } static Symbol garg(int n){ return Symbol.intern(null, (n == -1 ? "rest" : ("p" + n)) + "__" + RT.nextID()); } public static class FnReader extends AFn{ public Object invoke(Object reader, Object lparen) throws Exception{ PushbackReader r = (PushbackReader) reader; if(ARG_ENV.deref() != null) throw new IllegalStateException("Nested #()s are not allowed"); try { Var.pushThreadBindings( RT.map(ARG_ENV, PersistentTreeMap.EMPTY)); r.unread('('); Object form = read(r, true, null, true); PersistentVector args = PersistentVector.EMPTY; PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref(); ISeq rargs = argsyms.rseq(); if(rargs != null) { int higharg = (Integer) ((Map.Entry) rargs.first()).getKey(); if(higharg > 0) { for(int i = 1; i <= higharg; ++i) { Object sym = argsyms.valAt(i); if(sym == null) sym = garg(i); args = args.cons(sym); } } Object restsym = argsyms.valAt(-1); if(restsym != null) { args = args.cons(Compiler._AMP_); args = args.cons(restsym); } } return RT.list(Compiler.FN, args, form); } finally { Var.popThreadBindings(); } } } static Symbol registerArg(int n){ PersistentTreeMap argsyms = (PersistentTreeMap) ARG_ENV.deref(); if(argsyms == null) { throw new IllegalStateException("arg literal not in #()"); } Symbol ret = (Symbol) argsyms.valAt(n); if(ret == null) { ret = garg(n); ARG_ENV.set(argsyms.assoc(n, ret)); } return ret; } static class ArgReader extends AFn{ public Object invoke(Object reader, Object pct) throws Exception{ PushbackReader r = (PushbackReader) reader; if(ARG_ENV.deref() == null) { return interpretToken(readToken(r, '%')); } int ch = r.read(); unread(r, ch); //% alone is first arg if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch)) { return registerArg(1); } Object n = read(r, true, null, true); if(n.equals(Compiler._AMP_)) return registerArg(-1); if(!(n instanceof Number)) throw new IllegalStateException("arg literal must be %, %& or %integer"); return registerArg(((Number) n).intValue()); } } public static class MetaReader extends AFn{ public Object invoke(Object reader, Object caret) throws Exception{ PushbackReader r = (PushbackReader) reader; int line = -1; if(r instanceof LineNumberingPushbackReader) line = ((LineNumberingPushbackReader) r).getLineNumber(); Object meta = read(r, true, null, true); if(meta instanceof Symbol || meta instanceof Keyword || meta instanceof String) meta = RT.map(RT.TAG_KEY, meta); else if(!(meta instanceof IPersistentMap)) throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map"); Object o = read(r, true, null, true); if(o instanceof IMeta) { if(line != -1 && o instanceof ISeq) meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line); if(o instanceof IReference) { ((IReference)o).resetMeta((IPersistentMap) meta); return o; } return ((IObj) o).withMeta((IPersistentMap) meta); } else throw new IllegalArgumentException("Metadata can only be applied to IMetas"); } } public static class SyntaxQuoteReader extends AFn{ public Object invoke(Object reader, Object backquote) throws Exception{ PushbackReader r = (PushbackReader) reader; try { Var.pushThreadBindings( RT.map(GENSYM_ENV, PersistentHashMap.EMPTY)); Object form = read(r, true, null, true); return syntaxQuote(form); } finally { Var.popThreadBindings(); } } static Object syntaxQuote(Object form) throws Exception{ Object ret; if(Compiler.isSpecial(form)) ret = RT.list(Compiler.QUOTE, form); else if(form instanceof Symbol) { Symbol sym = (Symbol) form; if(sym.ns == null && sym.name.endsWith("#")) { IPersistentMap gmap = (IPersistentMap) GENSYM_ENV.deref(); if(gmap == null) throw new IllegalStateException("Gensym literal not in syntax-quote"); Symbol gs = (Symbol) gmap.valAt(sym); if(gs == null) GENSYM_ENV.set(gmap.assoc(sym, gs = Symbol.intern(null, sym.name.substring(0, sym.name.length() - 1) + "__" + RT.nextID() + "__auto__"))); sym = gs; } else if(sym.ns == null && sym.name.endsWith(".")) { Symbol csym = Symbol.intern(null, sym.name.substring(0, sym.name.length() - 1)); csym = Compiler.resolveSymbol(csym); sym = Symbol.intern(null, csym.name.concat(".")); } else if(sym.ns == null && sym.name.startsWith(".")) { // Simply quote method names. } else { Object maybeClass = null; if(sym.ns != null) maybeClass = Compiler.currentNS().getMapping( Symbol.intern(null, sym.ns)); if(maybeClass instanceof Class) { // Classname/foo -> package.qualified.Classname/foo sym = Symbol.intern( ((Class)maybeClass).getName(), sym.name); } else sym = Compiler.resolveSymbol(sym); } ret = RT.list(Compiler.QUOTE, sym); } else if(isUnquote(form)) return RT.second(form); else if(isUnquoteSplicing(form)) throw new IllegalStateException("splice not in list"); else if(form instanceof IPersistentCollection) { if(form instanceof IPersistentMap) { IPersistentVector keyvals = flattenMap(form); ret = RT.list(APPLY, HASHMAP, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(keyvals.seq())))); } else if(form instanceof IPersistentVector) { ret = RT.list(APPLY, VECTOR, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentVector) form).seq())))); } else if(form instanceof IPersistentSet) { ret = RT.list(APPLY, HASHSET, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentSet) form).seq())))); } else if(form instanceof ISeq || form instanceof IPersistentList) { ISeq seq = RT.seq(form); if(seq == null) ret = RT.cons(LIST,null); else ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq))); } else throw new UnsupportedOperationException("Unknown Collection type"); } else if(form instanceof Keyword || form instanceof Number || form instanceof Character || form instanceof String) ret = form; else ret = RT.list(Compiler.QUOTE, form); if(form instanceof IObj && RT.meta(form) != null) { //filter line numbers IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY); if(newMeta.count() > 0) return RT.list(WITH_META, ret, syntaxQuote(((IObj) form).meta())); } return ret; } private static ISeq sqExpandList(ISeq seq) throws Exception{ PersistentVector ret = PersistentVector.EMPTY; for(; seq != null; seq = seq.next()) { Object item = seq.first(); if(isUnquote(item)) ret = ret.cons(RT.list(LIST, RT.second(item))); else if(isUnquoteSplicing(item)) ret = ret.cons(RT.second(item)); else ret = ret.cons(RT.list(LIST, syntaxQuote(item))); } return ret.seq(); } private static IPersistentVector flattenMap(Object form){ IPersistentVector keyvals = PersistentVector.EMPTY; for(ISeq s = RT.seq(form); s != null; s = s.next()) { IMapEntry e = (IMapEntry) s.first(); keyvals = (IPersistentVector) keyvals.cons(e.key()); keyvals = (IPersistentVector) keyvals.cons(e.val()); } return keyvals; } } static boolean isUnquoteSplicing(Object form){ return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE_SPLICING); } static boolean isUnquote(Object form){ return form instanceof ISeq && Util.equals(RT.first(form),UNQUOTE); } static class UnquoteReader extends AFn{ public Object invoke(Object reader, Object comma) throws Exception{ PushbackReader r = (PushbackReader) reader; int ch = r.read(); if(ch == -1) throw new Exception("EOF while reading character"); if(ch == '@') { Object o = read(r, true, null, true); return RT.list(UNQUOTE_SPLICING, o); } else { unread(r, ch); Object o = read(r, true, null, true); return RT.list(UNQUOTE, o); } } } public static class CharacterReader extends AFn{ public Object invoke(Object reader, Object backslash) throws Exception{ PushbackReader r = (PushbackReader) reader; int ch = r.read(); if(ch == -1) throw new Exception("EOF while reading character"); String token = readToken(r, (char) ch); if(token.length() == 1) return Character.valueOf(token.charAt(0)); else if(token.equals("newline")) return '\n'; else if(token.equals("space")) return ' '; else if(token.equals("tab")) return '\t'; else if(token.equals("backspace")) return '\b'; else if(token.equals("formfeed")) return '\f'; else if(token.equals("return")) return '\r'; else if(token.startsWith("u")) { char c = (char) readUnicodeChar(token, 1, 4, 16); if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit? throw new Exception("Invalid character constant: \\u" + Integer.toString(c, 16)); return c; } else if(token.startsWith("o")) { int len = token.length() - 1; if(len > 3) throw new Exception("Invalid octal escape sequence length: " + len); int uc = readUnicodeChar(token, 1, len, 8); if(uc > 0377) throw new Exception("Octal escape sequence must be in range [0, 377]."); return (char) uc; } throw new Exception("Unsupported character: \\" + token); } } public static class ListReader extends AFn{ public Object invoke(Object reader, Object leftparen) throws Exception{ PushbackReader r = (PushbackReader) reader; int line = -1; if(r instanceof LineNumberingPushbackReader) line = ((LineNumberingPushbackReader) r).getLineNumber(); List list = readDelimitedList(')', r, true); if(list.isEmpty()) return PersistentList.EMPTY; IObj s = (IObj) PersistentList.create(list); // IObj s = (IObj) RT.seq(list); if(line != -1) return s.withMeta(RT.map(RT.LINE_KEY, line)); else return s; } } static class CtorReader extends AFn{ static final Symbol cls = Symbol.create("class"); public Object invoke(Object reader, Object leftangle) throws Exception{ PushbackReader r = (PushbackReader) reader; // #<class classname> // #<classname args*> // #<classname/staticMethod args*> List list = readDelimitedList('>', r, true); if(list.isEmpty()) throw new Exception("Must supply 'class', classname or classname/staticMethod"); Symbol s = (Symbol) list.get(0); Object[] args = list.subList(1, list.size()).toArray(); if(s.equals(cls)) { return RT.classForName(args[0].toString()); } else if(s.ns != null) //static method { String classname = s.ns; String method = s.name; return Reflector.invokeStaticMethod(classname, method, args); } else { return Reflector.invokeConstructor(RT.classForName(s.name), args); } } } public static class EvalReader extends AFn{ public Object invoke(Object reader, Object eq) throws Exception{ if (!RT.booleanCast(RT.READEVAL.deref())) { throw new Exception("EvalReader not allowed when *read-eval* is false."); } PushbackReader r = (PushbackReader) reader; Object o = read(r, true, null, true); if(o instanceof Symbol) { return RT.classForName(o.toString()); } else if(o instanceof IPersistentList) { Symbol fs = (Symbol) RT.first(o); if(fs.equals(THE_VAR)) { Symbol vs = (Symbol) RT.second(o); return RT.var(vs.ns, vs.name); //Compiler.resolve((Symbol) RT.second(o),true); } if(fs.name.endsWith(".")) { Object[] args = RT.toArray(RT.next(o)); return Reflector.invokeConstructor(RT.classForName(fs.name.substring(0, fs.name.length() - 1)), args); } if(Compiler.namesStaticMember(fs)) { Object[] args = RT.toArray(RT.next(o)); return Reflector.invokeStaticMethod(fs.ns, fs.name, args); } Object v = Compiler.maybeResolveIn(Compiler.currentNS(), fs); if(v instanceof Var) { return ((IFn) v).applyTo(RT.next(o)); } throw new Exception("Can't resolve " + fs); } else throw new IllegalArgumentException("Unsupported #= form"); } } //static class ArgVectorReader extends AFn{ // public Object invoke(Object reader, Object leftparen) throws Exception{ // PushbackReader r = (PushbackReader) reader; // return ArgVector.create(readDelimitedList('|', r, true)); // } // //} public static class VectorReader extends AFn{ public Object invoke(Object reader, Object leftparen) throws Exception{ PushbackReader r = (PushbackReader) reader; return LazilyPersistentVector.create(readDelimitedList(']', r, true)); } } public static class MapReader extends AFn{ public Object invoke(Object reader, Object leftparen) throws Exception{ PushbackReader r = (PushbackReader) reader; return RT.map(readDelimitedList('}', r, true).toArray()); } } public static class SetReader extends AFn{ public Object invoke(Object reader, Object leftbracket) throws Exception{ PushbackReader r = (PushbackReader) reader; return PersistentHashSet.create(readDelimitedList('}', r, true)); } } public static class UnmatchedDelimiterReader extends AFn{ public Object invoke(Object reader, Object rightdelim) throws Exception{ throw new Exception("Unmatched delimiter: " + rightdelim); } } public static class UnreadableReader extends AFn{ public Object invoke(Object reader, Object leftangle) throws Exception{ throw new Exception("Unreadable form"); } } public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive) throws Exception{ ArrayList a = new ArrayList(); for(; ;) { int ch = r.read(); while(isWhitespace(ch)) ch = r.read(); if(ch == -1) throw new Exception("EOF while reading"); if(ch == delim) break; IFn macroFn = getMacro(ch); if(macroFn != null) { Object mret = macroFn.invoke(r, (char) ch); //no op macros return the reader if(mret != r) a.add(mret); } else { unread(r, ch); Object o = read(r, true, null, isRecursive); if(o != r) a.add(o); } } return a; } /* public static void main(String[] args) throws Exception{ //RT.init(); PushbackReader rdr = new PushbackReader( new java.io.StringReader( "(+ 21 21)" ) ); Object input = LispReader.read(rdr, false, new Object(), false ); System.out.println(Compiler.eval(input)); } public static void main(String[] args){ LineNumberingPushbackReader r = new LineNumberingPushbackReader(new InputStreamReader(System.in)); OutputStreamWriter w = new OutputStreamWriter(System.out); Object ret = null; try { for(; ;) { ret = LispReader.read(r, true, null, false); RT.print(ret, w); w.write('\n'); if(ret != null) w.write(ret.getClass().toString()); w.write('\n'); w.flush(); } } catch(Exception e) { e.printStackTrace(); } } */ }