From eced2517108e221aa7f7d641d5647e103e5b9e94 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 3 May 2019 13:14:30 -0400 Subject: [PATCH] Create GoString helper methods for anything that has a "WithString" method. Multiple bug fixes in type system, fixed parsing of id, instancename and instancetype, allow NullableAnnotations after generic lists (e.g. "id _Nullable"). Helper function to identify types that are function pointers. --- examples/bluetooth/main.go | 5 +-- examples/foundation/main.go | 6 ++-- examples/foundation/nswrap.toml | 2 +- examples/simple/nswrap.toml | 1 + main.go | 7 +++-- types/convert.go | 27 ++++++++++------ types/cparser.go | 20 +++++++++--- types/main.go | 13 ++++++-- wrap/main.go | 55 ++++++++++++++++++++++++++++----- 9 files changed, 103 insertions(+), 33 deletions(-) diff --git a/examples/bluetooth/main.go b/examples/bluetooth/main.go index 015e27a..9c5a6f8 100644 --- a/examples/bluetooth/main.go +++ b/examples/bluetooth/main.go @@ -11,7 +11,8 @@ func main() { fmt.Println("LE capable:",cd.IsLECapableHardware()) time.Sleep(time.Second * 1) fmt.Println("LE capable:",cd.IsLECapableHardware()) - uuid := ble.CBUUIDWithString(ble.NSStringWithUTF8String(ble.CharFromString("180d"))) + uuid := ble.CBUUIDWithGoString("180d") cd.ScanFor(uuid) - time.Sleep(time.Second * 15) + + select { } } diff --git a/examples/foundation/main.go b/examples/foundation/main.go index b33368e..8cae95e 100644 --- a/examples/foundation/main.go +++ b/examples/foundation/main.go @@ -8,12 +8,12 @@ import ( ) func main() { - n1 := ns.NSStringWithUTF8String(ns.CharFromString("hi there")) + n1 := ns.NSStringWithUTF8String(ns.CharWithGoString("hi there")) c1 := n1.CapitalizedString() gs := c1.UTF8String().String() fmt.Println(gs) - n2 := ns.NSStringWithUTF8String(ns.CharFromString("hi world")) - n3 := ns.NSStringWithUTF8String(ns.CharFromString("ok bye")) + n2 := ns.NSStringWithGoString("hi world") + n3 := ns.NSStringWithGoString("ok bye") a := ns.NSMutableArrayWithObjects(n1,n2,n3) fmt.Println("Length(a) = ",a.Count()) fmt.Println("is n2 in a?",a.ContainsObject(n2)) diff --git a/examples/foundation/nswrap.toml b/examples/foundation/nswrap.toml index 5dd9082..decb2c1 100644 --- a/examples/foundation/nswrap.toml +++ b/examples/foundation/nswrap.toml @@ -22,6 +22,6 @@ Functions = [ Enums = [ "CF.*", ] -SysImports = [ "Foundation/Foundation.h" ] +Frameworks = [ "Foundation" ] Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ] VaArgs = 32 diff --git a/examples/simple/nswrap.toml b/examples/simple/nswrap.toml index e28fd80..fa84eca 100644 --- a/examples/simple/nswrap.toml +++ b/examples/simple/nswrap.toml @@ -2,3 +2,4 @@ Package = "ClassOne" InputFiles = [ "ClassOne/simple.h" ] Classes = [ "ClassOne","ClassTwo" ] Imports = [ "simple.h" ] +Frameworks = [ "Foundation" ] diff --git a/main.go b/main.go index 51fbcfc..7321300 100644 --- a/main.go +++ b/main.go @@ -149,7 +149,6 @@ func matches(x string, rs []string) bool { // Start begins transpiling an input file. func Start() (err error) { - // 1. Compile it first (checking for errors) for _, in := range Config.InputFiles { _, err := os.Stat(in) if err != nil { @@ -159,10 +158,11 @@ func Start() (err error) { // 2. Preprocess NOT DONE - // 3. Generate JSON from AST + // 3. Generate AST cargs := []string{"-xobjective-c", "-Xclang", "-ast-dump", "-fsyntax-only","-fno-color-diagnostics"} cargs = append(cargs,Config.InputFiles...) + fmt.Printf("Generating AST\n") astPP, err := exec.Command("clang",cargs...).Output() if err != nil { // If clang fails it still prints out the AST, so we have to run it @@ -175,9 +175,11 @@ func Start() (err error) { lines := readAST(astPP) // Converting to nodes + fmt.Printf("Building nodes\n") nodes := convertLinesToNodesParallel(lines) // build tree + fmt.Printf("Assembling tree\n") tree := buildTree(nodes, 0) w := wrap.NewWrapper(Debug) w.Package = Config.Package @@ -190,6 +192,7 @@ func Start() (err error) { } w.VaArgs = Config.VaArgs for _, u := range tree { + fmt.Printf("--processing translation unit\n") for _, n := range(u.Children()) { switch x := n.(type) { case *ast.ObjCInterfaceDecl: diff --git a/types/convert.go b/types/convert.go index a772df3..9fe3300 100644 --- a/types/convert.go +++ b/types/convert.go @@ -50,7 +50,7 @@ func init() { TypeParameters = make(map[string]map[string]string) typedefs = make(map[string]*Type) - r_id = regexp.MustCompile("\bid\b") + r_id = regexp.MustCompile(`\bid\b`) r_instancename = regexp.MustCompile(`\binstancename\b`) r_instancetype = regexp.MustCompile(`\binstancetype\b`) } @@ -117,7 +117,6 @@ func NewType(n *Node, c string) *Type { return &Type{ Node: n2, Class: c, - //ctype: "", } } @@ -125,19 +124,16 @@ func NewTypeFromString(t,c string) *Type { //fmt.Printf("t/c: %s/%s\n",t,c) n,err := Parse(t) //fmt.Printf("%p %s",n,n.String()) - if n.IsId() { - n,err = Parse("NSObject*") - } if err != nil { return &Type{} } if n2,ok := clean(n, c); ok { + //found type parameters, re-parse return NewTypeFromString(n2.Ctype(),c) } return &Type{ Node: n, Class: c, - //ctype: "", } } @@ -219,9 +215,9 @@ func (t *Type) _CType(attrib bool) string { //fmt.Println("nil sent to _CType()") return "" } - if !attrib && t.ctype != "" { // cache - return t.ctype - } + //if !attrib && t.ctype != "" { // cache + // return t.ctype + //} var ct string if attrib { ignore := map[string]bool { "GenericList": true } @@ -296,6 +292,19 @@ func (o *Id) %s() *%s { return (*%s)(unsafe.Pointer(o)) } `,t.Node.Ctype(),t.BaseType().GoType(),gt,super,gt,gt,gt,gt) } +func (t *Type) IsFunctionPtr() bool { + if t == nil { + return false + } + if td := t.Typedef(); td != nil { + return td.IsFunctionPtr() + } + for pt := t.PointsTo(); pt != nil; pt = pt.PointsTo() { + return pt.IsFunction() + } + return false +} + func (t *Type) IsFunction() bool { if t == nil { fmt.Println("nil sent to IsFunction()") diff --git a/types/cparser.go b/types/cparser.go index ce7f629..d7e0015 100644 --- a/types/cparser.go +++ b/types/cparser.go @@ -81,21 +81,31 @@ digit: [0-9] */ +import ( + "regexp" +) + var TypeName func(s string, n *Node) (string, *Node) func init() { + instancename := regexp.MustCompile("instancename") + instancetype := regexp.MustCompile("instancetype") + cacheable := func(s string) bool { + return !instancetype.MatchString(s) && !instancename.MatchString(s) + } + cache := map[string]*Node{} TypeName = func(s string, n *Node) (string, *Node) { if n2,ok := cache[s]; ok { return "",n2 } s2,n2 := _TypeName(s,n) - if s2 == "" { + if s2 == "" && cacheable(s) { cache[s] = n2 } return s2,n2 } - TypeName = _TypeName + //TypeName = _TypeName } func _TypeName(s string, n *Node) (string, *Node) { @@ -318,10 +328,10 @@ func BareTypedefName(s string, n *Node) (string, *Node) { } func TypedefName(s string, n *Node) (string, *Node) { - return OneOf( - Seq(BareTypedefName, AngBracketed(GenericList)), - Seq(BareTypedefName, NullableAnnotation), + return Seq( BareTypedefName, + Opt(AngBracketed(GenericList)), + Opt(NullableAnnotation), )(s,n) } diff --git a/types/main.go b/types/main.go index 24fbedc..817c574 100644 --- a/types/main.go +++ b/types/main.go @@ -53,7 +53,7 @@ func (n *Node) stripAbstract(k string) *Node { return nil } ret := NewNode(n.Kind) - cs := n.Children[:] + cs := append([]*Node{},n.Children...) dbg("stripAbstract(): i = %d\n",i) //Scan backwords skipping TypeQualifier and NullableAnnotation tags @@ -97,7 +97,10 @@ func (n *Node) PointsTo() *Node { //IsPointer returns true if the node is a pointer func (n *Node) IsPointer() bool { - return n.IsId() || n.IsInstancetype() || n.PointsTo() != nil + if pt := n.PointsTo(); pt != nil { + return true + } + return n.IsInstancetype() || n.IsId() } //ArrayOf, when called on an array node returns a node describing the type @@ -124,6 +127,9 @@ func (n *Node) IsFunction() bool { if n == nil || len(n.Children) < 1 { return false } + if pt := n.PointsTo(); pt != nil { + return false + } return n.Children[len(n.Children)-1].Kind == "Function" } @@ -140,7 +146,8 @@ func (n *Node) IsId() bool { if n == nil || len(n.Children) < 1 { return false } - return n.Children[0].Kind == "TypedefName" && + return !n.IsFunction() && + n.Children[0].Kind == "TypedefName" && n.Children[0].Content == "id" } diff --git a/wrap/main.go b/wrap/main.go index 62e7323..e74a494 100644 --- a/wrap/main.go +++ b/wrap/main.go @@ -113,7 +113,7 @@ func (m Method) isVoid() bool { //hasFunctionParam() returns true if a method has a function as a parameter. func (m Method) hasFunctionParam() bool { for _,p := range m.Parameters { - if p.Type.IsFunction() { + if p.Type.IsFunction() || p.Type.IsFunctionPtr() { return true } } @@ -521,7 +521,7 @@ func (w *Wrapper) processType(tp *types.Type) { if gt == "NSEnumerator" { w.EnumeratorHelpers() } - if bt.IsFunction() { + if bt.IsFunction() || bt.IsFunctionPtr() { return } super := types.Super(gt) @@ -535,7 +535,7 @@ func (w *Wrapper) processType(tp *types.Type) { func (w *Wrapper) CharHelpers() { w.goHelpers.WriteString(` -func CharFromString(s string) *Char { +func CharWithGoString(s string) *Char { return (*Char)(unsafe.Pointer(C.CString(s))) } @@ -555,7 +555,8 @@ func (e *NSEnumerator) ForIn(f func(*Id) bool) { for o := e.NextObject(); o != nil; o = e.NextObject() { if !f(o) { break } } -}`) +} +`) } func (w *Wrapper) ProcessMethod(m *Method) { @@ -570,13 +571,12 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) { if Debug { fmt.Printf(" method: %s (%s)\n", m.Name, m.Type) } - if m.Type.IsFunction() || m.hasFunctionParam() { + if m.Type.IsFunction() || m.Type.IsFunctionPtr() || m.hasFunctionParam() { return } gname := strings.Title(m.Name) switch { case !m.ClassMethod: - gname = "(o *" + m.GoClass + ") " + gname case m.Type.GoType() != "*" + m.GoClass: gname = m.GoClass + gname default: @@ -592,6 +592,10 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) { gname = m.GoClass + gname[lens1-i:] } } + receiver := "" + if !m.ClassMethod { + receiver = "(o *" + m.GoClass + ") " + } cname := m.Name if m.Class != "" { cname = m.Class + "_" + cname @@ -611,8 +615,8 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) { } w.goCode.WriteString(fmt.Sprintf(` //%s -func %s(%s) %s { -`,m.Type.CType(),gname,gplist,grtype)) +func %s%s(%s) %s { +`,m.Type.CType(),receiver,gname,gplist,grtype)) lparm := len(tps)-1 if len(tps) > 0 && tps[lparm].Variadic { vn := ns[lparm] @@ -659,7 +663,42 @@ func %s(%s) %s { w.cCode.WriteString(fmt.Sprintf(` %s[%s %s]; }`, cret, cobj, w.objcparamlist(m))) } + + // create GoString helper method + if ok,_ := regexp.MatchString("WithString$",m.Name); ok { + //fmt.Printf("--%s\n",gname) + gname2 := gname[:len(gname)-6] + "GoString" + gps := []string{} + i := 0 + if !m.ClassMethod { i = 1 } + for ; i < len(ns); i++ { + gt := tps[i].GoType() + //fmt.Printf(" %s\n",gt) + ns2 := ns[i] + if gt == "*NSString" { + gt = "string" + ns[i] = gStringToNsstring(ns[i]) + } + gps = append(gps,ns2 + " " + gt) + } + gplist = strings.Join(gps,", ") + obj := "" + if !m.ClassMethod { + obj = "o." + ns = ns[1:] + } + w.goCode.WriteString(fmt.Sprintf(` +func %s%s(%s) %s { + return %s%s(%s) } +`,receiver,gname2,gplist,grtype,obj,gname,strings.Join(ns,", "))) + } +} + +func gStringToNsstring(s string) string { + return fmt.Sprintf("NSStringWithUTF8String(CharWithGoString(%s))",s) +} + func (w *Wrapper) ProcessEnum(e *Enum) { gtp := ""