diff options
author | Rich Hickey <richhickey@gmail.com> | 2007-08-22 21:46:52 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2007-08-22 21:46:52 +0000 |
commit | 5d1806f75ee05769a7683afc806c389091667e04 (patch) | |
tree | f1917351540eee94a70f029b2279d6d884a3caf1 | |
parent | f12a76d3063e4a024b1e67a63c4f9fc12d168088 (diff) |
interim checkin, starting bytecode compiler
-rw-r--r-- | clojure.iml | 1 | ||||
-rw-r--r-- | src/jvm/clojure/lang/BytecodeCompiler.java | 298 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 4 | ||||
-rw-r--r-- | src/jvm/clojure/lang/DynamicClassLoader.java | 43 | ||||
-rw-r--r-- | src/jvm/clojure/lang/DynamicVar.java | 4 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 7 |
6 files changed, 353 insertions, 4 deletions
diff --git a/clojure.iml b/clojure.iml index 9b0f11d5..b20440f2 100644 --- a/clojure.iml +++ b/clojure.iml @@ -11,6 +11,7 @@ <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="library" name="asm-2.2.1" level="application" /> <orderEntry type="library" name="antlr-3" level="application" /> + <orderEntry type="library" name="asm-3.0" level="application" /> <orderEntryProperties /> </component> <component name="VcsManagerConfiguration"> diff --git a/src/jvm/clojure/lang/BytecodeCompiler.java b/src/jvm/clojure/lang/BytecodeCompiler.java new file mode 100644 index 00000000..75080688 --- /dev/null +++ b/src/jvm/clojure/lang/BytecodeCompiler.java @@ -0,0 +1,298 @@ +/** + * 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 Aug 21, 2007 */ + +package clojure.lang; + +public class BytecodeCompiler{ + +static Symbol DEF = Symbol.create("def"); +static Symbol LOOP = Symbol.create("loop"); +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 IMPORT = Symbol.create("import"); +static Symbol USE = Symbol.create("use"); +static Symbol _AMP_KEY = Symbol.create("&key"); +static Symbol _AMP_REST = Symbol.create("&rest"); + +//symbol->localbinding +static public DynamicVar LOCAL_ENV = DynamicVar.create(); + + +//FnFrame +static public DynamicVar METHOD = DynamicVar.create(); + +enum C{ + STATEMENT, //value ignored + EXPRESSION, //value required + RETURN //tail position relative to enclosing recur frame +} + +interface Expr{ + Object eval() throws Exception; +} + +static class DefExpr implements Expr{ + final DynamicVar var; + final Expr init; + + public DefExpr(DynamicVar var, Expr init){ + this.var = var; + this.init = init; + } + + public Object eval() throws Exception{ + return var.bindRoot(init.eval()); + } +} + +static class VarExpr implements Expr{ + final DynamicVar var; + Symbol tag; + + public VarExpr(DynamicVar var, Symbol tag){ + this.var = var; + this.tag = tag; + } + + public Object eval() throws Exception{ + return var.get(); + } +} + +static abstract class LiteralExpr implements Expr{ + abstract Object val(); + + public Object eval(){ + return val(); + } +} + +static class NilExpr extends LiteralExpr{ + Object val(){ + return null; + } +} + +static NilExpr NIL_EXPR = new NilExpr(); + + +static class NumExpr extends LiteralExpr{ + Num num; + + public NumExpr(Num num){ + this.num = num; + } + + Object val(){ + return num; + } +} + +static class StringExpr extends LiteralExpr{ + String str; + + public StringExpr(String str){ + this.str = str; + } + + Object val(){ + return str; + } +} + +static class CharExpr extends LiteralExpr{ + final Character ch; + + public CharExpr(Character ch){ + this.ch = ch; + } + + Object val(){ + return ch; + } +} + +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) + ); + } + + public Object eval() throws Exception{ + //todo - implement + //ask the DynamicClassLoader (found through Var?) to load our class + //create instance through Class object newInstance() + return null; + } +} + +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; + //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; + } +} + +static class LocalBinding{ + final Symbol sym; + boolean isClosed = false; + Symbol tag; + public boolean valueTaken = false; + FnExpr letfn = null; + + + public LocalBinding(Symbol sym, Symbol tag){ + this.sym = sym; + this.tag = tag; + } + + boolean bindsToStaticFn(){ + return letfn != null && letfn.willBeStaticMethod(); + } + +} + +static class LocalBindingExpr implements Expr{ + final LocalBinding b; + final Symbol tag; + + public LocalBindingExpr(LocalBinding b, Symbol tag){ + this.b = b; + this.tag = tag; + } + + public Object eval() throws Exception{ + throw new UnsupportedOperationException("Can't eval locals"); + } +} + +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{ + if(form == null) + return NIL_EXPR; + else if(form instanceof Num) + return new NumExpr((Num) form); + else if(form instanceof String) + return new StringExpr((String) form); + else if(form instanceof Character) + 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 + throw new UnsupportedOperationException(); +} + +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{ + 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"); + +} + +static DynamicVar lookupVar(Symbol sym) throws Exception{ + //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(); + return DynamicVar.intern(Symbol.intern(ns, sym.name)); +} + +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.get()); + if(b != null) + { + closeOver(b, (FnMethod) METHOD.get()); + } + return b; +} + +private static Symbol tagOf(Symbol sym){ + if(sym.meta() != null) + return (Symbol) sym.meta().valAt(RT.TAG_KEY); + return null; +} + +} diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index e9c4fe87..f450e021 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//, FN } interface Expr{ @@ -1962,7 +1962,7 @@ static class DefExpr extends AnExpr{ } public void emitExpression() throws Exception{ - format("~A.bind(~A)", var.getName(), init.emitExpressionString()); + format("~A.bindRoot(~A)", var.getName(), init.emitExpressionString()); } } diff --git a/src/jvm/clojure/lang/DynamicClassLoader.java b/src/jvm/clojure/lang/DynamicClassLoader.java new file mode 100644 index 00000000..237b9ca0 --- /dev/null +++ b/src/jvm/clojure/lang/DynamicClassLoader.java @@ -0,0 +1,43 @@ +/** + * 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 Aug 21, 2007 */ + +package clojure.lang; + +import java.util.HashMap; + +//todo: possibly extend URLClassLoader? + +public class DynamicClassLoader extends ClassLoader{ + +HashMap<String, byte[]> map = new HashMap<String, byte[]>(); + +public DynamicClassLoader(){ + super(); +} + +public DynamicClassLoader(ClassLoader parent){ + super(parent); +} + +public void addBytecode(String className, byte[] bytes){ + if(map.containsKey(className)) + throw new IllegalStateException(String.format("Class %s already present", className)); + map.put(className, bytes); +} + +protected Class<?> findClass(String name) throws ClassNotFoundException{ + byte[] bytes = map.get(name); + if(bytes != null) + return defineClass(name, bytes, 0, bytes.length); + throw new ClassNotFoundException(name); +} +} diff --git a/src/jvm/clojure/lang/DynamicVar.java b/src/jvm/clojure/lang/DynamicVar.java index 53b26c2d..748040b1 100644 --- a/src/jvm/clojure/lang/DynamicVar.java +++ b/src/jvm/clojure/lang/DynamicVar.java @@ -135,9 +135,9 @@ final public boolean hasRoot(){ return root != dvals; } -public DynamicVar bindRoot(Object root){ +public Object bindRoot(Object root){ this.root = root; - return this; + return root; } public void unbindRoot(){ diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 9098124f..94451504 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -26,6 +26,13 @@ final static Keyword TAG_KEY = new Keyword("clojure", "tag"); final static public DynamicVar CURRENT_MODULE = DynamicVar.intern(Symbol.create("clojure", "current-module"), Module.findOrCreateModule("clojure/user")); +//string +final static DynamicVar CURRENT_NS = DynamicVar.intern(Symbol.create("clojure", "current-ns"), "clojure-user"); +//simple-symbol->var +final static DynamicVar USES = DynamicVar.intern(Symbol.create("clojure", "uses"), PersistentHashMap.EMPTY); +//simple-symbol->fully-qualified-class-name-symbol +final static DynamicVar IMPORTS = DynamicVar.intern(Symbol.create("clojure", "imports"), PersistentHashMap.EMPTY); + static public final Object[] EMPTY_ARRAY = new Object[]{}; static public final Character[] chars; static AtomicInteger id = new AtomicInteger(1); |