-
Notifications
You must be signed in to change notification settings - Fork 241
/
main.go
146 lines (124 loc) · 3.17 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"unicode"
)
func isCodeFile(info os.FileInfo) bool {
return !strings.HasSuffix(info.Name(), "test.go")
}
func main() {
var output = prelude
fset := token.NewFileSet()
// Parse the whole `mobile/` directory, excluding test files
parsedAST, err := parser.ParseDir(fset, "mobile/", isCodeFile, parser.AllErrors)
if err != nil {
fmt.Printf("Error parsing directory: %+v\n", err)
os.Exit(1)
}
for _, a := range parsedAST {
for _, file := range a.Files {
// handle each file and append the output
output += handleFile(file)
}
}
// To free memory allocated to strings
output += "//export Free\n"
output += "func Free (param unsafe.Pointer){\n"
output += "C.free(param);\n"
output += "}\n"
fmt.Println(output)
}
func handleFunction(name string, funcDecl *ast.FuncDecl) string {
params := funcDecl.Type.Params.List
results := funcDecl.Type.Results
// add export tag
output := fmt.Sprintf("//export %s\n", name)
// add initial func declaration
output += fmt.Sprintf("func %s (", name)
// iterate over parameters and correctly add the C type
paramCount := 0
for _, p := range params {
for _, paramIdentity := range p.Names {
if paramCount != 0 {
output += ", "
}
paramCount++
output += paramIdentity.Name
typeString := fmt.Sprint(paramIdentity.Obj.Decl.(*ast.Field).Type)
// We match against the stringified type,
// could not find a better way to match this
switch typeString {
case stringType:
output += " *C.char"
case intType, boolType:
output += " C.int"
case unsafePointerType:
output += " unsafe.Pointer"
default:
// ignore if the type is any different
return ""
}
}
}
output += ")"
// check if it has a return value, convert to CString if so and return
if results != nil {
output += " *C.char {\nreturn C.CString("
} else {
output += " {\n"
}
// call the mobile equivalent function
output += fmt.Sprintf("mobile.%s(", name)
// iterate through the parameters, convert to go types and close
// the function call
paramCount = 0
for _, p := range params {
for _, paramIdentity := range p.Names {
if paramCount != 0 {
output += ", "
}
paramCount++
typeString := fmt.Sprint(paramIdentity.Obj.Decl.(*ast.Field).Type)
switch typeString {
case stringType:
output += fmt.Sprintf("C.GoString(%s)", paramIdentity.Name)
case intType:
output += fmt.Sprintf("int(%s)", paramIdentity.Name)
case unsafePointerType:
output += paramIdentity.Name
case boolType:
output += paramIdentity.Name
// convert int to bool
output += " == 1"
default:
// ignore otherwise
return ""
}
}
}
// close function call
output += ")"
// close conversion to CString
if results != nil {
output += ")\n"
}
// close function declaration
output += "}\n"
return output
}
func handleFile(parsedAST *ast.File) string {
output := ""
for name, obj := range parsedAST.Scope.Objects {
// Ignore non-functions or non exported fields
if obj.Kind != ast.Fun || !unicode.IsUpper(rune(name[0])) {
continue
}
output += handleFunction(name, obj.Decl.(*ast.FuncDecl))
}
return output
}