From ae1f7718797239cc13ca2201c3e2dcb5d92d5184 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 1 Nov 2020 07:29:45 +1100 Subject: [PATCH] Support Array types --- .../build/internal/backendjs/conversion.go | 19 ++- .../build/internal/backendjs/parse.go | 144 +++++++++++++++--- 2 files changed, 134 insertions(+), 29 deletions(-) diff --git a/v2/pkg/commands/build/internal/backendjs/conversion.go b/v2/pkg/commands/build/internal/backendjs/conversion.go index bed88fbc..c24c4c1e 100644 --- a/v2/pkg/commands/build/internal/backendjs/conversion.go +++ b/v2/pkg/commands/build/internal/backendjs/conversion.go @@ -40,16 +40,17 @@ func goTypeToJS(input string) JSType { } } -func goTypeToTS(input string) string { - switch input { +func goTypeToTS(input *ParsedParameter) string { + var result string + switch input.Type { case "string": - return "string" + result = "string" case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": - return "number" + result = "number" case "float32", "float64": - return "number" + result = "number" case "bool": - return "boolean" + result = "boolean" // case reflect.Array, reflect.Slice: // return string(JsArray) // case reflect.Ptr, reflect.Struct: @@ -61,4 +62,10 @@ func goTypeToTS(input string) string { println("UNSUPPORTED: ", input) return JsUnsupported } + + if input.IsArray { + result = "Array<" + result + ">" + } + + return result } diff --git a/v2/pkg/commands/build/internal/backendjs/parse.go b/v2/pkg/commands/build/internal/backendjs/parse.go index 8735b303..f9d4d1a4 100644 --- a/v2/pkg/commands/build/internal/backendjs/parse.go +++ b/v2/pkg/commands/build/internal/backendjs/parse.go @@ -3,6 +3,7 @@ package backendjs import ( "fmt" "go/ast" + "os" "strings" "github.com/leaanthony/slicer" @@ -16,19 +17,24 @@ type Parser struct { boundStructLiterals slicer.StringSlicer boundMethods []string + boundStructs map[string]*ParsedStruct boundStructPointerLiterals []string boundVariables slicer.StringSlicer - variableFunctionDecls map[string]string - variableStructDecls map[string]string - internalMethods slicer.StringSlicer + + variableFunctionDecls map[string]string + variableStructDecls map[string]string + + internalMethods slicer.StringSlicer + structCache map[string]*ParsedStruct structPointerFunctionDecls map[string]string structFunctionDecls map[string]string } type ParsedParameter struct { - Name string - Type string + Name string + Type string + IsArray bool } func (p *ParsedParameter) JSType() string { @@ -49,7 +55,7 @@ func (m *ParsedMethod) InputsAsTSText() string { var inputs []string for _, input := range m.Inputs { - inputText := fmt.Sprintf("%s: %s", input.Name, goTypeToTS(input.Type)) + inputText := fmt.Sprintf("%s: %s", input.Name, goTypeToTS(input)) inputs = append(inputs, inputText) } @@ -79,15 +85,7 @@ func (m *ParsedMethod) OutputsAsTSText() string { var result []string for _, output := range m.Returns { - jsType := goTypeToJS(output.Type) - switch jsType { - case JsArray: - result = append(result, "Array") - case JsObject: - result = append(result, "any") - default: - result = append(result, string(jsType)) - } + result = append(result, goTypeToTS(output)) } return strings.Join(result, ", ") } @@ -105,6 +103,7 @@ func NewParser() *Parser { structCache: make(map[string]*ParsedStruct), structPointerFunctionDecls: make(map[string]string), structFunctionDecls: make(map[string]string), + boundStructs: make(map[string]*ParsedStruct), } } @@ -126,11 +125,11 @@ func parseProject(projectPath string) ([]*Package, error) { var result []*Package + p := NewParser() + // Iterate the packages for _, pkg := range pkgs { - p := NewParser() - thisPackage, err := p.parsePackage(pkg) if err != nil { return nil, err @@ -144,6 +143,12 @@ func parseProject(projectPath string) ([]*Package, error) { } + // Resolve links between data + err = p.Resolve() + if err != nil { + return nil, err + } + return result, nil } @@ -344,15 +349,31 @@ func (p *Parser) parseFile(file *ast.File) error { if x.Type.Results != nil { for _, outputField := range x.Type.Results.List { + // Check for basic types t, ok := outputField.Type.(*ast.Ident) if !ok { - continue - } - if len(outputField.Names) == 0 { - structMethod.Returns = append(structMethod.Returns, &ParsedParameter{Type: t.Name}) + // Check for arrays + a, ok := outputField.Type.(*ast.ArrayType) + if ok { + // spew.Dump(a) + ident := a.Elt.(*ast.Ident) + if len(outputField.Names) == 0 { + structMethod.Returns = append(structMethod.Returns, &ParsedParameter{Type: ident.Name, IsArray: true}) + } else { + for _, name := range outputField.Names { + structMethod.Returns = append(structMethod.Returns, &ParsedParameter{Name: name.Name, Type: ident.Name, IsArray: true}) + } + } + + } + } else { - for _, name := range outputField.Names { - structMethod.Returns = append(structMethod.Returns, &ParsedParameter{Name: name.Name, Type: t.Name}) + if len(outputField.Names) == 0 { + structMethod.Returns = append(structMethod.Returns, &ParsedParameter{Type: t.Name}) + } else { + for _, name := range outputField.Names { + structMethod.Returns = append(structMethod.Returns, &ParsedParameter{Name: name.Name, Type: t.Name}) + } } } } @@ -401,3 +422,80 @@ func (p *Parser) parseFile(file *ast.File) error { return nil } + +func (p *Parser) Resolve() error { + // Resolve bound Methods + for _, method := range p.boundMethods { + s, ok := p.structPointerFunctionDecls[method] + if !ok { + s, ok = p.structFunctionDecls[method] + if !ok { + return fmt.Errorf("bind statement using " + method + " but cannot find " + method + " declaration") + } else { + return fmt.Errorf("cannot bind struct using method `" + method + "` because it returns a struct (" + s + "). Return a pointer to " + s + " instead.") + } + } + structDefinition := p.structCache[s] + if structDefinition == nil { + return fmt.Errorf("Fatal: Bind statement using `" + method + "` but cannot find struct " + s + " definition") + } + p.boundStructs[s] = structDefinition + } + + // Resolve bound vars + for _, structLiteral := range p.boundStructPointerLiterals { + s, ok := p.structCache[structLiteral] + if !ok { + return fmt.Errorf("bind statement using " + structLiteral + " but cannot find " + structLiteral + " declaration") + } + p.boundStructs[structLiteral] = s + } + + var err error + + // Resolve bound variables + p.boundVariables.Each(func(variable string) { + v, ok := p.variableStructDecls[variable] + if !ok { + method, ok := p.variableFunctionDecls[variable] + if !ok { + if err == nil { + err = fmt.Errorf("bind statement using variable `" + variable + "` which does not resolve to a struct pointer") + } + } + + // Resolve function name + v, ok = p.structPointerFunctionDecls[method] + if !ok { + v, ok = p.structFunctionDecls[method] + if !ok { + if err == nil { + err = fmt.Errorf("bind statement using " + method + " but cannot find " + method + " declaration") + } + } else { + if err == nil { + err = fmt.Errorf("cannot bind variable `" + variable + "` because it resolves to a struct (" + v + "). Return a pointer to " + v + " instead.") + } + } + } + } + + s, ok := p.structCache[v] + if !ok { + println("Fatal: Bind statement using variable `" + variable + "` which resolves to a `" + v + "` but cannot find its declaration") + os.Exit(1) + } + p.boundStructs[v] = s + }) + + // Return first error when resolving bound variables + if err != nil { + return err + } + + // Check for struct literals + if p.boundStructLiterals.Length() > 0 { + return fmt.Errorf("cannot bind structs using struct literals. Create a pointer to the struct instead: %s", p.boundStructLiterals.Join(", ")) + } + return nil +}