/** * 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 = new Symbol("def"); static Symbol FN = new Symbol("fn"); static Symbol DO = new Symbol("do"); static Symbol IF = new Symbol("if"); static Symbol OR = new Symbol("or"); static Symbol AND = new Symbol("and"); static Symbol LET = new Symbol("let"); static Symbol LET_STAR_ = new Symbol("let*"); static Symbol LETFN = new Symbol("letfn"); static Symbol NOT = new Symbol("not"); static Symbol NULL_QM_ = new Symbol("null?"); static Symbol IMPORT = new Symbol("import"); static Symbol USE = new Symbol("use"); static Symbol _AMP_KEY = new Symbol("&key"); static Symbol _AMP_REST = new Symbol("&rest"); static public TRef _CRT_OUT = RT.OUT; static public TRef _CRT_MODULE = RT.CURRENT_MODULE; static NilExpr NIL_EXPR = new NilExpr(); //short-name-string->full-name-string static public TRef IMPORTS = new TRef(); //keyword->keywordexpr static public TRef KEYWORDS = new TRef(); //var->var static public TRef VARS = new TRef(); //symbol->localbinding static public TRef LOCAL_ENV = new TRef(); //FnFrame static public TRef METHOD = new TRef(); //module->module static public TRef USES = new TRef(); //ISeq FnExprs static public TRef FNS = new TRef(); static public IPersistentMap CHAR_MAP = new PersistentArrayMap(new Object[]{'-', "_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(PersistentVector.EMPTY); format("/* Generated by Clojure */~%~%"); format("package ~A;~%", ns); format("import clojure.lang.*;~%~%"); format("public class ~A{~%", className); PersistentVector forms = PersistentVector.EMPTY; 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.currentVal(); 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(importMap, iclassName, pkg + "." + iclassName); } IMPORTS.set(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.currentVal()); keys != null; keys = keys.rest()) { KeywordExpr k = (KeywordExpr) ((IMapEntry) keys.first()).val(); format("static Keyword ~A;~%", k.emitExpressionString()); } for(ISeq vars = RT.seq(VARS.currentVal()); vars != null; vars = vars.rest()) { TRef v = (TRef) ((IMapEntry) vars.first()).val(); format("static TRef ~A;~%", munge(v.toString())); } //todo declare static members for syms, quoted aggregates //emit nested static class/method declarations for nested fns PersistentVector fns = (PersistentVector) FNS.currentVal(); 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.currentVal()); 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.currentVal()); vars != null; vars = vars.rest()) { TRef v = (TRef) ((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.valAt(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 { PersistentVector args = PersistentVector.EMPTY; for(ISeq s = op instanceof InstanceMemberInvoker ? 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 StaticMemberInvoker) return new InvokeStaticMethodExpr((StaticMemberInvoker) op, args); else if(op instanceof InstanceMemberInvoker) return analyzeInstanceInvoke((InstanceMemberInvoker) 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(InstanceMemberInvoker sym, Expr target, PersistentVector 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; PersistentVector args; public InvokeExpr(Expr fexpr, PersistentVector 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; PersistentVector args; public InvokeConstructorExpr(ClassSymbol fexpr, PersistentVector 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 StaticMemberInvoker sym; final String resolvedClassName; final Class type; PersistentVector args; final Method method; final Class returnType; public InvokeStaticMethodExpr(StaticMemberInvoker sym, PersistentVector 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; PersistentVector args; public InvokeUntypedInstanceMemberExpr(String name, Expr target, PersistentVector 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; PersistentVector args; final Class targetType; final Field field; final Method method; final Class returnType; public InvokeInstanceMemberExpr(Class targetClass, String name, Expr target, PersistentVector 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(PersistentVector 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, PersistentVector 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, PersistentVector 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, PersistentVector 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) PersistentVector parms = PersistentVector.EMPTY; PersistentVector args = PersistentVector.EMPTY; 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))); } PersistentVector bindingInits = PersistentVector.EMPTY; //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.currentVal()); 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.currentVal()); ISeq bindings = (ISeq) RT.second(form); ISeq body = RT.rest(RT.rest(form)); PersistentVector bindingPairs = PersistentVector.EMPTY; //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(PersistentVector.create(lb, RT.cons(FN, RT.rest(bform)))); } PersistentVector bindingInits = PersistentVector.EMPTY; for(int i = 0; i < bindingPairs.count(); i++) { IPersistentArray bpair = (IPersistentArray) 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.currentVal()); PersistentVector bindingInits = PersistentVector.EMPTY; 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{ PersistentVector bindingInits; Expr body; public LetExpr(PersistentVector 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))); PersistentVector exprs = PersistentVector.EMPTY; 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 PersistentVector exprs; public AndExpr(PersistentVector 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(new Symbol("OR_TEMP")); registerLocal(tb); } PersistentVector exprs = PersistentVector.EMPTY; 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 PersistentVector exprs; final LocalBinding tb; public OrExpr(PersistentVector 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{ PersistentVector exprs = PersistentVector.EMPTY; 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{ PersistentVector exprs; public BodyExpr(PersistentVector 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{ PersistentVector closesDecls = null; if(closes != null) { closesDecls = PersistentVector.EMPTY; 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 PersistentVector reqParms = PersistentVector.EMPTY; PersistentVector 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.currentVal()); METHOD.pushThreadBinding(method); LOCAL_ENV.pushThreadBinding(LOCAL_ENV.currentVal()); 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 = PersistentVector.EMPTY; } 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.currentVal(); TRef var = null;//!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 new Symbol(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 StaticMemberInvoker) // return new HostStaticFieldExpr((StaticMemberInvoker) 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); } TRef v = lookupVar(sym); if(v != null) return new VarExpr(v, typeHint); throw new Exception("Unable to resolve symbol: " + sym.name + " in this context"); } } static TRef lookupVar(Symbol sym) throws Exception{ Module module = (Module) _CRT_MODULE.currentVal(); // Var v = module.find(sym); // if(v != null) // return v; // for(ISeq seq = RT.seq(USES.currentVal()); 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.currentVal(); KeywordExpr ke = (KeywordExpr) RT.get(keyword, keywordsMap); //! if(ke == null) //! KEYWORDS.set(RT.assoc(keyword, ke = new KeywordExpr(keyword), keywordsMap)); return ke; } private static void registerVar(TRef var) throws Exception{ IPersistentMap varsMap = (IPersistentMap) VARS.currentVal(); if(RT.get(var, varsMap) == null) VARS.set(RT.assoc(varsMap, var, var)); } private static void registerFn(FnExpr fn) throws Exception{ FNS.set(RT.cons(fn, (IPersistentCollection) FNS.currentVal())); } 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(method.fn.closes, b, b); closeOver(b, method.parent); } } static LocalBinding referenceLocal(Symbol sym) throws Exception{ LocalBinding b = (LocalBinding) RT.get(sym, LOCAL_ENV.currentVal()); if(b != null) { closeOver(b, (FnMethod) METHOD.currentVal()); } return b; } private static void registerLocal(LocalBinding b) throws Exception{ IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.currentVal(); //!LOCAL_ENV.setValue(RT.assoc(b.sym, b, localsMap)); FnMethod method = (FnMethod) METHOD.currentVal(); method.locals = (IPersistentMap) RT.assoc(method.locals, b, b); } /* (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.currentVal(); 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 StaticMemberInvoker sym; final String resolvedClassName; final Class type; final Class fieldType; public HostStaticFieldExpr(StaticMemberInvoker 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 TRef var; final String typeHint; public VarExpr(TRef var, String typeHint){ this.var = var; this.typeHint = typeHint; } public String getName(){ return munge(var.toString()); } public void emitExpression() throws Exception{ format("~A.currentVal()", 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(); } } } } //*/