nswrap/wrap/main.go

509 lines
11 KiB
Go
Raw Normal View History

2019-04-09 11:52:21 -04:00
package wrap
import (
"fmt"
//"reflect"
"strings"
2019-04-09 11:52:21 -04:00
"gitlab.wow.st/gmp/clast/ast"
)
var (
Debug = false
)
2019-04-09 11:52:21 -04:00
type Wrapper struct {
Interfaces map[string]Interface
Types map[string]string
cCode strings.Builder // put cGo code here
goTypes strings.Builder // put Go type declarations here
goCode strings.Builder // put Go code here
Processed map[string]bool
}
// translate C builtin types to CGo
var builtinTypes map[string]string = map[string]string{
"char": "byte",
"signed char": "byte",
"unsigned char": "byte",
"short": "int",
"unsigned short": "int",
"int": "int",
"unsigned int": "int",
"long": "int",
"unsigned long": "int",
"long long": "int",
"unsigned long long": "int",
"float": "float",
"double": "float",
"complex float": "C.complexfloat",
"complex double": "C.complexdouble",
}
var gobuiltinTypes map[string]bool = map[string]bool{
"byte": true,
"int": true,
"float": true,
}
/*
var builtinTypes map[string]string = map[string]string{
"char": "C.char",
"signed char": "C.schar",
"unsigned char": "C.uchar",
"short": "C.short",
"unsigned short": "C.ushort",
"int": "C.int",
"unsigned int": "C.uint",
"long": "C.long",
"unsigned long": "C.ulong",
"long long": "C.longlong",
"unsigned long long": "C.ulonglong",
"float": "C.float",
"double": "C.double",
"complex float": "C.complexfloat",
"complex double": "C.complexdouble",
}*/
2019-04-11 11:46:24 -04:00
func (w *Wrapper) AddType(t,class string) {
if _,ok := builtinTypes[t]; ok {
return
}
2019-04-11 11:46:24 -04:00
nt, err := goType(t,class)
if err != nil {
return
}
w.Types[nt] = t
}
2019-04-11 11:46:24 -04:00
func goType(t,class string) (string, error) {
// strip off pointers, < > and blank space
nt := strings.ReplaceAll(t,"*","")
if len(nt) > 3 && nt[0:3] == "id<" {
nt = t[3:len(t)-1]
}
2019-04-11 11:46:24 -04:00
nt = strings.ReplaceAll(nt," _Nullable","")
nt = strings.ReplaceAll(nt," _Nonnull","")
if t == "instancetype" {
return class, nil
}
nt = strings.ReplaceAll(nt,"<","__")
nt = strings.ReplaceAll(nt,">","__")
nt = strings.ReplaceAll(nt,",","_")
if x,ok := builtinTypes[nt]; ok { // do not add builtin types
return x, nil
}
if len(nt) > 5 && nt[0:5] == "enum " { // FIXME: deal with enums?
return "", fmt.Errorf("goType(): enum")
}
nt = strings.ReplaceAll(nt," ","")
if nt == "void" {
return "", fmt.Errorf("goType(): void")
}
if nt[len(nt)-1] == ')' { // skip function pointers
return "", fmt.Errorf("goType(): function pointer")
}
return nt, nil
2019-04-09 11:52:21 -04:00
}
2019-04-11 11:46:24 -04:00
func cType(t, class string) string {
nt := strings.ReplaceAll(t," _Nullable","")
nt = strings.ReplaceAll(nt," _Nonnull","")
if nt == "instancetype" {
return class + " *"
}
return nt
}
func NewWrapper(debug bool) *Wrapper {
Debug = debug
if Debug { fmt.Println("// Debug mode") }
return &Wrapper{
Interfaces: map[string]Interface{},
Types: map[string]string{},
}
}
type Property struct {
Name, Type, Type2, Attr string
}
type Parameter struct {
2019-04-11 11:46:24 -04:00
Pname, Vname, Type, Type2 string
}
type Method struct {
2019-04-11 11:46:24 -04:00
Name, Type, Type2, Class string
Parameters []Parameter
}
func (m Method) isVoid() bool {
return typeOrType2(m.Type,m.Type2) == "void"
}
2019-04-11 11:46:24 -04:00
func (m Method) isObject() bool { // FIXME
tp := typeOrType2(m.Type,m.Type2)
if len(tp) > 1 && tp[0:2] == "NS" {
return true
}
return false
}
func (m Method) cparamlist() string {
ret := []string{"void* obj"}
2019-04-11 11:46:24 -04:00
for _,p := range m.Parameters {
ret = append(ret,fmt.Sprintf("%s %s",typeOrType2(p.Type,p.Type2),p.Vname))
}
return strings.Join(ret,", ")
}
func (m Method) objcparamlist() string {
if len(m.Parameters) == 0 {
return m.Name
}
2019-04-11 11:46:24 -04:00
first := true
ret := []string{}
for _,p := range m.Parameters {
if first {
ret = append(ret,m.Name + ":" + p.Vname)
first = false
} else {
ret = append(ret, p.Pname + ":" + p.Vname)
}
}
return strings.Join(ret," ")
}
2019-04-11 11:46:24 -04:00
var goreserved map[string]bool = map[string]bool{
"range": true,
}
func (m Method) goparamlist() (string,[]string,bool) {
ret := []string{}
tps := []string{}
2019-04-11 11:46:24 -04:00
for _,p := range m.Parameters {
gname := p.Vname
tp,err := goType(typeOrType2(p.Type,p.Type2),m.Class)
if err != nil {
2019-04-11 11:46:24 -04:00
return "UNSUPPORTED TYPE", []string{},false
}
if goreserved[gname] {
gname = gname + "_"
}
tps = append(tps,tp)
2019-04-11 11:46:24 -04:00
ret = append(ret,fmt.Sprintf("%s %s",gname,tp))
}
2019-04-11 11:46:24 -04:00
return strings.Join(ret,", "),tps,true
}
func (m Method) goparamnames() string {
ret := []string{"o.ptr"}
2019-04-11 11:46:24 -04:00
for _,p := range m.Parameters {
gname := p.Vname
if goreserved[gname] {
gname = gname + "_"
}
ret = append(ret,gname)
}
return strings.Join(ret, ", ")
}
type Interface struct {
Name, Super string
Properties map[string]Property
Methods map[string]Method
}
func (i Interface) IsRoot() bool {
if i.Super == "" || i.Super == i.Name {
return true
}
return false
}
func (w *Wrapper) AddInterface(n *ast.ObjCInterfaceDecl) {
//fmt.Printf("ast.ObjCInterfaceDecl: %s\n",n.Name)
w.add(n.Name, n.Children())
}
func (w *Wrapper) AddCategory(n *ast.ObjCCategoryDecl) {
ns := n.Children()
switch x := ns[0].(type) {
case *ast.ObjCInterface:
w.add(x.Name, ns[1:])
default:
fmt.Printf("Not adding methods for %s: interface name not found in first child node of category defclaration\n",n.Name)
}
2019-04-09 11:52:21 -04:00
}
func (w *Wrapper) add(name string, ns []ast.Node) {
var i Interface
var ok bool
if i,ok = w.Interfaces[name]; !ok {
i = Interface{
Name: name,
Properties: map[string]Property{},
Methods: map[string]Method{},
}
2019-04-11 11:46:24 -04:00
w.AddType(name,name)
}
2019-04-11 11:46:24 -04:00
var avail bool
for _,c := range ns {
2019-04-09 11:52:21 -04:00
switch x := c.(type) {
case *ast.ObjCPropertyDecl:
2019-04-11 11:46:24 -04:00
//fmt.Printf("ObjCPropertyDecl: %s\n",x.Name)
p := Property{
Name: x.Name,
Type: x.Type,
Type2: x.Type2,
}
2019-04-11 11:46:24 -04:00
//_,avail = w.GetParms(x,name) // TODO
//if avail {
w.AddType(typeOrType2(x.Type,x.Type2),name)
i.Properties[p.Name] = p
//}
2019-04-09 11:52:21 -04:00
case *ast.ObjCMethodDecl:
//fmt.Printf("ObjCMethodDecl: %s\n",x.Name)
m := Method{
Name: x.Name,
Type: x.Type,
Type2: x.Type2,
2019-04-11 11:46:24 -04:00
Class: name,
}
m.Parameters, avail = w.GetParms(x,name)
if avail {
w.AddType(typeOrType2(x.Type,x.Type2),name)
i.Methods[m.Name] = m
}
case *ast.ObjCProtocol:
//fmt.Printf("ast.ObjCProtocol: %s\n",x.Name)
case *ast.ObjCInterface:
if x.Super {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
i.Super = x.Name
}
case *ast.Unknown:
//fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content)
2019-04-09 11:52:21 -04:00
default:
//fmt.Println(reflect.TypeOf(x))
}
}
w.Interfaces[i.Name] = i
}
2019-04-11 11:46:24 -04:00
type AvailAttr struct {
OS, Version string
Deprecated bool
}
func (w *Wrapper) GetParms(n *ast.ObjCMethodDecl,class string) ([]Parameter,bool) {
ret := make([]Parameter,0)
avail := make([]AvailAttr,0)
j := 0
for _,c := range n.Children() {
switch x := c.(type) {
case *ast.ParmVarDecl:
p := Parameter{
2019-04-11 11:46:24 -04:00
Pname: n.Parameters[j],
Vname: x.Name,
Type: x.Type,
Type2: x.Type2,
}
2019-04-11 11:46:24 -04:00
ret = append(ret,p)
j++
case *ast.AvailabilityAttr:
2019-04-11 11:46:24 -04:00
avail = append(avail,
AvailAttr{
OS: x.OS,
Version: x.Version,
Deprecated: x.Unknown1 != "0",
})
//fmt.Println("AvailAttr ",avail,x)
case *ast.UnavailableAttr:
avail = append(avail,
AvailAttr{ OS: "macos", Deprecated: true })
case *ast.Unknown:
if Debug { fmt.Printf("GetParms(): ast.Unknown: %s\n",x.Name) }
}
}
2019-04-11 11:46:24 -04:00
a := func() bool {
if len(avail) == 0 {
return true
}
2019-04-11 11:46:24 -04:00
for _,x := range avail {
if x.OS == "macos" && x.Deprecated == false {
return true
}
}
return false
2019-04-11 11:46:24 -04:00
}()
if !a {
return nil, false
}
2019-04-11 11:46:24 -04:00
if len(ret) != len(n.Parameters) {
fmt.Printf("Error in method declaration %s: Wrong number of ParmVarDecl children: %d parameters but %d ParmVarDecl children\n",n.Name,len(n.Parameters),len(ret))
}
2019-04-11 11:46:24 -04:00
return ret, true
}
func typeOrType2(t1, t2 string) string {
2019-04-11 11:46:24 -04:00
if t2 != "" && t2 != "id" {
return t2
}
return t1
}
func (w *Wrapper) ProcessType(gotype string) {
if gotype == "" {
return
}
if _,ok := gobuiltinTypes[gotype]; ok {
return
}
ctype := w.Types[gotype]
if _,ok := builtinTypes[ctype]; ok {
return
}
if Debug {
fmt.Printf("Processing %s (%s)\n",gotype,ctype)
}
if w.Processed[gotype] {
return
}
if i,ok := w.Interfaces[gotype]; ok {
if Debug {
fmt.Printf("Have interface for %s. super = %s\n",gotype,i.Super)
}
if i.Name != i.Super {
w.ProcessType(i.Super)
}
}
var fields string
// if there is an Interface known for this type, decide if it is the
// root object and if so give it a pointer element:
if i,ok := w.Interfaces[gotype]; ok && i.IsRoot() {
fields = "ptr unsafe.Pointer"
} else {
fields = i.Super // embed superclass
}
w.goTypes.WriteString(fmt.Sprintf(`
// %s
type %s struct { %s }
`,ctype, gotype, fields))
w.Processed[gotype] = true
}
func (w *Wrapper) ProcessTypes(tps []string) {
for _,tp := range tps {
w.ProcessType(tp)
}
}
func (w *Wrapper) Wrap(toproc string) {
w.Processed = make(map[string]bool)
w.cCode.WriteString(`/*
#cgo CFLAGS: -x objective-c
2019-04-11 11:46:24 -04:00
#cgo LDFLAGS: -framework Foundation
#import <Foundation/Foundation.h>
`)
pInterfaces := map[string]Interface {toproc: w.Interfaces[toproc]}
for k,v := range pInterfaces {
if Debug {
fmt.Printf("Interface %s: %d properties, %d methods\n",
k, len(v.Properties), len(v.Methods))
}
w.goCode.WriteString(fmt.Sprintf(`
func New%s() *%s {
ret := &%s{}
ret.ptr = unsafe.Pointer(C.New%s())
return ret
}
`,v.Name,v.Name,v.Name,v.Name))
w.cCode.WriteString(fmt.Sprintf(`
%s*
New%s() {
return [[%s alloc] init];
}
`, v.Name, v.Name, v.Name))
for _,y := range v.Properties {
if Debug {
fmt.Printf(" property: %s (%s)\n", y.Name, typeOrType2(y.Type,y.Type2))
}
}
for _,y := range v.Methods {
if Debug {
fmt.Printf(" method: %s (%s)\n", y.Name, typeOrType2(y.Type,y.Type2))
}
gname := strings.Title(y.Name)
grtype := ""
grptr := ""
2019-04-11 11:46:24 -04:00
cmtype := cType(typeOrType2(y.Type,y.Type2),v.Name)
if !y.isVoid() {
var err error
2019-04-11 11:46:24 -04:00
grtype,err = goType(cmtype,v.Name)
if err != nil {
grtype = fmt.Sprintf("// goType(%s): NOT IMPLEMENTED\n",cmtype)
2019-04-11 11:46:24 -04:00
continue
}
}
gcast := ""
gcast2 := ""
if grtype != "" {
if _,ok := w.Interfaces[grtype]; ok {
grptr = "*"
gcast = "ret := &" + grtype + "{}\n ret.ptr = unsafe.Pointer("
gcast2 = ")\n return ret"
} else {
gcast = fmt.Sprintf("return (%s)(",grtype)
gcast2 = ")"
}
}
w.ProcessType(grtype)
2019-04-11 11:46:24 -04:00
gplist, gptypes,ok := y.goparamlist()
if !ok {
continue
}
w.ProcessTypes(gptypes)
w.goCode.WriteString(fmt.Sprintf(`
func (o *%s) %s(%s) %s%s {
%sC.%s_%s(%s)%s
}`,v.Name, gname, gplist, grptr, grtype, gcast, v.Name, y.Name, y.goparamnames(),gcast2))
cret := ""
if !y.isVoid() {
cret = "return "
}
2019-04-11 11:46:24 -04:00
ccast := ""
if y.isObject() {
cret = "(id)"
}
w.cCode.WriteString(fmt.Sprintf(`
%s
%s_%s(%s) {
2019-04-11 11:46:24 -04:00
%s[%sobj %s];
}`, cmtype, v.Name, y.Name, y.cparamlist(), cret, ccast, y.objcparamlist()))
2019-04-09 11:52:21 -04:00
}
}
fmt.Println(`package main
`)
fmt.Println(w.cCode.String())
fmt.Println(`
*/
import "C"
import (
"unsafe"
)
`)
fmt.Println(w.goTypes.String())
fmt.Println(w.goCode.String())
2019-04-09 11:52:21 -04:00
}