diff options
author | Rich Hickey <richhickey@gmail.com> | 2007-08-30 22:41:50 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2007-08-30 22:41:50 +0000 |
commit | c894ed9bcc2bb8e45822d48f00b1cb8d90f8c603 (patch) | |
tree | c1c6a1b12b017751e1ee58544e5205ba90a273bc | |
parent | cb96c6d66d4e77b162359495840199cb94e0ccbf (diff) |
interim checkin
-rw-r--r-- | src/jvm/clojure/lang/BytecodeCompiler.java | 392 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 2 |
2 files changed, 346 insertions, 48 deletions
diff --git a/src/jvm/clojure/lang/BytecodeCompiler.java b/src/jvm/clojure/lang/BytecodeCompiler.java index 75080688..f5c10c4b 100644 --- a/src/jvm/clojure/lang/BytecodeCompiler.java +++ b/src/jvm/clojure/lang/BytecodeCompiler.java @@ -20,16 +20,29 @@ static Symbol RECUR = Symbol.create("recur"); static Symbol DOT = Symbol.create("."); static Symbol IF = Symbol.create("if"); static Symbol LET = Symbol.create("let"); -static Symbol NOT = Symbol.create("not"); +static Symbol DO = Symbol.create("do"); +static Symbol FN = Symbol.create("fn"); +static Symbol QUOTE = Symbol.create("quote"); +static Symbol THISFN = Symbol.create("thisfn"); +static Symbol CLASS = Symbol.create("class"); static Symbol IMPORT = Symbol.create("import"); static Symbol USE = Symbol.create("use"); -static Symbol _AMP_KEY = Symbol.create("&key"); -static Symbol _AMP_REST = Symbol.create("&rest"); +static Symbol _AMP_ = Symbol.create("&"); + +private static final int MAX_POSITIONAL_ARITY = 20; //symbol->localbinding static public DynamicVar LOCAL_ENV = DynamicVar.create(); +//vector<localbinding> +static public DynamicVar LOOP_LOCALS = DynamicVar.create(); + +//keyword->keywordexpr +static public DynamicVar KEYWORDS = DynamicVar.create(); + +//var->var +static public DynamicVar VARS = DynamicVar.create(); //FnFrame static public DynamicVar METHOD = DynamicVar.create(); @@ -37,7 +50,8 @@ static public DynamicVar METHOD = DynamicVar.create(); enum C{ STATEMENT, //value ignored EXPRESSION, //value required - RETURN //tail position relative to enclosing recur frame + RETURN, //tail position relative to enclosing recur frame + EVAL } interface Expr{ @@ -56,11 +70,25 @@ static class DefExpr implements Expr{ public Object eval() throws Exception{ return var.bindRoot(init.eval()); } + + public static Expr parse(C context, ISeq form) throws Exception{ + //(def x) or (def x initexpr) + if(form.count() > 3) + throw new Exception("Too many arguments to def"); + else if(form.count() < 2) + throw new Exception("Too few arguments to def"); + else if(!(RT.second(form) instanceof Symbol)) + throw new Exception("Second argument to def must be a Symbol"); + DynamicVar v = lookupVar((Symbol) RT.second(form)); + if(!v.sym.ns.equals(currentNS())) + throw new Exception("Can't create defs outside of current ns"); + return new DefExpr(v, analyze(C.EXPRESSION, RT.third(form), v.toString())); + } } static class VarExpr implements Expr{ final DynamicVar var; - Symbol tag; + final Symbol tag; public VarExpr(DynamicVar var, Symbol tag){ this.var = var; @@ -72,6 +100,18 @@ static class VarExpr implements Expr{ } } +static class KeywordExpr implements Expr{ + final Keyword k; + + public KeywordExpr(Keyword k){ + this.k = k; + } + + public Object eval() throws Exception{ + return k; + } +} + static abstract class LiteralExpr implements Expr{ abstract Object val(); @@ -90,7 +130,7 @@ static NilExpr NIL_EXPR = new NilExpr(); static class NumExpr extends LiteralExpr{ - Num num; + final Num num; public NumExpr(Num num){ this.num = num; @@ -102,7 +142,7 @@ static class NumExpr extends LiteralExpr{ } static class StringExpr extends LiteralExpr{ - String str; + final String str; public StringExpr(String str){ this.str = str; @@ -125,27 +165,96 @@ static class CharExpr extends LiteralExpr{ } } +static class IfExpr implements Expr{ + final Expr testExpr; + final Expr thenExpr; + final Expr elseExpr; + + + public IfExpr(Expr testExpr, Expr thenExpr, Expr elseExpr){ + this.testExpr = testExpr; + this.thenExpr = thenExpr; + this.elseExpr = elseExpr; + } + + public Object eval() throws Exception{ + if(testExpr.eval() != null) + return thenExpr.eval(); + return elseExpr.eval(); + } + + public static Expr parse(C context, ISeq form) throws Exception{ + //(if test then) or (if test then else) + if(form.count() > 4) + throw new Exception("Too many arguments to if"); + else if(form.count() < 3) + throw new Exception("Too few arguments to if"); + return new IfExpr(analyze(C.EXPRESSION, RT.second(form)), + analyze(context, RT.third(form)), + analyze(context, RT.fourth(form))); + } +} + static class FnExpr implements Expr{ IPersistentCollection methods; //if there is a variadic overload (there can only be one) it is stored here FnMethod variadicMethod = null; - //acts both as a flag to indicate a let-bound fn - //and a path to the binding in order to detect if its value is ever taken - LocalBinding binding; String name = null; - boolean isCalledDirectly = false; //localbinding->itself IPersistentMap closes = null; - boolean willBeStaticMethod(){ - return variadicMethod == null - && methods.count() == 1 - && - ( - isCalledDirectly - || - (binding != null && !binding.valueTaken) - ); + static Expr parse(C context, ISeq form) throws Exception{ + try + { + DynamicVar.pushThreadBindings( + RT.map( + KEYWORDS, null, + VARS, null)); //(fn [args] body...) or (fn ([args] body...) ([args2] body2...) ...) + //turn former into latter + if(RT.second(form) instanceof IPersistentArray) + 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; + } + finally + { + + } + registerFn(fn); + return fn; } public Object eval() throws Exception{ @@ -156,16 +265,77 @@ static class FnExpr implements Expr{ } } +enum PSTATE{ + REQ, REST, 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.get()); + DynamicVar.pushThreadBindings( + RT.map( + METHOD, method, + LOCAL_ENV, LOCAL_ENV.get())); + PSTATE state = PSTATE.REQ; + for(ISeq ps = parms; ps != null; ps = ps.rest()) + { + Object p = ps.first(); + if(p == _AMP_) + { + if(state == PSTATE.REQ) + state = PSTATE.REST; + 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; + + default: + throw new Exception("Unexpected parameter"); + } + } + } + if(method.reqParms.count() > MAX_POSITIONAL_ARITY) + throw new Exception("Can't specify more than " + MAX_POSITIONAL_ARITY + " params"); + method.body = analyze(C.RETURN, RT.cons(DO, body)); + return method; + } + finally + { + DynamicVar.popThreadBindings(); + } +} + +static LocalBinding createParamBinding(Symbol p) throws Exception{ + LocalBinding b = new LocalBinding(p, tagOf(p)); + registerLocal(b); + return b; +} + static class FnMethod{ //when closures are defined inside other closures, //the closed over locals need to be propagated to the enclosing fn - FnMethod parent = null; + final FnMethod parent; //localbinding->localbinding IPersistentMap locals = null; //localbinding->localbinding PersistentVector reqParms = PersistentVector.EMPTY; PersistentVector keyParms = null; - LocalBindingExpr restParm = null; + LocalBinding restParm = null; Expr body = null; FnExpr fn; @@ -181,21 +351,12 @@ static class FnMethod{ static class LocalBinding{ final Symbol sym; - boolean isClosed = false; - Symbol tag; - public boolean valueTaken = false; - FnExpr letfn = null; - + final Symbol tag; public LocalBinding(Symbol sym, Symbol tag){ this.sym = sym; this.tag = tag; } - - boolean bindsToStaticFn(){ - return letfn != null && letfn.willBeStaticMethod(); - } - } static class LocalBindingExpr implements Expr{ @@ -212,68 +373,205 @@ static class LocalBindingExpr implements Expr{ } } +static class BodyExpr implements Expr{ + PersistentVector exprs; + + public BodyExpr(PersistentVector exprs){ + this.exprs = exprs; + } + + static Expr parse(C context, ISeq forms) throws Exception{ + PersistentVector exprs = PersistentVector.EMPTY; + for(; forms != null; forms = forms.rest()) + { + Expr e = (context == C.STATEMENT || forms.rest() != null) ? + analyze(C.STATEMENT, forms.first()) + : + analyze(context, forms.first()); + exprs = exprs.cons(e); + } + return new BodyExpr(exprs); + } + + public Object eval() throws Exception{ + Object ret = null; + for(Object o : exprs) + { + Expr e = (Expr) o; + ret = e.eval(); + } + return ret; + } +} + +static class BindingInit{ + LocalBinding binding; + Expr init; + + public BindingInit(LocalBinding binding, Expr init){ + this.binding = binding; + this.init = init; + } +} + +static class LetExpr implements Expr{ + final PersistentVector bindingInits; + final Expr body; + final boolean isLoop; + + public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop){ + this.bindingInits = bindingInits; + this.body = body; + this.isLoop = isLoop; + } + + static Expr parse(C context, ISeq form, boolean isLoop) throws Exception{ + //(let [var val var2 val2 ...] body...) + ISeq bindings = (ISeq) RT.second(form); + + ISeq body = RT.rest(RT.rest(form)); + + if(context == C.EVAL) + return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form))); + + IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.get()); + if(isLoop) + dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null); + + try + { + DynamicVar.pushThreadBindings(dynamicBindings); + + PersistentVector bindingInits = PersistentVector.EMPTY; + PersistentVector loopLocals = PersistentVector.EMPTY; + for(ISeq bs = bindings; bs != null; bs = RT.rest(RT.rest(bs))) + { + if(!(RT.first(bs) instanceof Symbol)) + throw new IllegalArgumentException("Bad binding form, expected symbol, got: " + RT.first(bs)); + Symbol sym = (Symbol) RT.first(bs); + if(bs.rest() == null) + throw new IllegalArgumentException("Bad binding form, expected expression following: " + sym); + LocalBinding lb = new LocalBinding(sym, tagOf(sym)); + BindingInit bi = new BindingInit(lb, analyze(C.EXPRESSION, RT.second(bs))); + bindingInits = bindingInits.cons(bi); + + //sequential enhancement of env + registerLocal(lb); + + if(isLoop) + loopLocals = loopLocals.cons(lb); + } + if(isLoop) + LOOP_LOCALS.set(loopLocals); + return new LetExpr(bindingInits, BodyExpr.parse(isLoop ? C.RETURN : context, body), isLoop); + } + finally + { + DynamicVar.popThreadBindings(); + } + } + + public Object eval() throws Exception{ + throw new UnsupportedOperationException("Can't eval let"); + } +} + +private static void registerLocal(LocalBinding b) throws Exception{ + IPersistentMap localsMap = (IPersistentMap) LOCAL_ENV.get(); + LOCAL_ENV.set(RT.assoc(b.sym, b, localsMap)); + FnMethod method = (FnMethod) METHOD.get(); + method.locals = (IPersistentMap) RT.assoc(method.locals, b, b); +} + private static Expr analyze(C context, Object form) throws Exception{ return analyze(context, form, null); } private static Expr analyze(C context, Object form, String name) throws Exception{ + //todo macro expansion if(form == null) return NIL_EXPR; + Class fclass = form.getClass(); + if(fclass == Symbol.class) + return analyzeSymbol((Symbol) form); + else if(fclass == Keyword.class) + return registerKeyword((Keyword) form); else if(form instanceof Num) return new NumExpr((Num) form); - else if(form instanceof String) + else if(fclass == String.class) return new StringExpr((String) form); - else if(form instanceof Character) + else if(fclass == Character.class) return new CharExpr((Character) form); - else if(form instanceof Symbol) - return analyzeSymbol((Symbol) form, false); -// else if(form instanceof ISeq) -// return analyzeSeq(context, (ISeq) form); + else if(form instanceof ISeq) + return analyzeSeq(context, (ISeq) form); // else throw new UnsupportedOperationException(); } +private static Expr analyzeSeq(C context, ISeq form) throws Exception{ + Object op = RT.first(form); + if(op == DEF) + return DefExpr.parse(context, form); + else if(op == IF) + return IfExpr.parse(context, form); + else if(op == DO) + return BodyExpr.parse(context, form.rest()); + else if(op == LET) + return LetExpr.parse(context, form, false); + else if(op == LOOP) + return LetExpr.parse(context, form, true); +} + Object eval(Object form) throws Exception{ Expr expr = analyze(C.EXPRESSION, form); return expr.eval(); } -private static Expr analyzeSymbol(Symbol sym, boolean inFnPosition) throws Exception{ +private static KeywordExpr registerKeyword(Keyword keyword){ + IPersistentMap keywordsMap = (IPersistentMap) KEYWORDS.get(); + if(keywordsMap == null) //not bound, no fn context + return new KeywordExpr(keyword); + KeywordExpr ke = (KeywordExpr) RT.get(keyword, keywordsMap); + if(ke == null) + KEYWORDS.set(RT.assoc(keyword, ke = new KeywordExpr(keyword), keywordsMap)); + return ke; +} + +private static Expr analyzeSymbol(Symbol sym) throws Exception{ Symbol tag = tagOf(sym); if(sym.ns == null) //ns-qualified syms are always Vars { LocalBinding b = referenceLocal(sym); if(b != null) - { - if(!inFnPosition) - b.valueTaken = true; return new LocalBindingExpr(b, tag); - } } DynamicVar v = lookupVar(sym); if(v != null) return new VarExpr(v, tag); - throw new Exception("Unable to resolve symbol: " + sym.name + " in this context"); + throw new Exception("Unable to resolve symbol: " + sym + " in this context"); } static DynamicVar lookupVar(Symbol sym) throws Exception{ - //qualified vars must already exist + //note - qualified vars must already exist if(sym.ns != null) return DynamicVar.find(sym); IPersistentMap uses = (IPersistentMap) RT.USES.get(); DynamicVar var = (DynamicVar) uses.valAt(sym); if(var != null) return var; - String ns = (String) RT.CURRENT_NS.get(); + String ns = currentNS(); return DynamicVar.intern(Symbol.intern(ns, sym.name)); } +private static String currentNS(){ + return (String) RT.CURRENT_NS.get(); +} + 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); } diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index f450e021..bba2c96b 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -235,7 +235,7 @@ static String munge(String name){ } enum C{ - STATEMENT, EXPRESSION, RETURN//, FN + STATEMENT, EXPRESSION, RETURN, EVAL } interface Expr{ |