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 }