美文网首页
clojure git checkout bb50ab52

clojure git checkout bb50ab52

作者: onedam | 来源:发表于2022-03-28 16:20 被阅读0次
/**
 *   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();
            }
        }
}

}
//*/



相关文章

网友评论

      本文标题:clojure git checkout bb50ab52

      本文链接:https://www.haomeiwen.com/subject/upvpjrtx.html