Add constants for C enum types. Add NSEnumerator helper. Fix

bugs in Go class names. Add "Frameworks" option to nswrap.toml.
Improve handling of ast.EnumDecl and ast.EnumConstantDecl.
This commit is contained in:
Greg 2019-05-02 14:14:29 -04:00
parent 9b71889a69
commit 758ea40679
7 changed files with 219 additions and 52 deletions

View File

@ -1,5 +1,9 @@
package ast package ast
import (
"strings"
)
// EnumConstantDecl is node represents a enum constant declaration. // EnumConstantDecl is node represents a enum constant declaration.
type EnumConstantDecl struct { type EnumConstantDecl struct {
Addr Address Addr Address
@ -8,6 +12,7 @@ type EnumConstantDecl struct {
Referenced bool Referenced bool
Name string Name string
Type string Type string
Type2 string
ChildNodes []Node ChildNodes []Node
} }
@ -16,18 +21,25 @@ func parseEnumConstantDecl(line string) *EnumConstantDecl {
`<(?P<position>.*)> `<(?P<position>.*)>
( (?P<position2>[^ ]+))? ( (?P<position2>[^ ]+))?
( (?P<referenced>referenced))? ( (?P<referenced>referenced))?
(?P<name>.+) (?P<name> \w+)
'(?P<type>.+?)'`, (?P<type> '.*?')?
(?P<type2>:'.*?')?`,
line, line,
) )
type2 := groups["type2"]
if type2 != "" {
type2 = type2[2 : len(type2)-1]
}
return &EnumConstantDecl{ return &EnumConstantDecl{
Addr: ParseAddress(groups["address"]), Addr: ParseAddress(groups["address"]),
Pos: NewPositionFromString(groups["position"]), Pos: NewPositionFromString(groups["position"]),
Position2: groups["position2"], Position2: groups["position2"],
Referenced: len(groups["referenced"]) > 0, Referenced: len(groups["referenced"]) > 0,
Name: groups["name"], Name: strings.TrimSpace(groups["name"]),
Type: groups["type"], Type: removeQuotes(groups["type"]),
Type2: type2,
ChildNodes: []Node{}, ChildNodes: []Node{},
} }
} }

View File

@ -10,20 +10,34 @@ type EnumDecl struct {
Pos Position Pos Position
Position2 string Position2 string
Name string Name string
Type string
Type2 string
ChildNodes []Node ChildNodes []Node
} }
func parseEnumDecl(line string) *EnumDecl { func parseEnumDecl(line string) *EnumDecl {
groups := groupsFromRegex( groups := groupsFromRegex(
`(?:prev (?P<prev>0x[0-9a-f]+) )?<(?P<position>.*)>(?P<position2> .+:\d+)?(?P<name>.*)`, `(?:prev (?P<prev>0x[0-9a-f]+) )?
<(?P<position>.*)>
(?P<position2> .+:\d+)?
(?P<name> \w+)?
(?P<type> '.*?')?
(?P<type2>:'.*')?`,
line, line,
) )
type2 := groups["type2"]
if type2 != "" {
type2 = type2[2 : len(type2)-1]
}
return &EnumDecl{ return &EnumDecl{
Addr: ParseAddress(groups["address"]), Addr: ParseAddress(groups["address"]),
Pos: NewPositionFromString(groups["position"]), Pos: NewPositionFromString(groups["position"]),
Position2: groups["position2"], Position2: groups["position2"],
Name: strings.TrimSpace(groups["name"]), Name: strings.TrimSpace(groups["name"]),
Type: removeQuotes(groups["type"]),
Type2: type2,
ChildNodes: []Node{}, ChildNodes: []Node{},
} }
} }

View File

