Better handling of multiple classes and input files. Handle

Objective C type parameter declarations. Check Typedefs when
determining if a Type is a pointer or a function.
This commit is contained in:
Greg 2019-04-26 22:44:30 -04:00
parent 6135381cc7
commit 977a09e77e
17 changed files with 466 additions and 42 deletions

6
.gitignore vendored
View File

@ -1,6 +1,8 @@
clast
ast.txt
*.ast
simple
complex
program
examples/foundation/foundation
examples/foundation/ns
examples/simple/simple
examples/simple/simple/ClassOne

View File

@ -212,6 +212,8 @@ func Parse(fullline string) Node {
return parseObjCProtocol(line)
case "ObjCPropertyDecl":
return parseObjCPropertyDecl(line)
case "ObjCTypeParamDecl":
return parseObjCTypeParamDecl(line)
case "OffsetOfExpr":
return parseOffsetOfExpr(line)
case "PackedAttr":

View File

@ -0,0 +1,80 @@
package ast
import (
"strings"
)
// ObjCTypeParamDecl is node represents a parameter of variable declaration.
type ObjCTypeParamDecl struct {
Addr Address
Pos Position
Position2 string
Name string
Type string
Type2 string
IsReferenced bool
IsCovariant bool
IsBounded bool
ChildNodes []Node
}
func parseObjCTypeParamDecl(line string) *ObjCTypeParamDecl {
groups := groupsFromRegex(
`<(?P<position>.*)>
(?P<position2> [^ ]+:[\d:]+)?
(?P<referenced> referenced)?
(?P<name> \w+)?
(?P<covariant> covariant)?
(?P<bounded> bounded)?
'(?P<type>.*?)'
(?P<type2>:'.*?')?
`,
line,
)
type2 := groups["type2"]
if type2 != "" {
type2 = type2[2 : len(type2)-1]
}
if strings.Index(groups["position"], "<invalid sloc>") > -1 {
groups["position"] = "<invalid sloc>"
groups["position2"] = "<invalid sloc>"
}
return &ObjCTypeParamDecl{
Addr: ParseAddress(groups["address"]),
Pos: NewPositionFromString(groups["position"]),
Position2: strings.TrimSpace(groups["position2"]),
Name: strings.TrimSpace(groups["name"]),
Type: groups["type"],
Type2: type2,
IsReferenced: len(groups["referenced"]) > 0,
IsCovariant: len(groups["covariant"]) > 0,
IsBounded : len(groups["bounded"]) > 0,
ChildNodes: []Node{},
}
}
// AddChild adds a new child node. Child nodes can then be accessed with the
// Children attribute.
func (n *ObjCTypeParamDecl) 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 *ObjCTypeParamDecl) 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 *ObjCTypeParamDecl) Children() []Node {
return n.ChildNodes
}
// Position returns the position in the original source code.
func (n *ObjCTypeParamDecl) Position() Position {
return n.Pos
}

View File

@ -0,0 +1,18 @@
package main
import "C"
import (
"fmt"
"gitlab.wow.st/gmp/nswrap/examples/foundation/ns"
)
func main() {
fmt.Println("Started")
n := ns.StringWithUTF8String(ns.CharFromString("hi there"))
c := n.CapitalizedString()
gstring := c.UTF8String().String()
fmt.Println(gstring)
fmt.Println("ok")
}

View File

@ -0,0 +1,10 @@
InputFiles = [
"/System/Library/Frameworks/Foundation.framework/Headers/NSDictionary.h",
"/System/Library/Frameworks/Foundation.framework/Headers/NSString.h",
]
Classes = [
"NSString",
"NSDictionary",
]
SysImports = [ "Foundation/Foundation.h" ]
Pragma = [ 'clang diagnostic ignored "-Wformat-security"' ]

View File

