2019-04-26 09:04:53 -04:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-05-01 10:58:29 -04:00
|
|
|
"regexp"
|
2019-04-26 09:04:53 -04:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2019-04-26 10:07:34 -04:00
|
|
|
//super is a map recording which class is the parent of each other class
|
2019-04-26 09:04:53 -04:00
|
|
|
var super map[string]string
|
|
|
|
|
2019-04-26 10:07:34 -04:00
|
|
|
//wrapped is a map recording whether a given GoType is to be "wrapped" in a
|
|
|
|
//go struct.
|
|
|
|
var wrapped map[string]bool
|
|
|
|
|
2019-04-29 11:46:48 -04:00
|
|
|
func shouldWrap(gt string) bool {
|
|
|
|
return gt != "" && gt[0] == '*' && wrapped[gt[1:]]
|
|
|
|
}
|
|
|
|
|
|
|
|
//goInterfaces records the names of top level Go interfaces. Pointers to these
|
|
|
|
//are dereferenced to bare interface names.
|
|
|
|
var goInterfaces map[string]bool
|
|
|
|
|
|
|
|
func isGoInterface(gt string) bool {
|
|
|
|
return goInterfaces[gt]
|
|
|
|
}
|
|
|
|
|
2019-04-26 22:44:30 -04:00
|
|
|
//TypeParameters maps, for each class, a TypedefName to a type, representing
|
|
|
|
//the Objective-C type parameters for that class
|
|
|
|
var TypeParameters map[string]map[string]string
|
|
|
|
|
2019-04-29 13:31:13 -04:00
|
|
|
//Typedefs maps from C types to the Type of a typedef with that name.
|
|
|
|
var typedefs map[string]*Type
|
|
|
|
|
|
|
|
func (t *Type) Typedef() *Type {
|
2019-04-30 06:59:05 -04:00
|
|
|
//return typedefs[t.BaseType().CType()]
|
|
|
|
return typedefs[t.CType()]
|
2019-04-29 13:31:13 -04:00
|
|
|
}
|
|
|
|
|
2019-05-01 10:58:29 -04:00
|
|
|
var (
|
|
|
|
r_id *regexp.Regexp
|
|
|
|
r_instancename *regexp.Regexp
|
|
|
|
r_instancetype *regexp.Regexp
|
|
|
|
)
|
|
|
|
|
2019-04-29 13:31:13 -04:00
|
|
|
func init() {
|
|
|
|
super = make(map[string]string)
|
|
|
|
wrapped = make(map[string]bool)
|
|
|
|
goInterfaces = make(map[string]bool)
|
|
|
|
TypeParameters = make(map[string]map[string]string)
|
|
|
|
typedefs = make(map[string]*Type)
|
2019-05-01 10:58:29 -04:00
|
|
|
|
|
|
|
r_id = regexp.MustCompile("\bid\b")
|
|
|
|
r_instancename = regexp.MustCompile(`\binstancename\b`)
|
|
|
|
r_instancetype = regexp.MustCompile(`\binstancetype\b`)
|
2019-04-29 13:31:13 -04:00
|
|
|
}
|
2019-04-26 22:44:30 -04:00
|
|
|
|
2019-04-26 09:04:53 -04:00
|
|
|
func Super(c string) string {
|
|
|
|
return super[c]
|
|
|
|
}
|
|
|
|
|
|
|
|
func SetSuper(c, p string) {
|
|
|
|
super[c] = p
|
|
|
|
}
|
|
|
|
|
2019-04-26 22:44:30 -04:00
|
|
|
func SetTypeParam(c, n, t string) {
|
|
|
|
if TypeParameters[c] == nil {
|
|
|
|
TypeParameters[c] = make(map[string]string)
|
|
|
|
}
|
|
|
|
TypeParameters[c][n] = t
|
|
|
|
}
|
|
|
|
|
|
|
|
func AddTypedef(n,t string) {
|
2019-04-30 06:59:05 -04:00
|
|
|
//fmt.Printf("AddTypedef(): %s -> %s\n",n,t)
|
2019-04-29 13:31:13 -04:00
|
|
|
typedefs[n] = NewTypeFromString(t,"")
|
2019-04-26 22:44:30 -04:00
|
|
|
}
|
|
|
|
|
2019-04-26 09:04:53 -04:00
|
|
|
type Type struct {
|
|
|
|
Node *Node
|
|
|
|
Class string
|
2019-04-30 09:14:25 -04:00
|
|
|
ctype string
|
2019-04-29 16:14:45 -04:00
|
|
|
Variadic bool
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
|
2019-05-01 10:58:29 -04:00
|
|
|
func (t *Type) CloneToClass(c string) *Type {
|
|
|
|
return &Type{
|
|
|
|
Node: t.Node,
|
|
|
|
Class: c,
|
|
|
|
Variadic: t.Variadic,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-30 06:59:05 -04:00
|
|
|
func clean(n *Node,c string) (*Node,bool) {
|
2019-04-30 09:14:25 -04:00
|
|
|
if n == nil {
|
|
|
|
return nil,false
|
|
|
|
}
|
2019-04-30 06:59:05 -04:00
|
|
|
ret := NewNode(n.Kind,n.Content)
|
|
|
|
ret.Children = n.Children
|
|
|
|
//fmt.Printf("clean(%s,%s)\n",n.Ctype(),c)
|
|
|
|
recur := false
|
|
|
|
if TypeParameters[c] != nil {
|
|
|
|
for k,v := range TypeParameters[c] {
|
|
|
|
recur = ret.renameTypedefs(k,v)
|
|
|
|
}
|
|
|
|
}
|
2019-05-01 10:58:29 -04:00
|
|
|
// recur = recur || ret.renameTypedefs("instancename",c)
|
|
|
|
// recur = recur || ret.renameTypedefs("instancetype",c + "*")
|
2019-04-30 06:59:05 -04:00
|
|
|
if recur {
|
|
|
|
clean(n, c)
|
|
|
|
return ret,true
|
|
|
|
}
|
|
|
|
return n,false
|
|
|
|
}
|
|
|
|
|
2019-04-26 09:04:53 -04:00
|
|
|
func NewType(n *Node, c string) *Type {
|
2019-04-30 06:59:05 -04:00
|
|
|
n2,_ := clean(n, c)
|
2019-04-26 09:04:53 -04:00
|
|
|
return &Type{
|
2019-04-30 06:59:05 -04:00
|
|
|
Node: n2,
|
2019-04-26 09:04:53 -04:00
|
|
|
Class: c,
|
2019-04-30 06:59:05 -04:00
|
|
|
//ctype: "",
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewTypeFromString(t,c string) *Type {
|
2019-04-30 06:59:05 -04:00
|
|
|
//fmt.Printf("t/c: %s/%s\n",t,c)
|
2019-04-26 09:04:53 -04:00
|
|
|
n,err := Parse(t)
|
2019-04-30 06:59:05 -04:00
|
|
|
//fmt.Printf("%p %s",n,n.String())
|
2019-04-26 22:44:30 -04:00
|
|
|
if n.IsId() {
|
2019-04-26 14:08:43 -04:00
|
|
|
n,err = Parse("NSObject*")
|
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
if err != nil {
|
|
|
|
return &Type{}
|
|
|
|
}
|
2019-04-30 06:59:05 -04:00
|
|
|
if n2,ok := clean(n, c); ok {
|
|
|
|
return NewTypeFromString(n2.Ctype(),c)
|
2019-04-26 22:44:30 -04:00
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
return &Type{
|
|
|
|
Node: n,
|
|
|
|
Class: c,
|
2019-04-30 06:59:05 -04:00
|
|
|
//ctype: "",
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) String() string {
|
|
|
|
return t.Node.String()
|
|
|
|
}
|
|
|
|
|
2019-04-27 22:24:05 -04:00
|
|
|
func (t *Type) PointsTo() *Type {
|
2019-05-01 10:58:29 -04:00
|
|
|
if td := t.Typedef(); td != nil {
|
|
|
|
return td.PointsTo()
|
|
|
|
}
|
|
|
|
if pt := t.Node.PointsTo(); pt != nil {
|
|
|
|
return NewType(t.Node.PointsTo(), t.Class)
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
2019-04-27 22:24:05 -04:00
|
|
|
}
|
|
|
|
|
2019-04-26 14:08:43 -04:00
|
|
|
func Wrap(s string) {
|
2019-04-26 10:07:34 -04:00
|
|
|
// it is the pointers to this type that get wrapped
|
2019-04-27 22:24:05 -04:00
|
|
|
wrapped[s] = true
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) BaseType() *Type {
|
2019-05-01 10:58:29 -04:00
|
|
|
if t == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
ret := NewType(
|
|
|
|
t.Node.BaseType(),
|
|
|
|
t.Class,
|
|
|
|
)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func swapstars(s string) string {
|
|
|
|
for i := len(s) - 1; i > 0 && s[i] == '*'; {
|
|
|
|
s = "*" + s[:i]
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) CGoType() string {
|
|
|
|
ct := swapstars("C." + t.CType())
|
2019-04-26 14:15:18 -04:00
|
|
|
ct = strings.ReplaceAll(ct,"unsigned ","u")
|
|
|
|
ct = strings.ReplaceAll(ct,"signed ","u")
|
|
|
|
ct = strings.ReplaceAll(ct,"long ","long")
|
|
|
|
ct = strings.ReplaceAll(ct,"complex ","complex")
|
2019-04-26 09:04:53 -04:00
|
|
|
ct = strings.ReplaceAll(ct," ","_")
|
|
|
|
return ct
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) GoType() string {
|
2019-04-26 10:56:00 -04:00
|
|
|
return _goType(t.CType())
|
|
|
|
}
|
|
|
|
|
|
|
|
func _goType(ct string) string {
|
|
|
|
ct = swapstars(ct)
|
2019-04-26 09:04:53 -04:00
|
|
|
ct = strings.Title(ct)
|
|
|
|
ct = strings.ReplaceAll(ct," ","")
|
|
|
|
ct = strings.ReplaceAll(ct,"Struct","")
|
2019-04-29 11:46:48 -04:00
|
|
|
if len(ct) > 0 && ct[0] == '*' && isGoInterface(ct[1:]) {
|
|
|
|
return ct[1:]
|
|
|
|
}
|
2019-05-01 10:58:29 -04:00
|
|
|
if ct == "Id" {
|
|
|
|
ct = "*Id"
|
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
return ct
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) CType() string {
|
2019-04-26 15:08:44 -04:00
|
|
|
return t._CType(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) CTypeAttrib() string {
|
|
|
|
return t._CType(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) _CType(attrib bool) string {
|
2019-05-01 10:58:29 -04:00
|
|
|
if t == nil {
|
2019-05-01 11:01:50 -04:00
|
|
|
//fmt.Println("nil sent to _CType()")
|
2019-05-01 10:58:29 -04:00
|
|
|
return ""
|
|
|
|
}
|
2019-04-26 22:44:30 -04:00
|
|
|
//if !attrib && c.ctype != "" ... FIXME?
|
2019-04-30 09:14:25 -04:00
|
|
|
if t.ctype != "" { // cache
|
|
|
|
return t.ctype
|
|
|
|
}
|
2019-04-26 15:08:44 -04:00
|
|
|
var ct string
|
|
|
|
if attrib {
|
2019-04-26 22:44:30 -04:00
|
|
|
ignore := map[string]bool { "GenericList": true }
|
|
|
|
ct = t.Node._Ctype(ignore)
|
2019-04-26 15:08:44 -04:00
|
|
|
} else {
|
|
|
|
ct = t.Node.CtypeSimplified()
|
|
|
|
}
|
2019-05-01 10:58:29 -04:00
|
|
|
ct = r_id.ReplaceAllString(ct,"NSObject*")
|
|
|
|
ct = r_instancename.ReplaceAllString(ct,t.Class)
|
|
|
|
ct = r_instancetype.ReplaceAllString(ct,t.Class + "*")
|
2019-04-26 15:08:44 -04:00
|
|
|
if attrib {
|
|
|
|
t._CType(false)
|
2019-04-30 09:14:25 -04:00
|
|
|
} else {
|
|
|
|
t.ctype = ct
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
return ct
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) GoTypeDecl() string {
|
2019-04-26 10:07:34 -04:00
|
|
|
if wrapped[t.GoType()] {
|
2019-04-26 09:04:53 -04:00
|
|
|
return t.GoInterfaceDecl()
|
|
|
|
}
|
2019-04-26 10:56:00 -04:00
|
|
|
tp := t.BaseType()
|
2019-04-26 22:44:30 -04:00
|
|
|
if tp.Node.IsId() {
|
|
|
|
return ""
|
|
|
|
}
|
2019-04-26 14:08:43 -04:00
|
|
|
gt := tp.GoType()
|
|
|
|
switch gt {
|
|
|
|
case "", "Void":
|
|
|
|
return ""
|
|
|
|
default:
|
2019-04-29 13:31:13 -04:00
|
|
|
extra := t.Node.CtypeSimplified()
|
|
|
|
var cgt string
|
|
|
|
if td := tp.Typedef(); td != nil {
|
|
|
|
cgt = td.CGoType()
|
|
|
|
extra = "typedef " + td.Node.Ctype()
|
|
|
|
} else {
|
|
|
|
cgt = tp.CGoType()
|
|
|
|
}
|
2019-04-26 14:08:43 -04:00
|
|
|
return fmt.Sprintf(`
|
2019-04-29 13:31:13 -04:00
|
|
|
//%s (%s)
|
2019-04-26 09:04:53 -04:00
|
|
|
type %s %s
|
2019-04-29 13:31:13 -04:00
|
|
|
`,t.Node.Ctype(),extra,gt,cgt)
|
2019-04-26 14:08:43 -04:00
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) GoInterfaceDecl() string {
|
2019-04-26 14:08:43 -04:00
|
|
|
gt := t.GoType()
|
|
|
|
if gt[0] == '*' {
|
|
|
|
gt = gt[1:] // dereference wrapped types
|
2019-04-26 10:56:00 -04:00
|
|
|
}
|
2019-04-26 14:08:43 -04:00
|
|
|
super := Super(gt)
|
2019-04-26 09:04:53 -04:00
|
|
|
if super == "" {
|
2019-04-29 11:46:48 -04:00
|
|
|
goInterfaces[gt] = true
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
//%s (%s)
|
|
|
|
type %s interface {
|
|
|
|
Ptr() unsafe.Pointer
|
|
|
|
}
|
|
|
|
`,t.Node.Ctype(),t.BaseType().GoType(),gt)
|
|
|
|
}
|
|
|
|
if isGoInterface(super) {
|
|
|
|
super = "Id"
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
return fmt.Sprintf(`
|
2019-04-27 22:24:05 -04:00
|
|
|
//%s (%s)
|
|
|
|
type %s struct { %s }
|
2019-04-30 09:14:25 -04:00
|
|
|
func (o *%s) Ptr() unsafe.Pointer { return unsafe.Pointer(o) }
|
2019-04-29 11:46:48 -04:00
|
|
|
`,t.Node.Ctype(),t.BaseType().GoType(),gt,super,gt)
|
2019-04-26 22:44:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) IsFunction() bool {
|
2019-05-01 10:58:29 -04:00
|
|
|
if t == nil {
|
|
|
|
fmt.Println("nil sent to IsFunction()")
|
|
|
|
return false
|
|
|
|
}
|
2019-04-29 13:31:13 -04:00
|
|
|
if td := t.Typedef(); td != nil {
|
2019-04-26 22:44:30 -04:00
|
|
|
return td.IsFunction()
|
|
|
|
}
|
|
|
|
return t.Node.IsFunction()
|
|
|
|
}
|
|
|
|
|
2019-05-01 10:58:29 -04:00
|
|
|
func (t *Type) ReturnType() *Type {
|
|
|
|
if rt := t.Node.ReturnType(); rt != nil {
|
|
|
|
return NewType(rt,t.Class)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-26 22:44:30 -04:00
|
|
|
func (t *Type) IsPointer() bool {
|
2019-04-29 13:31:13 -04:00
|
|
|
if td := t.Typedef(); td != nil {
|
2019-04-26 22:44:30 -04:00
|
|
|
return td.IsPointer()
|
|
|
|
}
|
|
|
|
return t.Node.IsPointer()
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Type) CToGo(cval string) string { // cast C value to CGo
|
2019-04-26 22:44:30 -04:00
|
|
|
if t.IsPointer() {
|
2019-04-26 09:04:53 -04:00
|
|
|
cval = "unsafe.Pointer(" + cval + ")"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("(%s)(%s)",t.GoType(),cval)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call a C function from Go with a given return type and parameter types
|
|
|
|
func GoToC(name string, pnames []string, rtype *Type, ptypes []*Type) string {
|
2019-05-01 10:58:29 -04:00
|
|
|
if rtype == nil {
|
|
|
|
fmt.Println("nil sent to GoToC")
|
|
|
|
return ""
|
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
var ret strings.Builder
|
|
|
|
rt := rtype.CType()
|
|
|
|
if rt != "void" {
|
2019-04-29 11:46:48 -04:00
|
|
|
rtgt := rtype.GoType()
|
2019-04-30 09:14:25 -04:00
|
|
|
if isGoInterface(rtgt) {
|
|
|
|
rtgt = "*Id"
|
|
|
|
}
|
2019-05-01 10:58:29 -04:00
|
|
|
ret.WriteString("return (" + rtgt + ")(")
|
|
|
|
if rtype.IsPointer() {
|
2019-04-30 09:14:25 -04:00
|
|
|
ret.WriteString("unsafe.Pointer(")
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ret.WriteString("C." + name + "(")
|
|
|
|
parms := []string{}
|
|
|
|
for i := 0; i < len(pnames); i++ {
|
|
|
|
pn,pt := pnames[i],ptypes[i]
|
|
|
|
p := pn
|
2019-04-29 16:14:45 -04:00
|
|
|
if (shouldWrap(pt.GoType()) || isGoInterface(pt.GoType())) && !pt.Variadic {
|
2019-04-29 11:46:48 -04:00
|
|
|
p = pn + ".Ptr()"
|
2019-04-26 09:04:53 -04:00
|
|
|
} else {
|
2019-04-29 16:14:45 -04:00
|
|
|
switch {
|
|
|
|
case pt.Variadic:
|
|
|
|
p = "unsafe.Pointer(&" + p + ")"
|
2019-05-01 10:58:29 -04:00
|
|
|
case pt.IsPointer():
|
2019-04-26 14:08:43 -04:00
|
|
|
p = "unsafe.Pointer(" + pn + ")"
|
2019-04-29 16:14:45 -04:00
|
|
|
default:
|
2019-04-26 14:08:43 -04:00
|
|
|
p = "(" + pt.CGoType() + ")(" + pn + ")"
|
|
|
|
}
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
parms = append(parms,p)
|
|
|
|
}
|
|
|
|
ret.WriteString(strings.Join(parms,", "))
|
|
|
|
ret.WriteString(")")
|
|
|
|
if rt != "void" {
|
2019-04-30 09:14:25 -04:00
|
|
|
ret.WriteString(")")
|
2019-05-01 10:58:29 -04:00
|
|
|
if rtype.IsPointer() {
|
2019-04-26 09:04:53 -04:00
|
|
|
ret.WriteString(")")
|
|
|
|
}
|
2019-04-30 09:14:25 -04:00
|
|
|
ret.WriteString("\n")
|
2019-04-26 09:04:53 -04:00
|
|
|
}
|
|
|
|
return ret.String()
|
|
|
|
}
|
|
|
|
|