2019-04-09 11:52:21 -04:00
package wrap
import (
"fmt"
2019-04-26 22:44:30 -04:00
"os"
"path"
2019-04-29 16:14:45 -04:00
"strconv"
2019-04-09 23:19:49 -04:00
"strings"
2019-04-09 11:52:21 -04:00
2019-04-26 15:34:36 -04:00
"gitlab.wow.st/gmp/nswrap/ast"
"gitlab.wow.st/gmp/nswrap/types"
2019-04-09 11:52:21 -04:00
)
2019-04-10 14:00:48 -04:00
var (
Debug = false
)
2019-04-09 11:52:21 -04:00
type Wrapper struct {
2019-04-26 22:44:30 -04:00
Package string
2019-04-09 23:19:49 -04:00
Interfaces map [ string ] Interface
2019-04-23 15:48:11 -04:00
2019-04-26 14:08:43 -04:00
cCode strings . Builder // put cGo code here
goTypes strings . Builder // put Go type declarations here
goCode strings . Builder // put Go code here
goHelpers strings . Builder // put Go helper functions here
2019-04-10 14:00:48 -04:00
Processed map [ string ] bool
2019-04-29 16:14:45 -04:00
VaArgs int
2019-04-09 11:52:21 -04:00
}
2019-04-11 11:46:24 -04:00
func NewWrapper ( debug bool ) * Wrapper {
Debug = debug
if Debug { fmt . Println ( "// Debug mode" ) }
2019-04-26 15:34:36 -04:00
ret := & Wrapper {
2019-04-09 23:19:49 -04:00
Interfaces : map [ string ] Interface { } ,
2019-04-23 15:48:11 -04:00
Processed : map [ string ] bool { } ,
2019-04-29 16:14:45 -04:00
VaArgs : 16 ,
2019-04-09 23:19:49 -04:00
}
2019-04-26 15:34:36 -04:00
ret . cCode . WriteString ( ` / *
# cgo CFLAGS : - x objective - c
# cgo LDFLAGS : - framework Foundation
2019-04-29 11:46:48 -04:00
` )
ret . goTypes . WriteString ( `
2019-04-30 09:14:25 -04:00
type Id struct { }
func ( o * Id ) Ptr ( ) unsafe . Pointer { return unsafe . Pointer ( o ) }
2019-04-26 15:34:36 -04:00
` )
return ret
}
func ( w * Wrapper ) Import ( ss [ ] string ) {
for _ , s := range ss {
w . cCode . WriteString ( `
# import "` + s + `"
` )
}
}
func ( w * Wrapper ) SysImport ( ss [ ] string ) {
for _ , s := range ss {
2019-04-26 15:41:29 -04:00
w . cCode . WriteString ( "\n#import <" + s + ">\n" )
}
}
func ( w * Wrapper ) Pragma ( ss [ ] string ) {
for _ , s := range ss {
w . cCode . WriteString ( "\n#pragma " + s + "\n" )
2019-04-26 15:34:36 -04:00
}
2019-04-09 23:19:49 -04:00
}
type Property struct {
2019-04-26 15:08:44 -04:00
Name , Attr string
Type * types . Type
2019-04-09 23:19:49 -04:00
}
type Parameter struct {
2019-04-26 15:08:44 -04:00
Pname , Vname string
Type * types . Type
2019-04-09 23:19:49 -04:00
}
type Method struct {
2019-04-26 15:08:44 -04:00
Name , Class string
Type * types . Type
2019-04-11 17:00:36 -04:00
ClassMethod bool
2019-04-11 11:46:24 -04:00
Parameters [ ] Parameter
2019-04-09 23:19:49 -04:00
}
2019-04-26 15:08:44 -04:00
//isVoid() returns true if the method has no return value.
2019-04-10 14:00:48 -04:00
func ( m Method ) isVoid ( ) bool {
2019-04-26 15:08:44 -04:00
return m . Type . CType ( ) == "void"
2019-04-10 14:00:48 -04:00
}
2019-04-26 14:08:43 -04:00
//hasFunctionParam() returns true if a method has a function as a parameter.
func ( m Method ) hasFunctionParam ( ) bool {
for _ , p := range m . Parameters {
2019-04-26 22:44:30 -04:00
if p . Type . IsFunction ( ) {
2019-04-26 14:08:43 -04:00
return true
}
}
return false
}
2019-04-11 13:09:02 -04:00
func ( w Wrapper ) cparamlist ( m Method ) string {
2019-04-11 17:00:36 -04:00
ret := make ( [ ] string , 0 )
if ! m . ClassMethod {
ret = append ( ret , "void* obj" )
}
2019-04-11 11:46:24 -04:00
for _ , p := range m . Parameters {
2019-04-26 14:08:43 -04:00
var tp string
2019-04-29 16:14:45 -04:00
if p . Type . Node . IsPointer ( ) || p . Type . Variadic {
2019-04-11 13:09:02 -04:00
tp = "void*"
2019-04-26 14:08:43 -04:00
} else {
2019-04-26 15:08:44 -04:00
tp = p . Type . CType ( )
2019-04-11 13:09:02 -04:00
}
ret = append ( ret , fmt . Sprintf ( "%s %s" , tp , p . Vname ) )
2019-04-10 14:00:48 -04:00
}
return strings . Join ( ret , ", " )
}
2019-04-23 15:48:11 -04:00
func ( w Wrapper ) objcparamlist ( m Method ) string {
2019-04-10 14:00:48 -04:00
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 {
2019-04-29 16:14:45 -04:00
if first && ! p . Type . Variadic {
2019-04-11 11:46:24 -04:00
ret = append ( ret , m . Name + ":" + p . Vname )
first = false
} else {
2019-04-29 16:14:45 -04:00
if p . Type . Variadic {
str := [ ] string { m . Name + ":arr[0]" }
for i := 1 ; i < w . VaArgs ; i ++ {
str = append ( str , "arr[" + strconv . Itoa ( i ) + "]" )
}
str = append ( str , "nil" )
ret = append ( ret , strings . Join ( str , ", " ) )
} else {
ret = append ( ret , p . Pname + ":" + p . Vname )
}
2019-04-11 11:46:24 -04:00
}
2019-04-10 14:00:48 -04:00
}
return strings . Join ( ret , " " )
}
2019-04-23 15:48:11 -04:00
//goreserved is a map telling whether a word is a go reserved word that is not
//also a C/Objective-C reserved word.
2019-04-11 11:46:24 -04:00
var goreserved map [ string ] bool = map [ string ] bool {
"range" : true ,
}
2019-04-26 10:56:00 -04:00
func ( m * Method ) gpntp ( ) ( [ ] string , [ ] * types . Type , string ) {
2019-04-26 09:04:53 -04:00
ns := [ ] string { }
tps := [ ] * types . Type { }
2019-04-26 10:56:00 -04:00
if ! m . ClassMethod {
ns = append ( ns , "o" )
tps = append ( tps , types . NewTypeFromString ( m . Class + "*" , "" ) )
}
2019-04-26 09:04:53 -04:00
for _ , p := range m . Parameters {
gname := p . Vname
if goreserved [ gname ] {
gname = gname + "_"
}
ns = append ( ns , gname )
2019-04-26 15:08:44 -04:00
tps = append ( tps , p . Type )
2019-04-26 09:04:53 -04:00
}
2019-04-26 10:56:00 -04:00
ret := [ ] string { }
i := 0
if ! m . ClassMethod { i = 1 }
for ; i < len ( ns ) ; i ++ {
2019-04-26 14:08:43 -04:00
gt := tps [ i ] . GoType ( )
if gt == "*Void" {
gt = "unsafe.Pointer"
}
2019-04-29 16:14:45 -04:00
if tps [ i ] . Variadic {
gt = "..." + gt
ns [ i ] = ns [ i ] + "s"
}
2019-04-26 14:08:43 -04:00
ret = append ( ret , ns [ i ] + " " + gt )
2019-04-26 10:56:00 -04:00
}
return ns , tps , strings . Join ( ret , ", " )
2019-04-26 09:04:53 -04:00
}
2019-04-10 14:00:48 -04:00
2019-04-09 23:19:49 -04:00
type Interface struct {
2019-04-26 15:08:44 -04:00
Name string
2019-04-09 23:19:49 -04:00
Properties map [ string ] Property
Methods map [ string ] Method
}
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 ( )
2019-04-23 15:48:11 -04:00
if len ( ns ) > 0 {
switch x := ns [ 0 ] . ( type ) {
case * ast . ObjCInterface :
w . add ( x . Name , ns [ 1 : ] )
return
}
2019-04-09 23:19:49 -04:00
}
2019-04-23 15:48:11 -04:00
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
}
2019-04-09 23:19:49 -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-26 10:07:34 -04:00
tp := types . NewTypeFromString ( name , name )
2019-04-26 14:08:43 -04:00
types . Wrap ( tp . GoType ( ) )
2019-04-11 11:46:24 -04:00
var avail bool
2019-04-09 23:19:49 -04:00
for _ , c := range ns {
2019-04-09 11:52:21 -04:00
switch x := c . ( type ) {
2019-04-09 23:19:49 -04:00
case * ast . ObjCPropertyDecl :
2019-04-11 11:46:24 -04:00
//fmt.Printf("ObjCPropertyDecl: %s\n",x.Name)
2019-04-09 23:19:49 -04:00
p := Property {
Name : x . Name ,
2019-04-26 15:08:44 -04:00
Type : types . NewTypeFromString ( x . Type , name ) ,
2019-04-09 23:19:49 -04:00
}
2019-04-11 11:46:24 -04:00
//_,avail = w.GetParms(x,name) // TODO
//if avail {
i . Properties [ p . Name ] = p
//}
2019-04-09 11:52:21 -04:00
case * ast . ObjCMethodDecl :
2019-04-30 06:59:05 -04:00
//fmt.Printf("ObjCMethodDecl: %s (%s) %s\n",x.Type,name,x.Name)
2019-04-09 23:19:49 -04:00
m := Method {
Name : x . Name ,
2019-04-26 15:08:44 -04:00
Type : types . NewTypeFromString ( x . Type , name ) ,
2019-04-11 11:46:24 -04:00
Class : name ,
2019-04-11 17:00:36 -04:00
ClassMethod : x . ClassMethod ,
2019-04-11 11:46:24 -04:00
}
2019-04-30 06:59:05 -04:00
//fmt.Println(m.Type.Node.String())
2019-04-11 11:46:24 -04:00
m . Parameters , avail = w . GetParms ( x , name )
if avail {
i . Methods [ m . Name ] = m
2019-04-09 23:19:49 -04:00
}
case * ast . ObjCProtocol :
//fmt.Printf("ast.ObjCProtocol: %s\n",x.Name)
case * ast . ObjCInterface :
2019-04-10 14:00:48 -04:00
if x . Super {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
2019-04-26 09:04:53 -04:00
types . SetSuper ( name , x . Name )
2019-04-10 14:00:48 -04:00
}
2019-04-26 22:44:30 -04:00
case * ast . ObjCTypeParamDecl :
//fmt.Printf("ObjCTypeParamDecl: %s = %s\n",x.Name,x.Type)
types . SetTypeParam ( name , x . Name , x . Type )
2019-04-09 23:19:49 -04:00
case * ast . Unknown :
//fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content)
2019-04-09 11:52:21 -04:00
default :
2019-04-09 23:19:49 -04:00
//fmt.Println(reflect.TypeOf(x))
}
}
2019-04-23 15:48:11 -04:00
//fmt.Println("Add interface ",i.Name)
2019-04-09 23:19:49 -04:00
w . Interfaces [ i . Name ] = i
}
2019-04-11 11:46:24 -04:00
type AvailAttr struct {
OS , Version string
Deprecated bool
}
2019-04-23 15:48:11 -04:00
//GetParms returns the parameters of a method declaration and a bool
//indicating whether the given method is available on MacOS and not
//deprecated.
2019-04-11 11:46:24 -04:00
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 ( ) {
2019-04-09 23:19:49 -04:00
switch x := c . ( type ) {
case * ast . ParmVarDecl :
p := Parameter {
2019-04-11 11:46:24 -04:00
Pname : n . Parameters [ j ] ,
Vname : x . Name ,
2019-04-26 15:08:44 -04:00
Type : types . NewTypeFromString ( x . Type , class ) ,
2019-04-09 23:19:49 -04:00
}
2019-04-11 11:46:24 -04:00
ret = append ( ret , p )
j ++
2019-04-29 16:14:45 -04:00
case * ast . Variadic :
ret [ j - 1 ] . Type . Variadic = true
2019-04-09 23:19:49 -04:00
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-09 23:19:49 -04:00
}
}
2019-04-18 09:38:46 -04:00
// check that the method is available for this OS and not deprecated
2019-04-11 11:46:24 -04:00
a := func ( ) bool {
if len ( avail ) == 0 {
2019-04-09 23:19:49 -04:00
return true
}
2019-04-11 11:46:24 -04:00
for _ , x := range avail {
if x . OS == "macos" && x . Deprecated == false {
2019-04-09 23:19:49 -04:00
return true
}
}
return false
2019-04-11 11:46:24 -04:00
} ( )
if ! a {
return nil , false
2019-04-09 23:19:49 -04:00
}
2019-04-18 09:38:46 -04:00
// check that we found the right number of parameters
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-09 23:19:49 -04:00
}
2019-04-11 11:46:24 -04:00
return ret , true
2019-04-09 23:19:49 -04:00
}
2019-04-26 15:08:44 -04:00
func ( w * Wrapper ) processTypes ( tps [ ] * types . Type ) {
2019-04-26 10:07:34 -04:00
switch len ( tps ) {
case 0 :
return
case 1 :
2019-04-26 15:08:44 -04:00
w . processType ( tps [ 0 ] )
2019-04-26 10:07:34 -04:00
default :
for _ , tp := range tps {
2019-04-26 15:08:44 -04:00
w . processType ( tp )
2019-04-26 10:07:34 -04:00
}
return
}
2019-04-26 14:08:43 -04:00
}
2019-04-26 15:08:44 -04:00
func ( w * Wrapper ) processType ( tp * types . Type ) {
2019-04-29 10:37:43 -04:00
bt := tp . BaseType ( )
gt := bt . GoType ( )
2019-04-27 22:24:05 -04:00
if gt == "" {
2019-04-26 10:07:34 -04:00
return
}
2019-04-27 22:24:05 -04:00
if gt [ 0 ] == '*' {
2019-04-29 10:37:43 -04:00
w . processType ( bt . PointsTo ( ) )
2019-04-27 22:24:05 -04:00
return
}
if w . Processed [ gt ] { return }
w . Processed [ gt ] = true
if gt == "Char" {
2019-04-26 14:08:43 -04:00
w . CharHelpers ( )
}
2019-04-29 10:37:43 -04:00
if bt . IsFunction ( ) {
2019-04-27 22:24:05 -04:00
return
}
super := types . Super ( gt )
2019-04-26 14:08:43 -04:00
if super != "" {
types . Wrap ( super )
2019-04-27 22:24:05 -04:00
w . processType ( types . NewTypeFromString ( super , "" ) )
2019-04-26 14:08:43 -04:00
}
2019-04-29 11:46:48 -04:00
w . goTypes . WriteString ( bt . GoTypeDecl ( ) )
2019-04-26 14:08:43 -04:00
}
func ( w * Wrapper ) CharHelpers ( ) {
w . goHelpers . WriteString ( `
func CharFromString ( s string ) * Char {
return ( * Char ) ( unsafe . Pointer ( C . CString ( s ) ) )
}
func CharFromBytes ( b [ ] byte ) * Char {
return ( * Char ) ( unsafe . Pointer ( C . CString ( string ( b ) ) ) )
}
func ( c * Char ) String ( ) string {
return C . GoString ( ( * C . char ) ( c ) )
}
` )
2019-04-26 10:07:34 -04:00
}
2019-04-10 14:00:48 -04:00
2019-04-23 15:48:11 -04:00
func ( w * Wrapper ) Wrap ( toproc [ ] string ) {
2019-04-26 22:44:30 -04:00
if w . Package == "" { w . Package = "ns" }
err := os . MkdirAll ( w . Package , 0755 )
if err != nil {
fmt . Printf ( "Error creating directory '%s'\n%s\n" , w . Package , err )
os . Exit ( - 1 )
}
of , err := os . Create ( path . Join ( w . Package , "main.go" ) )
if err != nil {
fmt . Printf ( "Error opening file %s\n%s\n" , path . Join ( w . Package , "main.go" ) , err )
os . Exit ( - 1 )
}
fmt . Printf ( "Writing output to %s\n" , path . Join ( w . Package , "main.go" ) )
2019-04-23 15:48:11 -04:00
pInterfaces := map [ string ] Interface { }
for _ , iface := range toproc {
pInterfaces [ iface ] = w . Interfaces [ iface ]
}
2019-04-26 15:08:44 -04:00
//FIXME: sort pInterfaces
2019-04-26 22:44:30 -04:00
for _ , i := range pInterfaces {
fmt . Printf ( "Interface %s: %d properties, %d methods\n" ,
i . Name , len ( i . Properties ) , len ( i . Methods ) )
2019-04-10 14:00:48 -04:00
2019-04-26 15:34:36 -04:00
w . goCode . WriteString ( fmt . Sprintf ( `
2019-04-10 14:00:48 -04:00
func New % s ( ) * % s {
2019-04-30 09:14:25 -04:00
return ( * % s ) ( unsafe . Pointer ( C . New % s ( ) ) )
2019-04-10 14:00:48 -04:00
}
2019-04-23 15:48:11 -04:00
` , i . Name , i . Name , i . Name , i . Name ) )
2019-04-10 14:00:48 -04:00
w . cCode . WriteString ( fmt . Sprintf ( `
% s *
New % s ( ) {
2019-04-11 17:00:36 -04:00
return [ % s alloc ] ;
2019-04-10 14:00:48 -04:00
}
2019-04-26 15:34:36 -04:00
` , i . Name , i . Name , i . Name ) )
2019-04-10 14:00:48 -04:00
2019-04-26 15:08:44 -04:00
//FIXME: sort properties
2019-04-23 15:48:11 -04:00
for _ , p := range i . Properties {
2019-04-10 14:00:48 -04:00
if Debug {
2019-04-26 15:08:44 -04:00
fmt . Printf ( " property: %s (%s)\n" , p . Name , p . Type . CType ( ) )
2019-04-10 14:00:48 -04:00
}
2019-04-09 23:19:49 -04:00
}
2019-04-26 15:08:44 -04:00
//FIXME: sort methods
2019-04-23 15:48:11 -04:00
for _ , m := range i . Methods {
2019-04-10 14:00:48 -04:00
if Debug {
2019-04-23 15:48:11 -04:00
fmt . Printf ( " method: %s (%s)\n" , m . Name , m . Type )
2019-04-10 14:00:48 -04:00
}
2019-04-26 22:44:30 -04:00
if m . Type . IsFunction ( ) {
2019-04-26 10:07:34 -04:00
continue
}
2019-04-26 14:08:43 -04:00
if m . hasFunctionParam ( ) {
continue
}
2019-04-23 15:48:11 -04:00
gname := strings . Title ( m . Name )
2019-04-26 22:44:30 -04:00
if ! m . ClassMethod {
2019-04-26 15:08:44 -04:00
gname = "(o *" + i . Name + ") " + gname
}
2019-04-26 10:07:34 -04:00
cname := i . Name + "_" + m . Name
2019-04-10 14:00:48 -04:00
2019-04-26 15:08:44 -04:00
cmtype := m . Type . CTypeAttrib ( )
2019-04-26 10:56:00 -04:00
ns , tps , gplist := m . gpntp ( )
2019-04-26 15:08:44 -04:00
w . processTypes ( tps )
w . processType ( m . Type )
grtype := m . Type . GoType ( )
2019-04-26 14:08:43 -04:00
if grtype == "Void" {
grtype = ""
}
2019-04-10 14:00:48 -04:00
w . goCode . WriteString ( fmt . Sprintf ( `
2019-04-30 06:59:05 -04:00
//%s
2019-04-26 15:08:44 -04:00
func % s ( % s ) % s {
2019-04-30 06:59:05 -04:00
` , m . Type . CType ( ) , gname , gplist , grtype ) )
2019-04-29 16:14:45 -04:00
lparm := len ( tps ) - 1
if len ( tps ) > 0 && tps [ lparm ] . Variadic {
vn := ns [ lparm ]
vn = vn [ : len ( vn ) - 1 ]
ns [ lparm ] = vn
w . goCode . WriteString ( fmt . Sprintf (
` var % s [ % d ] unsafe . Pointer
for i , o := range % ss {
% s [ i ] = o . Ptr ( )
}
` , vn , w . VaArgs , vn , vn ) )
}
2019-04-26 10:07:34 -04:00
w . goCode . WriteString (
2019-04-26 15:08:44 -04:00
types . GoToC ( cname , ns , m . Type , tps ) + "}\n\n" )
2019-04-10 14:00:48 -04:00
cret := ""
2019-04-23 15:48:11 -04:00
if ! m . isVoid ( ) {
2019-04-10 14:00:48 -04:00
cret = "return "
}
2019-04-11 17:00:36 -04:00
var cobj string
2019-04-23 15:48:11 -04:00
if m . ClassMethod {
cobj = i . Name
2019-04-11 17:00:36 -04:00
} else {
cobj = "(id)obj"
}
2019-04-10 14:00:48 -04:00
w . cCode . WriteString ( fmt . Sprintf ( `
% s
2019-04-26 10:07:34 -04:00
% s ( % s ) {
2019-04-29 16:14:45 -04:00
` , cmtype , cname , w . cparamlist ( m ) ) )
if len ( tps ) > 0 && tps [ lparm ] . Variadic {
w . cCode . WriteString ( fmt . Sprintf (
` % s * arr = % s ;
` , tps [ lparm ] . CType ( ) , ns [ lparm ] ) )
}
w . cCode . WriteString ( fmt . Sprintf ( ` % s [ % s % s ] ;
} ` , cret , cobj , w . objcparamlist ( m ) ) )
2019-04-09 11:52:21 -04:00
}
}
2019-04-26 22:44:30 -04:00
of . WriteString ( "package " + w . Package + "\n\n" )
of . WriteString ( w . cCode . String ( ) )
of . WriteString ( `
2019-04-10 14:00:48 -04:00
* /
import "C"
import (
"unsafe"
)
` )
2019-04-26 22:44:30 -04:00
of . WriteString ( w . goTypes . String ( ) )
of . WriteString ( w . goHelpers . String ( ) )
of . WriteString ( w . goCode . String ( ) )
of . Close ( )
2019-04-09 11:52:21 -04:00
}