/**
* 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<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.getValue());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.getValue());vars != null;vars = vars.rest())
{
Var v = (Var) ((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.get(c);
if(sub != null)
sb.append(sub);
else
sb.append(c);
}
return sb.toString();
}
enum C{STATEMENT,EXPRESSION,RETURN}
interface Expr{
void emitReturn() throws Exception;
void emitStatement() throws Exception;
void emitExpression() throws Exception;
String emitExpressionString() 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 String toString() {
try
{
return emitExpressionString();
}
catch (Exception e)
{
//declared exceptions are an incredibly bad idea !!!
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return e.toString();
}
}
}
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);
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
{
Expr fexpr = analyze(C.EXPRESSION, op);
PersistentArrayList args = new PersistentArrayList(4);
for(ISeq s = RT.rest(form);s != null;s=s.rest())
args = args.cons(analyze(C.EXPRESSION, macroexpand(s.first())));
if(fexpr instanceof FnExpr)
((FnExpr)fexpr).isCalledDirectly = true;
if(fexpr instanceof HostExpr)
return new InvokeHostExpr((HostExpr) fexpr, args);
else
return new InvokeExpr(fexpr, args);
}
}
static class InvokeExpr extends AnExpr{
Expr fexpr;
PersistentArrayList args;
public InvokeExpr(Expr fexpr, PersistentArrayList 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.seq(staticMethod.closes);
format("~A(~{~A~^, ~}",staticMethod.getName(),RT.seq(staticMethod.closes));
if(closes != null && argseq != null)
format(",");
format("~{~A~^, ~})", argseq);
}
else
{
format("((IFn)~A).invoke(~{~A~^, ~})", fexpr, argseq);
}
}
}
static class InvokeHostExpr extends AnExpr{
HostExpr fexpr;
PersistentArrayList args;
public InvokeHostExpr(HostExpr fexpr, PersistentArrayList args) {
this.fexpr = fexpr;
this.args = args;
}
}
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)
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<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.getValue());
ISeq bindings = (ISeq) RT.second(form);
ISeq body = RT.rest(RT.rest(form));
PersistentArrayList bindingPairs = new PersistentArrayList(4);
//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(new Tuple(lb, RT.cons(FN, RT.rest(bform))));
}
PersistentArrayList bindingInits = new PersistentArrayList(4);
for(int i=0;i<bindingPairs.count();i++)
{
Tuple bpair = (Tuple) 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.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;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)));
PersistentArrayList exprs = new PersistentArrayList(2);
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 PersistentArrayList exprs;
public AndExpr(PersistentArrayList 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(Symbol.intern("OR_TEMP"));
registerLocal(tb);
}
PersistentArrayList exprs = new PersistentArrayList(2);
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 PersistentArrayList exprs;
final LocalBinding tb;
public OrExpr(PersistentArrayList 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 {
PersistentArrayList exprs = new PersistentArrayList(4);
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{
PersistentArrayList exprs;
public BodyExpr(PersistentArrayList 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__" + 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<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();
}
}
}
}
//*/
网友评论