/**
* 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;
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 = Module.intern("clojure", "^compiler-imports");
//keyword->keywordexpr
static public Var KEYWORDS = Module.intern("clojure", "^compiler-keywords");
//var->var
static public Var VARS = Module.intern("clojure", "^compiler-vars");
//symbol->localbinding
static public Var LOCAL_ENV = Module.intern("clojure", "^compiler-local-env");
//FnFrame
static public Var METHOD = Module.intern("clojure", "^compiler-method");
//module->module
static public Var USES = Module.intern("clojure", "^compiler-uses");
//ISeq FnExprs
static public Var FNS = Module.intern("clojure", "^compiler-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);
System.out.println("此时的form: " + RT.first(form) +":" + RT.second(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)
{
//todo implement import
}
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 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 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;iitself
IPersistentMap closes = null;
String getName(){
if(name == null)
{
if(binding != null)
name = "FN__" + 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) {
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) throws Exception {
if(sym instanceof Keyword)
return registerKeyword((Keyword)sym);
else if(sym instanceof HostSymbol)
return new HostExpr((HostSymbol)sym);
else
{
String typeHint = typeHint(sym);
sym = baseSymbol(sym);
LocalBinding b = referenceLocal(sym);
if(b != null)
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){
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.hidden)
return v;
}
return null;
}
static Object macroexpand(Object x){
return x; //placeholder
}
private static KeywordExpr registerKeyword(Keyword keyword) {
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) {
IPersistentMap varsMap = (IPersistentMap) VARS.getValue();
if(RT.get(var,varsMap) == null)
VARS.setValue(RT.assoc(var, var, varsMap));
}
private static void registerFn(FnExpr fn) {
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) {
LocalBinding b = (LocalBinding) RT.get(sym, LOCAL_ENV.getValue());
if(b != null)
{
b.valueTaken = true;
closeOver(b,(FnMethod) METHOD.getValue());
}
return b;
}
private static void registerLocal(LocalBinding b) {
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;
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 class KeyParam{
public KeyParam(LocalBindingExpr b, Expr init) {
this.bindingExpression = b;
this.init = init;
kw = registerKeyword((Keyword) Symbol.intern(":" + bindingExpression.b.sym.name));
}
public KeyParam(LocalBindingExpr b) {
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);
}
}
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);
}
}
static class HostExpr extends AnExpr{
final HostSymbol sym;
public HostExpr(HostSymbol sym){
this.sym = sym;
}
public void emitExpression() throws Exception{
if(sym instanceof ClassSymbol)
format("~A.class", resolveHostClassname(((ClassSymbol) sym).className));
}
}
/*
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));
}
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 emitExpression() throws Exception{
format("~A", b.getExpr());
}
}
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());
}
}
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 {
args=new String[]{"pfeng","one", "E:\\clojure\\yanjiuclojure\\src\\lisp\\test.lisp"};
String pkg = args[0];
String classname = args[1];
LineNumberingPushbackReader[] rs = new LineNumberingPushbackReader[args.length - 2];
String ret;
try
{
for(int i=0;i