@ -27,4 +27,8 @@ func main() {
fmt.Println("Length(a2) = ",a2.Count()) fmt.Println("Length(a2) = ",a2.Count())
i1 := a.ObjectAtIndex(1).NSString() i1 := a.ObjectAtIndex(1).NSString()
fmt.Println(i1.UTF8String()) fmt.Println(i1.UTF8String())
a.ObjectEnumerator().ForIn(func(o *ns.Id) bool {
fmt.Println(o.NSString().UTF8String())
return true
})
} }

View File

@ -19,6 +19,9 @@ Classes = [
Functions = [ Functions = [
"NSMakeRange", "NSMakeRange",
] ]
Enums = [
"CF.*",
]
SysImports = [ "Foundation/Foundation.h" ] SysImports = [ "Foundation/Foundation.h" ]
Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ] Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ]
VaArgs = 32 VaArgs = 32

View File

@ -21,6 +21,8 @@ type conf struct {
InputFiles []string InputFiles []string
Classes []string Classes []string
Functions []string Functions []string
Enums []string
Frameworks []string
Imports []string Imports []string
SysImports []string SysImports []string
Pragma []string Pragma []string
@ -177,9 +179,9 @@ func Start() (err error) {
// build tree // build tree
tree := buildTree(nodes, 0) tree := buildTree(nodes, 0)
//unit := tree[0]
w := wrap.NewWrapper(Debug) w := wrap.NewWrapper(Debug)
w.Package = Config.Package w.Package = Config.Package
w.Frameworks(Config.Frameworks)
w.Import(Config.Imports) w.Import(Config.Imports)
w.SysImport(Config.SysImports) w.SysImport(Config.SysImports)
w.Pragma(Config.Pragma) w.Pragma(Config.Pragma)
@ -200,6 +202,8 @@ func Start() (err error) {
if matches(x.Name,Config.Functions) { if matches(x.Name,Config.Functions) {
w.AddFunction(x) w.AddFunction(x)
} }
case *ast.EnumDecl:
w.AddEnum(x,Config.Enums)
} }
} }
} }

View File

