2019-04-09 11:52:21 -04:00
package wrap
import (
"fmt"
2019-04-09 23:19:49 -04:00
//"reflect"
"strings"
2019-04-09 11:52:21 -04:00
"gitlab.wow.st/gmp/clast/ast"
)
2019-04-10 14:00:48 -04:00
var (
Debug = false
)
2019-04-09 11:52:21 -04:00
type Wrapper struct {
2019-04-09 23:19:49 -04:00
Interfaces map [ string ] Interface
Types map [ string ] string
2019-04-10 14:00:48 -04:00
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
2019-04-09 23:19:49 -04:00
}
// translate C builtin types to CGo
2019-04-10 14:00:48 -04:00
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" ,
2019-04-11 13:09:02 -04:00
"float" : "float64" ,
"double" : "float64" ,
2019-04-10 14:00:48 -04:00
"complex float" : "C.complexfloat" ,
"complex double" : "C.complexdouble" ,
}
var gobuiltinTypes map [ string ] bool = map [ string ] bool {
"byte" : true ,
"int" : true ,
2019-04-11 13:09:02 -04:00
"float64" : true ,
2019-04-10 14:00:48 -04:00
}
/ *
2019-04-09 23:19:49 -04:00
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-10 14:00:48 -04:00
} * /
2019-04-09 23:19:49 -04:00
2019-04-18 09:38:46 -04:00
func ( w * Wrapper ) AddType ( t1 , t2 , class string ) {
fmt . Printf ( "Type: %s\n" , t1 )
t := typeOrType2 ( t1 , t2 )
2019-04-10 14:00:48 -04:00
if _ , ok := builtinTypes [ t ] ; ok {
return
}
2019-04-11 11:46:24 -04:00
nt , err := goType ( t , class )
2019-04-18 09:38:46 -04:00
if Debug {
fmt . Printf ( "AddType(): (%s) (%s) -> %s\n" , t1 , t2 , nt )
}
2019-04-10 14:00:48 -04:00
if err != nil {
return
}
w . Types [ nt ] = t
}
2019-04-11 11:46:24 -04:00
func goType ( t , class string ) ( string , error ) {
2019-04-09 23:19:49 -04:00
// 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 13:09:02 -04:00
nt = strings . ReplaceAll ( nt , "const " , "" )
2019-04-11 11:46:24 -04:00
nt = strings . ReplaceAll ( nt , " _Nullable" , "" )
nt = strings . ReplaceAll ( nt , " _Nonnull" , "" )
if t == "instancetype" {
return class , nil
}
2019-04-10 14:00:48 -04:00
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
2019-04-09 23:19:49 -04:00
}
if len ( nt ) > 5 && nt [ 0 : 5 ] == "enum " { // FIXME: deal with enums?
2019-04-10 14:00:48 -04:00
return "" , fmt . Errorf ( "goType(): enum" )
2019-04-09 23:19:49 -04:00
}
nt = strings . ReplaceAll ( nt , " " , "" )
if nt == "void" {
2019-04-10 14:00:48 -04:00
return "" , fmt . Errorf ( "goType(): void" )
2019-04-09 23:19:49 -04:00
}
2019-04-10 14:00:48 -04:00
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" ) }
2019-04-09 23:19:49 -04:00
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
2019-04-09 23:19:49 -04:00
}
type Method struct {
2019-04-11 11:46:24 -04:00
Name , Type , Type2 , Class string
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-10 14:00:48 -04:00
func ( m Method ) isVoid ( ) bool {
return typeOrType2 ( m . Type , m . Type2 ) == "void"
}
2019-04-11 13:09:02 -04:00
func ( w Wrapper ) isObject ( tp string ) bool { // takes a goType
2019-04-11 12:00:10 -04:00
if _ , ok := w . Interfaces [ tp ] ; ok {
2019-04-11 11:46:24 -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-11 13:09:02 -04:00
tp := typeOrType2 ( p . Type , p . Type2 )
gtp , _ := goType ( tp , m . Class )
if w . isObject ( gtp ) {
tp = "void*"
}
ret = append ( ret , fmt . Sprintf ( "%s %s" , tp , p . Vname ) )
2019-04-10 14:00:48 -04:00
}
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 )
}
2019-04-10 14:00:48 -04:00
}
return strings . Join ( ret , " " )
}
2019-04-11 11:46:24 -04:00
var goreserved map [ string ] bool = map [ string ] bool {
"range" : true ,
}
2019-04-11 13:09:02 -04:00
func ( w Wrapper ) goparamlist ( m Method ) ( string , [ ] string , bool ) {
2019-04-10 14:00:48 -04:00
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 )
2019-04-10 14:00:48 -04:00
if err != nil {
2019-04-11 11:46:24 -04:00
return "UNSUPPORTED TYPE" , [ ] string { } , false
}
2019-04-11 13:09:02 -04:00
if w . isObject ( tp ) {
tp = "*" + tp
}
2019-04-11 11:46:24 -04:00
if goreserved [ gname ] {
gname = gname + "_"
2019-04-10 14:00:48 -04:00
}
tps = append ( tps , tp )
2019-04-11 11:46:24 -04:00
ret = append ( ret , fmt . Sprintf ( "%s %s" , gname , tp ) )
2019-04-10 14:00:48 -04:00
}
2019-04-11 11:46:24 -04:00
return strings . Join ( ret , ", " ) , tps , true
2019-04-10 14:00:48 -04:00
}
2019-04-11 13:09:02 -04:00
func ( w Wrapper ) goparamnames ( m Method ) string {
2019-04-11 17:00:36 -04:00
ret := make ( [ ] string , 0 )
if ! m . ClassMethod {
ret = append ( ret , "o.ptr" )
}
2019-04-11 11:46:24 -04:00
for _ , p := range m . Parameters {
gname := p . Vname
if goreserved [ gname ] {
gname = gname + "_"
}
2019-04-11 13:09:02 -04:00
gt , _ := goType ( typeOrType2 ( p . Type , p . Type2 ) , m . Class )
if w . isObject ( gt ) {
gname = gname + ".ptr"
}
2019-04-11 11:46:24 -04:00
ret = append ( ret , gname )
2019-04-10 14:00:48 -04:00
}
return strings . Join ( ret , ", " )
}
2019-04-09 23:19:49 -04:00
type Interface struct {
2019-04-10 14:00:48 -04:00
Name , Super string
2019-04-09 23:19:49 -04:00
Properties map [ string ] Property
Methods map [ string ] Method
}
2019-04-10 14:00:48 -04:00
func ( i Interface ) IsRoot ( ) bool {
if i . Super == "" || i . Super == i . Name {
return true
}
return false
}
2019-04-09 23:19:49 -04:00
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
}
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-18 09:38:46 -04:00
w . AddType ( name , "" , name )
2019-04-09 23:19:49 -04:00
}
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 ,
Type : x . Type ,
Type2 : x . Type2 ,
}
2019-04-11 11:46:24 -04:00
//_,avail = w.GetParms(x,name) // TODO
//if avail {
2019-04-18 09:38:46 -04:00
w . AddType ( x . Type , x . Type2 , name )
2019-04-11 11:46:24 -04:00
i . Properties [ p . Name ] = p
//}
2019-04-09 11:52:21 -04:00
case * ast . ObjCMethodDecl :
2019-04-10 14:00:48 -04:00
//fmt.Printf("ObjCMethodDecl: %s\n",x.Name)
2019-04-09 23:19:49 -04:00
m := Method {
Name : x . Name ,
Type : x . Type ,
Type2 : x . Type2 ,
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
}
m . Parameters , avail = w . GetParms ( x , name )
if avail {
2019-04-18 09:38:46 -04:00
w . AddType ( x . Type , x . Type2 , name )
2019-04-11 11:46:24 -04:00
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)
i . Super = x . Name
}
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))
}
}
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 ( ) {
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-09 23:19:49 -04:00
Type : x . Type ,
Type2 : x . Type2 ,
}
2019-04-11 11:46:24 -04:00
ret = append ( ret , p )
j ++
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
}
func typeOrType2 ( t1 , t2 string ) string {
2019-04-11 11:46:24 -04:00
if t2 != "" && t2 != "id" {
2019-04-09 23:19:49 -04:00
return t2
}
return t1
}
2019-04-10 14:00:48 -04:00
func ( w * Wrapper ) ProcessType ( gotype string ) {
if gotype == "" {
return
}
2019-04-11 13:09:02 -04:00
if gotype [ 0 ] == '*' {
gotype = gotype [ 1 : ]
}
2019-04-10 14:00:48 -04:00
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 ( `
2019-04-09 23:19:49 -04:00
// %s
2019-04-10 14:00:48 -04:00
type % s struct { % s }
` , ctype , gotype , fields ) )
w . Processed [ gotype ] = true
}
func ( w * Wrapper ) ProcessTypes ( tps [ ] string ) {
for _ , tp := range tps {
w . ProcessType ( tp )
2019-04-09 23:19:49 -04:00
}
2019-04-10 14:00:48 -04:00
}
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 >
2019-04-10 14:00:48 -04:00
` )
pInterfaces := map [ string ] Interface { toproc : w . Interfaces [ toproc ] }
for k , v := range pInterfaces {
if Debug {
fmt . Printf ( "Interface %s: %d properties, %d methods\n" ,
2019-04-09 23:19:49 -04:00
k , len ( v . Properties ) , len ( v . Methods ) )
2019-04-10 14:00:48 -04:00
}
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 ( ) {
2019-04-11 17:00:36 -04:00
return [ % s alloc ] ;
2019-04-10 14:00:48 -04:00
}
` , v . Name , v . Name , v . Name ) )
2019-04-09 23:19:49 -04:00
for _ , y := range v . Properties {
2019-04-10 14:00:48 -04:00
if Debug {
fmt . Printf ( " property: %s (%s)\n" , y . Name , typeOrType2 ( y . Type , y . Type2 ) )
}
2019-04-09 23:19:49 -04:00
}
for _ , y := range v . Methods {
2019-04-10 14:00:48 -04:00
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 )
2019-04-10 14:00:48 -04:00
if ! y . isVoid ( ) {
var err error
2019-04-11 11:46:24 -04:00
grtype , err = goType ( cmtype , v . Name )
2019-04-10 14:00:48 -04:00
if err != nil {
grtype = fmt . Sprintf ( "// goType(%s): NOT IMPLEMENTED\n" , cmtype )
2019-04-11 11:46:24 -04:00
continue
2019-04-10 14:00:48 -04:00
}
2019-04-09 23:19:49 -04:00
}
2019-04-10 14:00:48 -04:00
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 = ")"
}
}
2019-04-11 13:09:02 -04:00
if grtype == "id" { // can't return id
continue
}
2019-04-10 14:00:48 -04:00
w . ProcessType ( grtype )
2019-04-11 13:09:02 -04:00
gplist , gptypes , ok := w . goparamlist ( y )
2019-04-11 11:46:24 -04:00
if ! ok {
continue
}
2019-04-10 14:00:48 -04:00
w . ProcessTypes ( gptypes )
w . goCode . WriteString ( fmt . Sprintf ( `
func ( o * % s ) % s ( % s ) % s % s {
% sC . % s_ % s ( % s ) % s
2019-04-11 13:09:02 -04:00
} ` , v . Name , gname , gplist , grptr , grtype , gcast , v . Name , y . Name , w . goparamnames ( y ) , gcast2 ) )
2019-04-10 14:00:48 -04:00
cret := ""
if ! y . isVoid ( ) {
cret = "return "
}
2019-04-11 17:00:36 -04:00
var cobj string
if y . ClassMethod {
cobj = v . Name
} else {
cobj = "(id)obj"
}
2019-04-10 14:00:48 -04:00
w . cCode . WriteString ( fmt . Sprintf ( `
% s
% s_ % s ( % s ) {
2019-04-11 17:00:36 -04:00
% s [ % s % s ] ;
} ` , cmtype , v . Name , y . Name , w . cparamlist ( y ) , cret , cobj , y . objcparamlist ( ) ) )
2019-04-09 11:52:21 -04:00
}
}
2019-04-10 14:00:48 -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
}