gopy/bind/gen_func.go

290 lines
6.4 KiB
Go

// Copyright 2019 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bind
import (
"fmt"
"strings"
)
func (g *pybindGen) genFuncSig(sym *symbol, fsym Func) {
isMethod := (sym != nil)
switch {
case isMethod:
mnm := sym.goname + "_" + fsym.GoName()
g.gofile.Printf("\n//export %s\n", mnm)
g.gofile.Printf("func %s(", mnm)
g.pybuild.Printf("mod.add_function('%s', ", mnm)
g.pywrap.Printf("def %s(", fsym.GoName())
default:
g.gofile.Printf("\n//export %s\n", fsym.GoName())
g.gofile.Printf("func %s(", fsym.GoName())
g.pybuild.Printf("mod.add_function('%s', ", fsym.GoName())
g.pywrap.Printf("def %s(", fsym.GoName())
}
sig := fsym.sig
args := sig.Params()
res := sig.Results()
nargs := 0
nres := 0
goArgs := []string{}
pyArgs := []string{}
wpArgs := []string{}
if isMethod {
goArgs = append(goArgs, "_handle CGoHandle")
pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '_handle')", PyHandle))
wpArgs = append(wpArgs, "self")
}
nargs = len(args)
for i := 0; i < nargs; i++ {
arg := args[i]
sarg := g.pkg.syms.symtype(arg.GoType())
if sarg == nil {
panic(fmt.Errorf(
"gopy: could not find symbol for %q",
arg.Name(),
))
}
goArgs = append(goArgs, fmt.Sprintf("%s %s", arg.Name(), sarg.cgoname))
pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", sarg.cpyname, arg.Name()))
wpArgs = append(wpArgs, arg.Name())
}
goRet := ""
nres = len(res)
if nres > 0 {
ret := res[0]
sret := g.pkg.syms.symtype(ret.GoType())
if sret == nil {
panic(fmt.Errorf(
"gopy: could not find symbol for %q",
ret.Name(),
))
}
// todo: check for pointer return type
g.pybuild.Printf("retval('%s')", sret.cpyname)
goRet = fmt.Sprintf("%s", sret.cgoname)
} else {
g.pybuild.Printf("None")
}
if len(goArgs) > 0 {
gstr := strings.Join(goArgs, ", ")
g.gofile.Printf("%v) %v", gstr, goRet)
pstr := strings.Join(pyArgs, ", ")
g.pybuild.Printf(", [%v])\n", pstr)
wstr := strings.Join(wpArgs, ", ")
g.pywrap.Printf("%v)", wstr)
} else {
g.gofile.Printf(") %v", goRet)
g.pybuild.Printf(", [])\n")
g.pywrap.Printf(")")
}
}
func (g *pybindGen) genFunc(o Func) {
g.genFuncSig(nil, o)
g.genFuncBody(nil, o)
}
func (g *pybindGen) genMethod(s Struct, o Func) {
g.genFuncSig(s.sym, o)
g.genFuncBody(s.sym, o)
}
func (g *pybindGen) genIfcMethod(ifc Interface, o Func) {
g.genFuncSig(ifc.sym, o)
g.genFuncBody(ifc.sym, o)
}
func (g *pybindGen) genFuncBody(sym *symbol, fsym Func) {
isMethod := (sym != nil)
isIface := false
symNm := ""
if isMethod {
symNm = sym.gofmt()
isIface = sym.isInterface()
if !isIface {
symNm = "*" + symNm
}
}
pkgname := fsym.Package().Name()
sig := fsym.Signature()
res := sig.Results()
args := sig.Params()
nres := len(res)
if nres > 2 {
panic(fmt.Errorf("gopy: function/method with more than 2 results not supported! (%s)", fsym.ID()))
}
if nres == 2 && !fsym.err {
panic(fmt.Errorf("gopy: function/method with 2 results must have error as second result! (%s)", fsym.ID()))
}
rvIsErr := false // set to true if the main return is an error
if nres == 1 {
ret := res[0]
if isErrorType(ret.GoType()) {
rvIsErr = true
}
}
g.pywrap.Printf(":\n")
g.pywrap.Indent()
g.gofile.Printf(" {\n")
g.gofile.Indent()
if isMethod {
g.gofile.Printf(
`vifc, err := varHand.varFmHandleTry(_handle, "%s")
if err != nil {
`, symNm)
g.gofile.Indent()
if nres > 0 {
ret := res[0]
if ret.sym.go2py != "" {
g.gofile.Printf("return %s(%s)\n", ret.sym.go2py, ret.sym.zval)
} else {
g.gofile.Printf("return %s\n", ret.sym.zval)
}
} else {
g.gofile.Printf("return\n")
}
g.gofile.Outdent()
g.gofile.Printf("}\n")
} else if rvIsErr {
g.gofile.Printf("var err error\n")
}
// pywrap output
mnm := fsym.GoName()
if isMethod {
mnm = sym.goname + "_" + mnm
}
rvHasHandle := false
if nres > 0 {
ret := res[0]
if !rvIsErr && ret.sym.hasHandle() {
rvHasHandle = true
g.pywrap.Printf("return %s(handle=_%s.%s(", ret.sym.nonPointerName(), pkgname, mnm)
} else {
g.pywrap.Printf("return _%s.%s(", pkgname, mnm)
}
} else {
g.pywrap.Printf("_%s.%s(", pkgname, mnm)
}
callArgs := []string{}
wrapArgs := []string{}
if isMethod {
wrapArgs = append(wrapArgs, "self.handle")
}
for _, arg := range args {
na := ""
if arg.sym.py2go != "" {
if arg.sym.hasHandle() && !arg.sym.isPtrOrIface() {
na = fmt.Sprintf("*%s(%s)", arg.sym.py2go, arg.Name())
} else {
na = fmt.Sprintf("%s(%s)", arg.sym.py2go, arg.Name())
}
} else {
na = arg.Name()
}
callArgs = append(callArgs, na)
if arg.sym.hasHandle() {
wrapArgs = append(wrapArgs, fmt.Sprintf("%s.handle", arg.Name()))
} else {
wrapArgs = append(wrapArgs, arg.Name())
}
}
hasRetCvt := false
hasAddrOfTmp := false
if nres > 0 {
ret := res[0]
if rvIsErr {
g.gofile.Printf("err = ")
} else if nres == 2 {
g.gofile.Printf("cret, err := ")
} else if ret.sym.hasHandle() && !ret.sym.isPtrOrIface() {
hasAddrOfTmp = true
g.gofile.Printf("cret := ")
} else {
if ret.sym.go2py != "" {
hasRetCvt = true
g.gofile.Printf("return %s(", ret.sym.go2py)
} else {
g.gofile.Printf("return ")
}
}
}
g.pywrap.Printf("%s)", strings.Join(wrapArgs, ", "))
if rvHasHandle {
g.pywrap.Printf(")")
}
if isMethod {
g.gofile.Printf("vifc.(%s).%s(%s)", symNm, fsym.GoName(), strings.Join(callArgs, ", "))
} else {
g.gofile.Printf("%s(%s)", fsym.GoFmt(), strings.Join(callArgs, ", "))
}
if hasRetCvt {
g.gofile.Printf(")")
}
if rvIsErr || nres == 2 {
g.gofile.Printf("\n")
g.gofile.Printf("if err != nil {\n")
g.gofile.Indent()
g.gofile.Printf("C.PyErr_SetString(C.PyExc_RuntimeError, C.CString(err.Error()))\n")
if rvIsErr {
g.gofile.Printf("return C.CString(err.Error())")
}
g.gofile.Outdent()
g.gofile.Printf("}\n")
if rvIsErr {
g.gofile.Printf("return C.CString(\"\")")
} else {
ret := res[0]
if ret.sym.go2py != "" {
if ret.sym.hasHandle() && !ret.sym.isPtrOrIface() {
g.gofile.Printf("return %s(&cret)", ret.sym.go2py)
} else {
g.gofile.Printf("return %s(cret)", ret.sym.go2py)
}
} else {
g.gofile.Printf("return cret")
}
}
} else if hasAddrOfTmp {
ret := res[0]
g.gofile.Printf("\nreturn %s(&cret)", ret.sym.go2py)
}
g.gofile.Printf("\n")
g.gofile.Outdent()
g.gofile.Printf("}\n")
g.pywrap.Printf("\n")
g.pywrap.Outdent()
g.pywrap.Printf("\n")
}