Created
May 16, 2018 02:56
-
-
Save matiasinsaurralde/861ceece0716f84af1c7b1b0891f588a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package generator | |
import ( | |
"bytes" | |
"fmt" | |
"go/ast" | |
"html/template" | |
"regexp" | |
"strconv" | |
"strings" | |
) | |
const ( | |
commentPrefix = "// " | |
delegatePrefix = "create_delegate" | |
delegateGoTemplate = ` | |
func {{.MethodName}}({{.Recv}}) {{.Results}} { | |
output := {{.Call}} | |
return {{.Ret}} | |
} | |
` | |
delegateCTemplate = ` | |
{{.Results}} {{.MethodName}}({{.Recv}}) { | |
{{.Call}} | |
return {{.Ret}}; | |
} | |
` | |
) | |
var ( | |
delegateExpr = regexp.MustCompile(`(.*)\s(.*)\s(.*)\((.*)\)\s?(.*)`) | |
) | |
// DelegateAnnotation contains parameters required for generating delegate code | |
type DelegateAnnotation struct { | |
AssemblyName string | |
TypeName string | |
MethodName string | |
*ast.FuncDecl | |
*Input | |
} | |
type delegateGoTemplateData struct { | |
Recv string | |
Results string | |
Call string | |
Ret string | |
*DelegateAnnotation | |
} | |
type delegateCTemplateData struct { | |
Results string | |
Recv string | |
Call string | |
Ret string | |
*DelegateAnnotation | |
} | |
// Render compiles the template using the available info. | |
// TODO: avoid dup code when matching types | |
func (d DelegateAnnotation) Render() string { | |
// Initialize the template data structures: | |
goTemplateData := delegateGoTemplateData{DelegateAnnotation: &d} | |
CTemplateData := delegateCTemplateData{DelegateAnnotation: &d} | |
// Build app the function parameters: | |
CParams := []string{} | |
params := []string{} | |
callParams := []string{} | |
for _, p := range d.FuncDecl.Type.Params.List { | |
paramName := p.Names[0].Name | |
t := fmt.Sprintf("%v", p.Type) | |
var goType string | |
var cType string | |
var cgoWrap string | |
switch t { | |
case "int": | |
cgoWrap = "C.int" | |
goType = "int" | |
cType = "int" | |
default: | |
log.Fatalf("Unsupported type '%s' in function '%s'\n", t, d.Name.Name) | |
} | |
param := fmt.Sprintf("%s %s", paramName, goType) | |
params = append(params, param) | |
CParam := fmt.Sprintf("%s %s", cType, paramName) | |
CParams = append(CParams, CParam) | |
// Append the cgo wrapper if it's available: | |
if cgoWrap != "" { | |
callParams = append(callParams, fmt.Sprintf("%s(%s)", cgoWrap, paramName)) | |
} | |
} | |
goTemplateData.Recv = strings.Join(params, ", ") | |
CTemplateData.Recv = strings.Join(CParams, ", ") | |
// Build the main cgo call: | |
cgoCall := bytes.NewBufferString("C.call(") | |
cgoCall.WriteString(strings.Join(callParams, ", ")) | |
cgoCall.WriteString(")") | |
// TODO: handle named results, multiple results: | |
results := []string{} | |
CResults := []string{} | |
if d.FuncDecl.Type.Results != nil { | |
for _, p := range d.FuncDecl.Type.Results.List { | |
var resultName string | |
if len(p.Names) > 0 { | |
resultName = p.Names[0].Name | |
} | |
t := fmt.Sprintf("%v", p.Type) | |
var goType string | |
var cType string | |
switch t { | |
case "int": | |
goType = "int" | |
cType = "int" | |
default: | |
log.Fatalf("Unsupported type '%s' in function '%s'\n", t, d.Name.Name) | |
} | |
result := fmt.Sprintf("%s %s", resultName, goType) | |
results = append(results, result) | |
CResult := fmt.Sprintf("%s %s", resultName, cType) | |
CResults = append(CResults, CResult) | |
} | |
goTemplateData.Results = strings.TrimSpace(strings.Join(results, ", ")) | |
CTemplateData.Results = strings.TrimSpace(strings.Join(CResults, ", ")) | |
} | |
goTemplateData.Call = cgoCall.String() | |
// Build return statement: | |
goTemplateData.Ret = "output" | |
// Write glue code and bindings: | |
Ccall := bytes.Buffer{} | |
Ccall.WriteString(CResults[0]) | |
delegateFuncName := fmt.Sprintf("createDelegate%s(%s, %s, %s)", | |
d.MethodName, | |
strconv.Quote(d.AssemblyName), | |
strconv.Quote(d.TypeName), | |
strconv.Quote(d.MethodName)) | |
fmt.Println("*** ", Ccall.String()) | |
fmt.Println("*** ", delegateFuncName) | |
/* | |
#include "binding.hpp" | |
int call(int s) { | |
int result = createDelegateHelloWorld("HelloWorld", "HelloWorld.HelloWorld", "Hello", 1, s); | |
return result; | |
} | |
*/ | |
cOutput := bytes.Buffer{} | |
cTemplate := template.Must(template.New("delegate_c").Parse(delegateCTemplate)) | |
err := cTemplate.Execute(&cOutput, CTemplateData) | |
if err != nil { | |
log.WithError(err).Fatal("Couldn't generate C code") | |
} | |
cOutput.WriteTo(d.glueCode) | |
// Render the function code: | |
// TODO: cache the templates | |
goOutput := bytes.Buffer{} | |
goTemplate := template.Must(template.New("delegate_go").Parse(delegateGoTemplate)) | |
err = goTemplate.Execute(&goOutput, goTemplateData) | |
if err != nil { | |
log.WithError(err).Fatal("Couldn't generate Go code") | |
} | |
return goOutput.String() | |
} | |
// Annotation is an interface. | |
type Annotation interface { | |
Render() string | |
} | |
// TODO: implement error handling | |
func (i *Input) parseFuncAnnotation(s string, f *ast.FuncDecl) Annotation { | |
s = strings.TrimLeft(s, commentPrefix) | |
var annotation Annotation | |
if strings.HasPrefix(s, delegatePrefix) { | |
prefix := fmt.Sprintf("//%s:", delegatePrefix) | |
s = strings.TrimLeft(s, prefix) | |
s = strings.TrimSpace(s) | |
submatches := delegateExpr.FindAllStringSubmatch(s, -1) | |
matches := submatches[0] | |
annotation = &DelegateAnnotation{ | |
AssemblyName: matches[1], | |
TypeName: matches[2], | |
MethodName: matches[3], | |
FuncDecl: f, | |
Input: i, | |
} | |
} | |
return annotation | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment