summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2007-08-30 22:41:50 +0000
committerRich Hickey <richhickey@gmail.com>2007-08-30 22:41:50 +0000
commitc894ed9bcc2bb8e45822d48f00b1cb8d90f8c603 (patch)
treec1c6a1b12b017751e1ee58544e5205ba90a273bc
parentcb96c6d66d4e77b162359495840199cb94e0ccbf (diff)
interim checkin
-rw-r--r--src/jvm/clojure/lang/BytecodeCompiler.java392
-rw-r--r--src/jvm/clojure/lang/Compiler.java2
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{