From 172a268e772f0cc114800dcc982f695ff74944bc Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 14 May 2024 17:22:36 +0800 Subject: [PATCH] llpyg: ready to test --- chore/_xtool/pydump/pydump.go | 2 +- chore/llpyg/llpyg.go | 117 ++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 8 deletions(-) diff --git a/chore/_xtool/pydump/pydump.go b/chore/_xtool/pydump/pydump.go index 4d97f167..f5912b29 100644 --- a/chore/_xtool/pydump/pydump.go +++ b/chore/_xtool/pydump/pydump.go @@ -46,7 +46,7 @@ func main() { sym.SetItem(c.Str("type"), cjson.String(val.Type().Name().CStr())) sym.SetItem(c.Str("name"), cjson.String(key.CStr())) sym.SetItem(c.Str("doc"), cjson.String(doc.CStr())) - if val.Callable() != 0 && false { + if val.Callable() != 0 { sig := inspect.Signature(val) sym.SetItem(c.Str("sig"), cjson.String(sig.Str().CStr())) } diff --git a/chore/llpyg/llpyg.go b/chore/llpyg/llpyg.go index 70590860..c486dc17 100644 --- a/chore/llpyg/llpyg.go +++ b/chore/llpyg/llpyg.go @@ -17,25 +17,128 @@ package main import ( + "bytes" + "encoding/json" "fmt" + "go/ast" + "go/types" + "log" "os" "os/exec" + "strings" + + "github.com/goplus/gogen" ) +type symbol struct { + Name string `json:"name"` + Type string `json:"type"` + Doc string `json:"doc"` + Sig string `json:"sig"` +} + +type module struct { + Name string `json:"name"` + Items []*symbol `json:"items"` +} + func main() { if len(os.Args) < 2 { - fmt.Fprintln(os.Stderr, "Usage: llpyg [destDir]") + fmt.Fprintln(os.Stderr, "Usage: llpyg ") return } pyLib := os.Args[1] - destDir := "." - if len(os.Args) > 2 { - destDir = os.Args[2] - } + var out bytes.Buffer pydump := exec.Command("pydump", pyLib) - pydump.Stdout = os.Stdout + pydump.Stdout = &out pydump.Run() - _ = destDir + var mod module + json.Unmarshal(out.Bytes(), &mod) + + pkg := gogen.NewPackage("", mod.Name, nil) + pkg.Import("unsafe").MarkForceUsed(pkg) // import _ "unsafe" + py := pkg.Import("github.com/goplus/llgo/py") // import "github.com/goplus/llgo/py" + + obj := py.Ref("Object").(*types.TypeName).Type().(*types.Named) + objPtr := types.NewPointer(obj) + ret := types.NewTuple(pkg.NewParam(0, "", objPtr)) + + ctx := &context{pkg, obj, objPtr, ret, py} + for _, sym := range mod.Items { + switch sym.Type { + case "builtin_function_or_method", "function": + ctx.genFunc(pkg, sym) + case "str", "float", "bool", "type", "dict", "list", "module", "int", "set": // skip + default: + t := sym.Type + if len(t) > 0 && (t[0] >= 'a' && t[0] <= 'z') { + log.Panicln("unsupport type:", sym.Type) + } + } + } + pkg.WriteTo(os.Stdout) +} + +type context struct { + pkg *gogen.Package + obj *types.Named + objPtr *types.Pointer + ret *types.Tuple + py gogen.PkgRef +} + +func (ctx *context) genFunc(pkg *gogen.Package, sym *symbol) { + name := sym.Name + if len(name) == 0 || name[0] == '_' { + return + } + params, variadic, skip := ctx.genParams(pkg, sym.Sig) + if skip { + // TODO(xsw): don't skip any func + log.Println("skip func:", name, sym.Sig) + return + } + if c := name[0]; c >= 'a' && c <= 'z' { + name = string(c+'A'-'a') + name[1:] + } + fn := pkg.NewFunc(nil, name, params, ctx.ret, variadic) + fn.SetComments(pkg, ctx.genComment(sym.Doc)) + fn.BodyStart(pkg).End() +} + +func (ctx *context) genParams(pkg *gogen.Package, sig string) (*types.Tuple, bool, bool) { + if sig == "" { + return nil, false, true + } + sig = strings.TrimSuffix(strings.TrimPrefix(sig, "("), ")") + parts := strings.Split(sig, ",") + n := len(parts) + if last := strings.TrimSpace(parts[n-1]); last == "/" { + n-- + } + objPtr := ctx.objPtr + list := make([]*types.Var, n) + for i := 0; i < n; i++ { + part := strings.TrimSpace(parts[i]) + if strings.HasPrefix(part, "*") { + if len(part) > 1 && part[1] == '*' || i != n-1 { + return nil, false, true + } + list[i] = pkg.NewParam(0, part[1:], types.NewSlice(objPtr)) + return types.NewTuple(list...), true, false + } + list[i] = pkg.NewParam(0, part, objPtr) + } + return types.NewTuple(list...), false, false +} + +func (ctx *context) genComment(doc string) *ast.CommentGroup { + lines := strings.Split(doc, "\n") + list := make([]*ast.Comment, len(lines)) + for i, line := range lines { + list[i] = &ast.Comment{Text: "// " + line} + } + return &ast.CommentGroup{List: list} }