Switch from toml to yaml for configuration files. Begin scaffolding

for Protocols and Delegates.
This commit is contained in:
Greg 2019-05-09 12:25:45 -04:00
parent cccbfbbc00
commit b00ecd4ac0
12 changed files with 298 additions and 138 deletions

View File

@ -210,6 +210,8 @@ func Parse(fullline string) Node {
return parseObjCObjectPointerType(line)
case "ObjCProtocol":
return parseObjCProtocol(line)
case "ObjCProtocolDecl":
return parseObjCProtocolDecl(line)
case "ObjCPropertyDecl":
return parseObjCPropertyDecl(line)
case "ObjCTypeParamDecl":

55
ast/objc_protocol_decl.go Normal file
View File

@ -0,0 +1,55 @@
package ast
import (
"strings"
)
// ObjCProtocolDecl is node represents an Objective-C property declaration
type ObjCProtocolDecl struct {
Addr Address
Pos Position
Position2 string
Name string
ChildNodes []Node
}
func parseObjCProtocolDecl(line string) *ObjCProtocolDecl {
groups := groupsFromRegex(
`(?:prev (?P<prev>0x[0-9a-f]+) )?
<(?P<position>.*<scratch space>.*?|.*<built-in>.*?|.*<invalid sloc>|.*?)>
(?P<position2> <invalid sloc>| col:\d+| line:\d+:\d+)?
(?P<name>.*?)`,
line,
)
return &ObjCProtocolDecl{
Addr: ParseAddress(groups["address"]),
Pos: NewPositionFromString(groups["position"]),
Position2: strings.TrimSpace(groups["position2"]),
Name: strings.TrimSpace(groups["name"]),
ChildNodes: []Node{},
}
}
// AddChild adds a new child node. Child nodes can then be accessed with the
// Children attribute.
func (n *ObjCProtocolDecl) AddChild(node Node) {
n.ChildNodes = append(n.ChildNodes, node)
}
// Address returns the numeric address of the node. See the documentation for
// the Address type for more information.
func (n *ObjCProtocolDecl) Address() Address {
return n.Addr
}
// Children returns the child nodes. If this node does not have any children or
// this node does not support children it will always return an empty slice.
func (n *ObjCProtocolDecl) Children() []Node {
return n.ChildNodes
}
// Position returns the position in the original source code.
func (n *ObjCProtocolDecl) Position() Position {
return n.Pos
}

View File

@ -2,13 +2,14 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"github.com/BurntSushi/toml"
"gopkg.in/yaml.v2"
"gitlab.wow.st/gmp/nswrap/ast"
"gitlab.wow.st/gmp/nswrap/types"
"gitlab.wow.st/gmp/nswrap/wrap"
@ -18,15 +19,16 @@ var Debug = false
type conf struct {
Package string
InputFiles []string
Inputfiles []string
Classes []string
Functions []string
Enums []string
Delegates map[string][]string
Frameworks []string
Imports []string
SysImports []string
Sysimports []string
Pragma []string
VaArgs int
Vaargs int
}
var Config conf
@ -149,7 +151,7 @@ func matches(x string, rs []string) bool {
// Start begins transpiling an input file.
func Start() (err error) {
for _, in := range Config.InputFiles {
for _, in := range Config.Inputfiles {
_, err := os.Stat(in)
if err != nil {
return fmt.Errorf("Input file %s is not found", in)
@ -161,7 +163,7 @@ func Start() (err error) {
// 3. Generate AST
cargs := []string{"-xobjective-c", "-Xclang", "-ast-dump",
"-fsyntax-only","-fno-color-diagnostics"}
cargs = append(cargs,Config.InputFiles...)
cargs = append(cargs,Config.Inputfiles...)
fmt.Printf("Generating AST\n")
astPP, err := exec.Command("clang",cargs...).Output()
if err != nil {
@ -192,12 +194,12 @@ func Start() (err error) {
w.Package = Config.Package
w.Frameworks(Config.Frameworks)
w.Import(Config.Imports)
w.SysImport(Config.SysImports)
w.SysImport(Config.Sysimports)
w.Pragma(Config.Pragma)
if Config.VaArgs == 0 {
Config.VaArgs = 16
if Config.Vaargs == 0 {
Config.Vaargs = 16
}
w.VaArgs = Config.VaArgs
w.Vaargs = Config.Vaargs
for _, u := range tree {
fmt.Printf("--processing translation unit\n")
for _, n := range(u.Children()) {
@ -212,6 +214,15 @@ func Start() (err error) {
if matches(x.Name,Config.Functions) {
w.AddFunction(x)
}
case *ast.ObjCProtocolDecl:
DELEGATES:
for _,ps := range Config.Delegates {
_ = ps
//if matches(x.Name,ps) {
// w.AddProtocol(x)
break DELEGATES
//}
}
case *ast.EnumDecl:
w.AddEnum(x,Config.Enums)
}
@ -222,8 +233,13 @@ func Start() (err error) {
}
func main() {
if _, err := toml.DecodeFile("nswrap.toml",&Config); err != nil {
fmt.Printf("Cannot open config file nswrap.toml.\n")
confbytes, err := ioutil.ReadFile("nswrap.yaml")
if err != nil {
fmt.Printf("Cannot open config file nswrap.yaml. %s\n",err)
os.Exit(-1)
}
if err = yaml.Unmarshal(confbytes,&Config); err != nil {
fmt.Printf("Cannot decode config file nswrap.yaml. %s\n",err)
os.Exit(-1)
}
if err := Start(); err != nil {

View File

@ -1,45 +0,0 @@
InputFiles = [
"/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h",
"/System/Library/Frameworks/AppKit.framework/Headers/AppKit.h",
]
Classes = [
"NSArray",
"NSMutableArray",
"NSDictionary",
"NSEnumerator",
"NSSet",
"NSDate",
"NSTimeZone",
"NSCalendar",
"NSLocale",
"NSCharacterSet",
"NSString",
"NSScanner",
"NSFileManager",
"NSApplication",
"NSBundle",
"NSApp",
"NSMenu",
"NSMenuItem",
"NSWindow",
"NSView",
"NSScreen",
"NSButton",
"NSEvent",
"NSResponder",
"NSRunLoop",
]
Functions = [
"NSMake.*",
]
Enums = [
"CF.*",
"NSApplication.*",
"NSBackingStore.*",
"NSWindowStyleMask.*",
"NSWindowButton",
"NSWindowOrderingMode",
]
Frameworks = [ "Foundation", "AppKit", "CoreGraphics" ]
Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ]
VaArgs = 32

49
examples/app/nswrap.yaml Normal file
View File

@ -0,0 +1,49 @@
inputfiles:
- /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
- /System/Library/Frameworks/AppKit.framework/Headers/AppKit.h
classes:
- NSArray"
- NSMutableArray
- NSDictionary
- NSEnumerator
- NSSet
- NSDate
- NSTimeZone
- NSCalendar
- NSLocale
- NSCharacterSet
- NSString
- NSScanner
- NSFileManager
- NSApplication
- NSBundle
- NSApp
- NSMenu
- NSMenuItem
- NSWindow
- NSView
- NSScreen
- NSButton
- NSEvent
- NSResponder
- NSRunLoop
functions: [NSMake.*]
enums:
- CF.*
- NSApplication.*
- NSBackingStore.*
- NSWindowStyleMask.*
- NSWindowButton
- NSWindowOrderingMode
delegates:
AppDelegate: [NSApplicationDelegate]
frameworks: [ Foundation, AppKit, CoreGraphics ]
pragma: [ clang diagnostic ignored "-Wformat-security" ]
vaargs: 32

View File

@ -1,44 +0,0 @@
Package = "ble"
InputFiles = [
"/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h",
"/System/Library/Frameworks/CoreBluetooth.framework/Headers/CoreBluetooth.h",
"ble/ble_delegate.h",
]
Classes = [
"ble_delegate",
"CBCentralManager",
"CBPeripheralManager",
"CBPeripheral",
"CBCentral",
"CBService",
"CBCharacteristic",
"CBDescriptor",
"CBError",
"CBUUID",
"CBAdvertisementData",
"CBATTRequest",
"NSArray",
"NSMutableArray",
"NSDictionary",
"NSEnumerator",
"NSSet",
"NSDate",
"NSTimeZone",
"NSString",
]
Functions = [
"NSMakeRange",
]
Enums = [
"CB.*",
]
Frameworks = [
"Foundation",
"CoreBluetooth",
]
Imports = [
"ble_delegate.h",
]
Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ]
VaArgs = 32

View File

@ -0,0 +1,35 @@
package: ble
inputfiles:
- /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
- /System/Library/Frameworks/CoreBluetooth.framework/Headers/CoreBluetooth.h
- ble/ble_delegate.h
classes:
- ble_delegate
- CBCentralManager
- CBPeripheralManager
- CBPeripheral
- CBCentral
- CBService
- CBCharacteristic
- CBDescriptor
- CBError
- CBUUID
- CBAdvertisementData
- CBATTRequest
- NSArray
- NSMutableArray
- NSDictionary
- NSEnumerator
- NSSet
- NSDate
- NSTimeZone
- NSString
functions: [ NSMakeRange ]
enums: [ CB.* ]
frameworks: [ Foundation, CoreBluetooth ]
imports: [ ble_delegate.h ]
pragma: [ clang diagnostic ignored "-Wformat-security" ]
vaargs: 32

View File

@ -1,28 +0,0 @@
InputFiles = [
"/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h",
]
Classes = [
"NSArray",
"NSMutableArray",
"NSDictionary",
"NSEnumerator",
"NSSet",
"NSDate",
"NSTimeZone",
"NSCalendar",
"NSLocale",
"NSCharacterSet",
"NSString",
"NSScanner",
"NSFileManager",
]
Functions = [
"NSMakeRange",
]
Enums = [
"P_ALL",
"CF.*",
]
Frameworks = [ "Foundation" ]
Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ]
VaArgs = 32

View File

@ -0,0 +1,21 @@
inputfiles:
- /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
classes:
- NSArray
- NSMutableArray
- NSDictionary
- NSEnumerator
- NSSet
- NSDate
- NSTimeZone
- NSCalendar
- NSLocale
- NSCharacterSet
- NSString
- NSScanner
- NSFileManager
functions: [ NSMakeRange ]
enums: [ P_ALL, CF.* ]
frameworks: [ Foundation ]
pragma: [ clang diagnostic ignored "-Wformat-security" ]
vaargs: 32

View File

@ -1,5 +0,0 @@
Package = "ClassOne"
InputFiles = [ "ClassOne/simple.h" ]
Classes = [ "ClassOne","ClassTwo" ]
Imports = [ "simple.h" ]
Frameworks = [ "Foundation" ]

View File

@ -0,0 +1,7 @@
package: ClassOne
inputfiles: [ ClassOne/simple.h ]
classes:
- ClassOne
- ClassTwo
imports: [ simple.h ]
frameworks: [ Foundation ]

View File

@ -22,15 +22,17 @@ type Wrapper struct {
Functions map[string]*Method
NamedEnums map[string]*Enum
AnonEnums []*Enum
Protocols []*Protocol
cgoFlags strings.Builder // put cGo directives here
cCode strings.Builder // put cGo code 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
goExports strings.Builder // put exported Go functions here
goHelpers strings.Builder // put Go helper functions here
Processed map[string]bool
VaArgs int
Vaargs int
}
func NewWrapper(debug bool) *Wrapper {
@ -41,8 +43,9 @@ func NewWrapper(debug bool) *Wrapper {
Functions: map[string]*Method{},
NamedEnums: map[string]*Enum{},
AnonEnums: []*Enum{},
Protocols: []*Protocol{},
Processed: map[string]bool{},
VaArgs: 16,
Vaargs: 16,
}
ret.cgoFlags.WriteString(fmt.Sprintf(`/*
#cgo CFLAGS: -x objective-c
@ -105,6 +108,12 @@ type Enum struct {
Constants []string
}
type Protocol struct {
Name, GoName string
Methods []*Method
Polymorphic map[string]bool
}
//isVoid() returns true if the method has no return value.
func (m Method) isVoid() bool {
return m.Type.CType() == "void"
@ -152,7 +161,7 @@ func (w Wrapper) objcparamlist(m *Method) string {
} else {
if p.Type.Variadic {
str := []string{m.Name + ":arr[0]"}
for i := 1; i < w.VaArgs; i++ {
for i := 1; i < w.Vaargs; i++ {
str = append(str,"arr["+strconv.Itoa(i)+"]")
}
str = append(str,"nil")
@ -268,6 +277,40 @@ func (w *Wrapper) AddFunction(n *ast.FunctionDecl) {
w.Functions[n.Name] = m
}
func (w *Wrapper) AddProtocol(n *ast.ObjCProtocolDecl) {
p := &Protocol{
Name: n.Name,
GoName: types.NewTypeFromString(n.Name,n.Name).GoType(),
Methods: []*Method{},
Polymorphic: map[string]bool{},
}
seen := make(map[string]bool)
fmt.Printf("Adding protocol %s\n",n.Name)
for _,c := range n.Children() {
switch x := c.(type) {
case *ast.ObjCMethodDecl:
m := &Method{
Name: x.Name,
Type: types.NewTypeFromString(x.Type,p.Name),
Class: p.Name,
GoClass: p.GoName,
ClassMethod: x.ClassMethod,
}
var avail bool
m.Parameters, avail = w.GetParms(x,p.Name)
if avail {
fmt.Printf(" method %s\n",m.Name)
p.Methods = append(p.Methods,m)
if seen[m.Name] {
p.Polymorphic[m.Name] = true
}
seen[m.Name] = true
}
}
}
w.Protocols = append(w.Protocols,p)
}
//FIXME: copied from nswrap/main.go, should put this in a utils package
func matches(x string, rs []string) bool {
for _,r := range rs {
@ -680,7 +723,7 @@ func %s%s(%s) %s {
for i,o := range %ss {
%s[i] = o.Ptr()
}
`,vn,w.VaArgs,vn,vn))
`,vn,w.Vaargs,vn,vn))
}
w.goCode.WriteString(` ` +
types.GoToC(cname,ns,m.Type,tps) + "\n}\n")
@ -781,6 +824,57 @@ const %s %s= C.%s
}
}
//FIXME: need to disambiguate polymorphic method names. Something like:
//func Disambiguate([]*Method) []*Method {} ...
//Can allow user to decide on a disambiguation strategy
func (w *Wrapper) ProcessProtocol(p *Protocol) {
fmt.Printf("Processing protocol (%s)\n",p.Name)
//To create (per protocol):
//1. ObjC protocol interface
//2. ObjC protocol implementation
//3. Go type
//4. Go constructor
//5. Go dispatch database for callbacks
//To create (per method):
//1. ObjC function prototypes for go exports
//2. ObjC constructor function
//3. Go exported callback function wrappers
var ccode, gocode, goexports strings.Builder
//1. ObjC protocol interface
ccode.WriteString(fmt.Sprintf(`
@interface %s : %s
`,p.Name,p.Name))
//2. ObjC protocol implementation
ccode.WriteString(fmt.Sprintf(`
`))
//3. Go type
gocode.WriteString(fmt.Sprintf(`
`))
//4. Go constructor
gocode.WriteString(fmt.Sprintf(`
`))
//5. Go dispatch database for callbacks
gocode.WriteString(fmt.Sprintf(`
`))
//6. Go callback registration function
gocode.WriteString(fmt.Sprintf(`
`))
//To create (per method):
for _,m := range p.Methods {
_ = m
//1. ObjC function prototypes for go exports
ccode.WriteString(fmt.Sprintf(`
`))
//2. ObjC constructor function
ccode.WriteString(fmt.Sprintf(`
`))
//3. Go exported callback function wrappers
goexports.WriteString(fmt.Sprintf(`
`))
}
}
func (w *Wrapper) Wrap(toproc []string) {
if w.Package == "" { w.Package = "ns" }
err := os.MkdirAll(w.Package,0755)
@ -851,6 +945,9 @@ void*
for _,e := range w.AnonEnums {
w.ProcessEnum(e)
}
for _,p := range w.Protocols {
w.ProcessProtocol(p)
}
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")