Slight refactor to improve output

This commit is contained in:
Lea Anthony
2020-11-05 11:48:42 +11:00
parent 8a854f000f
commit 9cbb0b75e2
8 changed files with 245 additions and 63 deletions

View File

@@ -1,8 +1,10 @@
package backendjs
import "go/ast"
import (
"go/ast"
)
func (p *Parser) parseAssignment(assignStmt *ast.AssignStmt) {
func (p *Parser) parseAssignment(assignStmt *ast.AssignStmt, pkg *Package) {
for _, rhs := range assignStmt.Rhs {
ce, ok := rhs.(*ast.CallExpr)
if ok {
@@ -34,7 +36,7 @@ func (p *Parser) parseAssignment(assignStmt *ast.AssignStmt) {
if ok {
// Store the variable -> Function mapping
// so we can later resolve the type
p.variablesThatWereAssignedByFunctions[i.Name] = fe.Name
pkg.variablesThatWereAssignedByFunctions[i.Name] = fe.Name
}
}
}
@@ -51,7 +53,20 @@ func (p *Parser) parseAssignment(assignStmt *ast.AssignStmt) {
if len(assignStmt.Lhs) == 1 {
i, ok := assignStmt.Lhs[0].(*ast.Ident)
if ok {
p.variablesThatWereAssignedByStructLiterals[i.Name] = t.Name
pkg.variablesThatWereAssignedByStructLiterals[i.Name] = t.Name
}
}
}
}
} else {
cl, ok := rhs.(*ast.CompositeLit)
if ok {
t, ok := cl.Type.(*ast.Ident)
if ok {
if len(assignStmt.Lhs) == 1 {
i, ok := assignStmt.Lhs[0].(*ast.Ident)
if ok {
pkg.variablesThatWereAssignedByStructLiterals[i.Name] = t.Name
}
}
}

View File

@@ -2,7 +2,7 @@ package backendjs
import "go/ast"
func (p *Parser) parseCallExpressions(x *ast.CallExpr) {
func (p *Parser) parseCallExpressions(x *ast.CallExpr, pkg *Package) {
f, ok := x.Fun.(*ast.SelectorExpr)
if ok {
n, ok := f.X.(*ast.Ident)
@@ -14,9 +14,7 @@ func (p *Parser) parseCallExpressions(x *ast.CallExpr) {
if ok {
fn, ok := ce.Fun.(*ast.Ident)
if ok {
// We found a bind method using a function call
// EG: app.Bind( newMyStruct() )
p.structMethodsThatWereBound.Add(fn.Name)
pkg.structMethodsThatWereBound.Add(fn.Name)
}
} else {
// We also want to check for Bind( &MyStruct{} )
@@ -27,8 +25,7 @@ func (p *Parser) parseCallExpressions(x *ast.CallExpr) {
if ok {
t, ok := cl.Type.(*ast.Ident)
if ok {
// We have found Bind( &MyStruct{} )
p.structPointerLiteralsThatWereBound.Add(t.Name)
pkg.structPointerLiteralsThatWereBound.Add(t.Name)
}
}
}
@@ -40,7 +37,8 @@ func (p *Parser) parseCallExpressions(x *ast.CallExpr) {
if ok {
t, ok := cl.Type.(*ast.Ident)
if ok {
p.structLiteralsThatWereBound.Add(t.Name)
pkg.structLiteralsThatWereBound.Add(t.Name)
}
} else {
// Also check for when we bind a variable
@@ -48,7 +46,7 @@ func (p *Parser) parseCallExpressions(x *ast.CallExpr) {
// app.Bind( myVariable )
i, ok := x.Args[0].(*ast.Ident)
if ok {
p.variablesThatWereBound.Add(i.Name)
pkg.variablesThatWereBound.Add(i.Name)
}
}
}

View File

@@ -132,7 +132,7 @@ func (p *Parser) parseFunctionDeclaration(funcDecl *ast.FuncDecl, pkg *Package)
s, ok := t.X.(*ast.Ident)
if ok {
// println("*** Function", functionName, "found which returns: *"+s.Name)
p.functionsThatReturnStructPointers[functionName] = s.Name
pkg.functionsThatReturnStructPointers[functionName] = s.Name
}
} else {
// Check for functions that return a struct
@@ -140,7 +140,7 @@ func (p *Parser) parseFunctionDeclaration(funcDecl *ast.FuncDecl, pkg *Package)
t, ok := funcDecl.Type.Results.List[0].Type.(*ast.Ident)
if ok {
// println("*** Function", functionName, "found which returns: "+t.Name)
p.functionsThatReturnStructs[functionName] = t.Name
pkg.functionsThatReturnStructs[functionName] = t.Name
}
}
}

View File

@@ -4,17 +4,22 @@
{{- range .DeclarationReferences}}
/// <reference types="../{{.}}" />{{- end}}
declare module {{.Name}} { {{range .Structs}}
{{if .Comments }}{{range .Comments}}// {{ . }}{{end}}{{end}}
interface {{.Name}} { {{ if $.StructIsUsedAsData .Name }}
declare module {{.Name}} { {{- range .Structs}}
{{- $usedAsData := $.StructIsUsedAsData .Name }}
{{- if or .IsBound $usedAsData}}
{{- if .Comments }}{{range .Comments}}// {{ . }}{{end}}{{- end}}
interface {{.Name}} { {{ if $usedAsData }}
{{- range .Fields}}{{if .Comments }}
{{range .Comments}}//{{ . }}{{end}}{{end}}
{{range .Comments}}//{{ . }}{{end}}{{- end}}
{{.Name}}: {{.TypeAsTSType}}; {{- end}} {{ end }}
{{- if .IsBound }}
{{- range .Methods}}
{{- range .Comments}}
// {{ . }}{{- end}}
{{.Name}}({{.InputsAsTSText}}): Promise<{{.OutputsAsTSText}}>;
{{- end}}
}{{end}}
{{- end}}{{end}}
}{{- end}}
{{- end}}
}

View File

@@ -25,12 +25,46 @@ type Package struct {
// These are the structs declared in this package
// that are used as data by either this or other packages
structsUsedAsData slicer.StringSlicer
// A list of functions that return struct pointers
functionsThatReturnStructPointers map[string]string
// A list of functions that return structs
functionsThatReturnStructs map[string]string
// A list of struct literals that were bound to the application
// EG: app.Bind( &mystruct{} )
structLiteralsThatWereBound slicer.StringSlicer
// A list of struct pointer literals that were bound to the application
// EG: app.Bind( &mystruct{} )
structPointerLiteralsThatWereBound slicer.StringSlicer
// A list of methods that returns structs to the Bind method
// EG: app.Bind( newMyStruct() )
structMethodsThatWereBound slicer.StringSlicer
// A list of variables that were used for binding
// Eg: myVar := &mystruct{}; app.Bind( myVar )
variablesThatWereBound slicer.StringSlicer
// A list of variables that were assigned using a function call
// EG: myVar := newStruct()
variablesThatWereAssignedByFunctions map[string]string
// A map of variables that were assigned using a struct literal
// EG: myVar := MyStruct{}
variablesThatWereAssignedByStructLiterals map[string]string
}
func (p *Parser) parsePackage(pkg *packages.Package, fset *token.FileSet) (*Package, error) {
result := &Package{
Name: pkg.Name,
Structs: make(map[string]*Struct),
Name: pkg.Name,
Structs: make(map[string]*Struct),
functionsThatReturnStructPointers: make(map[string]string),
functionsThatReturnStructs: make(map[string]string),
variablesThatWereAssignedByFunctions: make(map[string]string),
variablesThatWereAssignedByStructLiterals: make(map[string]string),
}
// Get the absolute path to the project's main.go file
@@ -74,12 +108,12 @@ func (p *Parser) parsePackage(pkg *packages.Package, fset *token.FileSet) (*Pack
// Capture call expressions
if callExpr, ok := n.(*ast.CallExpr); ok {
p.parseCallExpressions(callExpr)
p.parseCallExpressions(callExpr, result)
}
// Parse Assignments
if assignStmt, ok := n.(*ast.AssignStmt); ok {
p.parseAssignment(assignStmt)
p.parseAssignment(assignStmt, result)
}
// Parse Function declarations
@@ -158,3 +192,27 @@ func (p *Package) DeclarationReferences() []string {
func (p *Package) StructIsUsedAsData(structName string) bool {
return p.structsUsedAsData.Contains(structName)
}
func (p *Package) resolveBoundStructLiterals() {
p.structLiteralsThatWereBound.Each(func(structName string) {
strct := p.Structs[structName]
if strct == nil {
println("Warning: Cannot find bound struct", structName, "in package", p.Name)
return
}
println("Bound struct", strct.Name, "in package", p.Name)
strct.IsBound = true
})
}
func (p *Package) resolveBoundStructPointerLiterals() {
p.structPointerLiteralsThatWereBound.Each(func(structName string) {
strct := p.Structs[structName]
if strct == nil {
println("Warning: Cannot find bound struct", structName, "in package", p.Name)
return
}
println("Bound struct pointer", strct.Name, "in package", p.Name)
strct.IsBound = true
})
}

View File

@@ -1,7 +1,9 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
{{range $struct := .Structs }} {{ if .Methods }}
{{range $struct := .Structs }}
{{- if .IsBound }}
{{ if .Methods }}
export const {{.Name}} = {
{{range .Methods }}
/**{{if .Comments }}
@@ -18,6 +20,7 @@ export const {{.Name}} = {
{{end}}
}
{{end}}
{{- end}}
{{end}}

View File

@@ -1,6 +1,7 @@
package backendjs
import (
"github.com/davecgh/go-spew/spew"
"github.com/leaanthony/slicer"
)
@@ -17,52 +18,154 @@ type Parser struct {
// import mywails "github.com/wailsapp/wails/v2" -> mywails
wailsPackageVariable string
// A list of methods that returns structs to the Bind method
// EG: app.Bind( newMyStruct() )
structMethodsThatWereBound slicer.StringSlicer
// A list of struct literals that were bound to the application
// EG: app.Bind( &mystruct{} )
structLiteralsThatWereBound slicer.StringSlicer
// A list of struct pointer literals that were bound to the application
// EG: app.Bind( &mystruct{} )
structPointerLiteralsThatWereBound slicer.StringSlicer
// A list of variables that were used for binding
// Eg: myVar := &mystruct{}; app.Bind( myVar )
variablesThatWereBound slicer.StringSlicer
// A list of variables that were assigned using a function call
// EG: myVar := newStruct()
variablesThatWereAssignedByFunctions map[string]string
// A map of variables that were assigned using a struct literal
// EG: myVar := MyStruct{}
variablesThatWereAssignedByStructLiterals map[string]string
// Internal methods (WailsInit/WailsShutdown)
internalMethods *slicer.StringSlicer
// A list of functions that return struct pointers
functionsThatReturnStructPointers map[string]string
// A list of functions that return structs
functionsThatReturnStructs map[string]string
}
// NewParser creates a new Wails Project parser
func NewParser() *Parser {
return &Parser{
Packages: make(map[string]*Package),
variablesThatWereAssignedByFunctions: make(map[string]string),
variablesThatWereAssignedByStructLiterals: make(map[string]string),
functionsThatReturnStructPointers: make(map[string]string),
functionsThatReturnStructs: make(map[string]string),
internalMethods: slicer.String([]string{"WailsInit", "WailsShutdown"}),
Packages: make(map[string]*Package),
internalMethods: slicer.String([]string{"WailsInit", "WailsShutdown"}),
}
}
func (p *Parser) resolve() error {
// Resolve bound structs
err := p.resolveBoundStructs()
if err != nil {
return err
}
return nil
}
func (p *Parser) resolveBoundStructs() error {
// Resolve Struct Literals
p.resolveBoundStructLiterals()
// Resolve Struct Pointer Literals
p.resolveBoundStructPointerLiterals()
// Resolve functions that were bound
// EG: app.Bind( newBasic() )
p.resolveBoundFunctions()
// Resolve variables that were bound
p.resolveBoundVariables()
return nil
}
func (p *Parser) resolveBoundStructLiterals() {
// Resolve struct literals in each package
for _, pkg := range p.Packages {
pkg.resolveBoundStructLiterals()
}
}
func (p *Parser) resolveBoundStructPointerLiterals() {
// Resolve struct pointer literals
for _, pkg := range p.Packages {
pkg.resolveBoundStructPointerLiterals()
}
}
func (p *Parser) resolveBoundFunctions() {
// Loop over packages
for _, pkg := range p.Packages {
// Iterate over the method names
pkg.structMethodsThatWereBound.Each(func(functionName string) {
println("Resolving: ", functionName)
// Resolve each method name
structName := p.resolveFunctionReturnType(pkg, functionName)
strct := pkg.Structs[structName]
if strct == nil {
println("WARNING: Unable to find definition for struct", structName)
}
strct.IsBound = true
})
}
}
// resolveFunctionReturnType gets the return type for the given package/function name combination
func (p *Parser) resolveFunctionReturnType(pkg *Package, functionName string) string {
structName := pkg.functionsThatReturnStructPointers[functionName]
if structName == "" {
spew.Dump(pkg.functionsThatReturnStructs)
structName = pkg.functionsThatReturnStructs[functionName]
}
if structName == "" {
println("WARNING: Unable to resolve bound function", functionName, "in package", pkg.Name)
}
return structName
}
func (p *Parser) markStructAsBound(pkg *Package, structName string) {
strct := pkg.Structs[structName]
if strct == nil {
println("WARNING: Unable to find definition for struct", structName)
}
println("Found bound struct:", strct.Name)
strct.IsBound = true
}
func (p *Parser) resolveBoundVariables() {
for _, pkg := range p.Packages {
// Iterate over the method names
pkg.variablesThatWereBound.Each(func(variableName string) {
println("Resolving variable: ", variableName)
var structName string
// Resolve each method name
funcName := pkg.variablesThatWereAssignedByFunctions[variableName]
if funcName != "" {
// Found function name - resolve Function return type
structName = p.resolveFunctionReturnType(pkg, funcName)
}
// If we couldn't resolve to a function, then let's try struct literals
if structName == "" {
funcName = pkg.variablesThatWereAssignedByStructLiterals[variableName]
if funcName != "" {
// Found function name - resolve Function return type
structName = p.resolveFunctionReturnType(pkg, funcName)
}
}
if structName == "" {
println("WARNING: Unable to resolve bound variable", variableName, "in package", pkg.Name)
return
}
p.markStructAsBound(pkg, structName)
})
}
}
func (p *Parser) bindStructByStructName(sn *StructName) {
// Get package
pkg := p.Packages[sn.Package]
if pkg == nil {
// Ignore, it will get picked up by the compiler
return
}
strct := pkg.Structs[sn.Name]
if strct == nil {
// Ignore, it will get picked up by the compiler
return
}
println("Found bound Struct:", sn.ToString())
strct.IsBound = true
}

View File

@@ -29,7 +29,7 @@ type StructName struct {
Package string
}
// ToString returns a text representation of the struct name
// ToString returns a text representation of the struct anme
func (s *StructName) ToString() string {
result := ""
if s.Package != "" {
@@ -48,7 +48,7 @@ type Field struct {
// JSType returns the Javascript type for this field
func (f *Field) JSType() string {
return goTypeToJS(f)
return string(goTypeToJS(f))
}
// TypeAsTSType converts the Field type to something TS wants