@ -157,7 +157,6 @@ func (t *Type) PointsTo() *Type {
} }
func Wrap(s string) { func Wrap(s string) {
// it is the pointers to this type that get wrapped
wrapped[s] = true wrapped[s] = true
} }
@ -270,11 +269,13 @@ type %s %s
} }
func (t *Type) GoInterfaceDecl() string { func (t *Type) GoInterfaceDecl() string {
ct := t.CType()
gt := t.GoType() gt := t.GoType()
if gt[0] == '*' { if gt[0] == '*' {
gt = gt[1:] // dereference wrapped types gt = gt[1:] // dereference wrapped types
ct = ct[:len(ct)-1]
} }
super := Super(gt) super := Super(ct)
if super == "" { if super == "" {
goInterfaces[gt] = true goInterfaces[gt] = true
return fmt.Sprintf(` return fmt.Sprintf(`

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"regexp"
"strconv" "strconv"
"strings" "strings"
@ -19,9 +20,13 @@ type Wrapper struct {
Package string Package string
Interfaces map[string]*Interface Interfaces map[string]*Interface
Functions map[string]*Method Functions map[string]*Method
NamedEnums map[string]*Enum
AnonEnums []*Enum
cgoFlags strings.Builder // put cGo directives here
cCode strings.Builder // put cGo code here cCode strings.Builder // put cGo code here
goTypes strings.Builder // put Go type declarations here goTypes strings.Builder // put Go type declarations here
goConst strings.Builder // put Go constants (from C enums) here
goCode strings.Builder // put Go code here goCode strings.Builder // put Go code here
goHelpers strings.Builder // put Go helper functions here goHelpers strings.Builder // put Go helper functions here
Processed map[string]bool Processed map[string]bool
@ -34,13 +39,14 @@ func NewWrapper(debug bool) *Wrapper {
ret := &Wrapper{ ret := &Wrapper{
Interfaces: map[string]*Interface{}, Interfaces: map[string]*Interface{},
Functions: map[string]*Method{}, Functions: map[string]*Method{},
NamedEnums: map[string]*Enum{},
AnonEnums: []*Enum{},
Processed: map[string]bool{}, Processed: map[string]bool{},
VaArgs: 16, VaArgs: 16,
} }
ret.cCode.WriteString(`/* ret.cgoFlags.WriteString(fmt.Sprintf(`/*
#cgo CFLAGS: -x objective-c #cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation `))
`)
ret.goTypes.WriteString(` ret.goTypes.WriteString(`
type Id struct { } type Id struct { }
func (o *Id) Ptr() unsafe.Pointer { return unsafe.Pointer(o) } func (o *Id) Ptr() unsafe.Pointer { return unsafe.Pointer(o) }
@ -48,11 +54,19 @@ func (o *Id) Ptr() unsafe.Pointer { return unsafe.Pointer(o) }
return ret return ret
} }
func (w *Wrapper) Frameworks(ss []string) {
if len(ss) == 0 {
return
}
for _,s := range ss {
w.cCode.WriteString(fmt.Sprintf("#import <%s/%s.h>\n",s,s))
}
w.cgoFlags.WriteString("#cgo LDFLAGS: -framework " + strings.Join(ss," -framework "))
}
func (w *Wrapper) Import(ss []string) { func (w *Wrapper) Import(ss []string) {
for _,s := range ss { for _,s := range ss {
w.cCode.WriteString(` w.cCode.WriteString("\n#import \"" + s + "\"\n")
#import "` + s + `"
`)
} }
} }
@ -64,7 +78,7 @@ func (w *Wrapper) SysImport(ss []string) {
func (w *Wrapper) Pragma(ss []string) { func (w *Wrapper) Pragma(ss []string) {
for _,s := range ss { for _,s := range ss {
w.cCode.WriteString("\n#pragma " + s + "\n") w.cgoFlags.WriteString("\n#pragma " + s + "\n")
} }
} }
@ -79,12 +93,18 @@ type Parameter struct {
} }
type Method struct { type Method struct {
Name, Class string Name, Class, GoClass string
Type *types.Type Type *types.Type
ClassMethod bool ClassMethod bool
Parameters []*Parameter Parameters []*Parameter
} }
type Enum struct {
Name string
Type *types.Type
Constants []string
}
//isVoid() returns true if the method has no return value. //isVoid() returns true if the method has no return value.
func (m Method) isVoid() bool { func (m Method) isVoid() bool {
return m.Type.CType() == "void" return m.Type.CType() == "void"
@ -149,6 +169,7 @@ func (w Wrapper) objcparamlist(m *Method) string {
//also a C/Objective-C reserved word. //also a C/Objective-C reserved word.
var goreserved map[string]bool = map[string]bool{ var goreserved map[string]bool = map[string]bool{
"range": true, "range": true,
"type": true,
} }
func (w *Wrapper) gpntp(m *Method) ([]string,[]*types.Type,string) { func (w *Wrapper) gpntp(m *Method) ([]string,[]*types.Type,string) {
@ -187,7 +208,7 @@ func (w *Wrapper) gpntp(m *Method) ([]string,[]*types.Type,string) {
type Interface struct { type Interface struct {
Name string Name, GoName string
Properties map[string]*Property Properties map[string]*Property
Methods map[string]*Method Methods map[string]*Method
} }
@ -247,18 +268,69 @@ func (w *Wrapper) AddFunction(n *ast.FunctionDecl) {
w.Functions[n.Name] = m w.Functions[n.Name] = m
} }
//FIXME: copied from nswrap/main.go, should put this in a utils package
func matches(x string, rs []string) bool {
for _,r := range rs {
if m,_ := regexp.MatchString("^" + r + "$",x); m {
return true
}
}
return false
}
func (w *Wrapper) AddEnum(n *ast.EnumDecl,rs []string) {
if n.Name != "" && !matches(n.Name,rs) {
return
}
//fmt.Printf("Adding enum: (%s) %s\n",n.Type,n.Name)
var tp *types.Type
a := (*Avail)(&[]AvailAttr{})
if n.Type == "" {
tp = nil
} else {
tp = types.NewTypeFromString(n.Type,"")
//fmt.Printf(" type: %s\n",tp.CType())
}
e := &Enum{
Name: n.Name, // NOTE: may be empty string
Type: tp,
Constants: []string{},
}
for _,c := range n.Children() {
switch x := c.(type) {
case *ast.AvailabilityAttr, *ast.UnavailableAttr:
a.Add(x)
case *ast.EnumConstantDecl:
//fmt.Printf("*ast.EnumConstantDecl: (%s) '%s': %s\n",n.Type,n.Name,x.Name)
if n.Name == "" && !matches(x.Name,rs) {
continue
}
e.Constants = append(e.Constants,x.Name)
}
}
if a.Available() && len(e.Constants) > 0 {
if e.Name == "" {
w.AnonEnums = append(w.AnonEnums,e)
} else {
w.NamedEnums[e.Name] = e
}
//fmt.Printf(" added\n")
}
}
func (w *Wrapper) add(name string, ns []ast.Node) { func (w *Wrapper) add(name string, ns []ast.Node) {
var i *Interface var i *Interface
var ok bool var ok bool
goname := types.NewTypeFromString(name,name).GoType()
types.Wrap(goname)
if i,ok = w.Interfaces[name]; !ok { if i,ok = w.Interfaces[name]; !ok {
i = &Interface{ i = &Interface{
Name: name, Name: name,
GoName: goname,
Properties: map[string]*Property{}, Properties: map[string]*Property{},
Methods: map[string]*Method{}, Methods: map[string]*Method{},
} }
} }
tp := types.NewTypeFromString(name,name)
types.Wrap(tp.GoType())
var avail bool var avail bool
for _,c := range ns { for _,c := range ns {
switch x := c.(type) { switch x := c.(type) {
@ -278,6 +350,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
Name: x.Name, Name: x.Name,
Type: types.NewTypeFromString(x.Type,name), Type: types.NewTypeFromString(x.Type,name),
Class: name, Class: name,
GoClass: goname,
ClassMethod: x.ClassMethod, ClassMethod: x.ClassMethod,
} }
//fmt.Println(m.Type.Node.String()) //fmt.Println(m.Type.Node.String())
@ -314,6 +387,7 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
m2 := &Method{ m2 := &Method{
Name: m.Name, Name: m.Name,
Class: i.Name, Class: i.Name,
GoClass: i.GoName,
Type: m.Type.CloneToClass(i.Name), Type: m.Type.CloneToClass(i.Name),
ClassMethod: true, ClassMethod: true,
Parameters: []*Parameter{}, Parameters: []*Parameter{},
@ -341,12 +415,40 @@ type AvailAttr struct {
Deprecated bool Deprecated bool
} }
type Avail []AvailAttr
func (a *Avail) Add(n ast.Node) {
switch x := n.(type) {
case *ast.AvailabilityAttr:
*a = append(*a, AvailAttr{
OS: x.OS,
Version: x.Version,
Deprecated: (x.Unknown1 != "0") || x.IsUnavailable,
})
case *ast.UnavailableAttr:
*a = append(*a, AvailAttr{
OS: "macos", Deprecated: true })
}
}
func (a *Avail) Available() bool {
if len(*a) == 0 {
return true
}
for _,x := range *a {
if x.OS == "macos" && x.Deprecated == false {
return true
}
}
return false
}
//GetParms returns the parameters of a method declaration and a bool //GetParms returns the parameters of a method declaration and a bool
//indicating whether the given method is available on MacOS and not //indicating whether the given method is available on MacOS and not
//deprecated. //deprecated.
func (w *Wrapper) GetParms(n ast.Node,class string) ([]*Parameter,bool) { func (w *Wrapper) GetParms(n ast.Node,class string) ([]*Parameter,bool) {
ret := make([]*Parameter,0) ret := make([]*Parameter,0)
avail := make([]AvailAttr,0) avail := (*Avail)(&[]AvailAttr{})
var parms []string var parms []string
switch x := n.(type) { switch x := n.(type) {
case *ast.ObjCMethodDecl: case *ast.ObjCMethodDecl:
@ -370,34 +472,14 @@ func (w *Wrapper) GetParms(n ast.Node,class string) ([]*Parameter,bool) {
j++ j++
case *ast.Variadic: case *ast.Variadic:
ret[j-1].Type.Variadic = true ret[j-1].Type.Variadic = true
case *ast.AvailabilityAttr: case *ast.AvailabilityAttr, *ast.UnavailableAttr:
avail = append(avail, avail.Add(x)
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: case *ast.Unknown:
if Debug { fmt.Printf("GetParms(): ast.Unknown: %s\n",x.Name) } if Debug { fmt.Printf("GetParms(): ast.Unknown: %s\n",x.Name) }
} }
} }
// check that the method is available for this OS and not deprecated // check that the method is available for this OS and not deprecated
a := func() bool { if !avail.Available() {
if len(avail) == 0 {
return true
}
for _,x := range avail {
if x.OS == "macos" && x.Deprecated == false {
return true
}
}
return false
}()
if !a {
return nil, false return nil, false
} }
// check that we found the right number of parameters // check that we found the right number of parameters
@ -436,13 +518,17 @@ func (w *Wrapper) processType(tp *types.Type) {
if gt == "Char" { if gt == "Char" {
w.CharHelpers() w.CharHelpers()
} }
if gt == "NSEnumerator" {
w.EnumeratorHelpers()
}
if bt.IsFunction() { if bt.IsFunction() {
return return
} }
super := types.Super(gt) super := types.Super(gt)
if super != "" { if super != "" {
types.Wrap(super) tp := types.NewTypeFromString(super,"")
w.processType(types.NewTypeFromString(super,"")) types.Wrap(tp.GoType())
w.processType(tp)
} }
w.goTypes.WriteString(bt.GoTypeDecl()) w.goTypes.WriteString(bt.GoTypeDecl())
} }
@ -463,6 +549,15 @@ func (c *Char) String() string {
`) `)
} }
func (w *Wrapper) EnumeratorHelpers() {
w.goHelpers.WriteString(`
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) { func (w *Wrapper) ProcessMethod(m *Method) {
w._processMethod(m,false) w._processMethod(m,false)
} }
@ -481,9 +576,9 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
gname := strings.Title(m.Name) gname := strings.Title(m.Name)
switch { switch {
case !m.ClassMethod: case !m.ClassMethod:
gname = "(o *" + m.Class + ") " + gname gname = "(o *" + m.GoClass + ") " + gname
case m.Type.GoType() != "*" + m.Class: case m.Type.GoType() != "*" + m.GoClass:
gname = m.Class + gname gname = m.GoClass + gname
default: default:
lens1 := len(m.Class) lens1 := len(m.Class)
i := 0 i := 0
@ -492,9 +587,9 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
if m.Class[i:] == gname[:lens1 - i] { break } if m.Class[i:] == gname[:lens1 - i] { break }
} }
if lens1 - i >= len(gname) { if lens1 - i >= len(gname) {
gname = m.Class + gname gname = m.GoClass + gname
} else { } else {
gname = m.Class + gname[lens1-i:] gname = m.GoClass + gname[lens1-i:]
} }
} }
cname := m.Name cname := m.Name
@ -511,6 +606,9 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
if types.IsGoInterface(grtype) { if types.IsGoInterface(grtype) {
grtype = "*Id" grtype = "*Id"
} }
if gname == grtype { // avoid name conflicts between methods and types
gname = "Get" + gname
}
w.goCode.WriteString(fmt.Sprintf(` w.goCode.WriteString(fmt.Sprintf(`
//%s //%s
func %s(%s) %s { func %s(%s) %s {
@ -538,7 +636,7 @@ func %s(%s) %s {
if m.ClassMethod { if m.ClassMethod {
cobj = m.Class cobj = m.Class
} else { } else {
cobj = "(id)o" cobj = "(" + m.Class + "*)o"
} }
cns,cntps := w.cparamlist(m) cns,cntps := w.cparamlist(m)
_ = cns _ = cns
@ -563,6 +661,25 @@ func %s(%s) %s {
} }
} }
func (w *Wrapper) ProcessEnum(e *Enum) {
gtp := ""
if e.Type != nil {
gtp = e.Type.GoType()
if !w.Processed[gtp] {
w.goTypes.WriteString(fmt.Sprintf(`
type %s C.%s
`,gtp,e.Type.CType()))
w.Processed[gtp] = true
}
}
gtp = gtp + " "
for _,c := range e.Constants {
w.goConst.WriteString(fmt.Sprintf(`
const %s %s= C.%s
`,c,gtp,c))
}
}
func (w *Wrapper) Wrap(toproc []string) { func (w *Wrapper) Wrap(toproc []string) {
if w.Package == "" { w.Package = "ns" } if w.Package == "" { w.Package = "ns" }
err := os.MkdirAll(w.Package,0755) err := os.MkdirAll(w.Package,0755)
@ -582,6 +699,9 @@ func (w *Wrapper) Wrap(toproc []string) {
} }
//FIXME: sort pInterfaces //FIXME: sort pInterfaces
for _,i := range pInterfaces { for _,i := range pInterfaces {
if i == nil {
continue
}
fmt.Printf("Interface %s: %d properties, %d methods\n", fmt.Printf("Interface %s: %d properties, %d methods\n",
i.Name, len(i.Properties), len(i.Methods)) i.Name, len(i.Properties), len(i.Methods))
@ -589,7 +709,7 @@ func (w *Wrapper) Wrap(toproc []string) {
func New%s() *%s { func New%s() *%s {
return (*%s)(unsafe.Pointer(C.New%s())) return (*%s)(unsafe.Pointer(C.New%s()))
} }
`,i.Name,i.Name,i.Name,i.Name)) `,i.GoName,i.GoName,i.GoName,i.Name))
w.cCode.WriteString(fmt.Sprintf(` w.cCode.WriteString(fmt.Sprintf(`
%s* %s*
@ -614,8 +734,16 @@ New%s() {
//fmt.Printf("Processing function %s %s\n",m.Type.CType(),m.Name) //fmt.Printf("Processing function %s %s\n",m.Type.CType(),m.Name)
w.ProcessFunction(m) w.ProcessFunction(m)
} }
for _,e := range w.NamedEnums {
w.ProcessEnum(e)
}
for _,e := range w.AnonEnums {
w.ProcessEnum(e)
}
fmt.Printf("%d functions\n", len(w.Functions)) fmt.Printf("%d functions\n", len(w.Functions))
fmt.Printf("%d enums\n", len(w.NamedEnums) + len(w.AnonEnums))
of.WriteString("package " + w.Package + "\n\n") of.WriteString("package " + w.Package + "\n\n")
of.WriteString(w.cgoFlags.String() + "\n")
of.WriteString(w.cCode.String()) of.WriteString(w.cCode.String())
of.WriteString(` of.WriteString(`
*/ */
@ -626,6 +754,7 @@ import (
) )
`) `)
of.WriteString(w.goTypes.String()) of.WriteString(w.goTypes.String())
of.WriteString(w.goConst.String())
of.WriteString(w.goHelpers.String()) of.WriteString(w.goHelpers.String())
of.WriteString(w.goCode.String()) of.WriteString(w.goCode.String())
of.Close() of.Close()