/** * Copyright (c) Rich Hickey. All rights reserved. * The use and distribution terms for this software are covered by the * Common Public License 1.0 (http://opensource.org/licenses/cpl.php) * which can be found in the file CPL.TXT 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. **/ /* rich Sep 3, 2006 */ package clojure.lang; import java.io.StringWriter; import java.io.InputStreamReader; import java.io.FileInputStream; import java.lang.reflect.Field; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class Compiler { //* static Symbol DEF = Symbol.intern("def"); static Symbol FN = Symbol.intern("fn"); static Symbol DO = Symbol.intern("do"); static Symbol IF = Symbol.intern("if"); static Symbol OR = Symbol.intern("or"); static Symbol AND = Symbol.intern("and"); static Symbol LET = Symbol.intern("let"); static Symbol LET_STAR_ = Symbol.intern("let*"); static Symbol LETFN = Symbol.intern("letfn"); static Symbol NOT = Symbol.intern("not"); static Symbol NULL_QM_ = Symbol.intern("null?"); static Symbol IMPORT = Symbol.intern("import"); static Symbol USE = Symbol.intern("use"); static Symbol _AMP_KEY = Symbol.intern("&key"); static Symbol _AMP_REST = Symbol.intern("&rest"); static public Var _CRT_OUT = RT.OUT; static public Var _CRT_MODULE = RT._CT_MODULE; static NilExpr NIL_EXPR = new NilExpr(); //short-name-string->full-name-string static public Var IMPORTS; static { try { IMPORTS = Module.intern("clojure", "^compiler-imports"); KEYWORDS = Module.intern("clojure", "^compiler-keywords"); VARS = Module.intern("clojure", "^compiler-vars"); LOCAL_ENV = Module.intern("clojure", "^compiler-local-env"); METHOD = Module.intern("clojure", "^compiler-method"); USES = Module.intern("clojure", "^compiler-uses"); FNS = Module.intern("clojure", "^compiler-fns"); } catch(Exception e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } //keyword->keywordexpr static public Var KEYWORDS; //var->var static public Var VARS; //symbol->localbinding static public Var LOCAL_ENV; //FnFrame static public Var METHOD; //module->module static public Var USES; //ISeq FnExprs static public Var FNS; static public IPersistentMap CHAR_MAP = new PersistentArrayMap('-', "_DSH_", '.', "_DOT_", ':', "_CLN_", '+', "_PLS_", '>', "_GT_", '<', "_LT_", '=', "_EQ_", '~', "_TLD_", '!', "_EXC_", '@', "_AT_", '#', "_SHP_", '$', "_DS_", '%', "_PCT_", '^', "_CRT_", '&', "_AMP_", '*', "_STAR_", '{', "_LBC_", '}', "_RBC_", '[', "_LBK_", ']', "_RBK_", '/', "_FSL_", '\\', "_BSL_", '?', "_QM_"); private static final int MAX_POSITIONAL_ARITY = 20; static String compile(String ns, String className, LineNumberingPushbackReader... files) throws Exception { StringWriter w = new StringWriter(); try { _CRT_OUT.pushThreadBinding(w); KEYWORDS.pushThreadBinding(null); VARS.pushThreadBinding(null); METHOD.pushThreadBinding(null); LOCAL_ENV.pushThreadBinding(null); FNS.pushThreadBinding(new PersistentArrayList(4)); format("/* Generated by Clojure */~%~%"); format("package ~A;~%", ns); format("import clojure.lang.*;~%~%"); format("public class ~A{~%", className); PersistentArrayList forms = new PersistentArrayList(20); for (LineNumberingPushbackReader reader : files) { try { IMPORTS.pushThreadBinding(null); USES.pushThreadBinding(null); Object eof = new Object(); Object form = null; while ((form = LispReader.read(reader, false, eof, false)) != eof) { form = macroexpand(form); if (!(form instanceof ISeq)) throw new Exception("No atoms allowed at top level"); Object op = RT.first(form); //enact import and use at compile-time if (op == IMPORT) { //(import org.package ThisClass ThatClass ...) //makes entries in IMPORTS for: //"ThisClass"->"org.package.ThisClass" //"ThatClass"->"org.package.ThatClass" IPersistentMap importMap = (IPersistentMap) IMPORTS.getValue(); String pkg = RT.second(form).toString(); for (ISeq classes = RT.rest(RT.rest(form)); classes != null; classes = classes.rest()) { String iclassName = classes.first().toString(); importMap = (IPersistentMap) RT.assoc(iclassName, pkg + "." + iclassName, importMap); } IMPORTS.setValue(importMap); } else if (op == USE) { //todo implement use } else forms = forms.cons(analyze(C.STATEMENT, form)); } } finally { IMPORTS.popThreadBinding(); USES.popThreadBinding(); } } //declare static members for keywords, vars for (ISeq keys = RT.seq(KEYWORDS.getValue()); keys != null; keys = keys.rest()) { KeywordExpr k = (KeywordExpr) ((IMapEntry) keys.first()).val(); format("static Keyword ~A;~%", k.emitExpressionString()); } for (ISeq vars = RT.seq(VARS.getValue()); vars != null; vars = vars.rest()) { Var v = (Var) ((IMapEntry) vars.first()).val(); format("static Var ~A;~%", munge(v.toString())); } //todo declare static members for syms, quoted aggregates //emit nested static class/method declarations for nested fns PersistentArrayList fns = (PersistentArrayList) FNS.getValue(); for (int f = 0; f < fns.count(); f++) { FnExpr fn = (FnExpr) fns.nth(f); fn.emitDeclaration(); } //define the load function format("public void load() throws Exception{~%"); //init the keywords and vars for (ISeq keys = RT.seq(KEYWORDS.getValue()); keys != null; keys = keys.rest()) { KeywordExpr k = (KeywordExpr) ((IMapEntry) keys.first()).val(); format("~A = (Keyword)Symbol.intern(~S);~%", k.emitExpressionString(), k.sym.name); } for (ISeq vars = RT.seq(VARS.getValue()); vars != null; vars = vars.rest()) { Var v = (Var) ((IMapEntry) vars.first()).val(); format("~A = Module.intern(~S,~S);~%", munge(v.toString()), v.module.name, v.name.name); } //todo init syms and quoted aggregates //emit the top level forms for (int i = 0; i < forms.count(); i++) { Expr e = (Expr) forms.nth(i); e.emitStatement(); } //close load function format("}~%"); //close class def format("}~%"); } catch (Exception e) { e.printStackTrace(); } finally { _CRT_OUT.popThreadBinding(); KEYWORDS.popThreadBinding(); VARS.popThreadBinding(); METHOD.popThreadBinding(); LOCAL_ENV.popThreadBinding(); FNS.popThreadBinding(); } return w.toString(); } static String munge(String name) { StringBuilder sb = new StringBuilder(); for (char c : name.toCharArray()) { String sub = (String) CHAR_MAP.get(c); if (sub != null) sb.append(sub); else sb.append(c); } return sb.toString(); } enum C { STATEMENT,EXPRESSION,RETURN,FN} interface Expr { void emitReturn() throws Exception; void emitStatement() throws Exception; void emitExpression() throws Exception; String emitExpressionString() throws Exception; // may return null if clojure expression with no type hint, or host expression with unknown type //cannot be used to distinguish host expr vs not Class getHostType() throws Exception; boolean canEmitHostExpr(); void emitHostExpr() throws Exception; } static void format(String str, Object... args) throws Exception { RT.format(RT.T, str, args); } static class AnExpr implements Expr { public void emitReturn() throws Exception { format("return "); emitExpression(); format(";~%"); } public void emitStatement() throws Exception { emitExpression(); format(";~%"); } public void emitExpression() throws Exception { throw new UnsupportedOperationException(); } public String emitExpressionString() throws Exception { StringWriter w = new StringWriter(); try { _CRT_OUT.pushThreadBinding(w); emitExpression(); return w.toString(); } finally { _CRT_OUT.popThreadBinding(); } } public Class getHostType() throws Exception { return null; } public boolean canEmitHostExpr() { return false; } public void emitHostExpr() throws Exception { throw new Exception("Can't emit as host expr"); } public String toString() { try { return emitExpressionString(); } catch (Exception e) { //declared exceptions are an incredibly bad idea !!! e.printStackTrace(); return e.toString(); } } } static abstract class AHostExpr extends AnExpr { public boolean isHostExpr() { return false; } public void emitExpression() throws Exception { Class hostType = getHostType(); boolean needsBox = hostType == null || hostType.isPrimitive() || hostType == Boolean.class; if (needsBox) format("RT.box("); emitHostExpr(); if (needsBox) format(")"); } public boolean canEmitHostExpr() { return true; } } public static void processForm(Object form) throws Exception { if (RT.first(form) == DEF) { convert(form); } else throw new UnsupportedOperationException(); } private static void convert(Object form) throws Exception { Expr e = analyze(C.STATEMENT, form); } private static Expr analyze(C context, Object form) throws Exception { if (form == null) return NIL_EXPR; else if (form instanceof Symbol) return analyzeSymbol((Symbol) form, false); else if (form instanceof ISeq) return analyzeSeq(context, (ISeq) form); else if (form instanceof Num || form instanceof String) return new LiteralExpr(form); else if (form instanceof Character) return new CharExpr((Character) form); else throw new UnsupportedOperationException(); } private static Expr analyzeSeq(C context, ISeq form) throws Exception { Object op = RT.first(form); if (op == DEF) return analyzeDef(context, form); else if (op == FN) return analyzeFn(context, form); else if (op == DO) return analyzeDo(context, form); else if (op == IF) return analyzeIf(context, form); else if (op == OR) return analyzeOr(context, form); else if (op == AND) return analyzeAnd(context, form); else if (op == LET) return analyzeLet(context, form); else if (op == LET_STAR_) return analyzeLetStar(context, form); else if (op == LETFN) return analyzeLetFn(context, form); else if (op == NOT || op == NULL_QM_) return analyzeNot(context, form); else { PersistentArrayList args = new PersistentArrayList(4); for (ISeq s = op instanceof InstanceMemberSymbol ? RT.rrest(form) : RT.rest(form); s != null; s = s.rest()) args = args.cons(analyze(C.EXPRESSION, macroexpand(s.first()))); if (op instanceof ClassSymbol) return new InvokeConstructorExpr((ClassSymbol) op, args); else if (op instanceof StaticMemberSymbol) return new InvokeStaticMethodExpr((StaticMemberSymbol) op, args); else if (op instanceof InstanceMemberSymbol) return analyzeInstanceInvoke((InstanceMemberSymbol) op, analyze(C.EXPRESSION, macroexpand(RT.second(form))), args); Expr fexpr = (op instanceof Symbol) ? analyzeSymbol((Symbol) op, true) : analyze(C.EXPRESSION, op); if (fexpr instanceof FnExpr) ((FnExpr) fexpr).isCalledDirectly = true; return new InvokeExpr(fexpr, args); } } private static Expr analyzeInstanceInvoke(InstanceMemberSymbol sym, Expr target, PersistentArrayList args) throws Exception { Class targetClass = null; if (sym.className != null) targetClass = getTypeNamed(resolveHostClassname(sym.className)); else if (target.getHostType() != null) targetClass = target.getHostType(); else //must make reflection-based call return new InvokeUntypedInstanceMemberExpr(sym.memberName, target, args); return new InvokeInstanceMemberExpr(targetClass, sym.memberName, target, args); } static class InvokeExpr extends AnExpr { Expr fexpr; PersistentArrayList args; public InvokeExpr(Expr fexpr, PersistentArrayList args) { this.fexpr = fexpr; this.args = args; } public void emitExpression() throws Exception { FnExpr staticMethod = null; ISeq argseq = RT.seq(args); if (fexpr instanceof FnExpr && ((FnExpr) fexpr).willBeStaticMethod()) staticMethod = (FnExpr) fexpr; else if (fexpr instanceof LocalBindingExpr && ((LocalBindingExpr) fexpr).b.bindsToStaticFn()) staticMethod = ((LocalBindingExpr) fexpr).b.letfn; if (staticMethod != null) { ISeq closes = RT.keys(staticMethod.closes); format("~A(~{~A~^, ~}", staticMethod.getName(), closes); if (closes != null && argseq != null) format(","); format("~{~A~^, ~})", argseq); } else { format("((IFn)~A).invoke(~{~A~^, ~})", fexpr, argseq); } } } static class InvokeConstructorExpr extends AHostExpr { HostClassExpr fexpr; PersistentArrayList args; public InvokeConstructorExpr(ClassSymbol fexpr, PersistentArrayList args) throws Exception { this.fexpr = new HostClassExpr(fexpr); this.args = args; } public Class getHostType() throws Exception { return fexpr.type; } public void emitHostExpr() throws Exception { Constructor ctor = findSingleConstructor(fexpr.type, args); format("(new ~A(", fexpr.resolvedClassName); if (ctor != null) emitTypedArgs(ctor.getParameterTypes(), args, ctor.isVarArgs()); else emitHostArgs(args); format("))"); } } static class InvokeStaticMethodExpr extends AHostExpr { final StaticMemberSymbol sym; final String resolvedClassName; final Class type; PersistentArrayList args; final Method method; final Class returnType; public InvokeStaticMethodExpr(StaticMemberSymbol sym, PersistentArrayList args) throws Exception { this.sym = sym; this.args = args; this.resolvedClassName = resolveHostClassname(sym.className); this.type = getTypeNamed(resolvedClassName); Object sm = findSingleMethod(type, sym.memberName, args, true); if(sm instanceof Method) { method = (Method) sm; returnType = method.getReturnType(); } else if(sm instanceof Class) { method = null; returnType = (Class) sm; } else { method = null; returnType = null; } } public Class getHostType() throws Exception { return returnType; } public void emitHostExpr() throws Exception { format("~A.~A(", resolvedClassName, sym.memberName); if (method != null) emitTypedArgs(method.getParameterTypes(), args, method.isVarArgs()); else emitHostArgs(args); format(")"); } } static class InvokeUntypedInstanceMemberExpr extends AnExpr { final String name; final Expr target; PersistentArrayList args; public InvokeUntypedInstanceMemberExpr(String name, Expr target, PersistentArrayList args) throws Exception { this.name = name; this.args = args; this.target = target; } public void emitExpression() throws Exception { format("Reflector.invokeInstanceMember(~S,~A~{,~A~})", name, target, RT.seq(args)); } } static class InvokeInstanceMemberExpr extends AHostExpr { final String name; final Expr target; PersistentArrayList args; final Class targetType; final Field field; final Method method; final Class returnType; public InvokeInstanceMemberExpr(Class targetClass, String name, Expr target, PersistentArrayList args) throws Exception { this.name = name; this.args = args; this.target = target; this.targetType = targetClass; field = Reflector.getField(targetClass, name, false); if (field != null) { method = null; returnType = field.getType(); } else { Object sm = findSingleMethod(targetClass, name, args, false); if(sm instanceof Method) { method = (Method) sm; returnType = method.getReturnType(); } else if(sm instanceof Class) { method = null; returnType = (Class) sm; } else { method = null; returnType = null; } } } public Class getHostType() throws Exception { return returnType; } public void emitHostExpr() throws Exception { if(target.canEmitHostExpr()) target.emitHostExpr(); else emitConvert(targetType, target); if(field != null) { format(".~A", name); } else { format(".~A(", name); if (method != null) emitTypedArgs(method.getParameterTypes(), args, method.isVarArgs()); else emitHostArgs(args); format(")"); } } } static void emitHostArgs(PersistentArrayList args) throws Exception { for (int i = 0; i < args.count(); i++) { Expr arg = (Expr) args.nth(i); if (arg.canEmitHostExpr()) arg.emitHostExpr(); else if (arg.getHostType() != null) emitConvert(arg.getHostType(), arg); else arg.emitExpression(); if (i < args.count() - 1) format(","); } } static void emitTypedArgs(Class[] parameterTypes, PersistentArrayList args, boolean isVariadic) throws Exception { for (int i = 0; i < (isVariadic ? parameterTypes.length - 1 : args.count()); i++) { Expr arg = (Expr) args.nth(i); if (arg.canEmitHostExpr()) arg.emitHostExpr(); else emitConvert(parameterTypes[i], arg); if (i < args.count() - 1) format(","); } if (isVariadic) { Class vtype = parameterTypes[parameterTypes.length - 1].getComponentType(); for (int i = parameterTypes.length - 1; i < args.count(); i++) { Expr arg = (Expr) args.nth(i); if (arg.canEmitHostExpr()) arg.emitHostExpr(); else emitConvert(vtype, arg); if (i < args.count() - 1) format(","); } } } static void emitConvert(Class parameterType, Expr arg) throws Exception { if (parameterType == Object.class) arg.emitExpression(); else if (parameterType == boolean.class || parameterType == Boolean.class) format("(~A!=null)", arg); else if (parameterType == int.class || parameterType == Integer.class) format("((Number)~A).intValue()", arg); else if (parameterType == double.class || parameterType == Double.class) format("((Number)~A).doubleValue()", arg); else if (parameterType == float.class || parameterType == Float.class) format("((Number)~A).floatValue()", arg); else if (parameterType == long.class || parameterType == Long.class) format("((Number)~A).longValue()", arg); else if (parameterType == short.class || parameterType == Short.class) format("((Number)~A).shortValue()", arg); else if (parameterType == byte.class || parameterType == Byte.class) format("((Number)~A).byteValue()", arg); else format("((~A)~A)", parameterType.getName(), arg); } /* static void emitCast(Class parameterType, Expr arg) throws Exception { Class hostType = arg.getHostType(); boolean needsCast = false; //parameterType != Object.class && (hostType == null || !parameterType.isAssignableFrom(hostType)); if (needsCast) format("((~A)", parameterType.getName()); arg.emitHostExpr(); if (needsCast) format(")"); } */ /*returns null unless single ctor with matching arity, throws if no ctor can handle arity*/ public static Constructor findSingleConstructor(Class c, PersistentArrayList args) throws Exception { Constructor[] allctors = c.getConstructors(); Constructor found = null; for (int i = 0; i < allctors.length; i++) { Constructor ctor = allctors[i]; if (ctor.getParameterTypes().length == args.count() || (ctor.isVarArgs() && ctor.getParameterTypes().length <= args.count())) { if (found == null) found = ctor; else return null; //more than one matching } } //verify that at least one ctor can handle arity (requires variadic detection) if (found == null) throw new Exception("No constructor that can handle arity"); return found; } /*returns Method if single method with matching arity, returns Class of return type if all methods with same arity have same return type else returns null throws if no method can handle arity*/ public static Object findSingleMethod(Class c, String methodName, PersistentArrayList args, boolean isStatic) throws Exception { Method[] allmethods = c.getMethods(); Method found = null; int foundCount = 0; boolean returnsMatch = true; for (int i = 0; i < allmethods.length; i++) { Method method = allmethods[i]; if (Modifier.isStatic(method.getModifiers()) == isStatic && method.getName().equals(methodName) && (method.getParameterTypes().length == args.count() || (method.isVarArgs() && method.getParameterTypes().length <= args.count()))) { if (found == null) { found = method; foundCount = 1; } else { ++foundCount; if (method.getReturnType() != found.getReturnType()) returnsMatch = false; } } } //verify that at least one method can handle arity (requires variadic detection) if (foundCount == 0) throw new Exception("No method that can handle arity"); else if(foundCount == 1) return found; else if(returnsMatch) return found.getReturnType(); return null; } private static Expr analyzeLet(C context, ISeq form) throws Exception { //(let (var val var2 val2 ...) body...) ISeq bindings = (ISeq) RT.second(form); //special case (let () expr) ==> expr if (bindings == null && form.count() < 4) return analyze(context, macroexpand(RT.third(form))); ISeq body = RT.rest(RT.rest(form)); if (context == C.EXPRESSION) { //(let (a b) c) -> ((fn (a) c) b) PersistentArrayList parms = new PersistentArrayList(4); PersistentArrayList args = new PersistentArrayList(4); for (ISeq bs = bindings; bs != null; bs = RT.rest(RT.rest(bs))) { parms = parms.cons(RT.first(bs)); args = args.cons(RT.second(bs)); } return analyze(context, RT.cons(RT.listStar(FN, RT.seq(parms), body), RT.seq(args))); } PersistentArrayList bindingInits = new PersistentArrayList(4); //analyze inits before adding bindings to env for (ISeq bs = bindings; bs != null; bs = RT.rest(RT.rest(bs))) { LocalBinding lb = new LocalBinding(baseSymbol((Symbol) RT.first(bs))); lb.typeHint = typeHint((Symbol) RT.first(bs)); bindingInits = bindingInits.cons(new BindingInit(lb, analyze(C.EXPRESSION, RT.second(bs)))); } try { LOCAL_ENV.pushThreadBinding(LOCAL_ENV.getValue()); for (int i = 0; i < bindingInits.count(); i++) { BindingInit bi = (BindingInit) bindingInits.nth(i); if (bi.init instanceof FnExpr) { bi.binding.letfn = (FnExpr) bi.init; ((FnExpr) bi.init).binding = bi.binding; } registerLocal(bi.binding); } return new LetExpr(bindingInits, analyzeBody(context, body)); } finally { LOCAL_ENV.popThreadBinding(); } } private static Expr analyzeLetFn(C context, ISeq form) throws Exception { //(letfn ((foo [what can occur after fn]) (bar [what can occur after fn])) body ...) if (context == C.EXPRESSION) return analyze(context, RT.list(RT.list(FN, null, form))); try { LOCAL_ENV.pushThreadBinding(LOCAL_ENV.getValue()); ISeq bindings = (ISeq) RT.second(form); ISeq body = RT.rest(RT.rest(form)); PersistentArrayList bindingPairs = new PersistentArrayList(4); //add all fn names to env before analyzing bodies for (ISeq bs = bindings; bs != null; bs = RT.rest(bs)) { Object bform = RT.first(bs); Symbol fsym = (Symbol) RT.first(bform); LocalBinding lb = new LocalBinding(baseSymbol(fsym)); lb.typeHint = typeHint(fsym); registerLocal(lb); bindingPairs = bindingPairs.cons(new Tuple(lb, RT.cons(FN, RT.rest(bform)))); } PersistentArrayList bindingInits = new PersistentArrayList(4); for (int i = 0; i < bindingPairs.count(); i++) { Tuple bpair = (Tuple) bindingPairs.nth(i); LocalBinding lb = (LocalBinding) bpair.nth(0); FnExpr fexpr = (FnExpr) analyze(C.EXPRESSION, bpair.nth(1)); fexpr.binding = lb; lb.letfn = fexpr; bindingInits = bindingInits.cons(new BindingInit(lb, fexpr)); } return new LetExpr(bindingInits, analyzeBody(context, body)); } finally { LOCAL_ENV.popThreadBinding(); } } private static Expr analyzeLetStar(C context, ISeq form) throws Exception { //(let* (var val var2 val2 ...) body...) ISeq bindings = (ISeq) RT.second(form); //special case (let* () expr) ==> expr if (bindings == null && form.count() < 4) return analyze(context, macroexpand(RT.third(form))); ISeq body = RT.rest(RT.rest(form)); if (context == C.EXPRESSION) return analyze(context, RT.list(RT.list(FN, null, form))); try { LOCAL_ENV.pushThreadBinding(LOCAL_ENV.getValue()); PersistentArrayList bindingInits = new PersistentArrayList(4); for (ISeq bs = bindings; bs != null; bs = RT.rest(RT.rest(bs))) { LocalBinding lb = new LocalBinding(baseSymbol((Symbol) RT.first(bs))); lb.typeHint = typeHint((Symbol) RT.first(bs)); BindingInit bi = new BindingInit(lb, analyze(C.EXPRESSION, RT.second(bs))); bindingInits = bindingInits.cons(bi); if (bi.init instanceof FnExpr) { bi.binding.letfn = (FnExpr) bi.init; ((FnExpr) bi.init).binding = bi.binding; } //sequential enhancement of env registerLocal(lb); } return new LetExpr(bindingInits, analyzeBody(context, body)); } finally { LOCAL_ENV.popThreadBinding(); } } static class LetExpr extends AnExpr { PersistentArrayList bindingInits; Expr body; public LetExpr(PersistentArrayList bindingInits, Expr body) { this.bindingInits = bindingInits; this.body = body; } public void emitStatement() throws Exception { emitBindings(); body.emitStatement(); } private void emitBindings() throws Exception { for (int i = 0; i < bindingInits.count(); i++) { BindingInit bi = (BindingInit) bindingInits.nth(i); if (!(bi.init instanceof FnExpr && ((FnExpr) bi.init).willBeStaticMethod())) format("~A = ~A;~%", bi.binding.getExpr(), bi.init.emitExpressionString()); } } public void emitReturn() throws Exception { emitBindings(); body.emitReturn(); } } static class BindingInit { LocalBinding binding; Expr init; public BindingInit(LocalBinding binding, Expr init) { this.binding = binding; this.init = init; } } private static Expr analyzeAnd(C context, ISeq form) throws Exception { //(and) (and x) (and x y ...) //(or) (or X) (or x y ...) if (RT.count(form) == 1) return analyze(context, RT.T); else if (RT.count(form) == 2) return analyze(context, macroexpand(RT.second(form))); PersistentArrayList exprs = new PersistentArrayList(2); for (ISeq es = RT.rest(form); es != null; es = es.rest()) exprs = exprs.cons(analyze(C.EXPRESSION, macroexpand(es.first()))); return new AndExpr(exprs); } static class AndExpr extends AnExpr { final PersistentArrayList exprs; public AndExpr(PersistentArrayList exprs) { this.exprs = exprs; } public void emitStatement() throws Exception { format("if("); for (int i = 0; i < exprs.count(); i++) { format("~A != null", ((Expr) exprs.nth(i)).emitExpressionString()); if (i < exprs.count() - 1) format(" && "); } format(")~%;~%"); } public void emitExpression() throws Exception { format("(("); for (int i = 0; i < exprs.count(); i++) { if (i < exprs.count() - 1) format("~A != null", ((Expr) exprs.nth(i)).emitExpressionString()); if (i < exprs.count() - 2) format(" && "); if (i == exprs.count() - 1) format(")?~A:null)", ((Expr) exprs.nth(i)).emitExpressionString()); } } } private static Expr analyzeOr(C context, ISeq form) throws Exception { //(or) (or X) (or x y ...) if (RT.count(form) == 1) return NIL_EXPR; else if (RT.count(form) == 2) return analyze(context, macroexpand(RT.second(form))); LocalBinding tb = null; if (context != C.STATEMENT) { //we'll need a temp var tb = new LocalBinding(Symbol.intern("OR_TEMP")); registerLocal(tb); } PersistentArrayList exprs = new PersistentArrayList(2); for (ISeq es = RT.rest(form); es != null; es = es.rest()) exprs = exprs.cons(analyze(C.EXPRESSION, macroexpand(es.first()))); return new OrExpr(exprs, tb); } static class OrExpr extends AnExpr { final PersistentArrayList exprs; final LocalBinding tb; public OrExpr(PersistentArrayList exprs, LocalBinding tb) { this.exprs = exprs; this.tb = tb; } public void emitStatement() throws Exception { format("if("); for (int i = 0; i < exprs.count(); i++) { format("~A != null", ((Expr) exprs.nth(i)).emitExpressionString()); if (i < exprs.count() - 1) format(" || "); } format(")~%;~%"); } public void emitExpression() throws Exception { format("(("); for (int i = 0; i < exprs.count(); i++) { format("(~A = ~A) != null", tb.getName(), ((Expr) exprs.nth(i)).emitExpressionString()); if (i < exprs.count() - 1) format(" || "); } format(")?~A:null)", tb.getName()); } } private static Expr analyzeNot(C context, ISeq form) throws Exception { //(not x) or (null? x) //hmmm - will these be the same with host boolean arg? return new NotExpr(analyze(C.EXPRESSION, macroexpand(RT.second(form)))); } private static Expr analyzeIf(C context, ISeq form) throws Exception { //(if test then) or (if test then else) if (RT.second(form) == RT.T) //optimize macro-generated (if :t ...) forms return analyze(context, macroexpand(RT.third(form))); else if (RT.second(form) == null) return analyze(context, macroexpand(RT.fourth(form))); Expr testExpr = analyze(C.EXPRESSION, macroexpand(RT.second(form))); String compare = "!="; //lift tests that are not exprs by taking value as test and inverting compare if (testExpr instanceof NotExpr) { testExpr = ((NotExpr) testExpr).expr; compare = "=="; } return new IfExpr(testExpr, compare, analyze(context, macroexpand(RT.third(form))), analyze(context, macroexpand(RT.fourth(form)))); } private static Expr analyzeDo(C context, ISeq form) throws Exception { //(do ...) //(do) == null if (RT.rest(form) == null) return NIL_EXPR; else if (RT.rest(RT.rest(form)) == null) //(do x) == x return analyze(context, macroexpand(RT.second(form))); else if (context == C.EXPRESSION) return analyze(context, RT.list(RT.cons(FN, RT.cons(null, RT.rest(form))))); else return analyzeBody(context, RT.rest(form)); } static class IfExpr extends AnExpr { final Expr testExpr; final String compare; final Expr thenExpr; final Expr elseExpr; public IfExpr(Expr testExpr, String compare, Expr thenExpr, Expr elseExpr) { this.testExpr = testExpr; this.compare = compare; this.thenExpr = thenExpr; this.elseExpr = elseExpr; } public void emitReturn() throws Exception { format("if(~A ~A null)~%", testExpr.emitExpressionString(), compare); format("{~%"); thenExpr.emitReturn(); format("}~%"); format("else~%"); format("{~%"); elseExpr.emitReturn(); format("}~%"); } public void emitStatement() throws Exception { format("if(~A ~A null)~%", testExpr.emitExpressionString(), compare); format("{~%"); thenExpr.emitStatement(); format("}~%"); if (!(elseExpr instanceof NilExpr)) { format("else~%"); format("{~%"); elseExpr.emitStatement(); format("}~%"); } } public void emitExpression() throws Exception { format("(~A ~A null?", testExpr.emitExpressionString(), compare); thenExpr.emitExpression(); format(":"); elseExpr.emitExpression(); format(")"); } } private static Expr analyzeBody(C context, ISeq forms) throws Exception { PersistentArrayList exprs = new PersistentArrayList(4); for (; forms != null; forms = forms.rest()) { Expr e = (context == C.STATEMENT || RT.rest(forms) != null) ? analyze(C.STATEMENT, macroexpand(forms.first())) : analyze(C.RETURN, macroexpand(forms.first())); exprs = exprs.cons(e); } return new BodyExpr(exprs); } static class BodyExpr extends AnExpr { PersistentArrayList exprs; public BodyExpr(PersistentArrayList exprs) { this.exprs = exprs; } public void emitStatement() throws Exception { if (exprs.count() == 0) return; for (int i = 0; i < exprs.count(); i++) ((Expr) exprs.nth(i)).emitStatement(); } public void emitReturn() throws Exception { if (exprs.count() == 0) NIL_EXPR.emitReturn(); else { for (int i = 0; i < exprs.count(); i++) { if (i < exprs.count() - 1) ((Expr) exprs.nth(i)).emitStatement(); else ((Expr) exprs.nth(i)).emitReturn(); } } } } private static Expr analyzeFn(C context, ISeq form) throws Exception { //(fn (args) body) or (fn ((args) body) ((args2) body2) ...) //turn former into latter if (RT.second(form) == null || !(RT.first(RT.second(form)) == null || RT.first(RT.second(form)) instanceof ISeq)) form = RT.list(FN, RT.rest(form)); FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1]; FnMethod variadicMethod = null; FnExpr fn = new FnExpr(); for (ISeq s = RT.rest(form); s != null; s = RT.rest(s)) { FnMethod f = analyzeMethod(fn, (ISeq) RT.first(s)); if (f.isVariadic()) { if (variadicMethod == null) variadicMethod = f; else throw new Exception("Can't have more than 1 variadic overload"); } else if (methodArray[f.reqParms.count()] == null) methodArray[f.reqParms.count()] = f; else throw new Exception("Can't have 2 overloads with same arity"); } if (variadicMethod != null) { for (int i = variadicMethod.reqParms.count() + 1; i <= MAX_POSITIONAL_ARITY; i++) if (methodArray[i] != null) throw new Exception("Can't have fixed arity function with more params than variadic function"); } IPersistentCollection methods = null; for (int i = 0; i < methodArray.length; i++) if (methodArray[i] != null) methods = RT.cons(methodArray[i], methods); if (variadicMethod != null) methods = RT.cons(variadicMethod, methods); fn.methods = methods; fn.variadicMethod = variadicMethod; registerFn(fn); return fn; } static class FnExpr extends AnExpr { IPersistentCollection methods; FnMethod variadicMethod; LocalBinding binding; String name = null; boolean isCalledDirectly = false; //localbinding->itself IPersistentMap closes = null; String getName() { if (name == null) { if (binding != null) name = "FN__" + binding.getName();//munge(binding.sym.name) + "__" + RT.nextID(); else name = "FN__" + RT.nextID(); } return name; } public void emitExpression() throws Exception { format("(new ~A(", getName()); for (ISeq s = RT.seq(closes); s != null; s = s.rest()) { LocalBinding b = (LocalBinding) ((IMapEntry) s.first()).key(); format("~A", b.getName()); if (s.rest() != null) format(","); } format("))"); } public void emitDeclaration() throws Exception { PersistentArrayList closesDecls = null; if (closes != null) { closesDecls = new PersistentArrayList(closes.count() * 2); for (ISeq s = RT.seq(closes); s != null; s = s.rest()) { LocalBinding b = (LocalBinding) ((IMapEntry) s.first()).key(); if (!b.bindsToStaticFn()) { closesDecls = closesDecls.cons(b.typeDeclaration()); closesDecls = closesDecls.cons(b.getName()); } } } if (!willBeStaticMethod()) { //emit class declaration format("static public class ~A extends ~A{~%", getName(), variadicMethod != null ? "clojure.lang.RestFn" : "AFn"); if (closes != null) { //emit members and ctor if closure format("~{~A ~A;~%~}", closesDecls); format("public ~A (~{~A ~A~^, ~}){~%", getName(), closesDecls); if (variadicMethod != null) //must call base ctor format("super(~A);~%", variadicMethod.reqParms.count()); for (ISeq s = RT.seq(closes); s != null; s = s.rest()) { LocalBinding b = (LocalBinding) ((IMapEntry) s.first()).key(); if (!b.bindsToStaticFn()) { format("this.~A = ~A;~%", b.getName(), b.getName()); if (s.rest() != null) format(","); } } format("}~%"); } else if (variadicMethod != null) //must create ctor in order to call base ctor { format("public ~A (){~%", getName()); format("super(~A);~%", variadicMethod.reqParms.count()); format("}~%"); } } else { format("static public Object ~A(~{~A ~A~^, ~}", getName(), closesDecls); } for (ISeq methods = RT.seq(this.methods); methods != null; methods = methods.rest()) { //this will run once if static method FnMethod m = (FnMethod) methods.first(); if (!willBeStaticMethod()) format("public Object ~A(", m.isVariadic() ? "doInvoke" : "invoke"); for (ISeq reqs = RT.seq(m.reqParms); reqs != null; reqs = reqs.rest()) { LocalBindingExpr be = (LocalBindingExpr) reqs.first(); format("Object ~A", be.b.getName()); if (be.b.needsBox()) format("__arg"); if (reqs.rest() != null) format(","); } if (m.isVariadic()) { if (m.reqParms.count() > 0) format(","); format("ISeq "); if (m.restParm != null) { format("~A", m.restParm.b.getName()); if (m.restParm.b.needsBox()) format("__arg"); } else format("__keys"); } format(") throws Exception{~%"); //emit declarations for any boxed args for (ISeq reqs = RT.seq(m.reqParms); reqs != null; reqs = reqs.rest()) { LocalBindingExpr be = (LocalBindingExpr) reqs.first(); if (be.b.needsBox()) be.b.emitDeclaration(be.b.getName() + "__arg"); } //emit declaration for any boxed rest arg if (m.restParm != null && m.restParm.b.needsBox()) m.restParm.b.emitDeclaration(m.restParm.b.getName() + "__arg"); //keys are locals, plucked out of rest arg if (m.keyParms != null) { format("ISeq __valseq = null;~%"); for (ISeq keys = RT.seq(m.keyParms); keys != null; keys = keys.rest()) { KeyParam key = (KeyParam) keys.first(); KeywordExpr kw = registerKeyword((Keyword) Symbol.intern(":" + key.bindingExpression.b.sym.name)); format("__valseq = RT.findKey(~A,__keys);~%", kw.emitExpressionString()); key.bindingExpression.b.emitDeclaration( (String) RT.format(null, "(__valseq!=null)?clojure.lang.RT.first(__valseq):~A" , key.init.emitExpressionString())); } } //local variables for (ISeq locals = RT.seq(m.locals); locals != null; locals = locals.rest()) { LocalBinding b = (LocalBinding) ((IMapEntry) locals.first()).key(); if (!b.isParam && !b.bindsToStaticFn()) b.emitDeclaration("null"); } m.body.emitReturn(); //end of function format("}~%"); } //end of class if (!willBeStaticMethod()) format("}~%"); } boolean willBeStaticMethod() { return variadicMethod == null && methods.count() == 1 && ( isCalledDirectly || (binding != null && !binding.isAssigned && !binding.valueTaken) ); } } static class FnMethod { FnMethod parent = null; //localbinding->localbinding IPersistentMap locals = null; //localbinding->localbinding PersistentArrayList reqParms = new PersistentArrayList(4); PersistentArrayList keyParms = null; LocalBindingExpr restParm = null; Expr body = null; FnExpr fn; public FnMethod(FnExpr fn, FnMethod parent) { this.parent = parent; this.fn = fn; } boolean isVariadic() { return keyParms != null || restParm != null; } } enum PSTATE { REQ,REST,KEY,DONE} private static FnMethod analyzeMethod(FnExpr fn, ISeq form) throws Exception { //((args) body) ISeq parms = (ISeq) RT.first(form); ISeq body = RT.rest(form); try { FnMethod method = new FnMethod(fn, (FnMethod) METHOD.getValue()); METHOD.pushThreadBinding(method); LOCAL_ENV.pushThreadBinding(LOCAL_ENV.getValue()); PSTATE state = PSTATE.REQ; for (ISeq ps = parms; ps != null; ps = ps.rest()) { Object p = ps.first(); if (p == _AMP_REST) { if (state == PSTATE.REQ) state = PSTATE.REST; else throw new Exception("Invalid parameter list"); } else if (p == _AMP_KEY) { if (state == PSTATE.REQ) { state = PSTATE.KEY; method.keyParms = new PersistentArrayList(4); } else throw new Exception("Invalid parameter list"); } else { switch (state) { case REQ: method.reqParms = method.reqParms.cons(createParamBinding((Symbol) p)); break; case REST: method.restParm = createParamBinding((Symbol) p); state = PSTATE.DONE; break; case KEY: if (p instanceof ISeq) method.keyParms = method.keyParms.cons( new KeyParam(createParamBinding((Symbol) RT.first(p)), analyze(C.EXPRESSION, RT.second(p)))); else method.keyParms = method.keyParms.cons( new KeyParam(createParamBinding((Symbol) p))); break; default: throw new Exception("Unexpected parameter"); } } } if (method.reqParms.count() > MAX_POSITIONAL_ARITY) throw new Exception("Sorry, can't specify more than " + MAX_POSITIONAL_ARITY + " params"); method.body = analyze(C.RETURN, RT.cons(DO, body)); return method; } finally { METHOD.popThreadBinding(); LOCAL_ENV.popThreadBinding(); } } static LocalBindingExpr createParamBinding(Symbol p) throws Exception{ Symbol basep = baseSymbol(p); LocalBinding b = new LocalBinding(basep); b.isParam = true; String typeHint = typeHint(p); b.typeHint = typeHint; registerLocal(b); return new LocalBindingExpr(b, typeHint); } private static Expr analyzeDef(C context, ISeq form) throws Exception { //(def x) or (def x initexpr) if (form.count() > 3) throw new Exception("Too many arguments to def"); Symbol sym = (Symbol) RT.second(form); Module module = (Module) _CRT_MODULE.getValue(); Var var = module.intern(baseSymbol(sym)); registerVar(var); VarExpr ve = new VarExpr(var, typeHint(sym)); Expr init = analyze(C.EXPRESSION, macroexpand(RT.third(form))); if (init instanceof FnExpr) ((FnExpr) init).name = "FN__" + munge(var.name.toString()) + "__" + RT.nextID(); return new DefExpr(ve, init); } static Symbol baseSymbol(Symbol sym) { String base = baseName(sym); if (base == sym.name) //no typeHint return sym; return Symbol.intern(base); } static String baseName(Symbol sym) { int slash = sym.name.indexOf('/'); if (slash > 0) return sym.name.substring(0, slash); return sym.name; } static String typeHint(Symbol sym) { int slash = sym.name.indexOf('/'); if (slash > 0) return sym.name.substring(slash + 1); return null; } private static Expr analyzeSymbol(Symbol sym, boolean inFnPosition) throws Exception { if (sym instanceof Keyword) return registerKeyword((Keyword) sym); else if (sym instanceof ClassSymbol) return new HostClassExpr((ClassSymbol) sym); else if (sym instanceof StaticMemberSymbol) return new HostStaticFieldExpr((StaticMemberSymbol) sym); //todo have InstanceMemberSymbol yield accessor when expression else { String typeHint = typeHint(sym); sym = baseSymbol(sym); LocalBinding b = referenceLocal(sym); if (b != null) { if (!inFnPosition) b.valueTaken = true; return new LocalBindingExpr(b, typeHint); } Var v = lookupVar(sym); if (v != null) return new VarExpr(v, typeHint); throw new Exception("Unable to resolve symbol: " + sym.name + " in this context"); } } static Var lookupVar(Symbol sym) throws Exception{ Module module = (Module) _CRT_MODULE.getValue(); Var v = module.find(sym); if (v != null) return v; for (ISeq seq = RT.seq(USES.getValue()); seq != null; seq = RT.rest(seq)) { module = (Module) ((IMapEntry) RT.first(seq)).key(); v = module.find(sym); if (v != null && !v.exported) return v; } return null; } static Object macroexpand(Object x) { return x; //placeholder } private static KeywordExpr registerKeyword(Keyword keyword) throws Exception{ IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.getValue(); KeywordExpr ke = (KeywordExpr) RT.get(keyword, keywordsMap); if (ke == null) KEYWORDS.setValue(RT.assoc(keyword, ke = new KeywordExpr(keyword), keywordsMap)); return ke; } private static void registerVar(Var var) throws Exception{ IPersistentMap varsMap = (IPersistentMap) VARS.getValue(); if (RT.get(var, varsMap) == null) VARS.setValue(RT.assoc(var, var, varsMap)); } private static void registerFn(FnExpr fn) throws Exception{ FNS.setValue(RT.cons(fn, (IPersistentCollection) FNS.getValue())); } static void closeOver(LocalBinding b, FnMethod method) { if (b != null && method != null && RT.get(b, method.locals) == null) { b.isClosed = true; method.fn.closes = (IPersistentMap) RT.assoc(b, b, method.fn.closes); closeOver(b, method.parent); } } static LocalBinding referenceLocal(Symbol sym) throws Exception{ LocalBinding b = (LocalBinding) RT.get(sym, LOCAL_ENV.getValue()); if (b != null) { closeOver(b, (FnMethod) METHOD.getValue()); } return b; } private static void registerLocal(LocalBinding b) throws Exception{ IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.getValue(); LOCAL_ENV.setValue(RT.assoc(b.sym, b, localsMap)); FnMethod method = (FnMethod) METHOD.getValue(); method.locals = (IPersistentMap) RT.assoc(b, b, method.locals); } /* (defun reference-var (sym) (let ((b (first (member sym *var-env* :key (lambda (b) (@ :symbol b)))))) (labels ((check-closed (b frame) (when (and b frame (not (member b (@ :local-bindings frame)))) ;closed over (setf (@ :closed? b) t) (pushnew b (@ :closes frame)) (check-closed b (@ :parent frame))))) (check-closed b *frame*)) b)) */ static String resolveHostClassname(String classname) throws Exception { if (classname.indexOf('.') != -1) //presume fully qualified if contains . return classname; if (isPrimitive(classname)) return classname; IPersistentMap importMap = (IPersistentMap) IMPORTS.getValue(); String fullyQualifiedName = (String) RT.get(classname, importMap); if (fullyQualifiedName == null) throw new Exception("Can't resolve type name: " + classname); return fullyQualifiedName; } static boolean isPrimitive(String classname) { return classname.equals("int") || classname.equals("double") || classname.equals("float") || classname.equals("long") || classname.equals("boolean") || classname.equals("char") || classname.equals("short") || classname.equals("byte"); } static Class getTypeNamed(String classname) throws ClassNotFoundException { if (classname.equals("int")) return int.class; if (classname.equals("double")) return double.class; if (classname.equals("float")) return float.class; if (classname.equals("long")) return long.class; if (classname.equals("boolean")) return boolean.class; if (classname.equals("char")) return char.class; if (classname.equals("short")) return short.class; if (classname.equals("byte")) return byte.class; return Class.forName(classname); } static class KeyParam { public KeyParam(LocalBindingExpr b, Expr init) throws Exception{ this.bindingExpression = b; this.init = init; kw = registerKeyword((Keyword) Symbol.intern(":" + bindingExpression.b.sym.name)); } public KeyParam(LocalBindingExpr b) throws Exception{ this(b, NIL_EXPR); } LocalBindingExpr bindingExpression; Expr init; KeywordExpr kw; } static class NilExpr extends AnExpr { public void emitExpression() throws Exception { format("null"); } } static class LiteralExpr extends AnExpr { final Object val; public LiteralExpr(Object val) { this.val = val; } public void emitExpression() throws Exception { format("~S", val); } public Class getHostType() throws Exception { if (val instanceof DoubleNum) return double.class; if (val instanceof FixNum) return int.class; return val.getClass(); } public boolean canEmitHostExpr() { return val instanceof FixNum || val instanceof String || val instanceof DoubleNum; } public void emitHostExpr() throws Exception { if (val instanceof FixNum) format("~A", ((FixNum) val).val); else if (val instanceof String) format("~S", val); else if (val instanceof DoubleNum) format("~A", ((DoubleNum) val).val); else super.emitHostExpr(); } } static class NotExpr extends AnExpr { final Expr expr; public NotExpr(Expr expr) { this.expr = expr; } public void emitStatement() throws Exception { //just execute expr for side effects - no negation expr.emitStatement(); } public void emitExpression() throws Exception { format("(("); expr.emitExpression(); format("==null)?RT.T:null)"); } } static class CharExpr extends AnExpr { final Character val; public CharExpr(Character val) { this.val = val; } public void emitExpression() throws Exception { format("'~A'", val); } public Class getHostType() throws Exception { return char.class; } public boolean canEmitHostExpr() { return true; } public void emitHostExpr() throws Exception { emitExpression(); } } static class HostClassExpr extends AHostExpr { final ClassSymbol sym; final String resolvedClassName; final Class type; public HostClassExpr(ClassSymbol sym) throws Exception { this.sym = sym; this.resolvedClassName = resolveHostClassname(sym.className); this.type = getTypeNamed(resolvedClassName); } public void emitHostExpr() throws Exception { format("~A.class", resolvedClassName); } public Class getHostType() throws Exception { return type; } } static class HostStaticFieldExpr extends AHostExpr { final StaticMemberSymbol sym; final String resolvedClassName; final Class type; final Class fieldType; public HostStaticFieldExpr(StaticMemberSymbol sym) throws Exception { this.sym = sym; this.resolvedClassName = resolveHostClassname(sym.className); this.type = getTypeNamed(resolvedClassName); Field f = Reflector.getField(type, sym.memberName, true); if (f == null) throw new Exception(String.format("Can't find field %1$ in class %2$", sym.memberName, resolvedClassName)); fieldType = f.getType(); } public void emitHostExpr() throws Exception { format("~A.~A", resolvedClassName, sym.memberName); } public Class getHostType() throws Exception { return fieldType; } } /* static class SymExpr extends AnExpr{ Symbol sym; String typeHint; public SymExpr(Symbol sym, String typeHint){ this.sym = sym; this.typeHint = typeHint; } public void emitExpression() throws Exception{ format("~A", munge(sym.name)); } } */ static class KeywordExpr extends AnExpr { final Symbol sym; public KeywordExpr(Symbol sym) { this.sym = sym; } public void emitExpression() throws Exception { format("~A", munge(sym.name)); } } static class LocalBinding { final Symbol sym; boolean isClosed = false; boolean isParam = false; final int id = RT.nextID(); String typeHint; public boolean valueTaken = false; boolean isAssigned = false; FnExpr letfn = null; public LocalBinding(Symbol sym) { this.sym = sym; } public String getName() { return munge(sym.name) + (isParam ? "" : ("__" + id)); } public String toString() { return getName(); } boolean needsBox() { return (isClosed && isAssigned) || letfn != null && isClosed && valueTaken; } boolean bindsToStaticFn() { return letfn != null && letfn.willBeStaticMethod(); } String typeDeclaration() { if (needsBox()) return "clojure.lang.Box"; return "Object"; } String getExpr() { if (needsBox()) return getName() + ".val"; return getName(); } void emitDeclaration(String init) throws Exception { format("~A ~A = ", typeDeclaration(), getName()); if (needsBox()) format("new clojure.lang.Box(~A);~%", init); else format("~A;~%", init); } } static class LocalBindingExpr extends AnExpr { final LocalBinding b; final String typeHint; public LocalBindingExpr(LocalBinding b, String typeHint) { this.b = b; this.typeHint = typeHint; } public void emitStatement() throws Exception { //nop } public void emitExpression() throws Exception { format("~A", b.getExpr()); } public Class getHostType() throws Exception { String th = typeHint; if (th == null) th = b.typeHint; if (th == null) return null; return getTypeNamed(resolveHostClassname(th)); } } static class VarExpr extends AnExpr { final Var var; final String typeHint; public VarExpr(Var var, String typeHint) { this.var = var; this.typeHint = typeHint; } public String getName() { return munge(var.toString()); } public void emitExpression() throws Exception { format("~A.getValue()", getName()); } public Class getHostType() throws Exception { if (typeHint == null) return null; return getTypeNamed(resolveHostClassname(typeHint)); } } static class DefExpr extends AnExpr { final VarExpr var; final Expr init; public DefExpr(VarExpr var, Expr init) { this.var = var; this.init = init; } public void emitExpression() throws Exception { format("~A.bind(~A)", var.getName(), init.emitExpressionString()); } } public static void main(String[] args) throws Exception { if (!(args.length >= 3)) System.err.println("Usage: clojure.lang.Compiler package class file1 [file2 ...]"); String pkg = args[0]; String classname = args[1]; LineNumberingPushbackReader[] rs = new LineNumberingPushbackReader[args.length - 2]; String ret; try { for (int i = 0; i < rs.length; i++) { rs[i] = new LineNumberingPushbackReader(new InputStreamReader(new FileInputStream(args[i + 2]))); } ret = compile(pkg, classname, rs); System.out.print(ret); } finally { for (LineNumberingPushbackReader r : rs) { if (r != null) r.close(); } } } } //*/