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" ,
"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 ,
}
/ *
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-11 11:46:24 -04:00
func ( w * Wrapper ) AddType ( t , class string ) {
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-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 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
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 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
}
2019-04-10 14:00:48 -04:00
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 ) )
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 ,
}
func ( m Method ) goparamlist ( ) ( 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
}
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
}
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 )
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-11 11:46:24 -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 {
w . AddType ( typeOrType2 ( x . Type , x . Type2 ) , name )
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 ,
}
m . Parameters , avail = w . GetParms ( x , name )
if avail {
w . AddType ( typeOrType2 ( x . Type , x . Type2 ) , name )
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-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-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
}
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 ( ) {
return [ [ % s alloc ] init ] ;
}
` , 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 = ")"
}
}
w . ProcessType ( grtype )
2019-04-11 11:46:24 -04:00
gplist , gptypes , ok := y . goparamlist ( )
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
} ` , 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)"
}
2019-04-10 14:00:48 -04:00
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
}
}
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
}