@ -0,0 +1,103 @@
package ClassOne
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation
#import "simple.h"
ClassOne*
NewClassOne() {
return [ClassOne alloc];
}
int
ClassOne_geti1(void* obj) {
return [(id)obj geti1];
}
int*
ClassOne_getp1(void* obj) {
return [(id)obj getp1];
}
int
ClassOne_hi1(void* obj, struct stru in) {
return [(id)obj hi1:in];
}
int
ClassOne_hi2(void* obj, void* in) {
return [(id)obj hi2:in];
}
struct stru
ClassOne_nstru1(void* obj) {
return [(id)obj nstru1];
}
struct stru*
ClassOne_nstru2(void* obj) {
return [(id)obj nstru2];
}
ClassOne*
ClassOne_init(void* obj) {
return [(id)obj init];
}
*/
import "C"
import (
"unsafe"
)
//ClassOne*
type ClassOne struct { NSObject }
//NSObject*
type NSObject struct { ptr unsafe.Pointer }
//int
type Int C.int
//struct stru
type Stru C.struct_stru
func NewClassOne() *ClassOne {
ret := &ClassOne{}
ret.ptr = unsafe.Pointer(C.NewClassOne())
//ret = ret.Init()
return ret
}
func (o *ClassOne) Geti1() Int {
return (Int)(C.ClassOne_geti1(o.ptr))
}
func (o *ClassOne) Getp1() *Int {
return (*Int)(unsafe.Pointer(C.ClassOne_getp1(o.ptr)))
}
func (o *ClassOne) Hi1(in Stru) Int {
return (Int)(C.ClassOne_hi1(o.ptr, (C.struct_stru)(in)))
}
func (o *ClassOne) Hi2(in *Stru) Int {
return (Int)(C.ClassOne_hi2(o.ptr, unsafe.Pointer(in)))
}
func (o *ClassOne) Nstru1() Stru {
return (Stru)(C.ClassOne_nstru1(o.ptr))
}
func (o *ClassOne) Nstru2() *Stru {
return (*Stru)(unsafe.Pointer(C.ClassOne_nstru2(o.ptr)))
}
func (o *ClassOne) Init() *ClassOne {
ret := &ClassOne{}
ret.ptr = unsafe.Pointer(C.ClassOne_init(o.ptr))
return ret
}

View File

@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
struct stru {int a,b;};
@interface ClassOne : NSObject
{
int i1;
int *p1;
int a1[2];
int (*f)();
}
- (ClassOne*) init;
- (int) geti1;
- (int *) getp1;
- (int (*)()) getf1;
- (int) hi1:(struct stru)in;
- (int) hi2:(struct stru*)in;
- (struct stru) nstru1;
- (struct stru*) nstru2;
@end

View File

@ -0,0 +1,59 @@
#import "simple.h"
@implementation ClassOne
- (ClassOne*) init
{
ClassOne *ret;
ret = [ClassOne alloc];
ret->i1 = 12;
ret->p1 = malloc(sizeof(int));
*ret->p1 = 16;
ret->a1[0] = 4;
ret ->a1[1] = 5;
return ret;
}
- (int) geti1
{
return i1;
}
- (int *) getp1
{
return p1;
}
- (int (*)()) getf1
{
return f;
}
- (int) hi1:(struct stru)in
{
return in.a;
}
- (int) hi2:(struct stru*)in
{
return in->a;
}
- (struct stru) nstru1
{
struct stru ret;
ret.a = 7;
ret.b = 8;
return ret;
}
- (struct stru*) nstru2
{
struct stru* ret;
ret = malloc(sizeof(struct stru));
ret->a = 9;
ret->b = 10;
return ret;
}
@end

21
examples/simple/main.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"fmt"
"gitlab.wow.st/gmp/nswrap/examples/simple/ClassOne"
)
func main() {
o := ClassOne.NewClassOne().Init()
fmt.Println("i1 = ",o.Geti1())
fmt.Println("p1 = ",o.Getp1())
p1 := o.Getp1()
fmt.Println("*p1 = ", *p1)
*p1 = 17
fmt.Println("*p1 = ", *o.Getp1())
ns := o.Nstru1()
np := o.Nstru2()
fmt.Println(o.Hi1(ns))
fmt.Println(o.Hi2(np))
}

View File

