package main import "bufio" import "flag" import "fmt" import "go/ast" import "go/parser" import "go/token" import "go/types" import "os" import "regexp" func Creat(filename string) *bufio.Writer { println("Creat: " + filename) z, err := os.Create(filename) if err != nil { panic(err) } return bufio.NewWriter(z) } var NonIdent *regexp.Regexp = regexp.MustCompile(`[^A-Za-z0-9_]`) func Skidify(s string) string { return NonIdent.ReplaceAllString(s, "_") } func Fmt(s string, aa ...interface{}) string { return fmt.Sprintf(s, aa...) } type BScope struct { Up *BScope Gs *ast.Scope // go Scope map Cmap map[string]string // lookup C name } func NewBScope(up *BScope, gs *ast.Scope) *BScope { return &BScope{Up: up, Gs: gs, Cmap: make(map[string]string, 4)} } func (p *BScope) Lookup(k string) (bs *BScope, obj *ast.Object, cname string) { bs = p for bs != nil { _, ok := bs.Gs.Objects[k] if ok { cname, _ = bs.Cmap[k] return } bs = bs.Up } panic("cannot Lookup: " + k) return } func (p *BScope) String() string { return Fmt("BScope(%#v) ", p.Cmap) } // Bak is the Gobak compiler for one file. type Bak struct { cw *bufio.Writer hw *bufio.Writer iw *bufio.Writer jw *bufio.Writer c func(f string, aa ...interface{}) h func(f string, aa ...interface{}) i func(f string, aa ...interface{}) j func(f string, aa ...interface{}) file *ast.File fset *token.FileSet indent int Top *BScope Builtin *BScope } func NewBak(file *ast.File, fset *token.FileSet) *Bak { z := &Bak{ file: file, fset: fset, cw: Creat("__." + file.Name.Name + ".gobak.cc"), hw: Creat("__." + file.Name.Name + ".gobak.h"), iw: Creat("__." + file.Name.Name + ".gobak.i"), jw: Creat("__." + file.Name.Name + ".gobak.j"), } z.c = func(f string, aa ...interface{}) { z.WriteSmartCw(f, aa...) } z.h = func(f string, aa ...interface{}) { z.hw.WriteString(fmt.Sprintf(f+"\n", aa...)) } z.i = func(f string, aa ...interface{}) { z.iw.WriteString(fmt.Sprintf(f+"\n", aa...)) } z.j = func(f string, aa ...interface{}) { z.jw.WriteString(fmt.Sprintf(f+"\n", aa...)) } builtinScope := ast.NewScope(nil) builtinScope.Objects = types.Universe.Objects z.Builtin = NewBScope(nil, builtinScope) z.Top = NewBScope(z.Builtin, z.file.Scope) return z } func (b *Bak) WriteIndentedCw(s string) { for i := 0; i < b.indent; i++ { s = " " + s } b.cw.WriteString(s) } func (b *Bak) WriteSmartCw(f string, aa ...interface{}) { s := Fmt(f+"\n", aa...) if len(s) > 2 && s[0] == '/' && s[1] == '/' { // It's a comment } else if len(s) > 1 && s[len(s)-2] == '{' && s[0] == '}' { b.indent-- b.WriteIndentedCw(s) b.indent++ } else if len(s) > 1 && s[len(s)-2] == '{' { b.WriteIndentedCw(s) b.indent++ } else if len(s) > 1 && s[0] == '}' { b.indent-- b.WriteIndentedCw(s) } else { b.WriteIndentedCw(s) } } func (b *Bak) Mangle(i *ast.Ident) string { return b.MangleObj(i.Obj) } func (b *Bak) MangleObj(obj *ast.Object) string { for k, v := range types.Universe.Objects { if v == obj { return k + "_Go" } } for _, v := range b.file.Scope.Objects { if v == obj { return Fmt("%s__%s", b.file.Name.Name, obj.Name) } } return Fmt("%s_", obj.Name) // Just add _ to avoid C reserved words. } func (b *Bak) Init() { guard := Skidify(b.file.Name.Name + ".gobak.h") b.h(`#include `) b.h(`#include "gobak_rt.h"`) b.h(`#ifndef %s`, guard) b.h(`#define %s`, guard) b.c(`#include "%s"`, "__."+b.file.Name.Name+".gobak.h") b.c(`#include "%s"`, "__."+b.file.Name.Name+".gobak.i") b.j(`void main__init() {`) b.Top = NewBScope(nil, b.file.Scope) for k, v := range b.file.Scope.Objects { b.Top.Cmap[k] = b.MangleObj(v) } println("TOP SCOPE: " + b.Top.String()) } func (b *Bak) Finish() { guard := Skidify(b.file.Name.Name + ".gobak.h") b.c(`#include "%s"`, "__."+b.file.Name.Name+".gobak.j") b.h(`#endif // %s`, guard) b.j(`} // end main__init()`) b.cw.Flush() b.hw.Flush() b.iw.Flush() b.jw.Flush() } func (b *Bak) Run() { b.Init() decls := b.file.Decls for i, decl := range decls { Say("Decl", i, decl) switch d := decl.(type) { case *ast.FuncDecl: b.doFuncDecl(d) case *ast.GenDecl: b.doGenDecl(d) default: Say("TODO", decl) } } } func (b *Bak) doGenDecl(decl *ast.GenDecl) { Say("TODO: doGenDecl") for i, x := range decl.Specs { Say(i, x) switch xx := x.(type) { case *ast.ValueSpec: b.doValueSpec(xx) default: Say("TODO", x) panic(x) } } } func (b *Bak) doValueSpec(p *ast.ValueSpec) { Say("doValueSpec: Type", p.Type) for i, x := range p.Names { Say("doValueSpec: Names !", i, x) } for i, x := range p.Values { Say("doValueSpec: Values !", i, x) } Say(p) if p.Type == nil { // TODO const panic(0) } // do var for i, name := range p.Names { // TODO: this BScope thing needs to handle consts, types, imports?... cname := b.Mangle(name) ctype := b.Mangle(p.Type.(*ast.Ident)) b.h("extern %s %s;", ctype, cname) b.i("%s %s;", ctype, cname) b.j("%s = %s;", cname, b.doExpr(p.Values[i])) } } func (b *Bak) doFuncDecl(d *ast.FuncDecl) { Say("doFuncDecl", d) Say("Doc", d.Doc) name := d.Name.Name ttype := d.Type body := d.Body Say("name", name) Say("ttype", ttype) Say("body", body) params := d.Type.Params results := d.Type.Results stmts := d.Body.List Say("params", params) Say("results", results) Say("stmts", stmts) ret_t := "void" if results != nil { // The *last* Go result is the C function return value. nr := len(results.List) // There may be more than on ename, but we only do this for the *last* one. ret_t = b.Mangle(results.List[nr-1].Type.(*ast.Ident)) } Say(params) p_list := b.MangledFuncParams(params, results) b.h("%s %s(%s);", ret_t, b.Mangle(d.Name), p_list) b.c("") b.c("") b.c("%s %s(%s) {", ret_t, b.Mangle(d.Name), p_list) for _, x := range stmts { b.doStmt(x) b.c("") } b.c("} // end %s()", b.Mangle(d.Name)) } var nextGen int = 1000 func Gen(prefix string) string { nextGen++ return Fmt("%s_%d", prefix, nextGen) } func (b *Bak) doStmt(stmt ast.Stmt) { b.c("//==stmt// %#v", stmt) if stmt == nil { return } switch x := stmt.(type) { case *ast.ExprStmt: b.c(b.doExpr(x.X) + ";") case *ast.ReturnStmt: z := "" for i, y := range x.Results { if i < len(x.Results)-1 { // if not the last one, set the C out argument. z += Fmt(" *_r_%d = ", i+1) } z += Fmt(" (%s),", b.doExpr(y)) } z = Chomp(z, ',') b.c(`return %s;`, z) case *ast.BlockStmt: for _, y := range x.List { b.doStmt(y) } case *ast.IfStmt: b.doStmt(x.Init) b.c("if (%s) {", b.doExpr(x.Cond)) b.doStmt(x.Body) b.c("} else {") b.doStmt(x.Else) b.c("} // end if") case *ast.ForStmt: b.doStmt(x.Init) b.c("while (%s) {", b.doExpr(x.Cond)) b.doStmt(x.Body) b.c("post:") b.doStmt(x.Post) b.c("} // end while") case *ast.IncDecStmt: b.c("%s %s;", b.doExpr(x.X), x.Tok) case *ast.AssignStmt: for k_, v_ := range x.Lhs { b.c("//LHS[%d]: %#v", k_, v_) } for k_, v_ := range x.Rhs { b.c("//RHS[%d]: %#v", k_, v_) } if x.Tok == 47 { // ":=" for _, l := range x.Lhs { b.c("int %s = 0;", b.Mangle(l.(*ast.Ident))) } } // Temp place to hold multi-return results. temp := make([]string, len(x.Lhs)) if len(x.Lhs) > 1 { for i, lhs := range x.Lhs { temp[i] = Gen("lhs") // Declare vars and init them. b.c("%s %s = 0;", CTypeOfIdent(lhs.(*ast.Ident)), temp[i]) } if len(x.Rhs) == len(x.Lhs) { // Pairwise assignments. for i, r := range x.Rhs { rhs := b.doExpr(r) b.c("%s = (%s);", temp[i], rhs) } for i, l := range x.Lhs { lhs := b.doExpr(l) b.c("%s = (%s);", lhs, temp[i]) } } else if len(x.Rhs) == 1 { // Many lhs, One rhs: must be function call on rhs. switch rhx := x.Rhs[0].(type) { case *ast.CallExpr: b.c("%s = %s;", temp[len(temp)-1], b.doCallExpr(rhx, temp)) default: Say(rhx) panic(rhx) } for i, l := range x.Lhs { lhs := b.doExpr(l) b.c("%s = (%s);", lhs, temp[i]) } } else { panic(Fmt("len(lhs)=%d len(rhs)=%d", len(x.Lhs), len(x.Rhs))) } } else if len(x.Rhs) == 1 { lhs := b.doExpr(x.Lhs[0]) rhs := b.doExpr(x.Rhs[0]) b.c("(%s) = (%s);", lhs, rhs) } else { panic(Fmt("len(lhs)=%d len(rhs)=%d", len(x.Lhs), len(x.Rhs))) } /* for i, _ := range x.Lhs { lhs := b.doExpr(x.Lhs[i]) rhs := b.doExpr(x.Rhs[i]) if x.Tok == 47 { // ":=" switch lhx := x.Lhs[i].(type) { case *ast.Ident: switch rhx := x.Rhs[i].(type) { case *ast.BasicLit: switch rhx.Kind { case token.INT: b.c("int_Go %s = 0;", lhs) } } } } b.c("(%s) = (%s);", lhs, rhs) } */ default: b.c("STATEMENT ???<%#v>??? ", stmt) } } func CTypeOfIdent(id *ast.Ident) string { switch kind := id.Obj.Kind; kind { default: Say(id.Obj) return "int" } return "int" } func (b *Bak) doCallExpr(x *ast.CallExpr, temp []string) string { b.c("//==::::: %#v", x.Fun) b.c("//==::::: %#v", x.Args) funStr := b.doExpr(x.Fun) argsStr := "" for _, y := range x.Args { argsStr += b.doExpr(y) + "," } for i, t := range temp { if i < len(temp)-1 { argsStr += Fmt(" &%s,", t) } } argsStr = Chomp(argsStr, ',') b.c("//==::::: %#v", funStr) return Fmt(`(%s)(%s)`, funStr, argsStr) } func (b *Bak) doExpr(expr ast.Expr) string { b.c("//==expr: %#v", expr) switch x := expr.(type) { case *ast.CallExpr: return b.doCallExpr(x, nil) case *ast.Ident: return b.Mangle(x) case *ast.BasicLit: return x.Value case *ast.BinaryExpr: xx := b.doExpr(x.X) yy := b.doExpr(x.Y) return Fmt(`(%s)%s(%s)`, xx, x.Op, yy) } return Fmt(" ???<%#v>??? ", expr) } func (b *Bak) MangledFuncParams(params, results *ast.FieldList) string { z := "" if params != nil { for _, p := range params.List { names := p.Names if names == nil { z += b.MangleTypeOfField(p) + " " z += "_" + "," // TODO -- does anonymous happen? } else { for _, name := range names { z += b.MangleTypeOfField(p) + " " z += b.Mangle(name) + "," } } } } if results != nil { n := 1 for i, p := range results.List { names := p.Names if names == nil { if i == len(results.List)-1 { break // Don't do the last one, it's the C return value. } z += Fmt("%s* _r_%d,", b.MangleTypeOfField(p), n) n++ } else { for j, name := range names { if i == len(results.List)-1 && j == len(names)-1 { break // Don't do the last one, it's the C return value. } // TODO: support setting return values by name. z += Fmt("%s* /*%s*/ _r_%d,", b.MangleTypeOfField(p), name, n) n++ } } } } return Chomp(z, ',') } func Chomp(s string, c byte) string { slen := len(s) if slen > 1 && s[slen-1] == c { s = s[:slen-1] } // chomp final c return s } func (b *Bak) MangleTypeOfField(f *ast.Field) string { z := "void" if f != nil { z = b.Mangle(f.Type.(*ast.Ident)) } return z } func main() { flag.Parse() fset := token.NewFileSet() files := make(map[string]*ast.File) for _, filename := range flag.Args() { tree, err := parser.ParseFile(fset, filename, nil, 0) if err != nil { panic(err) } // println("## Universe") for k, v := range types.Universe.Objects { println("k=", k, " v=", v) } println("## Unsafe") for k, v := range types.Unsafe.Objects { println("k=", k, " v=", v) } // println("#####################################") importer := types.GcImporter univ := types.Universe files[filename] = tree pkg, err := ast.NewPackage(fset, files, importer, univ) if err != nil { panic(err) } _ = pkg //n, err := ast.Fprint(os.Stdout, fset, tree, nil) //println("n=", n) } for name, tree := range files { println("#####################################") println("#######################") println("####################### ", name) println("#######################") n, err := ast.Fprint(os.Stdout, fset, tree, nil) if err != nil { panic(err) } println("n=", n) println("") b := NewBak(tree, fset) defer b.Finish() b.Run() } } func Say(a ...interface{}) { for _, x := range a { print(fmt.Sprintf(" %#v =====", x)) } }