status-go/cmd/library/main.go

147 lines
3.2 KiB
Go
Raw Permalink Normal View History

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
}