@ -0,0 +1,4 @@
Package = "ClassOne"
InputFiles = [ "ClassOne/simple.h" ]
Classes = [ "ClassOne" ]
Imports = [ "simple.h" ]

25
main.go
View File

@ -9,12 +9,14 @@ import (
"github.com/BurntSushi/toml"
"gitlab.wow.st/gmp/nswrap/ast"
"gitlab.wow.st/gmp/nswrap/types"
"gitlab.wow.st/gmp/nswrap/wrap"
)
var Debug = false
type conf struct {
Package string
InputFiles []string
Classes []string
Imports []string
@ -163,17 +165,22 @@ func Start() (err error) {
// build tree
tree := buildTree(nodes, 0)
unit := tree[0]
//unit := tree[0]
w := wrap.NewWrapper(Debug)
w.Package = Config.Package
w.Import(Config.Imports)
w.SysImport(Config.SysImports)
w.Pragma(Config.Pragma)
for _, n := range(unit.Children()) {
switch x := n.(type) {
case *ast.ObjCInterfaceDecl:
w.AddInterface(x)
case *ast.ObjCCategoryDecl:
w.AddCategory(x)
for _, u := range tree {
for _, n := range(u.Children()) {
switch x := n.(type) {
case *ast.ObjCInterfaceDecl:
w.AddInterface(x)
case *ast.ObjCCategoryDecl:
w.AddCategory(x)
case *ast.TypedefDecl:
types.AddTypedef(x.Name,x.Type)
}
}
}
w.Wrap(Config.Classes)
@ -181,8 +188,8 @@ func Start() (err error) {
}
func main() {
if _, err := toml.DecodeFile("conf.toml",&Config); err != nil {
fmt.Printf("Cannot open config file conf.toml.\n")
if _, err := toml.DecodeFile("nswrap.toml",&Config); err != nil {
fmt.Printf("Cannot open config file nswrap.toml.\n")
os.Exit(-1)
}
if err := Start(); err != nil {

View File

@ -102,11 +102,6 @@ func NodeNamed(k string, p Parser) Parser {
// Combinators
//Id is the identity parser
func Id(s string, n *Node) (string, *Node) {
return s,n
}
//Opt optionally runs a Parser, returning the input node if it fails
func Opt(p Parser) Parser {
return func(s string, n *Node) (string, *Node) {

View File

@ -12,6 +12,12 @@ var super map[string]string
//go struct.
var wrapped map[string]bool
//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
var Typedefs map[string]*Type
func Super(c string) string {
if super == nil {
super = make(map[string]string)
@ -26,6 +32,23 @@ func SetSuper(c, p string) {
super[c] = p
}
func SetTypeParam(c, n, t string) {
if TypeParameters == nil {
TypeParameters = make(map[string]map[string]string)
}
if TypeParameters[c] == nil {
TypeParameters[c] = make(map[string]string)
}
TypeParameters[c][n] = t
}
func AddTypedef(n,t string) {
if Typedefs == nil {
Typedefs = make(map[string]*Type)
}
Typedefs[n] = NewTypeFromString(t,"")
}
type Type struct {
Node *Node
Class string
@ -42,12 +65,22 @@ func NewType(n *Node, c string) *Type {
func NewTypeFromString(t,c string) *Type {
n,err := Parse(t)
if n.CtypeSimplified() == "id" {
if n.IsId() {
//if n.CtypeSimplified() == "id" {
n,err = Parse("NSObject*")
}
if err != nil {
return &Type{}
}
if TypeParameters[c] != nil {
recur := false
for k,v := range TypeParameters[c] {
recur = n.renameTypedefs(k,v)
}
if recur {
return NewTypeFromString(n.Ctype(),c)
}
}
return &Type{
Node: n,
Class: c,
@ -117,19 +150,21 @@ func (t *Type) CTypeAttrib() string {
}
func (t *Type) _CType(attrib bool) string {
//if !attrib && c.ctype != "" ... FIXME?
if t.ctype != "" { // cache
return t.ctype
}
var ct string
if attrib {
ct = t.Node.Ctype()
ignore := map[string]bool { "GenericList": true }
ct = t.Node._Ctype(ignore)
} else {
ct = t.Node.CtypeSimplified()
}
ct = strings.ReplaceAll(ct,"instancename",t.Class)
ct = strings.ReplaceAll(ct,"instancetype",t.Class + " *")
if len(ct) > 1 && ct[:2] == "id" {
ct = "NSObject *" + ct[2:]
ct = "NSObject*" + ct[2:]
}
if len(ct) > 11 {
if ct[:12] == "instancename" { ct = t.Class + ct[12:] }
@ -148,14 +183,18 @@ func (t *Type) GoTypeDecl() string {
return t.GoInterfaceDecl()
}
tp := t.BaseType()
if tp.Node.IsId() {
return ""
}
gt := tp.GoType()
switch gt {
case "", "Void":
return ""
default:
return fmt.Sprintf(`
//%s
type %s %s
`,gt,tp.CGoType())
`,t.Node.CtypeSimplified(),gt,tp.CGoType())
}
}
@ -170,12 +209,27 @@ func (t *Type) GoInterfaceDecl() string {
super = "ptr unsafe.Pointer"
}
return fmt.Sprintf(`
//%s
%stype %s struct { %s }
`,x,gt,super)
`,t.CTypeAttrib(),x,gt,super)
}
func (t *Type) IsFunction() bool {
if td,ok := Typedefs[t.BaseType().CType()]; ok {
return td.IsFunction()
}
return t.Node.IsFunction()
}
func (t *Type) IsPointer() bool {
if td,ok := Typedefs[t.BaseType().CType()]; ok {
return td.IsPointer()
}
return t.Node.IsPointer()
}
func (t *Type) CToGo(cval string) string { // cast C value to CGo
if t.Node.IsPointer() {
if t.IsPointer() {
cval = "unsafe.Pointer(" + cval + ")"
}
return fmt.Sprintf("(%s)(%s)",t.GoType(),cval)

View File

@ -30,6 +30,7 @@ func AbstractDeclarator(s string, n *Node) (string, *Node) {
Opt(Pointer),
OneOrMore(DirectAbstractDeclarator)),
Pointer,
Id,
Block,
)(s,n)
}
@ -136,6 +137,14 @@ func NullableAnnotation(s string, n *Node) (string, *Node) {
))(s,n)
}
func Id(s string, n *Node) (string, *Node) {
return Seq(
NodeNamed("Id",Lit("id")),
Opt(TypeQualifierList),
Opt(NullableAnnotation),
)(s,n)
}
func Pointer(s string, n *Node) (string, *Node) {
return Seq(
NodeNamed("Pointer",Lit("*")),

View File

@ -96,7 +96,7 @@ func (n *Node) PointsTo() *Node {
//IsPointer returns true if the node is a pointer
func (n *Node) IsPointer() bool {
return n.PointsTo() != nil
return n.IsId() || n.PointsTo() != nil
}
//ArrayOf, when called on an array node returns a node describing the type
@ -126,6 +126,13 @@ func (n *Node) IsFunction() bool {
return n.Children[len(n.Children)-1].Kind == "Function"
}
func (n *Node) IsId() bool {
if n == nil || len(n.Children) < 1 {
return false
}
return n.Children[0].Kind == "TypedefName" && n.Children[0].Content == "id"
}
//BaseType strips off all layers of pointer indirection
func (n *Node) BaseType() *Node {
if n == nil {

View File

@ -67,6 +67,24 @@ func (n *Node) AddChild(c *Node) *Node {
return n
}
//returns true if anything gets renamed
func (n *Node) renameTypedefs(a,b string) (ret bool) {
ret = false
if n == nil { return }
for i,c := range n.Children {
if c.Kind == "TypedefName" && c.Content == a {
ret = true
n.Children[i] = NewNode("TypedefName", b)
n.Children[i].Children = c.Children
}
if len(c.Children) > 0 {
ret2 := c.renameTypedefs(a,b)
ret = ret || ret2
}
}
return
}
func (n *Node) CtypeSimplified() string {
ignore := map[string]bool{
"NullableAnnotation": true,

View File

@ -2,6 +2,8 @@ package wrap
import (
"fmt"
"os"
"path"
"strings"
"gitlab.wow.st/gmp/nswrap/ast"
@ -13,6 +15,7 @@ var (
)
type Wrapper struct {
Package string
Interfaces map[string]Interface
cCode strings.Builder // put cGo code here
@ -81,7 +84,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.Node.IsFunction() {
if p.Type.IsFunction() {
return true
}
}
@ -224,6 +227,9 @@ func (w *Wrapper) add(name string, ns []ast.Node) {
//fmt.Printf("ast.ObjCInterface: %s inherits from %s\n",name,x.Name)
types.SetSuper(name,x.Name)
}
case *ast.ObjCTypeParamDecl:
//fmt.Printf("ObjCTypeParamDecl: %s = %s\n",x.Name,x.Type)
types.SetTypeParam(name,x.Name,x.Type)
case *ast.Unknown:
//fmt.Printf("(*ast.Unkonwn %s: %s)\n",x.Name,x.Content)
default:
@ -312,7 +318,7 @@ func (w *Wrapper) processType(tp *types.Type) {
bt := tp.BaseType()
if w.Processed[bt.GoType()] { return }
w.Processed[bt.GoType()] = true
if bt.Node.IsFunction() {
if bt.IsFunction() {
return
}
w.goTypes.WriteString(tp.GoTypeDecl())
@ -345,17 +351,26 @@ func (c *Char) String() string {
func (w *Wrapper) Wrap(toproc []string) {
if w.Package == "" { w.Package = "ns" }
err := os.MkdirAll(w.Package,0755)
if err != nil {
fmt.Printf("Error creating directory '%s'\n%s\n",w.Package,err)
os.Exit(-1)
}
of,err := os.Create(path.Join(w.Package,"main.go"))
if err != nil {
fmt.Printf("Error opening file %s\n%s\n",path.Join(w.Package,"main.go"),err)
os.Exit(-1)
}
fmt.Printf("Writing output to %s\n",path.Join(w.Package,"main.go"))
pInterfaces := map[string]Interface{}
for _,iface := range toproc {
pInterfaces[iface] = w.Interfaces[iface]
}
//FIXME: sort pInterfaces
for iname,i := range pInterfaces {
if Debug {
fmt.Printf("Interface %s: %d properties, %d methods\n",
iname, len(i.Properties), len(i.Methods))
}
for _,i := range pInterfaces {
fmt.Printf("Interface %s: %d properties, %d methods\n",
i.Name, len(i.Properties), len(i.Methods))
w.goCode.WriteString(fmt.Sprintf(`
func New%s() *%s {
@ -384,16 +399,14 @@ New%s() {
if Debug {
fmt.Printf(" method: %s (%s)\n", m.Name, m.Type)
}
if m.Type.Node.IsFunction() {
if m.Type.IsFunction() {
continue
}
if m.hasFunctionParam() {
continue
}
gname := strings.Title(m.Name)
if m.ClassMethod {
gname = i.Name + gname
} else {
if !m.ClassMethod {
gname = "(o *" + i.Name + ") " + gname
}
cname := i.Name + "_" + m.Name
@ -429,10 +442,9 @@ func %s(%s) %s {
}`, cmtype, cname, w.cparamlist(m), cret, cobj, w.objcparamlist(m)))
}
}
fmt.Println(`package main
`)
fmt.Println(w.cCode.String())
fmt.Println(`
of.WriteString("package " + w.Package + "\n\n")
of.WriteString(w.cCode.String())
of.WriteString(`
*/
import "C"
@ -440,7 +452,8 @@ import (
"unsafe"
)
`)
fmt.Println(w.goTypes.String())
fmt.Println(w.goHelpers.String())
fmt.Println(w.goCode.String())
of.WriteString(w.goTypes.String())
of.WriteString(w.goHelpers.String())
of.WriteString(w.goCode.String())
of.Close()
}