diff --git a/go.mod b/go.mod index 24210593..14335206 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/ClickHouse/clickhouse-go v1.4.3 github.com/DATA-DOG/go-sqlmock v1.4.1 github.com/alicebob/miniredis/v2 v2.14.1 + github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2 github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/dchest/siphash v1.2.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible diff --git a/go.sum b/go.sum index aa3933fe..1f1ed143 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGn github.com/alicebob/miniredis/v2 v2.14.1 h1:GjlbSeoJ24bzdLRs13HoMEeaRZx9kg5nHoRW7QV/nCs= github.com/alicebob/miniredis/v2 v2.14.1/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2 h1:rL2miklL5rhxUaZO7hntBcy/VHaiyuPQ4EJoy/NMwaM= +github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/tools/goctl/CHANGE_LOG.MD b/tools/goctl/CHANGE_LOG.MD new file mode 100644 index 00000000..793cc73a --- /dev/null +++ b/tools/goctl/CHANGE_LOG.MD @@ -0,0 +1,15 @@ +# 2020-01-08 + +## features ![](https://img.shields.io/static/v1?label=&message=new&color=red) + +* reactor api parse by g4 +* add syntax lexer for api +* support java-style documentation comments +* support parsing doc and comment +* support import group + +> original: [api grammar document](./api/parser/readme.md) + +# 2020-01-08 + +* add change log \ No newline at end of file diff --git a/tools/goctl/api/apigen/gen.go b/tools/goctl/api/apigen/gen.go index 0206192f..2b16aebe 100644 --- a/tools/goctl/api/apigen/gen.go +++ b/tools/goctl/api/apigen/gen.go @@ -12,18 +12,21 @@ import ( "github.com/urfave/cli" ) -const apiTemplate = `info( +const apiTemplate = ` +syntax = "v1" + +info( title: // TODO: add title desc: // TODO: add description author: "{{.gitUser}}" email: "{{.gitEmail}}" ) -type request struct { +type request { // TODO: add members here and delete this comment } -type response struct { +type response { // TODO: add members here and delete this comment } diff --git a/tools/goctl/api/dartgen/gen.go b/tools/goctl/api/dartgen/gen.go index 6ee7623a..dc5af11a 100644 --- a/tools/goctl/api/dartgen/gen.go +++ b/tools/goctl/api/dartgen/gen.go @@ -19,11 +19,7 @@ func DartCommand(c *cli.Context) error { return errors.New("missing -dir") } - p, err := parser.NewParser(apiFile) - if err != nil { - return err - } - api, err := p.Parse() + api, err := parser.Parse(apiFile) if err != nil { return err } diff --git a/tools/goctl/api/dartgen/gendata.go b/tools/goctl/api/dartgen/gendata.go index 511e6e5c..a4d0dbd8 100644 --- a/tools/goctl/api/dartgen/gendata.go +++ b/tools/goctl/api/dartgen/gendata.go @@ -59,7 +59,6 @@ func genData(dir string, api *spec.ApiSpec) error { return e } - convertMemberType(api) return t.Execute(file, api) } diff --git a/tools/goctl/api/dartgen/util.go b/tools/goctl/api/dartgen/util.go index be98c2fe..833fff4d 100644 --- a/tools/goctl/api/dartgen/util.go +++ b/tools/goctl/api/dartgen/util.go @@ -1,12 +1,10 @@ package dartgen import ( - "log" "os" "reflect" "strings" - "github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/util" ) @@ -39,47 +37,6 @@ func tagGet(tag, k string) (reflect.Value, error) { return reflect.ValueOf(out), nil } -func convertMemberType(api *spec.ApiSpec) { - for i, t := range api.Types { - for j, mem := range t.Members { - api.Types[i].Members[j].Type = goTypeToDart(mem.Type) - } - } -} - -func goTypeToDart(t string) string { - t = strings.Replace(t, "*", "", -1) - if strings.HasPrefix(t, "[]") { - return "List<" + goTypeToDart(t[2:]) + ">" - } - - if strings.HasPrefix(t, "map") { - tys, e := util.DecomposeType(t) - if e != nil { - log.Fatal(e) - } - - if len(tys) != 2 { - log.Fatal("Map type number !=2") - } - - return "Map" - } - - switch t { - case "string": - return "String" - case "int", "int32", "int64": - return "int" - case "float", "float32", "float64": - return "double" - case "bool": - return "bool" - default: - return t - } -} - func isDirectType(s string) bool { return isAtomicType(s) || isListType(s) && isAtomicType(getCoreType(s)) } diff --git a/tools/goctl/api/docgen/doc.go b/tools/goctl/api/docgen/doc.go index 12acf748..1f5cb55a 100644 --- a/tools/goctl/api/docgen/doc.go +++ b/tools/goctl/api/docgen/doc.go @@ -41,7 +41,7 @@ func genDoc(api *spec.ApiSpec, dir string, filename string) error { var builder strings.Builder for index, route := range api.Service.Routes() { - routeComment, _ := util.GetAnnotationValue(route.Annotations, "doc", "summary") + routeComment := route.JoinedDoc() if len(routeComment) == 0 { routeComment = "N/A" } @@ -58,8 +58,8 @@ func genDoc(api *spec.ApiSpec, dir string, filename string) error { "routeComment": routeComment, "method": strings.ToUpper(route.Method), "uri": route.Path, - "requestType": "`" + stringx.TakeOne(route.RequestType.Name, "-") + "`", - "responseType": "`" + stringx.TakeOne(route.ResponseType.Name, "-") + "`", + "requestType": "`" + stringx.TakeOne(route.RequestTypeName(), "-") + "`", + "responseType": "`" + stringx.TakeOne(route.ResponseTypeName(), "-") + "`", "responseContent": responseContent, }) if err != nil { @@ -73,10 +73,28 @@ func genDoc(api *spec.ApiSpec, dir string, filename string) error { } func responseBody(api *spec.ApiSpec, route spec.Route) (string, error) { - tps := util.GetLocalTypes(api, route) + if len(route.ResponseTypeName()) == 0 { + return "", nil + } + + var tps = make([]spec.Type, 0) + tps = append(tps, route.ResponseType) + if definedType, ok := route.ResponseType.(spec.DefineStruct); ok { + associatedTypes(definedType, &tps) + } value, err := gogen.BuildTypes(tps) if err != nil { return "", err } + return fmt.Sprintf("\n\n```golang\n%s\n```\n", value), nil } + +func associatedTypes(tp spec.DefineStruct, tps *[]spec.Type) { + *tps = append(*tps, tp) + for _, item := range tp.Members { + if definedType, ok := item.Type.(spec.DefineStruct); ok { + associatedTypes(definedType, tps) + } + } +} diff --git a/tools/goctl/api/docgen/gen.go b/tools/goctl/api/docgen/gen.go index a5a5af52..4d5ce1b7 100644 --- a/tools/goctl/api/docgen/gen.go +++ b/tools/goctl/api/docgen/gen.go @@ -29,14 +29,11 @@ func DocCommand(c *cli.Context) error { return err } for _, f := range files { - p, err := parser.NewParser(f) + api, err := parser.Parse(f) if err != nil { return errors.New(fmt.Sprintf("parse file: %s, err: %s", f, err.Error())) } - api, err := p.Parse() - if err != nil { - return err - } + index := strings.Index(f, dir) if index < 0 { continue diff --git a/tools/goctl/api/format/format.go b/tools/goctl/api/format/format.go index 4c6482bb..0abf13a5 100644 --- a/tools/goctl/api/format/format.go +++ b/tools/goctl/api/format/format.go @@ -97,7 +97,7 @@ func ApiFormatByPath(apiFilePath string) error { } func apiFormat(data string) (string, error) { - _, err := parser.ParseApi(data) + _, err := parser.ParseContent(data) if err != nil { return "", err } @@ -208,5 +208,9 @@ func mayInsertStructKeyword(line string, token *int) string { if strings.HasSuffix(noCommentLine, leftParenthesis) { *token++ } + + if strings.Contains(noCommentLine, "`") { + return util.UpperFirst(strings.TrimSpace(line)) + } return line } diff --git a/tools/goctl/api/format/format_test.go b/tools/goctl/api/format/format_test.go index 0d975b5c..9b8d989a 100644 --- a/tools/goctl/api/format/format_test.go +++ b/tools/goctl/api/format/format_test.go @@ -9,13 +9,11 @@ import ( const ( notFormattedStr = ` type Request struct { - Name string + Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` } - type Response struct { - Message string + Message string ` + "`" + `json:"message"` + "`" + ` } - service A-api { @server( handler: GreetHandler @@ -25,13 +23,11 @@ handler: GreetHandler ` formattedStr = `type Request { - Name string + Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` } - type Response { - Message string + Message string ` + "`" + `json:"message"` + "`" + ` } - service A-api { @server( handler: GreetHandler diff --git a/tools/goctl/api/gogen/gen.go b/tools/goctl/api/gogen/gen.go index 2d3ec5b5..161c5c94 100644 --- a/tools/goctl/api/gogen/gen.go +++ b/tools/goctl/api/gogen/gen.go @@ -41,11 +41,7 @@ func GoCommand(c *cli.Context) error { } func DoGenProject(apiFile, dir, style string) error { - p, err := parser.NewParser(apiFile) - if err != nil { - return err - } - api, err := p.Parse() + api, err := parser.Parse(apiFile) if err != nil { return err } diff --git a/tools/goctl/api/gogen/gen_test.go b/tools/goctl/api/gogen/gen_test.go index 742375b1..db8ad3cd 100644 --- a/tools/goctl/api/gogen/gen_test.go +++ b/tools/goctl/api/gogen/gen_test.go @@ -16,9 +16,9 @@ import ( const testApiTemplate = ` info( title: doc title - desc: > + desc: "> doc description first part, - doc description second part< + doc description second part<" version: 1.0 ) @@ -55,9 +55,7 @@ service A-api { const testMultiServiceTemplate = ` info( title: doc title - desc: > - doc description first part, - doc description second part< + desc: doc description first part version: 1.0 ) @@ -229,7 +227,7 @@ type Response struct { } service A-api { - @doc(helloworld) + @doc ("helloworld") @server( handler: GreetHandler ) @@ -249,7 +247,7 @@ type Response struct { } service A-api { - @doc(helloworld) + @doc ("helloworld") @server( handler: GreetHandler ) @@ -325,10 +323,7 @@ func TestParser(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - api, err := parser.Parse() + api, err := parser.Parse(filename) assert.Nil(t, err) assert.Equal(t, len(api.Types), 2) @@ -337,8 +332,8 @@ func TestParser(t *testing.T) { assert.Equal(t, api.Service.Routes()[0].Path, "/greet/from/:name") assert.Equal(t, api.Service.Routes()[1].Path, "/greet/get") - assert.Equal(t, api.Service.Routes()[1].RequestType.Name, "Request") - assert.Equal(t, api.Service.Routes()[1].ResponseType.Name, "") + assert.Equal(t, api.Service.Routes()[1].RequestTypeName(), "Request") + assert.Equal(t, api.Service.Routes()[1].ResponseType, nil) validate(t, filename) } @@ -349,10 +344,7 @@ func TestMultiService(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - api, err := parser.Parse() + api, err := parser.Parse(filename) assert.Nil(t, err) assert.Equal(t, len(api.Service.Routes()), 2) @@ -367,10 +359,7 @@ func TestApiNoInfo(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() + _, err = parser.Parse(filename) assert.Nil(t, err) validate(t, filename) @@ -382,7 +371,7 @@ func TestInvalidApiFile(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - _, err = parser.NewParser(filename) + _, err = parser.Parse(filename) assert.NotNil(t, err) } @@ -392,14 +381,11 @@ func TestAnonymousAnnotation(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - api, err := parser.Parse() + api, err := parser.Parse(filename) assert.Nil(t, err) assert.Equal(t, len(api.Service.Routes()), 1) - assert.Equal(t, api.Service.Routes()[0].Annotations[0].Value, "GreetHandler") + assert.Equal(t, api.Service.Routes()[0].Handler, "GreetHandler") validate(t, filename) } @@ -410,10 +396,7 @@ func TestApiHasMiddleware(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() + _, err = parser.Parse(filename) assert.Nil(t, err) validate(t, filename) @@ -425,10 +408,7 @@ func TestApiHasJwt(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() + _, err = parser.Parse(filename) assert.Nil(t, err) validate(t, filename) @@ -440,10 +420,7 @@ func TestApiHasJwtAndMiddleware(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() + _, err = parser.Parse(filename) assert.Nil(t, err) validate(t, filename) @@ -455,13 +432,8 @@ func TestApiHasNoRequestBody(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) + _, err = parser.Parse(filename) assert.Nil(t, err) - - _, err = parser.Parse() - assert.Nil(t, err) - - validate(t, filename) } func TestApiRoutes(t *testing.T) { @@ -470,10 +442,7 @@ func TestApiRoutes(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() + _, err = parser.Parse(filename) assert.Nil(t, err) validate(t, filename) @@ -485,10 +454,7 @@ func TestHasCommentRoutes(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() + _, err = parser.Parse(filename) assert.Nil(t, err) validate(t, filename) @@ -500,13 +466,8 @@ func TestInlineTypeNotExist(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - _, err = parser.Parse() - assert.Nil(t, err) - - validate(t, filename) + _, err = parser.Parse(filename) + assert.NotNil(t, err) } func TestHasImportApi(t *testing.T) { @@ -520,15 +481,12 @@ func TestHasImportApi(t *testing.T) { assert.Nil(t, err) defer os.Remove(importApiName) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - api, err := parser.Parse() + api, err := parser.Parse(filename) assert.Nil(t, err) var hasInline bool for _, ty := range api.Types { - if ty.Name == "ImportData" { + if ty.Name() == "ImportData" { hasInline = true break } @@ -544,10 +502,7 @@ func TestNoStructApi(t *testing.T) { assert.Nil(t, err) defer os.Remove(filename) - parser, err := parser.NewParser(filename) - assert.Nil(t, err) - - spec, err := parser.Parse() + spec, err := parser.Parse(filename) assert.Nil(t, err) assert.Equal(t, len(spec.Types), 5) @@ -559,8 +514,8 @@ func TestNestTypeApi(t *testing.T) { err := ioutil.WriteFile(filename, []byte(nestTypeApi), os.ModePerm) assert.Nil(t, err) defer os.Remove(filename) - _, err = parser.NewParser(filename) + _, err = parser.Parse(filename) assert.NotNil(t, err) } @@ -569,7 +524,8 @@ func TestCamelStyle(t *testing.T) { err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm) assert.Nil(t, err) defer os.Remove(filename) - _, err = parser.NewParser(filename) + + _, err = parser.Parse(filename) assert.Nil(t, err) validateWithCamel(t, filename, "GoZero") diff --git a/tools/goctl/api/gogen/genetc.go b/tools/goctl/api/gogen/genetc.go index 5f72a139..f8e83154 100644 --- a/tools/goctl/api/gogen/genetc.go +++ b/tools/goctl/api/gogen/genetc.go @@ -5,7 +5,6 @@ import ( "strconv" "github.com/tal-tech/go-zero/tools/goctl/api/spec" - "github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/config" "github.com/tal-tech/go-zero/tools/goctl/util/format" ) @@ -26,14 +25,8 @@ func genEtc(dir string, cfg *config.Config, api *spec.ApiSpec) error { } service := api.Service - host, ok := util.GetAnnotationValue(service.Groups[0].Annotations, "server", "host") - if !ok { - host = "0.0.0.0" - } - port, ok := util.GetAnnotationValue(service.Groups[0].Annotations, "server", "port") - if !ok { - port = strconv.Itoa(defaultPort) - } + host := "0.0.0.0" + port := strconv.Itoa(defaultPort) return genFile(fileGenConfig{ dir: dir, diff --git a/tools/goctl/api/gogen/genhandlers.go b/tools/goctl/api/gogen/genhandlers.go index f6cd2f5e..26549d7c 100644 --- a/tools/goctl/api/gogen/genhandlers.go +++ b/tools/goctl/api/gogen/genhandlers.go @@ -1,14 +1,11 @@ package gogen import ( - "errors" "fmt" "path" "strings" - "unicode" "github.com/tal-tech/go-zero/tools/goctl/api/spec" - apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/config" "github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util/format" @@ -65,11 +62,11 @@ func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Rou return doGenToFile(dir, handler, cfg, group, route, Handler{ ImportPackages: genHandlerImports(group, route, parentPkg), HandlerName: handler, - RequestType: util.Title(route.RequestType.Name), + RequestType: util.Title(route.RequestTypeName()), LogicType: strings.Title(getLogicName(route)), Call: strings.Title(strings.TrimSuffix(handler, "Handler")), - HasResp: len(route.ResponseType.Name) > 0, - HasRequest: len(route.RequestType.Name) > 0, + HasResp: len(route.ResponseTypeName()) > 0, + HasRequest: len(route.RequestTypeName()) > 0, }) } @@ -109,7 +106,7 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str imports = append(imports, fmt.Sprintf("\"%s\"", util.JoinPackages(parentPkg, getLogicFolderPath(group, route)))) imports = append(imports, fmt.Sprintf("\"%s\"", util.JoinPackages(parentPkg, contextDir))) - if len(route.RequestType.Name) > 0 { + if len(route.RequestTypeName()) > 0 { imports = append(imports, fmt.Sprintf("\"%s\"\n", util.JoinPackages(parentPkg, typesDir))) } imports = append(imports, fmt.Sprintf("\"%s/rest/httpx\"", vars.ProjectOpenSourceUrl)) @@ -118,18 +115,7 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str } func getHandlerBaseName(route spec.Route) (string, error) { - handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") - if !ok { - return "", fmt.Errorf("missing handler annotation for %q", route.Path) - } - - for _, char := range handler { - if !unicode.IsDigit(char) && !unicode.IsLetter(char) { - return "", errors.New(fmt.Sprintf("route [%s] handler [%s] invalid, handler name should only contains letter or digit", - route.Path, handler)) - } - } - + handler := route.Handler handler = strings.TrimSpace(handler) handler = strings.TrimSuffix(handler, "handler") handler = strings.TrimSuffix(handler, "Handler") @@ -137,10 +123,10 @@ func getHandlerBaseName(route spec.Route) (string, error) { } func getHandlerFolderPath(group spec.Group, route spec.Route) string { - folder, ok := apiutil.GetAnnotationValue(route.Annotations, "server", groupProperty) - if !ok { - folder, ok = apiutil.GetAnnotationValue(group.Annotations, "server", groupProperty) - if !ok { + folder := route.GetAnnotation(groupProperty) + if len(folder) == 0 { + folder = group.GetAnnotation(groupProperty) + if len(folder) == 0 { return handlerDir } } diff --git a/tools/goctl/api/gogen/genlogic.go b/tools/goctl/api/gogen/genlogic.go index 9efb9cd7..77c45bc0 100644 --- a/tools/goctl/api/gogen/genlogic.go +++ b/tools/goctl/api/gogen/genlogic.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/tal-tech/go-zero/tools/goctl/api/spec" - "github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/config" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util/format" @@ -68,16 +67,20 @@ func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spe var responseString string var returnString string var requestString string - if len(route.ResponseType.Name) > 0 { - resp := strings.Title(route.ResponseType.Name) - responseString = "(*types." + resp + ", error)" - returnString = fmt.Sprintf("return &types.%s{}, nil", resp) + if len(route.ResponseTypeName()) > 0 { + resp := responseGoTypeName(route, typesPacket) + responseString = "(" + resp + ", error)" + if strings.HasPrefix(resp, "*") { + returnString = fmt.Sprintf("return &%s{}, nil", strings.TrimPrefix(resp, "*")) + } else { + returnString = fmt.Sprintf("return %s{}, nil", resp) + } } else { responseString = "error" returnString = "return nil" } - if len(route.RequestType.Name) > 0 { - requestString = "req " + "types." + strings.Title(route.RequestType.Name) + if len(route.RequestTypeName()) > 0 { + requestString = "req " + requestGoTypeName(route, typesPacket) } return genFile(fileGenConfig{ @@ -100,10 +103,10 @@ func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spe } func getLogicFolderPath(group spec.Group, route spec.Route) string { - folder, ok := util.GetAnnotationValue(route.Annotations, "server", groupProperty) - if !ok { - folder, ok = util.GetAnnotationValue(group.Annotations, "server", groupProperty) - if !ok { + folder := route.GetAnnotation(groupProperty) + if len(folder) == 0 { + folder = group.GetAnnotation(groupProperty) + if len(folder) == 0 { return logicDir } } @@ -116,7 +119,7 @@ func genLogicImports(route spec.Route, parentPkg string) string { var imports []string imports = append(imports, `"context"`+"\n") imports = append(imports, fmt.Sprintf("\"%s\"", ctlutil.JoinPackages(parentPkg, contextDir))) - if len(route.ResponseType.Name) > 0 || len(route.RequestType.Name) > 0 { + if len(route.ResponseTypeName()) > 0 || len(route.RequestTypeName()) > 0 { imports = append(imports, fmt.Sprintf("\"%s\"\n", ctlutil.JoinPackages(parentPkg, typesDir))) } imports = append(imports, fmt.Sprintf("\"%s/core/logx\"", vars.ProjectOpenSourceUrl)) diff --git a/tools/goctl/api/gogen/genroutes.go b/tools/goctl/api/gogen/genroutes.go index 65aa27e4..8d311652 100644 --- a/tools/goctl/api/gogen/genroutes.go +++ b/tools/goctl/api/gogen/genroutes.go @@ -10,7 +10,6 @@ import ( "github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/tools/goctl/api/spec" - apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/config" "github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util/format" @@ -151,10 +150,10 @@ func genRouteImports(parentPkg string, api *spec.ApiSpec) string { importSet.AddStr(fmt.Sprintf("\"%s\"", util.JoinPackages(parentPkg, contextDir))) for _, group := range api.Service.Groups { for _, route := range group.Routes { - folder, ok := apiutil.GetAnnotationValue(route.Annotations, "server", groupProperty) - if !ok { - folder, ok = apiutil.GetAnnotationValue(group.Annotations, "server", groupProperty) - if !ok { + folder := route.GetAnnotation(groupProperty) + if len(folder) == 0 { + folder = group.GetAnnotation(groupProperty) + if len(folder) == 0 { continue } } @@ -176,12 +175,12 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) { for _, r := range g.Routes { handler := getHandlerName(r) handler = handler + "(serverCtx)" - folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty) - if ok { + folder := r.GetAnnotation(groupProperty) + if len(folder) > 0 { handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:] } else { - folder, ok = apiutil.GetAnnotationValue(g.Annotations, "server", groupProperty) - if ok { + folder = g.GetAnnotation(groupProperty) + if len(folder) > 0 { handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:] } } @@ -192,12 +191,14 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) { }) } - if value, ok := apiutil.GetAnnotationValue(g.Annotations, "server", "jwt"); ok { - groupedRoutes.authName = value + jwt := g.GetAnnotation("jwt") + if len(jwt) > 0 { + groupedRoutes.authName = jwt groupedRoutes.jwtEnabled = true } - if value, ok := apiutil.GetAnnotationValue(g.Annotations, "server", "middleware"); ok { - for _, item := range strings.Split(value, ",") { + middleware := g.GetAnnotation("middleware") + if len(middleware) > 0 { + for _, item := range strings.Split(middleware, ",") { groupedRoutes.middlewares = append(groupedRoutes.middlewares, item) } } diff --git a/tools/goctl/api/gogen/gentypes.go b/tools/goctl/api/gogen/gentypes.go index 4dbd98f3..c109dc7a 100644 --- a/tools/goctl/api/gogen/gentypes.go +++ b/tools/goctl/api/gogen/gentypes.go @@ -35,8 +35,8 @@ func BuildTypes(types []spec.Type) (string, error) { } else { builder.WriteString("\n\n") } - if err := writeType(&builder, tp, types); err != nil { - return "", apiutil.WrapErr(err, "Type "+tp.Name+" generate error") + if err := writeType(&builder, tp); err != nil { + return "", apiutil.WrapErr(err, "Type "+tp.Name()+" generate error") } } @@ -53,6 +53,7 @@ func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error { if err != nil { return err } + typeFilename = typeFilename + ".go" filename := path.Join(dir, typesDir, typeFilename) os.Remove(filename) @@ -67,67 +68,28 @@ func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error { builtinTemplate: typesTemplate, data: map[string]interface{}{ "types": val, - "containsTime": api.ContainsTime(), + "containsTime": false, }, }) } -func convertTypeCase(types []spec.Type, t string) (string, error) { - ts, err := apiutil.DecomposeType(t) - if err != nil { - return "", err +func writeType(writer io.Writer, tp spec.Type) error { + structType, ok := tp.(spec.DefineStruct) + if !ok { + return errors.New(fmt.Sprintf("unspport struct type: %s", tp.Name())) } - var defTypes []string - for _, tp := range ts { - for _, typ := range types { - if typ.Name == tp { - defTypes = append(defTypes, tp) - } - } - } - - for _, tp := range defTypes { - t = strings.ReplaceAll(t, tp, util.Title(tp)) - } - - return t, nil -} - -func writeType(writer io.Writer, tp spec.Type, types []spec.Type) error { - fmt.Fprintf(writer, "type %s struct {\n", util.Title(tp.Name)) - for _, member := range tp.Members { + fmt.Fprintf(writer, "type %s struct {\n", util.Title(tp.Name())) + for _, member := range structType.Members { if member.IsInline { - var found = false - for _, ty := range types { - if strings.ToLower(ty.Name) == strings.ToLower(member.Name) { - found = true - } - } - if !found { - return errors.New("inline type " + member.Name + " not exist, please correct api file") - } - if _, err := fmt.Fprintf(writer, "%s\n", strings.Title(member.Type)); err != nil { + if _, err := fmt.Fprintf(writer, "%s\n", strings.Title(member.Type.Name())); err != nil { return err } else { continue } } - tpString, err := convertTypeCase(types, member.Type) - if err != nil { - return err - } - pm, err := member.GetPropertyName() - if err != nil { - return err - } - if !strings.Contains(pm, "_") { - if strings.Title(member.Name) != strings.Title(pm) { - fmt.Printf("type: %s, property name %s json tag illegal, "+ - "should set json tag as `json:\"%s\"` \n", tp.Name, member.Name, util.Untitle(member.Name)) - } - } - if err := writeProperty(writer, member.Name, tpString, member.Tag, member.GetComment(), 1); err != nil { + + if err := writeProperty(writer, member.Name, member.Tag, member.GetComment(), member.Type, 1); err != nil { return err } } diff --git a/tools/goctl/api/gogen/util.go b/tools/goctl/api/gogen/util.go index cf554980..7557a7e6 100644 --- a/tools/goctl/api/gogen/util.go +++ b/tools/goctl/api/gogen/util.go @@ -72,15 +72,15 @@ func getParentPackage(dir string) (string, error) { return filepath.ToSlash(filepath.Join(projectCtx.Path, strings.TrimPrefix(projectCtx.WorkDir, projectCtx.Dir))), nil } -func writeProperty(writer io.Writer, name, tp, tag, comment string, indent int) error { +func writeProperty(writer io.Writer, name, tag, comment string, tp spec.Type, indent int) error { util.WriteIndent(writer, indent) var err error if len(comment) > 0 { comment = strings.TrimPrefix(comment, "//") comment = "//" + comment - _, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp, tag, comment) + _, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp.Name(), tag, comment) } else { - _, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp, tag) + _, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp.Name(), tag) } return err } @@ -88,11 +88,13 @@ func writeProperty(writer io.Writer, name, tp, tag, comment string, indent int) func getAuths(api *spec.ApiSpec) []string { authNames := collection.NewSet() for _, g := range api.Service.Groups { - if value, ok := util.GetAnnotationValue(g.Annotations, "server", "jwt"); ok { - authNames.Add(value) + jwt := g.GetAnnotation("jwt") + if len(jwt) > 0 { + authNames.Add(jwt) } - if value, ok := util.GetAnnotationValue(g.Annotations, "server", "signature"); ok { - authNames.Add(value) + signature := g.GetAnnotation("signature") + if len(signature) > 0 { + authNames.Add(signature) } } return authNames.KeysStr() @@ -101,8 +103,9 @@ func getAuths(api *spec.ApiSpec) []string { func getMiddleware(api *spec.ApiSpec) []string { result := collection.NewSet() for _, g := range api.Service.Groups { - if value, ok := util.GetAnnotationValue(g.Annotations, "server", "middleware"); ok { - for _, item := range strings.Split(value, ",") { + middleware := g.GetAnnotation("middleware") + if len(middleware) > 0 { + for _, item := range strings.Split(middleware, ",") { result.Add(strings.TrimSpace(item)) } } @@ -118,3 +121,70 @@ func formatCode(code string) string { return string(ret) } + +func responseGoTypeName(r spec.Route, pkg ...string) string { + if r.ResponseType == nil { + return "" + } + + return golangExpr(r.ResponseType, pkg...) +} + +func requestGoTypeName(r spec.Route, pkg ...string) string { + if r.RequestType == nil { + return "" + } + + return golangExpr(r.RequestType, pkg...) +} + +func golangExpr(ty spec.Type, pkg ...string) string { + switch v := ty.(type) { + case spec.PrimitiveType: + return v.RawName + case spec.DefineStruct: + if len(pkg) > 1 { + panic("package cannot be more than 1") + } + + if len(pkg) == 0 { + return v.RawName + } + + return fmt.Sprintf("%s.%s", pkg[0], strings.Title(v.RawName)) + case spec.ArrayType: + if len(pkg) > 1 { + panic("package cannot be more than 1") + } + + if len(pkg) == 0 { + return v.RawName + } + + return fmt.Sprintf("[]%s", golangExpr(v.Value, pkg...)) + case spec.MapType: + if len(pkg) > 1 { + panic("package cannot be more than 1") + } + + if len(pkg) == 0 { + return v.RawName + } + + return fmt.Sprintf("map[%s]%s", v.Key, golangExpr(v.Value, pkg...)) + case spec.PointerType: + if len(pkg) > 1 { + panic("package cannot be more than 1") + } + + if len(pkg) == 0 { + return v.RawName + } + + return fmt.Sprintf("*%s", golangExpr(v.Type, pkg...)) + case spec.InterfaceType: + return v.RawName + } + + return "" +} diff --git a/tools/goctl/api/javagen/gen.go b/tools/goctl/api/javagen/gen.go index 420265ef..ec687e43 100644 --- a/tools/goctl/api/javagen/gen.go +++ b/tools/goctl/api/javagen/gen.go @@ -22,11 +22,7 @@ func JavaCommand(c *cli.Context) error { return errors.New("missing -dir") } - p, err := parser.NewParser(apiFile) - if err != nil { - return err - } - api, err := p.Parse() + api, err := parser.Parse(apiFile) if err != nil { return err } diff --git a/tools/goctl/api/javagen/gencomponents.go b/tools/goctl/api/javagen/gencomponents.go index 613c7e8e..dbaedd1f 100644 --- a/tools/goctl/api/javagen/gencomponents.go +++ b/tools/goctl/api/javagen/gencomponents.go @@ -8,6 +8,7 @@ import ( "strings" "text/template" + "github.com/tal-tech/go-zero/core/stringx" "github.com/tal-tech/go-zero/tools/goctl/api/spec" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/util" @@ -17,21 +18,46 @@ const ( componentTemplate = `// Code generated by goctl. DO NOT EDIT. package com.xhb.logic.http.packet.{{.packet}}.model; -import com.xhb.logic.http.DeProguardable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +{{.imports}} {{.componentType}} ` + + httpResponseData = "import com.xhb.core.response.HttpResponseData;" + httpData = "import com.xhb.core.packet.HttpData;" ) +type componentsContext struct { + api *spec.ApiSpec + requestTypes []spec.Type + responseTypes []spec.Type + imports []string +} + func genComponents(dir, packetName string, api *spec.ApiSpec) error { - types := apiutil.GetSharedTypes(api) + types := api.Types if len(types) == 0 { return nil } + + var requestTypes []spec.Type + var responseTypes []spec.Type + for _, group := range api.Service.Groups { + for _, route := range group.Routes { + if route.RequestType != nil { + requestTypes = append(requestTypes, route.RequestType) + } + if route.ResponseType != nil { + responseTypes = append(responseTypes, route.ResponseType) + } + } + } + + context := componentsContext{api: api, requestTypes: requestTypes, responseTypes: responseTypes} for _, ty := range types { - if err := createComponent(dir, packetName, ty, api.Types); err != nil { + if err := context.createComponent(dir, packetName, ty); err != nil { return err } } @@ -39,8 +65,21 @@ func genComponents(dir, packetName string, api *spec.ApiSpec) error { return nil } -func createComponent(dir, packetName string, ty spec.Type, types []spec.Type) error { - modelFile := util.Title(ty.Name) + ".java" +func (c *componentsContext) createComponent(dir, packetName string, ty spec.Type) error { + defineStruct, ok := ty.(spec.DefineStruct) + if !ok { + return errors.New("unsupported type %s" + ty.Name()) + } + + for _, item := range c.requestTypes { + if item.Name() == defineStruct.Name() { + if len(defineStruct.GetFormMembers())+len(defineStruct.GetBodyMembers()) == 0 { + return nil + } + } + } + + modelFile := util.Title(ty.Name()) + ".java" filename := path.Join(dir, modelDir, modelFile) if err := util.RemoveOrQuit(filename); err != nil { return err @@ -55,77 +94,72 @@ func createComponent(dir, packetName string, ty spec.Type, types []spec.Type) er } defer fp.Close() - tys, err := buildType(ty, types) + tyString, err := c.buildType(defineStruct) if err != nil { return err } t := template.Must(template.New("componentType").Parse(componentTemplate)) return t.Execute(fp, map[string]string{ - "componentType": tys, + "componentType": tyString, "packet": packetName, + "imports": strings.Join(c.imports, "\n"), }) } -func buildType(ty spec.Type, types []spec.Type) (string, error) { +func (c *componentsContext) buildType(ty spec.DefineStruct) (string, error) { var builder strings.Builder - if err := writeType(&builder, ty, types); err != nil { - return "", apiutil.WrapErr(err, "Type "+ty.Name+" generate error") + if err := c.writeType(&builder, ty); err != nil { + return "", apiutil.WrapErr(err, "Type "+ty.Name()+" generate error") } + return builder.String(), nil } -func writeType(writer io.Writer, tp spec.Type, types []spec.Type) error { - fmt.Fprintf(writer, "public class %s implements DeProguardable {\n", util.Title(tp.Name)) - var members []spec.Member - err := writeMembers(writer, types, tp.Members, &members, 1) +func (c *componentsContext) writeType(writer io.Writer, defineStruct spec.DefineStruct) error { + responseData := "HttpData" + for _, item := range c.responseTypes { + if item.Name() == defineStruct.Name() { + responseData = "HttpResponseData" + if !stringx.Contains(c.imports, httpResponseData) { + c.imports = append(c.imports, httpResponseData) + } + break + } + } + if responseData == "HttpData" && !stringx.Contains(c.imports, httpData) { + c.imports = append(c.imports, httpData) + } + + fmt.Fprintf(writer, "public class %s extends %s {\n", util.Title(defineStruct.Name()), responseData) + err := c.writeMembers(writer, defineStruct, 1) if err != nil { return err } - genGetSet(writer, members, 1) + genGetSet(writer, defineStruct, 1) fmt.Fprintf(writer, "}") return nil } -func writeMembers(writer io.Writer, types []spec.Type, members []spec.Member, allMembers *[]spec.Member, indent int) error { - for _, member := range members { - if !member.IsInline { - _, err := member.GetPropertyName() - if err != nil { - return err - } - } - if !member.IsBodyMember() { - continue - } - - for _, item := range *allMembers { - if item.Name == member.Name { +func (c *componentsContext) writeMembers(writer io.Writer, ty spec.DefineStruct, indent int) error { + for _, member := range ty.Members { + if member.IsInline { + defineStruct, ok := member.Type.(spec.DefineStruct) + if ok { + err := c.writeMembers(writer, defineStruct, indent) + if err != nil { + return err + } continue } + return errors.New("unsupported inline type %s" + member.Type.Name()) } - if member.IsInline { - hasInline := false - for _, ty := range types { - if strings.ToLower(ty.Name) == strings.ToLower(member.Name) { - err := writeMembers(writer, types, ty.Members, allMembers, indent) - if err != nil { - return err - } - hasInline = true - break - } - } - if !hasInline { - return errors.New("inline type " + member.Name + " not exist, please correct api file") - } - } else { + if member.IsBodyMember() || member.IsFormMember() { if err := writeProperty(writer, member, indent); err != nil { return err } - *allMembers = append(*allMembers, member) } } return nil diff --git a/tools/goctl/api/javagen/genpacket.go b/tools/goctl/api/javagen/genpacket.go index def8f1ee..07aed720 100644 --- a/tools/goctl/api/javagen/genpacket.go +++ b/tools/goctl/api/javagen/genpacket.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "fmt" - "io" "os" "strings" "text/template" @@ -17,29 +16,16 @@ import ( const packetTemplate = `package com.xhb.logic.http.packet.{{.packet}}; -import com.google.gson.Gson; -import com.xhb.commons.JSON; -import com.xhb.commons.JsonMarshal; +import com.xhb.core.packet.HttpPacket; import com.xhb.core.network.HttpRequestClient; -import com.xhb.core.packet.HttpRequestPacket; -import com.xhb.core.response.HttpResponseData; -import com.xhb.logic.http.DeProguardable; -{{if not .HasRequestBody}} -import com.xhb.logic.http.request.EmptyRequest; -{{end}} -{{.import}} - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.json.JSONObject; - -public class {{.packetName}} extends HttpRequestPacket<{{.packetName}}.{{.packetName}}Response> { +{{.imports}} +public class {{.packetName}} extends HttpPacket<{{.responseType}}> { {{.paramsDeclaration}} - public {{.packetName}}({{.params}}{{if .HasRequestBody}}, {{.requestType}} request{{end}}) { + public {{.packetName}}({{.params}}{{if .HasRequestBody}}{{.requestType}} request{{end}}) { {{if .HasRequestBody}}super(request);{{else}}super(EmptyRequest.instance);{{end}} - {{if .HasRequestBody}}this.request = request;{{end}}{{.paramsSet}} + {{if .HasRequestBody}}this.request = request;{{end}}{{.paramsSetter}} } @Override @@ -51,32 +37,6 @@ public class {{.packetName}} extends HttpRequestPacket<{{.packetName}}.{{.packet public String requestUri() { return {{.uri}}; } - - @Override - public {{.packetName}}Response newInstanceFrom(JSON json) { - return new {{.packetName}}Response(json); - } - - public static class {{.packetName}}Response extends HttpResponseData { - - private {{.responseType}} responseData; - - {{.packetName}}Response(@NotNull JSON json) { - super(json); - JSONObject jsonObject = json.asObject(); - if (JsonParser.hasKey(jsonObject, "data")) { - Gson gson = new Gson(); - JSONObject dataJson = JsonParser.getJSONObject(jsonObject, "data"); - responseData = gson.fromJson(dataJson.toString(), {{.responseType}}.class); - } - } - - public {{.responseType}} get{{.responseType}} () { - return responseData; - } - } - - {{.types}} } ` @@ -91,10 +51,11 @@ func genPacket(dir, packetName string, api *spec.ApiSpec) error { } func createWith(dir string, api *spec.ApiSpec, route spec.Route, packetName string) error { - packet, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") + packet := route.Handler packet = strings.Replace(packet, "Handler", "Packet", 1) - if !ok { - return fmt.Errorf("missing packet annotation for %q", route.Path) + packet = strings.Title(packet) + if !strings.HasSuffix(packet, "Packet") { + packet += "Packet" } javaFile := packet + ".java" @@ -107,27 +68,24 @@ func createWith(dir string, api *spec.ApiSpec, route spec.Route, packetName stri } defer fp.Close() - var builder strings.Builder - var first bool - tps := apiutil.GetLocalTypes(api, route) - - for _, tp := range tps { - if first { - first = false - } else { - fmt.Fprintln(&builder) - } - - if err := genType(&builder, tp, api.Types); err != nil { - return err + var hasRequestBody = false + if route.RequestType != nil { + if defineStruct, ok := route.RequestType.(spec.DefineStruct); ok { + hasRequestBody = len(defineStruct.GetBodyMembers()) > 0 || len(defineStruct.GetFormMembers()) > 0 } } - types := builder.String() - writeIndent(&builder, 1) - params := paramsForRoute(route) + params := strings.TrimSpace(paramsForRoute(route)) + if len(params) > 0 && hasRequestBody { + params += ", " + } paramsDeclaration := declarationForRoute(route) - paramsSet := paramsSet(route) + paramsSetter := paramsSet(route) + imports := getImports(api, packetName) + + if len(route.ResponseTypeName()) == 0 { + imports += fmt.Sprintf("\v%s", "import com.xhb.core.response.EmptyResponse;") + } t := template.Must(template.New("packetTemplate").Parse(packetTemplate)) var tmplBytes bytes.Buffer @@ -135,15 +93,14 @@ func createWith(dir string, api *spec.ApiSpec, route spec.Route, packetName stri "packetName": packet, "method": strings.ToUpper(route.Method), "uri": processUri(route), - "types": strings.TrimSpace(types), - "responseType": stringx.TakeOne(util.Title(route.ResponseType.Name), "Object"), + "responseType": stringx.TakeOne(util.Title(route.ResponseTypeName()), "EmptyResponse"), "params": params, "paramsDeclaration": strings.TrimSpace(paramsDeclaration), - "paramsSet": paramsSet, + "paramsSetter": paramsSetter, "packet": packetName, - "requestType": util.Title(route.RequestType.Name), - "HasRequestBody": len(route.RequestType.GetBodyMembers()) > 0, - "import": getImports(api, route, packetName), + "requestType": util.Title(route.RequestTypeName()), + "HasRequestBody": hasRequestBody, + "imports": imports, }) if err != nil { return err @@ -152,17 +109,11 @@ func createWith(dir string, api *spec.ApiSpec, route spec.Route, packetName stri return nil } -func getImports(api *spec.ApiSpec, route spec.Route, packetName string) string { +func getImports(api *spec.ApiSpec, packetName string) string { var builder strings.Builder - allTypes := apiutil.GetAllTypes(api, route) - sharedTypes := apiutil.GetSharedTypes(api) - for _, at := range allTypes { - for _, item := range sharedTypes { - if item.Name == at.Name { - fmt.Fprintf(&builder, "import com.xhb.logic.http.packet.%s.model.%s;\n", packetName, item.Name) - break - } - } + allTypes := api.Types + if len(allTypes) > 0 { + fmt.Fprintf(&builder, "import com.xhb.logic.http.packet.%s.model.*;\n", packetName) } return builder.String() } @@ -241,6 +192,7 @@ func declarationForRoute(route spec.Route) string { func processUri(route spec.Route) string { path := route.Path + var builder strings.Builder cops := strings.Split(path, "/") for index, cop := range cops { @@ -261,29 +213,37 @@ func processUri(route spec.Route) string { result = result[:len(result)-4] } if strings.HasPrefix(result, "/") { + result = strings.TrimPrefix(result, "/") result = "\"" + result } - return result + return result + formString(route) } -func genType(writer io.Writer, tp spec.Type, types []spec.Type) error { - if len(tp.GetBodyMembers()) == 0 { - return nil +func formString(route spec.Route) string { + var keyValues []string + if defineStruct, ok := route.RequestType.(spec.DefineStruct); ok { + forms := defineStruct.GetFormMembers() + for _, item := range forms { + name, err := item.GetPropertyName() + if err != nil { + panic(err) + } + + strcat := "?" + if len(keyValues) > 0 { + strcat = "&" + } + if item.Type.Name() == "bool" { + name = strings.TrimPrefix(name, "Is") + name = strings.TrimPrefix(name, "is") + keyValues = append(keyValues, fmt.Sprintf(`"%s%s=" + request.is%s()`, strcat, name, strings.Title(name))) + } else { + keyValues = append(keyValues, fmt.Sprintf(`"%s%s=" + request.get%s()`, strcat, name, strings.Title(name))) + } + } + if len(keyValues) > 0 { + return " + " + strings.Join(keyValues, " + ") + } } - - writeIndent(writer, 1) - fmt.Fprintf(writer, "static class %s implements DeProguardable {\n", util.Title(tp.Name)) - var members []spec.Member - err := writeMembers(writer, types, tp.Members, &members, 2) - if err != nil { - return err - } - - writeNewline(writer) - writeIndent(writer, 1) - genGetSet(writer, members, 2) - writeIndent(writer, 1) - fmt.Fprintln(writer, "}") - - return nil + return "" } diff --git a/tools/goctl/api/javagen/util.go b/tools/goctl/api/javagen/util.go index aa2f5878..de976d53 100644 --- a/tools/goctl/api/javagen/util.go +++ b/tools/goctl/api/javagen/util.go @@ -8,19 +8,30 @@ import ( "strings" "text/template" + "github.com/tal-tech/go-zero/core/stringx" "github.com/tal-tech/go-zero/tools/goctl/api/spec" - apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/util" ) const getSetTemplate = ` {{.indent}}{{.decorator}} {{.indent}}public {{.returnType}} get{{.property}}() { -{{.indent}} return this.{{.propertyValue}}; +{{.indent}} return this.{{.tagValue}}; {{.indent}}} {{.indent}}public void set{{.property}}({{.type}} {{.propertyValue}}) { -{{.indent}} this.{{.propertyValue}} = {{.propertyValue}}; +{{.indent}} this.{{.tagValue}} = {{.propertyValue}}; +{{.indent}}} +` + +const boolTemplate = ` +{{.indent}}{{.decorator}} +{{.indent}}public {{.returnType}} is{{.property}}() { +{{.indent}} return this.{{.tagValue}}; +{{.indent}}} + +{{.indent}}public void set{{.property}}({{.type}} {{.propertyValue}}) { +{{.indent}} this.{{.tagValue}} = {{.propertyValue}}; {{.indent}}} ` @@ -31,22 +42,29 @@ func writeProperty(writer io.Writer, member spec.Member, indent int) error { if err != nil { return err } + name, err := member.GetPropertyName() if err != nil { return err } + _, err = fmt.Fprintf(writer, "private %s %s", ty, name) if err != nil { return err } + writeDefaultValue(writer, member) fmt.Fprint(writer, ";\n") return err } func writeDefaultValue(writer io.Writer, member spec.Member) error { - switch member.Type { - case "string": + javaType, err := goTypeToJava(member.Type) + if err != nil { + return err + } + + if javaType == "String" { _, err := fmt.Fprintf(writer, " = \"\"") return err } @@ -67,82 +85,84 @@ func indentString(indent int) string { return result } -func writeNewline(writer io.Writer) { - fmt.Fprint(writer, util.NL) +func goTypeToJava(tp spec.Type) (string, error) { + switch v := tp.(type) { + case spec.DefineStruct: + return util.Title(tp.Name()), nil + case spec.PrimitiveType: + r, ok := primitiveType(tp.Name()) + if !ok { + return "", errors.New("unsupported primitive type " + tp.Name()) + } + return r, nil + case spec.MapType: + valueType, err := goTypeToJava(v.Value) + if err != nil { + return "", err + } + + return fmt.Sprintf("java.util.HashMap", util.Title(valueType)), nil + case spec.ArrayType: + if tp.Name() == "[]byte" { + return "byte[]", nil + } + + valueType, err := goTypeToJava(v.Value) + if err != nil { + return "", err + } + + return fmt.Sprintf("java.util.ArrayList<%s>", util.Title(valueType)), nil + case spec.InterfaceType: + return "Object", nil + case spec.PointerType: + return goTypeToJava(v.Type) + } + + return "", errors.New("unsupported primitive type " + tp.Name()) } -func isPrimitiveType(tp string) bool { - switch tp { - case "int", "int32", "int64": - return true - case "float", "float32", "float64": - return true - case "bool": - return true - } - return false -} - -func goTypeToJava(tp string) (string, error) { - if len(tp) == 0 { - return "", errors.New("property type empty") - } - - if strings.HasPrefix(tp, "*") { - tp = tp[1:] - } +func primitiveType(tp string) (string, bool) { switch tp { case "string": - return "String", nil - case "int64": - return "long", nil - case "int", "int8", "int32": - return "int", nil + return "String", true + case "int64", "uint64": + return "long", true + case "int", "int8", "int32", "uint", "uint8", "uint16", "uint32": + return "int", true case "float", "float32", "float64": - return "double", nil + return "double", true case "bool": - return "boolean", nil + return "boolean", true } - if strings.HasPrefix(tp, "[]") { - tys, err := apiutil.DecomposeType(tp) - if err != nil { - return "", err - } - if len(tys) == 0 { - return "", fmt.Errorf("%s tp parse error", tp) - } - - return fmt.Sprintf("java.util.ArrayList<%s>", util.Title(tys[0])), nil - } else if strings.HasPrefix(tp, "map") { - tys, err := apiutil.DecomposeType(tp) - if err != nil { - return "", err - } - - if len(tys) == 2 { - return "", fmt.Errorf("%s tp parse error", tp) - } - - return fmt.Sprintf("java.util.HashMap", util.Title(tys[1])), nil - } - return util.Title(tp), nil + return "", false } -func genGetSet(writer io.Writer, members []spec.Member, indent int) error { - t := template.Must(template.New("getSetTemplate").Parse(getSetTemplate)) +func genGetSet(writer io.Writer, defineStruct spec.DefineStruct, indent int) error { + var members = defineStruct.GetBodyMembers() + members = append(members, defineStruct.GetFormMembers()...) for _, member := range members { - var tmplBytes bytes.Buffer - - oty, err := goTypeToJava(member.Type) + javaType, err := goTypeToJava(member.Type) if err != nil { - return err + return nil } - tyString := oty + var property = util.Title(member.Name) + var templateStr = getSetTemplate + if javaType == "boolean" { + templateStr = boolTemplate + property = strings.TrimPrefix(property, "Is") + property = strings.TrimPrefix(property, "is") + } + t := template.Must(template.New(templateStr).Parse(getSetTemplate)) + var tmplBytes bytes.Buffer + + tyString := javaType decorator := "" - if !isPrimitiveType(member.Type) { - if member.IsOptional() { + javaPrimitiveType := []string{"int", "long", "boolean", "float", "double", "short"} + if !stringx.Contains(javaPrimitiveType, javaType) { + if member.IsOptional() || member.IsOmitEmpty() { decorator = "@Nullable " } else { decorator = "@NotNull " @@ -150,12 +170,18 @@ func genGetSet(writer io.Writer, members []spec.Member, indent int) error { tyString = decorator + tyString } + tagName, err := member.GetPropertyName() + if err != nil { + return err + } + err = t.Execute(&tmplBytes, map[string]string{ - "property": util.Title(member.Name), + "property": property, "propertyValue": util.Untitle(member.Name), + "tagValue": tagName, "type": tyString, "decorator": decorator, - "returnType": oty, + "returnType": javaType, "indent": indentString(indent), }) if err != nil { diff --git a/tools/goctl/api/ktgen/cmd.go b/tools/goctl/api/ktgen/cmd.go index cdf31e47..2a845898 100644 --- a/tools/goctl/api/ktgen/cmd.go +++ b/tools/goctl/api/ktgen/cmd.go @@ -21,11 +21,7 @@ func KtCommand(c *cli.Context) error { return errors.New("missing -pkg") } - p, e := parser.NewParser(apiFile) - if e != nil { - return e - } - api, e := p.Parse() + api, e := parser.Parse(apiFile) if e != nil { return e } diff --git a/tools/goctl/api/ktgen/funcs.go b/tools/goctl/api/ktgen/funcs.go index 17dee136..7ea43d1c 100644 --- a/tools/goctl/api/ktgen/funcs.go +++ b/tools/goctl/api/ktgen/funcs.go @@ -1,6 +1,7 @@ package ktgen import ( + "fmt" "log" "strings" "text/template" @@ -44,7 +45,7 @@ func parseType(t string) string { } if strings.HasPrefix(t, "map") { - tys, e := util.DecomposeType(t) + tys, e := decomposeType(t) if e != nil { log.Fatal(e) } @@ -68,6 +69,47 @@ func parseType(t string) string { } } +func decomposeType(t string) (result []string, err error) { + add := func(tp string) error { + ret, err := decomposeType(tp) + if err != nil { + return err + } + + result = append(result, ret...) + return nil + } + if strings.HasPrefix(t, "map") { + t = strings.ReplaceAll(t, "map", "") + if t[0] == '[' { + pos := strings.Index(t, "]") + if pos > 1 { + if err = add(t[1:pos]); err != nil { + return + } + if len(t) > pos+1 { + err = add(t[pos+1:]) + return + } + } + } + } else if strings.HasPrefix(t, "[]") { + if len(t) > 2 { + err = add(t[2:]) + return + } + } else if strings.HasPrefix(t, "*") { + err = add(t[1:]) + return + } else { + result = append(result, t) + return + } + + err = fmt.Errorf("bad type %q", t) + return +} + func add(a, i int) int { return a + i } diff --git a/tools/goctl/api/ktgen/gen.go b/tools/goctl/api/ktgen/gen.go index 40d08543..b8ab3ba1 100644 --- a/tools/goctl/api/ktgen/gen.go +++ b/tools/goctl/api/ktgen/gen.go @@ -126,10 +126,25 @@ func genBase(dir, pkg string, api *spec.ApiSpec) error { } func genApi(dir, pkg string, api *spec.ApiSpec) error { - name := strcase.ToCamel(api.Info.Title + "Api") + properties := api.Info.Properties + if properties == nil { + return fmt.Errorf("none properties") + } + + title := properties["Title"] + if len(title) == 0 { + return fmt.Errorf("none title") + } + + desc := properties["Desc"] + if len(desc) == 0 { + return fmt.Errorf("none desc") + } + + name := strcase.ToCamel(title + "Api") path := filepath.Join(dir, name+".kt") api.Info.Title = name - api.Info.Desc = pkg + api.Info.Desc = desc e := os.MkdirAll(dir, 0755) if e != nil { diff --git a/tools/goctl/api/parser/apifileparser.go b/tools/goctl/api/parser/apifileparser.go deleted file mode 100644 index 26320cf6..00000000 --- a/tools/goctl/api/parser/apifileparser.go +++ /dev/null @@ -1,268 +0,0 @@ -package parser - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "strings" - - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/api/util" -) - -const ( - tokenInfo = "info" - tokenImport = "import" - tokenType = "type" - tokenService = "service" - tokenServiceAnnotation = "@server" - tokenStruct = "struct" -) - -type ( - ApiStruct struct { - Info string - Type string - Service string - Imports string - serviceBeginLine int - } - - apiFileState interface { - process(api *ApiStruct, token string) (apiFileState, error) - } - - apiRootState struct { - *baseState - } - - apiInfoState struct { - *baseState - } - - apiImportState struct { - *baseState - } - - apiTypeState struct { - *baseState - } - - apiServiceState struct { - *baseState - } -) - -func ParseApi(src string) (*ApiStruct, error) { - var buffer = new(bytes.Buffer) - buffer.WriteString(src) - api := new(ApiStruct) - var lineNumber = api.serviceBeginLine - apiFile := baseState{r: bufio.NewReader(buffer), lineNumber: &lineNumber} - st := apiRootState{&apiFile} - for { - st, err := st.process(api, "") - if err == io.EOF { - return api, nil - } - if err != nil { - return nil, fmt.Errorf("near line: %d, %s", lineNumber, err.Error()) - } - if st == nil { - return api, nil - } - } -} - -func (s *apiRootState) process(api *ApiStruct, _ string) (apiFileState, error) { - var builder strings.Builder - for { - ch, err := s.readSkipComment() - if err != nil { - return nil, err - } - - switch { - case isSpace(ch) || isNewline(ch) || ch == leftParenthesis: - token := builder.String() - token = strings.TrimSpace(token) - if len(token) == 0 { - continue - } - - builder.Reset() - switch token { - case tokenInfo: - info := apiInfoState{s.baseState} - return info.process(api, token+string(ch)) - case tokenImport: - tp := apiImportState{s.baseState} - return tp.process(api, token+string(ch)) - case tokenType: - ty := apiTypeState{s.baseState} - return ty.process(api, token+string(ch)) - case tokenService: - server := apiServiceState{s.baseState} - return server.process(api, token+string(ch)) - case tokenServiceAnnotation: - server := apiServiceState{s.baseState} - return server.process(api, token+string(ch)) - default: - if strings.HasPrefix(token, "//") { - continue - } - return nil, errors.New(fmt.Sprintf("invalid token %s at line %d", token, *s.lineNumber)) - } - default: - builder.WriteRune(ch) - } - } -} - -func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, error) { - for { - line, err := s.readLine() - if err != nil { - return nil, err - } - - api.Info += newline + token + line - token = "" - if strings.TrimSpace(line) == string(rightParenthesis) { - return &apiRootState{s.baseState}, nil - } - } -} - -func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, error) { - line, err := s.readLine() - if err != nil { - return nil, err - } - - line = token + line - line = util.RemoveComment(line) - if len(strings.Fields(line)) != 2 { - return nil, errors.New("import syntax error: " + line) - } - - api.Imports += newline + line - return &apiRootState{s.baseState}, nil -} - -func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) { - var blockCount = 0 - var braceCount = 0 - for { - line, err := s.readLine() - if err != nil { - return nil, err - } - - line = token + line - if braceCount == 0 { - line = mayInsertStructKeyword(line) - } - api.Type += newline + newline + line - line = strings.TrimSpace(line) - line = util.RemoveComment(line) - token = "" - - if strings.HasSuffix(line, leftBrace) { - blockCount++ - braceCount++ - } - if strings.HasSuffix(line, string(leftParenthesis)) { - blockCount++ - } - if strings.HasSuffix(line, string(rightBrace)) { - blockCount-- - braceCount-- - } - if strings.HasSuffix(line, string(rightParenthesis)) { - blockCount-- - } - - if braceCount >= 2 { - return nil, errors.New("nested type not supported: " + line) - } - if braceCount < 0 { - line = strings.TrimSuffix(line, string(rightBrace)) - line = strings.TrimSpace(line) - if strings.HasSuffix(line, leftBrace) { - blockCount++ - braceCount++ - } - } - - if blockCount == 0 { - return &apiRootState{s.baseState}, nil - } - } -} - -func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, error) { - var blockCount = 0 - for { - line, err := s.readLineSkipComment() - if err != nil { - return nil, err - } - - line = token + line - token = "" - api.Service += newline + line - line = strings.TrimSpace(line) - line = util.RemoveComment(line) - - if strings.HasSuffix(line, leftBrace) { - blockCount++ - } - if strings.HasSuffix(line, string(leftParenthesis)) { - blockCount++ - } - if line == string(rightBrace) { - blockCount-- - } - if line == string(rightParenthesis) { - blockCount-- - } - - if blockCount == 0 { - return &apiRootState{s.baseState}, nil - } - } -} - -func mayInsertStructKeyword(line string) string { - line = util.RemoveComment(line) - if !strings.HasSuffix(line, leftBrace) && !strings.HasSuffix(line, string(rightBrace)) { - return line - } - - fields := strings.Fields(line) - if stringx.Contains(fields, tokenStruct) || - stringx.Contains(fields, tokenStruct+leftBrace) || - stringx.Contains(fields, tokenStruct+leftBrace+string(rightBrace)) || - len(fields) <= 1 { - return line - } - - var insertIndex int - if fields[0] == tokenType { - insertIndex = 2 - } else { - insertIndex = 1 - } - if insertIndex >= len(fields) { - return line - } - - var result []string - result = append(result, fields[:insertIndex]...) - result = append(result, tokenStruct) - result = append(result, fields[insertIndex:]...) - return strings.Join(result, " ") -} diff --git a/tools/goctl/api/parser/basestate.go b/tools/goctl/api/parser/basestate.go deleted file mode 100644 index 547026a7..00000000 --- a/tools/goctl/api/parser/basestate.go +++ /dev/null @@ -1,236 +0,0 @@ -package parser - -import ( - "bufio" - "fmt" - "strings" -) - -const ( - startState = iota - attrNameState - attrValueState - attrColonState - multilineState -) - -type baseState struct { - r *bufio.Reader - lineNumber *int -} - -func newBaseState(r *bufio.Reader, lineNumber *int) *baseState { - return &baseState{ - r: r, - lineNumber: lineNumber, - } -} - -func (s *baseState) parseProperties() (map[string]string, error) { - var r = s.r - var attributes = make(map[string]string) - var builder strings.Builder - var key string - var st = startState - - for { - ch, err := s.readSkipComment() - if err != nil { - return nil, err - } - - switch st { - case startState: - switch { - case isNewline(ch): - return nil, fmt.Errorf("%q should be on the same line with %q", leftParenthesis, infoDirective) - case isSpace(ch): - continue - case ch == leftParenthesis: - st = attrNameState - default: - return nil, fmt.Errorf("unexpected char %q after %q", ch, infoDirective) - } - case attrNameState: - switch { - case isNewline(ch): - if builder.Len() > 0 { - return nil, fmt.Errorf("unexpected newline after %q", builder.String()) - } - case isLetterDigit(ch): - builder.WriteRune(ch) - case isSpace(ch): - if builder.Len() > 0 { - key = builder.String() - builder.Reset() - st = attrColonState - } - case ch == colon: - if builder.Len() == 0 { - return nil, fmt.Errorf("unexpected leading %q", ch) - } - key = builder.String() - builder.Reset() - st = attrValueState - case ch == rightParenthesis: - return attributes, nil - } - case attrColonState: - switch { - case isSpace(ch): - continue - case ch == colon: - st = attrValueState - default: - return nil, fmt.Errorf("bad char %q after %q in %q", ch, key, infoDirective) - } - case attrValueState: - switch { - case ch == multilineBeginTag: - if builder.Len() > 0 { - return nil, fmt.Errorf("%q before %q", builder.String(), multilineBeginTag) - } else { - st = multilineState - } - case isSpace(ch): - if builder.Len() > 0 { - builder.WriteRune(ch) - } - case isNewline(ch): - attributes[key] = builder.String() - builder.Reset() - st = attrNameState - case ch == rightParenthesis: - attributes[key] = builder.String() - builder.Reset() - return attributes, nil - default: - builder.WriteRune(ch) - } - case multilineState: - switch { - case ch == multilineEndTag: - attributes[key] = builder.String() - builder.Reset() - st = attrNameState - case isNewline(ch): - var multipleNewlines bool - loopAfterNewline: - for { - next, err := read(r) - if err != nil { - return nil, err - } - - switch { - case isSpace(next): - continue - case isNewline(next): - multipleNewlines = true - default: - if err := unread(r); err != nil { - return nil, err - } - break loopAfterNewline - } - } - - if multipleNewlines { - fmt.Fprintln(&builder) - } else { - builder.WriteByte(' ') - } - case ch == rightParenthesis: - if builder.Len() > 0 { - attributes[key] = builder.String() - builder.Reset() - } - return attributes, nil - default: - builder.WriteRune(ch) - } - } - } -} - -func (s *baseState) read() (rune, error) { - value, err := read(s.r) - if err != nil { - return 0, err - } - if isNewline(value) { - *s.lineNumber++ - } - return value, nil -} - -func (s *baseState) readSkipComment() (rune, error) { - ch, err := s.read() - if err != nil { - return 0, err - } - - if isSlash(ch) { - value, err := s.mayReadToEndOfLine() - if err != nil { - return 0, err - } - - if value > 0 { - ch = value - } - } - return ch, nil -} - -func (s *baseState) mayReadToEndOfLine() (rune, error) { - ch, err := s.read() - if err != nil { - return 0, err - } - - if isSlash(ch) { - for { - value, err := s.read() - if err != nil { - return 0, err - } - - if isNewline(value) { - return value, nil - } - } - } - err = s.unread() - return 0, err -} - -func (s *baseState) readLineSkipComment() (string, error) { - line, err := s.readLine() - if err != nil { - return "", err - } - - var commentIdx = strings.Index(line, "//") - if commentIdx >= 0 { - return line[:commentIdx], nil - } - return line, nil -} - -func (s *baseState) readLine() (string, error) { - line, _, err := s.r.ReadLine() - if err != nil { - return "", err - } - *s.lineNumber++ - return string(line), nil -} - -func (s *baseState) skipSpaces() error { - return skipSpaces(s.r) -} - -func (s *baseState) unread() error { - return unread(s.r) -} diff --git a/tools/goctl/api/parser/basestate_test.go b/tools/goctl/api/parser/basestate_test.go deleted file mode 100644 index 51a0b20b..00000000 --- a/tools/goctl/api/parser/basestate_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package parser - -import ( - "bufio" - "bytes" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestProperties(t *testing.T) { - const text = `(summary: hello world)` - var builder bytes.Buffer - builder.WriteString(text) - var lineNumber = 1 - var state = newBaseState(bufio.NewReader(&builder), &lineNumber) - m, err := state.parseProperties() - assert.Nil(t, err) - assert.Equal(t, "hello world", m["summary"]) -} diff --git a/tools/goctl/api/parser/entity.go b/tools/goctl/api/parser/entity.go deleted file mode 100644 index eba95a51..00000000 --- a/tools/goctl/api/parser/entity.go +++ /dev/null @@ -1,146 +0,0 @@ -package parser - -import ( - "errors" - "fmt" - "strings" - - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -type ( - entity struct { - state *baseState - api *spec.ApiSpec - parser entityParser - } - - entityParser interface { - parseLine(line string, api *spec.ApiSpec, annos []spec.Annotation) error - setEntityName(name string) - } -) - -func newEntity(state *baseState, api *spec.ApiSpec, parser entityParser) entity { - return entity{ - state: state, - api: api, - parser: parser, - } -} - -func (s *entity) process() error { - line, err := s.state.readLineSkipComment() - if err != nil { - return err - } - - fields := strings.Fields(line) - if len(fields) < 2 { - return fmt.Errorf("invalid type definition for %q", - strings.TrimSpace(strings.Trim(string(line), "{"))) - } - - if len(fields) == 2 { - if fields[1] != leftBrace { - return fmt.Errorf("bad string %q after type", fields[1]) - } - } else if len(fields) == 3 { - if fields[1] != typeStruct { - return fmt.Errorf("bad string %q after type", fields[1]) - } - if fields[2] != leftBrace { - return fmt.Errorf("bad string %q after type", fields[2]) - } - } - - s.parser.setEntityName(fields[0]) - - var annos []spec.Annotation -memberLoop: - for { - ch, err := s.state.readSkipComment() - if err != nil { - return err - } - - var annoName string - var builder strings.Builder - switch { - case ch == at: - annotationLoop: - for { - next, err := s.state.readSkipComment() - if err != nil { - return err - } - switch { - case isSpace(next): - if builder.Len() > 0 && annoName == "" { - annoName = builder.String() - builder.Reset() - } - case isNewline(next): - if builder.Len() == 0 { - return errors.New("invalid annotation format") - } - - if len(annoName) > 0 { - value := builder.String() - if value != string(leftParenthesis) { - builder.Reset() - annos = append(annos, spec.Annotation{ - Name: annoName, - Value: value, - }) - annoName = "" - break annotationLoop - } - } - case next == leftParenthesis: - if builder.Len() == 0 { - return errors.New("invalid annotation format") - } - annoName = builder.String() - builder.Reset() - if err := s.state.unread(); err != nil { - return err - } - attrs, err := s.state.parseProperties() - if err != nil { - return err - } - annos = append(annos, spec.Annotation{ - Name: annoName, - Properties: attrs, - }) - annoName = "" - break annotationLoop - default: - builder.WriteRune(next) - } - } - case ch == rightBrace: - break memberLoop - case isLetterDigit(ch): - if err := s.state.unread(); err != nil { - return err - } - - var line string - line, err = s.state.readLineSkipComment() - if err != nil { - return err - } - - line = strings.TrimSpace(line) - if err := s.parser.parseLine(line, s.api, annos); err != nil { - return err - } - - annos = nil - } - } - - return nil -} diff --git a/tools/goctl/api/parser/g4/ApiLexer.g4 b/tools/goctl/api/parser/g4/ApiLexer.g4 new file mode 100644 index 00000000..ff423e40 --- /dev/null +++ b/tools/goctl/api/parser/g4/ApiLexer.g4 @@ -0,0 +1,46 @@ +lexer grammar ApiLexer; + +// Keywords +ATDOC: '@doc'; +ATHANDLER: '@handler'; +INTERFACE: 'interface{}'; +ATSERVER: '@server'; + +// Whitespace and comments +WS: [ \t\r\n\u000C]+ -> channel(HIDDEN); +COMMENT: '/*' .*? '*/' -> channel(88); +LINE_COMMENT: '//' ~[\r\n]* -> channel(88); +STRING: '"' (~["\\] | EscapeSequence)* '"'; +RAW_STRING: '`' (~[`\\\r\n] | EscapeSequence)+ '`'; +LINE_VALUE: ':' [ \t]* (STRING|(~[\r\n"`]*)); +ID: Letter LetterOrDigit*; + + +fragment ExponentPart + : [eE] [+-]? Digits + ; + +fragment EscapeSequence + : '\\' [btnfr"'\\] + | '\\' ([0-3]? [0-7])? [0-7] + | '\\' 'u'+ HexDigit HexDigit HexDigit HexDigit + ; +fragment HexDigits + : HexDigit ((HexDigit | '_')* HexDigit)? + ; +fragment HexDigit + : [0-9a-fA-F] + ; +fragment Digits + : [0-9] ([0-9_]* [0-9])? + ; + +fragment LetterOrDigit + : Letter + | [0-9] + ; +fragment Letter + : [a-zA-Z$_] // these are the "java letters" below 0x7F + | ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate + | [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF + ; \ No newline at end of file diff --git a/tools/goctl/api/parser/g4/ApiParser.g4 b/tools/goctl/api/parser/g4/ApiParser.g4 new file mode 100644 index 00000000..0f3aed6f --- /dev/null +++ b/tools/goctl/api/parser/g4/ApiParser.g4 @@ -0,0 +1,73 @@ +grammar ApiParser; + +import ApiLexer; + +@lexer::members{ + const COMEMNTS = 88 +} + +api: spec*; +spec: syntaxLit + |importSpec + |infoSpec + |typeSpec + |serviceSpec + ; + +// syntax +syntaxLit: {match(p,"syntax")}syntaxToken=ID assign='=' {checkVersion(p)}version=STRING; + +// import +importSpec: importLit|importBlock; +importLit: {match(p,"import")}importToken=ID importValue ; +importBlock: {match(p,"import")}importToken=ID '(' importBlockValue+ ')'; +importBlockValue: importValue; +importValue: {checkImportValue(p)}STRING; + +// info +infoSpec: {match(p,"info")}infoToken=ID lp='(' kvLit+ rp=')'; + +// type +typeSpec: typeLit + |typeBlock; + +// eg: type Foo int +typeLit: {match(p,"type")}typeToken=ID typeLitBody; +// eg: type (...) +typeBlock: {match(p,"type")}typeToken=ID lp='(' typeBlockBody* rp=')'; +typeLitBody: typeStruct|typeAlias; +typeBlockBody: typeBlockStruct|typeBlockAlias; +typeStruct: {checkKeyword(p)}structName=ID structToken=ID? lbrace='{' field* rbrace='}'; +typeAlias: {checkKeyword(p)}alias=ID assign='='? dataType; +typeBlockStruct: {checkKeyword(p)}structName=ID structToken=ID? lbrace='{' field* rbrace='}'; +typeBlockAlias: {checkKeyword(p)}alias=ID assign='='? dataType; +field: {isNormal(p)}? normalField|anonymousFiled ; +normalField: {checkKeyword(p)}fieldName=ID dataType tag=RAW_STRING?; +anonymousFiled: star='*'? ID; +dataType: {isInterface(p)}ID + |mapType + |arrayType + |inter='interface{}' + |time='time.Time' + |pointerType + |typeStruct + ; +pointerType: star='*' {checkKeyword(p)}ID; +mapType: {match(p,"map")}mapToken=ID lbrack='[' {checkKey(p)}key=ID rbrack=']' value=dataType; +arrayType: lbrack='[' rbrack=']' dataType; + +// service +serviceSpec: atServer? serviceApi; +atServer: ATSERVER lp='(' kvLit+ rp=')'; +serviceApi: {match(p,"service")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; +serviceRoute: atDoc? (atServer|atHandler) route; +atDoc: ATDOC lp='('? ((kvLit+)|STRING) rp=')'?; +atHandler: ATHANDLER ID; +route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; +body: lp='(' (ID)? rp=')'; +replybody: lp='(' dataType? rp=')'; +// kv +kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; + +serviceName: (ID '-'?)+; +path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; \ No newline at end of file diff --git a/tools/goctl/api/parser/g4/ast/api.go b/tools/goctl/api/parser/g4/ast/api.go new file mode 100644 index 00000000..4760f842 --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/api.go @@ -0,0 +1,251 @@ +package ast + +import ( + "fmt" + "sort" + + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +type Api struct { + LinePrefix string + Syntax *SyntaxExpr + Import []*ImportExpr + importM map[string]PlaceHolder + Info *InfoExpr + Type []TypeExpr + typeM map[string]PlaceHolder + Service []*Service + serviceM map[string]PlaceHolder + handlerM map[string]PlaceHolder + routeM map[string]PlaceHolder +} + +func (v *ApiVisitor) VisitApi(ctx *api.ApiContext) interface{} { + defer func() { + if p := recover(); p != nil { + panic(fmt.Errorf("%+v", p)) + } + }() + + var final Api + final.importM = map[string]PlaceHolder{} + final.typeM = map[string]PlaceHolder{} + final.serviceM = map[string]PlaceHolder{} + final.handlerM = map[string]PlaceHolder{} + final.routeM = map[string]PlaceHolder{} + for _, each := range ctx.AllSpec() { + root := each.Accept(v).(*Api) + if root.Syntax != nil { + if final.Syntax != nil { + v.panic(root.Syntax.Syntax, fmt.Sprintf("mutiple syntax declaration")) + } + + final.Syntax = root.Syntax + } + + for _, imp := range root.Import { + if _, ok := final.importM[imp.Value.Text()]; ok { + v.panic(imp.Import, fmt.Sprintf("duplicate import '%s'", imp.Value.Text())) + } + + final.importM[imp.Value.Text()] = Holder + final.Import = append(final.Import, imp) + } + + if root.Info != nil { + infoM := map[string]PlaceHolder{} + if final.Info != nil { + v.panic(root.Info.Info, fmt.Sprintf("mutiple info declaration")) + } + + for _, value := range root.Info.Kvs { + if _, ok := infoM[value.Key.Text()]; ok { + v.panic(value.Key, fmt.Sprintf("duplicate key '%s'", value.Key.Text())) + } + infoM[value.Key.Text()] = Holder + } + + final.Info = root.Info + } + + for _, tp := range root.Type { + if _, ok := final.typeM[tp.NameExpr().Text()]; ok { + v.panic(tp.NameExpr(), fmt.Sprintf("duplicate type '%s'", tp.NameExpr().Text())) + } + + final.typeM[tp.NameExpr().Text()] = Holder + final.Type = append(final.Type, tp) + } + + for _, service := range root.Service { + if _, ok := final.serviceM[service.ServiceApi.Name.Text()]; !ok && len(final.serviceM) > 0 { + v.panic(service.ServiceApi.Name, fmt.Sprintf("mutiple service declaration")) + } + + if service.AtServer != nil { + atServerM := map[string]PlaceHolder{} + for _, kv := range service.AtServer.Kv { + if _, ok := atServerM[kv.Key.Text()]; ok { + v.panic(kv.Key, fmt.Sprintf("duplicate key '%s'", kv.Key.Text())) + } + + atServerM[kv.Key.Text()] = Holder + } + } + + for _, route := range service.ServiceApi.ServiceRoute { + uniqueRoute := fmt.Sprintf("%s %s", route.Route.Method.Text(), route.Route.Path.Text()) + if _, ok := final.routeM[uniqueRoute]; ok { + v.panic(route.Route.Method, fmt.Sprintf("duplicate route '%s'", uniqueRoute)) + } + + final.routeM[uniqueRoute] = Holder + var handlerExpr Expr + if route.AtServer != nil { + atServerM := map[string]PlaceHolder{} + for _, kv := range route.AtServer.Kv { + if _, ok := atServerM[kv.Key.Text()]; ok { + v.panic(kv.Key, fmt.Sprintf("duplicate key '%s'", kv.Key.Text())) + } + atServerM[kv.Key.Text()] = Holder + if kv.Key.Text() == "handler" { + handlerExpr = kv.Value + } + } + } + + if route.AtHandler != nil { + handlerExpr = route.AtHandler.Name + } + + if handlerExpr == nil { + v.panic(route.Route.Method, fmt.Sprintf("mismtached handler")) + } + + if handlerExpr.Text() == "" { + v.panic(handlerExpr, fmt.Sprintf("mismtached handler")) + } + + if _, ok := final.handlerM[handlerExpr.Text()]; ok { + v.panic(handlerExpr, fmt.Sprintf("duplicate handler '%s'", handlerExpr.Text())) + } + final.handlerM[handlerExpr.Text()] = Holder + } + final.Service = append(final.Service, service) + } + } + + return &final +} + +func (v *ApiVisitor) VisitSpec(ctx *api.SpecContext) interface{} { + var root Api + if ctx.SyntaxLit() != nil { + root.Syntax = ctx.SyntaxLit().Accept(v).(*SyntaxExpr) + } + + if ctx.ImportSpec() != nil { + root.Import = ctx.ImportSpec().Accept(v).([]*ImportExpr) + } + + if ctx.InfoSpec() != nil { + root.Info = ctx.InfoSpec().Accept(v).(*InfoExpr) + } + + if ctx.TypeSpec() != nil { + tp := ctx.TypeSpec().Accept(v) + root.Type = tp.([]TypeExpr) + } + + if ctx.ServiceSpec() != nil { + root.Service = []*Service{ctx.ServiceSpec().Accept(v).(*Service)} + } + + return &root +} + +func (a *Api) Format() error { + // todo + return nil +} + +func (a *Api) Equal(v interface{}) bool { + if v == nil { + return false + } + + root, ok := v.(*Api) + if !ok { + return false + } + + if !a.Syntax.Equal(root.Syntax) { + return false + } + + if len(a.Import) != len(root.Import) { + return false + } + + var expectingImport, actualImport []*ImportExpr + expectingImport = append(expectingImport, a.Import...) + actualImport = append(actualImport, root.Import...) + + sort.Slice(expectingImport, func(i, j int) bool { + return expectingImport[i].Value.Text() < expectingImport[j].Value.Text() + }) + + sort.Slice(actualImport, func(i, j int) bool { + return actualImport[i].Value.Text() < actualImport[j].Value.Text() + }) + + for index, each := range expectingImport { + ac := actualImport[index] + if !each.Equal(ac) { + return false + } + } + + if !a.Info.Equal(root.Info) { + return false + } + + if len(a.Type) != len(root.Type) { + return false + } + + var expectingType, actualType []TypeExpr + expectingType = append(expectingType, a.Type...) + actualType = append(actualType, root.Type...) + + sort.Slice(expectingType, func(i, j int) bool { + return expectingType[i].NameExpr().Text() < expectingType[j].NameExpr().Text() + }) + sort.Slice(actualType, func(i, j int) bool { + return actualType[i].NameExpr().Text() < actualType[j].NameExpr().Text() + }) + + for index, each := range expectingType { + ac := actualType[index] + if !each.Equal(ac) { + return false + } + } + + if len(a.Service) != len(root.Service) { + return false + } + + var expectingService, actualService []*Service + expectingService = append(expectingService, a.Service...) + actualService = append(actualService, root.Service...) + for index, each := range expectingService { + ac := actualService[index] + if !each.Equal(ac) { + return false + } + } + + return true +} diff --git a/tools/goctl/api/parser/g4/ast/apiparser.go b/tools/goctl/api/parser/g4/ast/apiparser.go new file mode 100644 index 00000000..3c05f550 --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/apiparser.go @@ -0,0 +1,405 @@ +package ast + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" + "github.com/tal-tech/go-zero/tools/goctl/util/console" +) + +type ( + Parser struct { + linePrefix string + debug bool + log console.Console + antlr.DefaultErrorListener + } + + ParserOption func(p *Parser) +) + +func NewParser(options ...ParserOption) *Parser { + p := &Parser{ + log: console.NewColorConsole(), + } + for _, opt := range options { + opt(p) + } + + return p +} + +// Accept can parse any terminalNode of api tree by fn. +// -- for debug +func (p *Parser) Accept(fn func(p *api.ApiParserParser, visitor *ApiVisitor) interface{}, content string) (v interface{}, err error) { + defer func() { + p := recover() + if p != nil { + switch e := p.(type) { + case error: + err = e + default: + err = fmt.Errorf("%+v", p) + } + } + }() + + inputStream := antlr.NewInputStream(content) + lexer := api.NewApiParserLexer(inputStream) + lexer.RemoveErrorListeners() + tokens := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel) + apiParser := api.NewApiParserParser(tokens) + apiParser.RemoveErrorListeners() + apiParser.AddErrorListener(p) + var visitorOptions []VisitorOption + visitorOptions = append(visitorOptions, WithVisitorPrefix(p.linePrefix)) + if p.debug { + visitorOptions = append(visitorOptions, WithVisitorDebug()) + } + visitor := NewApiVisitor(visitorOptions...) + v = fn(apiParser, visitor) + return +} + +// Parse is used to parse the api from the specified file name +func (p *Parser) Parse(filename string) (*Api, error) { + data, err := p.readContent(filename) + if err != nil { + return nil, err + } + + return p.parse(filename, data) +} + +// ParseContent is used to parse the api from the specified content +func (p *Parser) ParseContent(content string) (*Api, error) { + return p.parse("", content) +} + +// parse is used to parse api from the content +// filename is only used to mark the file where the error is located +func (p *Parser) parse(filename, content string) (*Api, error) { + root, err := p.invoke(filename, content) + if err != nil { + return nil, err + } + + var apiAstList []*Api + apiAstList = append(apiAstList, root) + for _, imp := range root.Import { + path := imp.Value.Text() + data, err := p.readContent(path) + if err != nil { + return nil, err + } + + nestedApi, err := p.invoke(path, data) + if err != nil { + return nil, err + } + + err = p.valid(root, nestedApi) + if err != nil { + return nil, err + } + + apiAstList = append(apiAstList, nestedApi) + } + + err = p.checkTypeDeclaration(apiAstList) + if err != nil { + return nil, err + } + + allApi := p.memberFill(apiAstList) + return allApi, nil +} + +func (p *Parser) invoke(linePrefix, content string) (v *Api, err error) { + defer func() { + p := recover() + if p != nil { + switch e := p.(type) { + case error: + err = e + default: + err = fmt.Errorf("%+v", p) + } + } + }() + + if linePrefix != "" { + p.linePrefix = linePrefix + } + + inputStream := antlr.NewInputStream(content) + lexer := api.NewApiParserLexer(inputStream) + lexer.RemoveErrorListeners() + tokens := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel) + apiParser := api.NewApiParserParser(tokens) + apiParser.RemoveErrorListeners() + apiParser.AddErrorListener(p) + var visitorOptions []VisitorOption + visitorOptions = append(visitorOptions, WithVisitorPrefix(p.linePrefix)) + if p.debug { + visitorOptions = append(visitorOptions, WithVisitorDebug()) + } + + visitor := NewApiVisitor(visitorOptions...) + v = apiParser.Api().Accept(visitor).(*Api) + v.LinePrefix = p.linePrefix + return +} + +func (p *Parser) valid(mainApi *Api, nestedApi *Api) error { + if len(nestedApi.Import) > 0 { + importToken := nestedApi.Import[0].Import + return fmt.Errorf("%s line %d:%d the nested api does not support import", + nestedApi.LinePrefix, importToken.Line(), importToken.Column()) + } + + if mainApi.Syntax != nil && nestedApi.Syntax != nil { + if mainApi.Syntax.Version.Text() != nestedApi.Syntax.Version.Text() { + syntaxToken := nestedApi.Syntax.Syntax + return fmt.Errorf("%s line %d:%d multiple syntax declaration, expecting syntax '%s', but found '%s'", + nestedApi.LinePrefix, syntaxToken.Line(), syntaxToken.Column(), mainApi.Syntax.Version.Text(), nestedApi.Syntax.Version.Text()) + } + } + + if len(mainApi.Service) > 0 { + mainService := mainApi.Service[0] + for _, service := range nestedApi.Service { + if mainService.ServiceApi.Name.Text() != service.ServiceApi.Name.Text() { + return fmt.Errorf("%s multiple service name declaration, expecting service name '%s', but found '%s'", + nestedApi.LinePrefix, mainService.ServiceApi.Name.Text(), service.ServiceApi.Name.Text()) + } + } + } + + mainHandlerMap := make(map[string]PlaceHolder) + mainRouteMap := make(map[string]PlaceHolder) + mainTypeMap := make(map[string]PlaceHolder) + + routeMap := func(list []*ServiceRoute) (map[string]PlaceHolder, map[string]PlaceHolder) { + handlerMap := make(map[string]PlaceHolder) + routeMap := make(map[string]PlaceHolder) + + for _, g := range list { + handler := g.GetHandler() + if handler.IsNotNil() { + var handlerName = handler.Text() + handlerMap[handlerName] = Holder + path := fmt.Sprintf("%s://%s", g.Route.Method.Text(), g.Route.Path.Text()) + routeMap[path] = Holder + } + } + + return handlerMap, routeMap + } + + for _, each := range mainApi.Service { + h, r := routeMap(each.ServiceApi.ServiceRoute) + + for k, v := range h { + mainHandlerMap[k] = v + } + + for k, v := range r { + mainRouteMap[k] = v + } + } + + for _, each := range mainApi.Type { + mainTypeMap[each.NameExpr().Text()] = Holder + } + + // duplicate route check + for _, each := range nestedApi.Service { + for _, r := range each.ServiceApi.ServiceRoute { + handler := r.GetHandler() + if !handler.IsNotNil() { + return fmt.Errorf("%s handler not exist near line %d", nestedApi.LinePrefix, r.Route.Method.Line()) + } + + if _, ok := mainHandlerMap[handler.Text()]; ok { + return fmt.Errorf("%s line %d:%d duplicate handler '%s'", + nestedApi.LinePrefix, handler.Line(), handler.Column(), handler.Text()) + } + + path := fmt.Sprintf("%s://%s", r.Route.Method.Text(), r.Route.Path.Text()) + if _, ok := mainRouteMap[path]; ok { + return fmt.Errorf("%s line %d:%d duplicate route '%s'", + nestedApi.LinePrefix, r.Route.Method.Line(), r.Route.Method.Column(), r.Route.Method.Text()+" "+r.Route.Path.Text()) + } + } + } + + // duplicate type check + for _, each := range nestedApi.Type { + if _, ok := mainTypeMap[each.NameExpr().Text()]; ok { + return fmt.Errorf("%s line %d:%d duplicate type declaration '%s'", + nestedApi.LinePrefix, each.NameExpr().Line(), each.NameExpr().Column(), each.NameExpr().Text()) + } + } + return nil +} + +func (p *Parser) memberFill(apiList []*Api) *Api { + var root Api + for index, each := range apiList { + if index == 0 { + root.Syntax = each.Syntax + root.Info = each.Info + root.Import = each.Import + } + + root.Type = append(root.Type, each.Type...) + root.Service = append(root.Service, each.Service...) + } + + return &root +} + +// checkTypeDeclaration checks whether a struct type has been declared in context +func (p *Parser) checkTypeDeclaration(apiList []*Api) error { + types := make(map[string]TypeExpr) + + for _, root := range apiList { + for _, each := range root.Type { + types[each.NameExpr().Text()] = each + } + } + + for _, apiItem := range apiList { + linePrefix := apiItem.LinePrefix + for _, each := range apiItem.Type { + tp, ok := each.(*TypeStruct) + if !ok { + continue + } + + for _, member := range tp.Fields { + err := p.checkType(linePrefix, types, member.DataType) + if err != nil { + return err + } + } + } + + for _, service := range apiItem.Service { + for _, each := range service.ServiceApi.ServiceRoute { + route := each.Route + if route.Req != nil && route.Req.Name.IsNotNil() && route.Req.Name.Expr().IsNotNil() { + _, ok := types[route.Req.Name.Expr().Text()] + if !ok { + return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context", + linePrefix, route.Req.Name.Expr().Line(), route.Req.Name.Expr().Column(), route.Req.Name.Expr().Text()) + } + } + + if route.Reply != nil && route.Reply.Name.IsNotNil() && route.Reply.Name.Expr().IsNotNil() { + reply := route.Reply.Name + var structName string + switch tp := reply.(type) { + case *Literal: + structName = tp.Literal.Text() + case *Array: + switch innerTp := tp.Literal.(type) { + case *Literal: + structName = innerTp.Literal.Text() + case *Pointer: + structName = innerTp.Name.Text() + } + } + + if api.IsBasicType(structName) { + continue + } + + _, ok := types[structName] + if !ok { + return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context", + linePrefix, route.Reply.Name.Expr().Line(), route.Reply.Name.Expr().Column(), structName) + } + } + } + } + } + return nil +} + +func (p *Parser) checkType(linePrefix string, types map[string]TypeExpr, expr DataType) error { + if expr == nil { + return nil + } + + switch v := expr.(type) { + case *Literal: + name := v.Literal.Text() + if api.IsBasicType(name) { + return nil + } + _, ok := types[name] + if !ok { + return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context", + linePrefix, v.Literal.Line(), v.Literal.Column(), name) + } + + case *Pointer: + name := v.Name.Text() + if api.IsBasicType(name) { + return nil + } + _, ok := types[name] + if !ok { + return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context", + linePrefix, v.Name.Line(), v.Name.Column(), name) + } + case *Map: + return p.checkType(linePrefix, types, v.Value) + case *Array: + return p.checkType(linePrefix, types, v.Literal) + default: + return nil + } + return nil +} + +func (p *Parser) readContent(filename string) (string, error) { + filename = strings.ReplaceAll(filename, `"`, "") + abs, err := filepath.Abs(filename) + if err != nil { + return "", err + } + + data, err := ioutil.ReadFile(abs) + if err != nil { + return "", err + } + + return string(data), nil +} + +func (p *Parser) SyntaxError(_ antlr.Recognizer, _ interface{}, line, column int, msg string, _ antlr.RecognitionException) { + str := fmt.Sprintf(`%s line %d:%d %s`, p.linePrefix, line, column, msg) + if p.debug { + p.log.Error(str) + } + panic(str) +} + +func WithParserDebug() ParserOption { + return func(p *Parser) { + p.debug = true + } +} + +func WithParserPrefix(prefix string) ParserOption { + return func(p *Parser) { + p.linePrefix = prefix + } +} diff --git a/tools/goctl/api/parser/g4/ast/ast.go b/tools/goctl/api/parser/g4/ast/ast.go new file mode 100644 index 00000000..fc8c3a63 --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/ast.go @@ -0,0 +1,325 @@ +package ast + +import ( + "fmt" + "sort" + "strings" + + "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" + "github.com/tal-tech/go-zero/tools/goctl/api/util" + "github.com/tal-tech/go-zero/tools/goctl/util/console" +) + +type ( + TokenStream interface { + GetStart() antlr.Token + GetStop() antlr.Token + GetParser() antlr.Parser + } + ApiVisitor struct { + api.BaseApiParserVisitor + debug bool + log console.Console + prefix string + infoFlag bool + } + + VisitorOption func(v *ApiVisitor) + + Spec interface { + Doc() []Expr + Comment() Expr + Format() error + Equal(v interface{}) bool + } + + Expr interface { + Prefix() string + Line() int + Column() int + Text() string + SetText(text string) + Start() int + Stop() int + Equal(expr Expr) bool + IsNotNil() bool + } +) + +func NewApiVisitor(options ...VisitorOption) *ApiVisitor { + v := &ApiVisitor{ + log: console.NewColorConsole(), + } + for _, opt := range options { + opt(v) + } + return v +} + +func (v *ApiVisitor) panic(expr Expr, msg string) { + errString := fmt.Sprintf("%s line %d:%d %s", v.prefix, expr.Line(), expr.Column(), msg) + if v.debug { + fmt.Println(errString) + } + + panic(errString) +} + +func WithVisitorPrefix(prefix string) VisitorOption { + return func(v *ApiVisitor) { + v.prefix = prefix + } +} + +func WithVisitorDebug() VisitorOption { + return func(v *ApiVisitor) { + v.debug = true + } +} + +type defaultExpr struct { + prefix, v string + line, column int + start, stop int +} + +func NewTextExpr(v string) *defaultExpr { + return &defaultExpr{ + v: v, + } +} + +func (v *ApiVisitor) newExprWithTerminalNode(node antlr.TerminalNode) *defaultExpr { + if node == nil { + return nil + } + token := node.GetSymbol() + return v.newExprWithToken(token) +} + +func (v *ApiVisitor) newExprWithToken(token antlr.Token) *defaultExpr { + if token == nil { + return nil + } + + instance := &defaultExpr{} + instance.prefix = v.prefix + instance.v = token.GetText() + instance.line = token.GetLine() + instance.column = token.GetColumn() + instance.start = token.GetStart() + instance.stop = token.GetStop() + + return instance +} + +func (v *ApiVisitor) newExprWithText(text string, line, column, start, stop int) *defaultExpr { + instance := &defaultExpr{} + instance.prefix = v.prefix + instance.v = text + instance.line = line + instance.column = column + instance.start = start + instance.stop = stop + return instance +} + +func (e *defaultExpr) Prefix() string { + if e == nil { + return "" + } + + return e.prefix +} + +func (e *defaultExpr) Line() int { + if e == nil { + return 0 + } + + return e.line +} + +func (e *defaultExpr) Column() int { + if e == nil { + return 0 + } + + return e.column +} + +func (e *defaultExpr) Text() string { + if e == nil { + return "" + } + + return e.v +} + +func (e *defaultExpr) SetText(text string) { + if e == nil { + return + } + + e.v = text +} + +func (e *defaultExpr) Start() int { + if e == nil { + return 0 + } + + return e.start +} + +func (e *defaultExpr) Stop() int { + if e == nil { + return 0 + } + + return e.stop +} + +func (e *defaultExpr) Equal(expr Expr) bool { + if e == nil { + if expr != nil { + return false + } + + return true + } + + if expr == nil { + return false + } + + return e.v == expr.Text() +} + +func (e *defaultExpr) IsNotNil() bool { + return e != nil +} + +func EqualDoc(spec1, spec2 Spec) bool { + if spec1 == nil { + if spec2 != nil { + return false + } + return true + } else { + if spec2 == nil { + return false + } + + var expectDoc, actualDoc []Expr + expectDoc = append(expectDoc, spec2.Doc()...) + actualDoc = append(actualDoc, spec1.Doc()...) + sort.Slice(expectDoc, func(i, j int) bool { + return expectDoc[i].Line() < expectDoc[j].Line() + }) + + for index, each := range actualDoc { + if !each.Equal(actualDoc[index]) { + return false + } + } + + if spec1.Comment() != nil { + if spec2.Comment() == nil { + return false + } + if !spec1.Comment().Equal(spec2.Comment()) { + return false + } + } else { + if spec2.Comment() != nil { + return false + } + } + } + return true +} + +func (v *ApiVisitor) getDoc(t TokenStream) []Expr { + list := v.getHiddenTokensToLeft(t, api.COMEMNTS, false) + return list +} + +func (v *ApiVisitor) getComment(t TokenStream) Expr { + list := v.getHiddenTokensToRight(t, api.COMEMNTS) + if len(list) == 0 { + return nil + } + + commentExpr := list[0] + stop := t.GetStop() + text := stop.GetText() + nlCount := strings.Count(text, "\n") + + if commentExpr.Line() != stop.GetLine()+nlCount { + return nil + } + + return commentExpr +} + +func (v *ApiVisitor) getHiddenTokensToLeft(t TokenStream, channel int, containsCommentOfDefaultChannel bool) []Expr { + ct := t.GetParser().GetTokenStream().(*antlr.CommonTokenStream) + tokens := ct.GetHiddenTokensToLeft(t.GetStart().GetTokenIndex(), channel) + var tmp []antlr.Token + for _, each := range tokens { + tmp = append(tmp, each) + } + + var list []Expr + for _, each := range tmp { + if !containsCommentOfDefaultChannel { + index := each.GetTokenIndex() - 1 + + if index > 0 { + allTokens := ct.GetAllTokens() + var flag = false + for i := index; i >= 0; i-- { + tk := allTokens[i] + if tk.GetChannel() == antlr.LexerDefaultTokenChannel { + if tk.GetLine() == each.GetLine() { + flag = true + break + } + } + } + + if flag { + continue + } + } + } + list = append(list, v.newExprWithToken(each)) + } + return list +} + +func (v *ApiVisitor) getHiddenTokensToRight(t TokenStream, channel int) []Expr { + ct := t.GetParser().GetTokenStream().(*antlr.CommonTokenStream) + tokens := ct.GetHiddenTokensToRight(t.GetStop().GetTokenIndex(), channel) + var list []Expr + for _, each := range tokens { + list = append(list, v.newExprWithToken(each)) + } + return list +} + +func (v *ApiVisitor) exportCheck(expr Expr) { + if expr == nil || !expr.IsNotNil() { + return + } + if api.IsBasicType(expr.Text()) { + return + } + + if util.UnExport(expr.Text()) { + v.log.Warning("%s line %d:%d unexported declaration '%s', use %s instead", expr.Prefix(), expr.Line(), + expr.Column(), expr.Text(), strings.Title(expr.Text())) + } +} diff --git a/tools/goctl/api/parser/g4/ast/import.go b/tools/goctl/api/parser/g4/ast/import.go new file mode 100644 index 00000000..24b9027a --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/import.go @@ -0,0 +1,96 @@ +package ast + +import ( + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +type ImportExpr struct { + Import Expr + Value Expr + DocExpr []Expr + CommentExpr Expr +} + +func (v *ApiVisitor) VisitImportSpec(ctx *api.ImportSpecContext) interface{} { + var list []*ImportExpr + if ctx.ImportLit() != nil { + lits := ctx.ImportLit().Accept(v).([]*ImportExpr) + list = append(list, lits...) + } + if ctx.ImportBlock() != nil { + blocks := ctx.ImportBlock().Accept(v).([]*ImportExpr) + list = append(list, blocks...) + } + + return list +} + +func (v *ApiVisitor) VisitImportLit(ctx *api.ImportLitContext) interface{} { + importToken := v.newExprWithToken(ctx.GetImportToken()) + valueExpr := ctx.ImportValue().Accept(v).(Expr) + return []*ImportExpr{ + { + Import: importToken, + Value: valueExpr, + DocExpr: v.getDoc(ctx), + CommentExpr: v.getComment(ctx), + }, + } +} + +func (v *ApiVisitor) VisitImportBlock(ctx *api.ImportBlockContext) interface{} { + importToken := v.newExprWithToken(ctx.GetImportToken()) + values := ctx.AllImportBlockValue() + var list []*ImportExpr + + for _, value := range values { + importExpr := value.Accept(v).(*ImportExpr) + importExpr.Import = importToken + list = append(list, importExpr) + } + + return list +} + +func (v *ApiVisitor) VisitImportBlockValue(ctx *api.ImportBlockValueContext) interface{} { + value := ctx.ImportValue().Accept(v).(Expr) + return &ImportExpr{ + Value: value, + DocExpr: v.getDoc(ctx), + CommentExpr: v.getComment(ctx), + } +} + +func (v *ApiVisitor) VisitImportValue(ctx *api.ImportValueContext) interface{} { + return v.newExprWithTerminalNode(ctx.STRING()) +} + +func (i *ImportExpr) Format() error { + // todo + return nil +} + +func (i *ImportExpr) Equal(v interface{}) bool { + if v == nil { + return false + } + + imp, ok := v.(*ImportExpr) + if !ok { + return false + } + + if !EqualDoc(i, imp) { + return false + } + + return i.Import.Equal(imp.Import) && i.Value.Equal(imp.Value) +} + +func (i *ImportExpr) Doc() []Expr { + return i.DocExpr +} + +func (i *ImportExpr) Comment() Expr { + return i.CommentExpr +} diff --git a/tools/goctl/api/parser/g4/ast/info.go b/tools/goctl/api/parser/g4/ast/info.go new file mode 100644 index 00000000..293d253c --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/info.go @@ -0,0 +1,67 @@ +package ast + +import ( + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +type InfoExpr struct { + Info Expr + Lp Expr + Rp Expr + Kvs []*KvExpr +} + +func (v *ApiVisitor) VisitInfoSpec(ctx *api.InfoSpecContext) interface{} { + var expr InfoExpr + expr.Info = v.newExprWithToken(ctx.GetInfoToken()) + expr.Lp = v.newExprWithToken(ctx.GetLp()) + expr.Rp = v.newExprWithToken(ctx.GetRp()) + list := ctx.AllKvLit() + for _, each := range list { + kvExpr := each.Accept(v).(*KvExpr) + expr.Kvs = append(expr.Kvs, kvExpr) + } + + if v.infoFlag { + v.panic(expr.Info, "duplicate declaration 'info'") + } + + return &expr +} + +func (i *InfoExpr) Format() error { + // todo + return nil +} + +func (i *InfoExpr) Equal(v interface{}) bool { + if v == nil { + return false + } + + info, ok := v.(*InfoExpr) + if !ok { + return false + } + + if !i.Info.Equal(info.Info) { + return false + } + + var expected, actual []*KvExpr + expected = append(expected, i.Kvs...) + actual = append(actual, info.Kvs...) + + if len(expected) != len(actual) { + return false + } + + for index, each := range expected { + ac := actual[index] + if !each.Equal(ac) { + return false + } + } + + return true +} diff --git a/tools/goctl/api/parser/g4/ast/kv.go b/tools/goctl/api/parser/g4/ast/kv.go new file mode 100644 index 00000000..00331fbd --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/kv.go @@ -0,0 +1,79 @@ +package ast + +import ( + "strings" + + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +type KvExpr struct { + Key Expr + Value Expr + DocExpr []Expr + CommentExpr Expr +} + +func (v *ApiVisitor) VisitKvLit(ctx *api.KvLitContext) interface{} { + var kvExpr KvExpr + kvExpr.Key = v.newExprWithToken(ctx.GetKey()) + commentExpr := v.getComment(ctx) + if ctx.GetValue() != nil { + valueText := ctx.GetValue().GetText() + valueExpr := v.newExprWithToken(ctx.GetValue()) + if strings.Contains(valueText, "//") { + if commentExpr == nil { + commentExpr = v.newExprWithToken(ctx.GetValue()) + commentExpr.SetText("") + } + + index := strings.Index(valueText, "//") + commentExpr.SetText(valueText[index:]) + valueExpr.SetText(strings.TrimSpace(valueText[:index])) + } else if strings.Contains(valueText, "/*") { + if commentExpr == nil { + commentExpr = v.newExprWithToken(ctx.GetValue()) + commentExpr.SetText("") + } + + index := strings.Index(valueText, "/*") + commentExpr.SetText(valueText[index:]) + valueExpr.SetText(strings.TrimSpace(valueText[:index])) + } + + kvExpr.Value = valueExpr + } + + kvExpr.DocExpr = v.getDoc(ctx) + kvExpr.CommentExpr = commentExpr + return &kvExpr +} + +func (k *KvExpr) Format() error { + // todo + return nil +} + +func (k *KvExpr) Equal(v interface{}) bool { + if v == nil { + return false + } + + kv, ok := v.(*KvExpr) + if !ok { + return false + } + + if !EqualDoc(k, kv) { + return false + } + + return k.Key.Equal(kv.Key) && k.Value.Equal(kv.Value) +} + +func (k *KvExpr) Doc() []Expr { + return k.DocExpr +} + +func (k *KvExpr) Comment() Expr { + return k.CommentExpr +} diff --git a/tools/goctl/api/parser/g4/ast/placeholder.go b/tools/goctl/api/parser/g4/ast/placeholder.go new file mode 100644 index 00000000..0217a043 --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/placeholder.go @@ -0,0 +1,5 @@ +package ast + +var Holder PlaceHolder + +type PlaceHolder struct{} diff --git a/tools/goctl/api/parser/g4/ast/service.go b/tools/goctl/api/parser/g4/ast/service.go new file mode 100644 index 00000000..8a45e7ca --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/service.go @@ -0,0 +1,603 @@ +package ast + +import ( + "fmt" + "sort" + + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +type Service struct { + AtServer *AtServer + ServiceApi *ServiceApi +} + +type KV []*KvExpr + +type AtServer struct { + AtServerToken Expr + Lp Expr + Rp Expr + Kv KV +} + +type ServiceApi struct { + ServiceToken Expr + Name Expr + Lbrace Expr + Rbrace Expr + ServiceRoute []*ServiceRoute +} + +type ServiceRoute struct { + AtDoc *AtDoc + AtServer *AtServer + AtHandler *AtHandler + Route *Route +} + +type AtDoc struct { + AtDocToken Expr + Lp Expr + Rp Expr + LineDoc Expr + Kv []*KvExpr +} + +type AtHandler struct { + AtHandlerToken Expr + Name Expr + DocExpr []Expr + CommentExpr Expr +} + +type Route struct { + Method Expr + Path Expr + Req *Body + ReturnToken Expr + Reply *Body + DocExpr []Expr + CommentExpr Expr +} + +type Body struct { + Lp Expr + Rp Expr + Name DataType +} + +func (v *ApiVisitor) VisitServiceSpec(ctx *api.ServiceSpecContext) interface{} { + var serviceSpec Service + if ctx.AtServer() != nil { + serviceSpec.AtServer = ctx.AtServer().Accept(v).(*AtServer) + } + + serviceSpec.ServiceApi = ctx.ServiceApi().Accept(v).(*ServiceApi) + return &serviceSpec +} + +func (v *ApiVisitor) VisitAtServer(ctx *api.AtServerContext) interface{} { + var atServer AtServer + atServer.AtServerToken = v.newExprWithTerminalNode(ctx.ATSERVER()) + atServer.Lp = v.newExprWithToken(ctx.GetLp()) + atServer.Rp = v.newExprWithToken(ctx.GetRp()) + + for _, each := range ctx.AllKvLit() { + atServer.Kv = append(atServer.Kv, each.Accept(v).(*KvExpr)) + } + + return &atServer +} + +func (v *ApiVisitor) VisitServiceApi(ctx *api.ServiceApiContext) interface{} { + var serviceApi ServiceApi + serviceApi.ServiceToken = v.newExprWithToken(ctx.GetServiceToken()) + serviceName := ctx.ServiceName() + serviceApi.Name = v.newExprWithText(serviceName.GetText(), serviceName.GetStart().GetLine(), serviceName.GetStart().GetColumn(), serviceName.GetStart().GetStart(), serviceName.GetStop().GetStop()) + serviceApi.Lbrace = v.newExprWithToken(ctx.GetLbrace()) + serviceApi.Rbrace = v.newExprWithToken(ctx.GetRbrace()) + + for _, each := range ctx.AllServiceRoute() { + serviceApi.ServiceRoute = append(serviceApi.ServiceRoute, each.Accept(v).(*ServiceRoute)) + } + + return &serviceApi +} + +func (v *ApiVisitor) VisitServiceRoute(ctx *api.ServiceRouteContext) interface{} { + var serviceRoute ServiceRoute + if ctx.AtDoc() != nil { + serviceRoute.AtDoc = ctx.AtDoc().Accept(v).(*AtDoc) + } + + if ctx.AtServer() != nil { + serviceRoute.AtServer = ctx.AtServer().Accept(v).(*AtServer) + } else if ctx.AtHandler() != nil { + serviceRoute.AtHandler = ctx.AtHandler().Accept(v).(*AtHandler) + } + + serviceRoute.Route = ctx.Route().Accept(v).(*Route) + return &serviceRoute +} + +func (v *ApiVisitor) VisitAtDoc(ctx *api.AtDocContext) interface{} { + var atDoc AtDoc + atDoc.AtDocToken = v.newExprWithTerminalNode(ctx.ATDOC()) + + if ctx.STRING() != nil { + atDoc.LineDoc = v.newExprWithTerminalNode(ctx.STRING()) + } else { + for _, each := range ctx.AllKvLit() { + atDoc.Kv = append(atDoc.Kv, each.Accept(v).(*KvExpr)) + } + } + atDoc.Lp = v.newExprWithToken(ctx.GetLp()) + atDoc.Rp = v.newExprWithToken(ctx.GetRp()) + + if ctx.GetLp() != nil { + if ctx.GetRp() == nil { + v.panic(atDoc.Lp, "mismatched ')'") + } + } + + if ctx.GetRp() != nil { + if ctx.GetLp() == nil { + v.panic(atDoc.Rp, "mismatched '('") + } + } + + return &atDoc +} + +func (v *ApiVisitor) VisitAtHandler(ctx *api.AtHandlerContext) interface{} { + var atHandler AtHandler + astHandlerExpr := v.newExprWithTerminalNode(ctx.ATHANDLER()) + atHandler.AtHandlerToken = astHandlerExpr + atHandler.Name = v.newExprWithTerminalNode(ctx.ID()) + atHandler.DocExpr = v.getDoc(ctx) + atHandler.CommentExpr = v.getComment(ctx) + return &atHandler +} + +func (v *ApiVisitor) VisitRoute(ctx *api.RouteContext) interface{} { + var route Route + path := ctx.Path() + methodExpr := v.newExprWithToken(ctx.GetHttpMethod()) + route.Method = methodExpr + route.Path = v.newExprWithText(path.GetText(), path.GetStart().GetLine(), path.GetStart().GetColumn(), path.GetStart().GetStart(), path.GetStop().GetStop()) + + if ctx.GetRequest() != nil { + req := ctx.GetRequest().Accept(v) + if req != nil { + route.Req = req.(*Body) + } + } + + if ctx.GetResponse() != nil { + reply := ctx.GetResponse().Accept(v) + if reply != nil { + route.Reply = reply.(*Body) + } + } + if ctx.GetReturnToken() != nil { + returnExpr := v.newExprWithToken(ctx.GetReturnToken()) + if ctx.GetReturnToken().GetText() != "returns" { + v.panic(returnExpr, fmt.Sprintf("expecting returns, found input '%s'", ctx.GetReturnToken().GetText())) + } + route.ReturnToken = returnExpr + } + + route.DocExpr = v.getDoc(ctx) + route.CommentExpr = v.getComment(ctx) + return &route +} + +func (v *ApiVisitor) VisitBody(ctx *api.BodyContext) interface{} { + if ctx.ID() == nil { + return nil + } + + idRxpr := v.newExprWithTerminalNode(ctx.ID()) + if api.IsGolangKeyWord(idRxpr.Text()) { + v.panic(idRxpr, fmt.Sprintf("expecting 'ID', but found golang keyword '%s'", idRxpr.Text())) + } + v.exportCheck(idRxpr) + + return &Body{ + Lp: v.newExprWithToken(ctx.GetLp()), + Rp: v.newExprWithToken(ctx.GetRp()), + Name: &Literal{Literal: idRxpr}, + } +} + +// note: forward compatible +func (v *ApiVisitor) VisitReplybody(ctx *api.ReplybodyContext) interface{} { + if ctx.DataType() == nil { + return nil + } + + dt := ctx.DataType().Accept(v).(DataType) + if dt == nil { + return nil + } + + switch dataType := dt.(type) { + case *Array: + lit := dataType.Literal + switch lit.(type) { + case *Literal, *Pointer: + if api.IsGolangKeyWord(lit.Expr().Text()) { + v.panic(lit.Expr(), fmt.Sprintf("expecting 'ID', but found golang keyword '%s'", lit.Expr().Text())) + } + default: + v.panic(dt.Expr(), fmt.Sprintf("unsupport %s", dt.Expr().Text())) + } + v.log.Warning("%s %d:%d deprecated array type near '%s'", v.prefix, dataType.ArrayExpr.Line(), dataType.ArrayExpr.Column(), dataType.ArrayExpr.Text()) + case *Literal: + lit := dataType.Literal.Text() + if api.IsGolangKeyWord(dataType.Literal.Text()) { + v.panic(dataType.Literal, fmt.Sprintf("expecting 'ID', but found golang keyword '%s'", dataType.Literal.Text())) + } + if api.IsBasicType(lit) { + v.panic(dt.Expr(), fmt.Sprintf("unsupport %s", dt.Expr().Text())) + } + default: + v.panic(dt.Expr(), fmt.Sprintf("unsupport %s", dt.Expr().Text())) + } + + return &Body{ + Lp: v.newExprWithToken(ctx.GetLp()), + Rp: v.newExprWithToken(ctx.GetRp()), + Name: dt, + } +} + +func (b *Body) Format() error { + // todo + return nil +} +func (b *Body) Equal(v interface{}) bool { + if v == nil { + return false + } + + body, ok := v.(*Body) + if !ok { + return false + } + + if !b.Lp.Equal(body.Lp) { + return false + } + + if !b.Rp.Equal(body.Rp) { + return false + } + + return b.Name.Equal(body.Name) +} + +func (r *Route) Format() error { + // todo + return nil +} + +func (r *Route) Doc() []Expr { + return r.DocExpr +} + +func (r *Route) Comment() Expr { + return r.CommentExpr +} + +func (r *Route) Equal(v interface{}) bool { + if v == nil { + return false + } + + route, ok := v.(*Route) + if !ok { + return false + } + + if !r.Method.Equal(route.Method) { + return false + } + + if !r.Path.Equal(route.Path) { + return false + } + + if r.Req != nil { + if !r.Req.Equal(route.Req) { + return false + } + } + + if r.ReturnToken != nil { + if !r.ReturnToken.Equal(route.ReturnToken) { + return false + } + } + + if r.Reply != nil { + if !r.Reply.Equal(route.Reply) { + return false + } + } + + return EqualDoc(r, route) +} + +func (a *AtHandler) Doc() []Expr { + return a.DocExpr +} + +func (a *AtHandler) Comment() Expr { + return a.CommentExpr +} + +func (a *AtHandler) Format() error { + // todo + return nil +} + +func (a *AtHandler) Equal(v interface{}) bool { + if v == nil { + return false + } + + atHandler, ok := v.(*AtHandler) + if !ok { + return false + } + + if !a.AtHandlerToken.Equal(atHandler.AtHandlerToken) { + return false + } + + if !a.Name.Equal(atHandler.Name) { + return false + } + + return EqualDoc(a, atHandler) +} + +func (a *AtDoc) Format() error { + // todo + return nil +} + +func (a *AtDoc) Equal(v interface{}) bool { + if v == nil { + return false + } + + atDoc, ok := v.(*AtDoc) + if !ok { + return false + } + + if !a.AtDocToken.Equal(atDoc.AtDocToken) { + return false + } + + if a.Lp.IsNotNil() { + if !a.Lp.Equal(atDoc.Lp) { + return false + } + } + + if a.Rp.IsNotNil() { + if !a.Rp.Equal(atDoc.Rp) { + return false + } + } + + if a.LineDoc != nil { + if !a.LineDoc.Equal(atDoc.LineDoc) { + return false + } + } + + var expecting, actual []*KvExpr + expecting = append(expecting, a.Kv...) + actual = append(actual, atDoc.Kv...) + + if len(expecting) != len(actual) { + return false + } + + for index, each := range expecting { + ac := actual[index] + if !each.Equal(ac) { + return false + } + } + + return true +} + +func (a *AtServer) Format() error { + // todo + return nil +} + +func (a *AtServer) Equal(v interface{}) bool { + if v == nil { + return false + } + + atServer, ok := v.(*AtServer) + if !ok { + return false + } + + if !a.AtServerToken.Equal(atServer.AtServerToken) { + return false + } + + if !a.Lp.Equal(atServer.Lp) { + return false + } + + if !a.Rp.Equal(atServer.Rp) { + return false + } + + var expecting, actual []*KvExpr + expecting = append(expecting, a.Kv...) + actual = append(actual, atServer.Kv...) + if len(expecting) != len(actual) { + return false + } + + sort.Slice(expecting, func(i, j int) bool { + return expecting[i].Key.Text() < expecting[j].Key.Text() + }) + + sort.Slice(actual, func(i, j int) bool { + return actual[i].Key.Text() < actual[j].Key.Text() + }) + + for index, each := range expecting { + ac := actual[index] + if !each.Equal(ac) { + return false + } + } + + return true +} + +func (s *ServiceRoute) Equal(v interface{}) bool { + if v == nil { + return false + } + + sr, ok := v.(*ServiceRoute) + if !ok { + return false + } + + if !s.AtDoc.Equal(sr.AtDoc) { + return false + } + + if s.AtServer != nil { + if !s.AtServer.Equal(sr.AtServer) { + return false + } + } + + if s.AtHandler != nil { + if !s.AtHandler.Equal(sr.AtHandler) { + return false + } + } + + return s.Route.Equal(sr.Route) +} + +func (s *ServiceRoute) Format() error { + // todo + return nil +} + +func (s *ServiceRoute) GetHandler() Expr { + if s.AtHandler != nil { + return s.AtHandler.Name + } else { + return s.AtServer.Kv.Get("handler") + } +} + +func (a *ServiceApi) Format() error { + // todo + return nil +} + +func (a *ServiceApi) Equal(v interface{}) bool { + if v == nil { + return false + } + + api, ok := v.(*ServiceApi) + if !ok { + return false + } + + if !a.ServiceToken.Equal(api.ServiceToken) { + return false + } + + if !a.Name.Equal(api.Name) { + return false + } + + if !a.Lbrace.Equal(api.Lbrace) { + return false + } + + if !a.Rbrace.Equal(api.Rbrace) { + return false + } + + var expecting, acutal []*ServiceRoute + expecting = append(expecting, a.ServiceRoute...) + acutal = append(acutal, api.ServiceRoute...) + if len(expecting) != len(acutal) { + return false + } + + sort.Slice(expecting, func(i, j int) bool { + return expecting[i].Route.Path.Text() < expecting[j].Route.Path.Text() + }) + + sort.Slice(acutal, func(i, j int) bool { + return acutal[i].Route.Path.Text() < acutal[j].Route.Path.Text() + }) + + for index, each := range expecting { + ac := acutal[index] + if !each.Equal(ac) { + return false + } + } + + return true +} + +func (s *Service) Format() error { + // todo + return nil +} + +func (s *Service) Equal(v interface{}) bool { + if v == nil { + return false + } + + service, ok := v.(*Service) + if !ok { + return false + } + + if s.AtServer != nil { + if !s.AtServer.Equal(service.AtServer) { + return false + } + } + + return s.ServiceApi.Equal(service.ServiceApi) +} + +func (kv KV) Get(key string) Expr { + for _, each := range kv { + if each.Key.Text() == key { + return each.Value + } + } + return nil +} diff --git a/tools/goctl/api/parser/g4/ast/syntax.go b/tools/goctl/api/parser/g4/ast/syntax.go new file mode 100644 index 00000000..55b3e57f --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/syntax.go @@ -0,0 +1,58 @@ +package ast + +import ( + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +type SyntaxExpr struct { + Syntax Expr + Assign Expr + Version Expr + DocExpr []Expr + CommentExpr Expr +} + +func (v *ApiVisitor) VisitSyntaxLit(ctx *api.SyntaxLitContext) interface{} { + syntax := v.newExprWithToken(ctx.GetSyntaxToken()) + assign := v.newExprWithToken(ctx.GetAssign()) + version := v.newExprWithToken(ctx.GetVersion()) + return &SyntaxExpr{ + Syntax: syntax, + Assign: assign, + Version: version, + DocExpr: v.getDoc(ctx), + CommentExpr: v.getComment(ctx), + } +} + +func (s *SyntaxExpr) Format() error { + // todo + return nil +} + +func (s *SyntaxExpr) Equal(v interface{}) bool { + if v == nil { + return false + } + + syntax, ok := v.(*SyntaxExpr) + if !ok { + return false + } + + if !EqualDoc(s, syntax) { + return false + } + + return s.Syntax.Equal(syntax.Syntax) && + s.Assign.Equal(syntax.Assign) && + s.Version.Equal(syntax.Version) +} + +func (s *SyntaxExpr) Doc() []Expr { + return s.DocExpr +} + +func (s *SyntaxExpr) Comment() Expr { + return s.CommentExpr +} diff --git a/tools/goctl/api/parser/g4/ast/type.go b/tools/goctl/api/parser/g4/ast/type.go new file mode 100644 index 00000000..2e644723 --- /dev/null +++ b/tools/goctl/api/parser/g4/ast/type.go @@ -0,0 +1,677 @@ +package ast + +import ( + "fmt" + "sort" + + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" + "github.com/tal-tech/go-zero/tools/goctl/api/util" +) + +type ( + // TypeAlias、 TypeStruct + TypeExpr interface { + Doc() []Expr + Format() error + Equal(v interface{}) bool + NameExpr() Expr + } + TypeAlias struct { + Name Expr + Assign Expr + DataType DataType + DocExpr []Expr + CommentExpr Expr + } + + TypeStruct struct { + Name Expr + Struct Expr + LBrace Expr + RBrace Expr + DocExpr []Expr + Fields []*TypeField + } + + TypeField struct { + IsAnonymous bool + // Name is nil if IsAnonymous + Name Expr + DataType DataType + Tag Expr + DocExpr []Expr + CommentExpr Expr + } + + // Literal, Interface, Map, Array, Time, Pointer + DataType interface { + Expr() Expr + Equal(dt DataType) bool + Format() error + IsNotNil() bool + } + + // int, bool, Foo,... + Literal struct { + Literal Expr + } + + Interface struct { + Literal Expr + } + + Map struct { + MapExpr Expr + Map Expr + LBrack Expr + RBrack Expr + Key Expr + Value DataType + } + + Array struct { + ArrayExpr Expr + LBrack Expr + RBrack Expr + Literal DataType + } + + Time struct { + Literal Expr + } + + Pointer struct { + PointerExpr Expr + Star Expr + Name Expr + } +) + +func (v *ApiVisitor) VisitTypeSpec(ctx *api.TypeSpecContext) interface{} { + if ctx.TypeLit() != nil { + return []TypeExpr{ctx.TypeLit().Accept(v).(TypeExpr)} + } + return ctx.TypeBlock().Accept(v) +} + +func (v *ApiVisitor) VisitTypeLit(ctx *api.TypeLitContext) interface{} { + typeLit := ctx.TypeLitBody().Accept(v) + alias, ok := typeLit.(*TypeAlias) + if ok { + return alias + } + + st, ok := typeLit.(*TypeStruct) + if ok { + return st + } + + return typeLit +} + +func (v *ApiVisitor) VisitTypeBlock(ctx *api.TypeBlockContext) interface{} { + list := ctx.AllTypeBlockBody() + var types []TypeExpr + for _, each := range list { + types = append(types, each.Accept(v).(TypeExpr)) + + } + return types +} + +func (v *ApiVisitor) VisitTypeLitBody(ctx *api.TypeLitBodyContext) interface{} { + if ctx.TypeAlias() != nil { + return ctx.TypeAlias().Accept(v) + } + return ctx.TypeStruct().Accept(v) +} + +func (v *ApiVisitor) VisitTypeBlockBody(ctx *api.TypeBlockBodyContext) interface{} { + if ctx.TypeBlockAlias() != nil { + return ctx.TypeBlockAlias().Accept(v).(*TypeAlias) + } + return ctx.TypeBlockStruct().Accept(v).(*TypeStruct) +} + +func (v *ApiVisitor) VisitTypeStruct(ctx *api.TypeStructContext) interface{} { + var st TypeStruct + st.Name = v.newExprWithToken(ctx.GetStructName()) + v.exportCheck(st.Name) + + if util.UnExport(ctx.GetStructName().GetText()) { + + } + if ctx.GetStructToken() != nil { + structExpr := v.newExprWithToken(ctx.GetStructToken()) + structTokenText := ctx.GetStructToken().GetText() + if structTokenText != "struct" { + v.panic(structExpr, fmt.Sprintf("expecting 'struct', found input '%s'", structTokenText)) + } + + if api.IsGolangKeyWord(structTokenText, "struct") { + v.panic(structExpr, fmt.Sprintf("expecting 'struct', but found golang keyword '%s'", structTokenText)) + } + + st.Struct = structExpr + } + + st.LBrace = v.newExprWithToken(ctx.GetLbrace()) + st.RBrace = v.newExprWithToken(ctx.GetRbrace()) + fields := ctx.AllField() + for _, each := range fields { + f := each.Accept(v) + if f == nil { + continue + } + st.Fields = append(st.Fields, f.(*TypeField)) + } + return &st +} + +func (v *ApiVisitor) VisitTypeBlockStruct(ctx *api.TypeBlockStructContext) interface{} { + var st TypeStruct + st.Name = v.newExprWithToken(ctx.GetStructName()) + v.exportCheck(st.Name) + + if ctx.GetStructToken() != nil { + structExpr := v.newExprWithToken(ctx.GetStructToken()) + structTokenText := ctx.GetStructToken().GetText() + if structTokenText != "struct" { + v.panic(structExpr, fmt.Sprintf("expecting 'struct', found imput '%s'", structTokenText)) + } + + if api.IsGolangKeyWord(structTokenText, "struct") { + v.panic(structExpr, fmt.Sprintf("expecting 'struct', but found golang keyword '%s'", structTokenText)) + } + + st.Struct = structExpr + } + st.DocExpr = v.getDoc(ctx) + st.LBrace = v.newExprWithToken(ctx.GetLbrace()) + st.RBrace = v.newExprWithToken(ctx.GetRbrace()) + fields := ctx.AllField() + for _, each := range fields { + f := each.Accept(v) + if f == nil { + continue + } + st.Fields = append(st.Fields, f.(*TypeField)) + } + return &st +} + +func (v *ApiVisitor) VisitTypeBlockAlias(ctx *api.TypeBlockAliasContext) interface{} { + var alias TypeAlias + alias.Name = v.newExprWithToken(ctx.GetAlias()) + alias.Assign = v.newExprWithToken(ctx.GetAssign()) + alias.DataType = ctx.DataType().Accept(v).(DataType) + alias.DocExpr = v.getDoc(ctx) + alias.CommentExpr = v.getComment(ctx) + // todo: reopen if necessary + v.panic(alias.Name, "unsupport alias") + return &alias +} + +func (v *ApiVisitor) VisitTypeAlias(ctx *api.TypeAliasContext) interface{} { + var alias TypeAlias + alias.Name = v.newExprWithToken(ctx.GetAlias()) + alias.Assign = v.newExprWithToken(ctx.GetAssign()) + alias.DataType = ctx.DataType().Accept(v).(DataType) + alias.DocExpr = v.getDoc(ctx) + alias.CommentExpr = v.getComment(ctx) + // todo: reopen if necessary + v.panic(alias.Name, "unsupport alias") + return &alias +} + +func (v *ApiVisitor) VisitField(ctx *api.FieldContext) interface{} { + iAnonymousFiled := ctx.AnonymousFiled() + iNormalFieldContext := ctx.NormalField() + if iAnonymousFiled != nil { + return iAnonymousFiled.Accept(v).(*TypeField) + } + if iNormalFieldContext != nil { + return iNormalFieldContext.Accept(v).(*TypeField) + } + return nil +} + +func (v *ApiVisitor) VisitNormalField(ctx *api.NormalFieldContext) interface{} { + var field TypeField + field.Name = v.newExprWithToken(ctx.GetFieldName()) + v.exportCheck(field.Name) + + iDataTypeContext := ctx.DataType() + if iDataTypeContext != nil { + field.DataType = iDataTypeContext.Accept(v).(DataType) + field.CommentExpr = v.getComment(ctx) + } + if ctx.GetTag() != nil { + tagText := ctx.GetTag().GetText() + tagExpr := v.newExprWithToken(ctx.GetTag()) + if !api.MatchTag(tagText) { + v.panic(tagExpr, fmt.Sprintf("mismatched tag, found input '%s'", tagText)) + } + field.Tag = tagExpr + field.CommentExpr = v.getComment(ctx) + } + field.DocExpr = v.getDoc(ctx) + return &field +} + +func (v *ApiVisitor) VisitAnonymousFiled(ctx *api.AnonymousFiledContext) interface{} { + start := ctx.GetStart() + stop := ctx.GetStop() + var field TypeField + field.IsAnonymous = true + if ctx.GetStar() != nil { + nameExpr := v.newExprWithTerminalNode(ctx.ID()) + v.exportCheck(nameExpr) + field.DataType = &Pointer{ + PointerExpr: v.newExprWithText(ctx.GetStar().GetText()+ctx.ID().GetText(), start.GetLine(), start.GetColumn(), start.GetStart(), stop.GetStop()), + Star: v.newExprWithToken(ctx.GetStar()), + Name: nameExpr, + } + } else { + nameExpr := v.newExprWithTerminalNode(ctx.ID()) + v.exportCheck(nameExpr) + field.DataType = &Literal{Literal: nameExpr} + } + field.DocExpr = v.getDoc(ctx) + field.CommentExpr = v.getComment(ctx) + return &field +} + +func (v *ApiVisitor) VisitDataType(ctx *api.DataTypeContext) interface{} { + if ctx.ID() != nil { + idExpr := v.newExprWithTerminalNode(ctx.ID()) + v.exportCheck(idExpr) + return &Literal{Literal: idExpr} + } + if ctx.MapType() != nil { + t := ctx.MapType().Accept(v) + return t + } + if ctx.ArrayType() != nil { + return ctx.ArrayType().Accept(v) + } + if ctx.GetInter() != nil { + return &Interface{Literal: v.newExprWithToken(ctx.GetInter())} + } + if ctx.GetTime() != nil { + // todo: reopen if it is necessary + timeExpr := v.newExprWithToken(ctx.GetTime()) + v.panic(timeExpr, "unsupport time.Time") + return &Time{Literal: timeExpr} + } + if ctx.PointerType() != nil { + return ctx.PointerType().Accept(v) + } + return ctx.TypeStruct().Accept(v) +} + +func (v *ApiVisitor) VisitPointerType(ctx *api.PointerTypeContext) interface{} { + nameExpr := v.newExprWithTerminalNode(ctx.ID()) + v.exportCheck(nameExpr) + return &Pointer{ + PointerExpr: v.newExprWithText(ctx.GetText(), ctx.GetStar().GetLine(), ctx.GetStar().GetColumn(), ctx.GetStar().GetStart(), ctx.ID().GetSymbol().GetStop()), + Star: v.newExprWithToken(ctx.GetStar()), + Name: nameExpr, + } +} + +func (v *ApiVisitor) VisitMapType(ctx *api.MapTypeContext) interface{} { + return &Map{ + MapExpr: v.newExprWithText(ctx.GetText(), ctx.GetMapToken().GetLine(), ctx.GetMapToken().GetColumn(), + ctx.GetMapToken().GetStart(), ctx.GetValue().GetStop().GetStop()), + Map: v.newExprWithToken(ctx.GetMapToken()), + LBrack: v.newExprWithToken(ctx.GetLbrack()), + RBrack: v.newExprWithToken(ctx.GetRbrack()), + Key: v.newExprWithToken(ctx.GetKey()), + Value: ctx.GetValue().Accept(v).(DataType), + } +} + +func (v *ApiVisitor) VisitArrayType(ctx *api.ArrayTypeContext) interface{} { + return &Array{ + ArrayExpr: v.newExprWithText(ctx.GetText(), ctx.GetLbrack().GetLine(), ctx.GetLbrack().GetColumn(), ctx.GetLbrack().GetStart(), ctx.DataType().GetStop().GetStop()), + LBrack: v.newExprWithToken(ctx.GetLbrack()), + RBrack: v.newExprWithToken(ctx.GetRbrack()), + Literal: ctx.DataType().Accept(v).(DataType), + } +} + +func (a *TypeAlias) NameExpr() Expr { + return a.Name +} + +func (a *TypeAlias) Doc() []Expr { + return a.DocExpr +} + +func (a *TypeAlias) Comment() Expr { + return a.CommentExpr +} + +func (a *TypeAlias) Format() error { + return nil +} + +func (a *TypeAlias) Equal(v interface{}) bool { + if v == nil { + return false + } + + alias := v.(*TypeAlias) + if !a.Name.Equal(alias.Name) { + return false + } + + if !a.Assign.Equal(alias.Assign) { + return false + } + + if !a.DataType.Equal(alias.DataType) { + return false + } + + return EqualDoc(a, alias) +} + +func (l *Literal) Expr() Expr { + return l.Literal +} + +func (l *Literal) Format() error { + // todo + return nil +} + +func (l *Literal) Equal(dt DataType) bool { + if dt == nil { + return false + } + + v, ok := dt.(*Literal) + if !ok { + return false + } + + return l.Literal.Equal(v.Literal) +} + +func (l *Literal) IsNotNil() bool { + return l != nil +} + +func (i *Interface) Expr() Expr { + return i.Literal +} + +func (i *Interface) Format() error { + // todo + return nil +} + +func (i *Interface) Equal(dt DataType) bool { + if dt == nil { + return false + } + + v, ok := dt.(*Interface) + if !ok { + return false + } + + return i.Literal.Equal(v.Literal) +} + +func (i *Interface) IsNotNil() bool { + return i != nil +} + +func (m *Map) Expr() Expr { + return m.MapExpr +} + +func (m *Map) Format() error { + // todo + return nil +} + +func (m *Map) Equal(dt DataType) bool { + if dt == nil { + return false + } + + v, ok := dt.(*Map) + if !ok { + return false + } + + if !m.Key.Equal(v.Key) { + return false + } + + if !m.Value.Equal(v.Value) { + return false + } + + if !m.MapExpr.Equal(v.MapExpr) { + return false + } + + return m.Map.Equal(v.Map) +} + +func (m *Map) IsNotNil() bool { + return m != nil +} + +func (a *Array) Expr() Expr { + return a.ArrayExpr +} + +func (a *Array) Format() error { + // todo + return nil +} + +func (a *Array) Equal(dt DataType) bool { + if dt == nil { + return false + } + + v, ok := dt.(*Array) + if !ok { + return false + } + + if !a.ArrayExpr.Equal(v.ArrayExpr) { + return false + } + + return a.Literal.Equal(v.Literal) +} + +func (a *Array) IsNotNil() bool { + return a != nil +} + +func (t *Time) Expr() Expr { + return t.Literal +} + +func (t *Time) Format() error { + // todo + return nil +} + +func (t *Time) Equal(dt DataType) bool { + if dt == nil { + return false + } + + v, ok := dt.(*Time) + if !ok { + return false + } + + return t.Literal.Equal(v.Literal) +} + +func (t *Time) IsNotNil() bool { + return t != nil +} + +func (p *Pointer) Expr() Expr { + return p.PointerExpr +} + +func (p *Pointer) Format() error { + return nil +} + +func (p *Pointer) Equal(dt DataType) bool { + if dt == nil { + return false + } + + v, ok := dt.(*Pointer) + if !ok { + return false + } + + if !p.PointerExpr.Equal(v.PointerExpr) { + return false + } + + if !p.Star.Equal(v.Star) { + return false + } + + return p.Name.Equal(v.Name) +} + +func (p *Pointer) IsNotNil() bool { + return p != nil +} + +func (s *TypeStruct) NameExpr() Expr { + return s.Name +} + +func (s *TypeStruct) Equal(dt interface{}) bool { + if dt == nil { + return false + } + + v, ok := dt.(*TypeStruct) + if !ok { + return false + } + + if !s.Name.Equal(v.Name) { + return false + } + + var expectDoc, actualDoc []Expr + expectDoc = append(expectDoc, s.DocExpr...) + actualDoc = append(actualDoc, v.DocExpr...) + sort.Slice(expectDoc, func(i, j int) bool { + return expectDoc[i].Line() < expectDoc[j].Line() + }) + + for index, each := range actualDoc { + if !each.Equal(actualDoc[index]) { + return false + } + } + + if s.Struct != nil { + if s.Struct != nil { + if !s.Struct.Equal(v.Struct) { + return false + } + } + } + + if len(s.Fields) != len(v.Fields) { + return false + } + + var expected, acual []*TypeField + expected = append(expected, s.Fields...) + acual = append(acual, v.Fields...) + + sort.Slice(expected, func(i, j int) bool { + return expected[i].DataType.Expr().Line() < expected[j].DataType.Expr().Line() + }) + sort.Slice(acual, func(i, j int) bool { + return acual[i].DataType.Expr().Line() < acual[j].DataType.Expr().Line() + }) + + for index, each := range expected { + ac := acual[index] + if !each.Equal(ac) { + return false + } + } + + return true +} + +func (s *TypeStruct) Doc() []Expr { + return s.DocExpr +} + +func (s *TypeStruct) Format() error { + // todo + return nil +} + +func (t *TypeField) Equal(v interface{}) bool { + if v == nil { + return false + } + + f, ok := v.(*TypeField) + if !ok { + return false + } + + if t.IsAnonymous != f.IsAnonymous { + return false + } + + if !t.DataType.Equal(f.DataType) { + return false + } + + if !t.IsAnonymous { + if !t.Name.Equal(f.Name) { + return false + } + + if t.Tag != nil { + if !t.Tag.Equal(f.Tag) { + return false + } + } + } + + return EqualDoc(t, f) +} + +func (t *TypeField) Doc() []Expr { + return t.DocExpr +} + +func (t *TypeField) Comment() Expr { + return t.CommentExpr +} + +func (t *TypeField) Format() error { + // todo + return nil +} diff --git a/tools/goctl/api/parser/g4/gen/api/apiparser_base_visitor.go b/tools/goctl/api/parser/g4/gen/api/apiparser_base_visitor.go new file mode 100644 index 00000000..7750939d --- /dev/null +++ b/tools/goctl/api/parser/g4/gen/api/apiparser_base_visitor.go @@ -0,0 +1,156 @@ +// Code generated from tools/goctl/api/parser/g4/ApiParser.g4 by ANTLR 4.9. DO NOT EDIT. + +package api // ApiParser +import "github.com/antlr/antlr4/runtime/Go/antlr" + +type BaseApiParserVisitor struct { + *antlr.BaseParseTreeVisitor +} + +func (v *BaseApiParserVisitor) VisitApi(ctx *ApiContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitSpec(ctx *SpecContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitSyntaxLit(ctx *SyntaxLitContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitImportSpec(ctx *ImportSpecContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitImportLit(ctx *ImportLitContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitImportBlock(ctx *ImportBlockContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitImportBlockValue(ctx *ImportBlockValueContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitImportValue(ctx *ImportValueContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitInfoSpec(ctx *InfoSpecContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeSpec(ctx *TypeSpecContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeLit(ctx *TypeLitContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeBlock(ctx *TypeBlockContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeLitBody(ctx *TypeLitBodyContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeBlockBody(ctx *TypeBlockBodyContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeStruct(ctx *TypeStructContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeAlias(ctx *TypeAliasContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeBlockStruct(ctx *TypeBlockStructContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitTypeBlockAlias(ctx *TypeBlockAliasContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitField(ctx *FieldContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitNormalField(ctx *NormalFieldContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitAnonymousFiled(ctx *AnonymousFiledContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitDataType(ctx *DataTypeContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitPointerType(ctx *PointerTypeContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitMapType(ctx *MapTypeContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitArrayType(ctx *ArrayTypeContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitServiceSpec(ctx *ServiceSpecContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitAtServer(ctx *AtServerContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitServiceApi(ctx *ServiceApiContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitServiceRoute(ctx *ServiceRouteContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitAtDoc(ctx *AtDocContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitAtHandler(ctx *AtHandlerContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitRoute(ctx *RouteContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitBody(ctx *BodyContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitReplybody(ctx *ReplybodyContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitKvLit(ctx *KvLitContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitServiceName(ctx *ServiceNameContext) interface{} { + return v.VisitChildren(ctx) +} + +func (v *BaseApiParserVisitor) VisitPath(ctx *PathContext) interface{} { + return v.VisitChildren(ctx) +} diff --git a/tools/goctl/api/parser/g4/gen/api/apiparser_lexer.go b/tools/goctl/api/parser/g4/gen/api/apiparser_lexer.go new file mode 100644 index 00000000..e825fc09 --- /dev/null +++ b/tools/goctl/api/parser/g4/gen/api/apiparser_lexer.go @@ -0,0 +1,234 @@ +// Code generated from tools/goctl/api/parser/g4/ApiParser.g4 by ANTLR 4.9. DO NOT EDIT. + +package api + +import ( + "fmt" + "unicode" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = unicode.IsLetter + +var serializedLexerAtn = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 25, 266, + 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, + 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, + 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, + 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, + 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, + 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 3, 2, 3, 2, 3, 3, 3, 3, 3, + 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, + 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, + 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, + 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, + 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, + 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 18, 6, 18, 132, + 10, 18, 13, 18, 14, 18, 133, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, + 7, 19, 142, 10, 19, 12, 19, 14, 19, 145, 11, 19, 3, 19, 3, 19, 3, 19, 3, + 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 7, 20, 156, 10, 20, 12, 20, 14, + 20, 159, 11, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 7, 21, 166, 10, 21, + 12, 21, 14, 21, 169, 11, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 6, 22, + 176, 10, 22, 13, 22, 14, 22, 177, 3, 22, 3, 22, 3, 23, 3, 23, 7, 23, 184, + 10, 23, 12, 23, 14, 23, 187, 11, 23, 3, 23, 3, 23, 7, 23, 191, 10, 23, + 12, 23, 14, 23, 194, 11, 23, 5, 23, 196, 10, 23, 3, 24, 3, 24, 7, 24, 200, + 10, 24, 12, 24, 14, 24, 203, 11, 24, 3, 25, 3, 25, 5, 25, 207, 10, 25, + 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 5, 26, 215, 10, 26, 3, 26, 5, + 26, 218, 10, 26, 3, 26, 3, 26, 3, 26, 6, 26, 223, 10, 26, 13, 26, 14, 26, + 224, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 5, 26, 232, 10, 26, 3, 27, 3, 27, + 3, 27, 7, 27, 237, 10, 27, 12, 27, 14, 27, 240, 11, 27, 3, 27, 5, 27, 243, + 10, 27, 3, 28, 3, 28, 3, 29, 3, 29, 7, 29, 249, 10, 29, 12, 29, 14, 29, + 252, 11, 29, 3, 29, 5, 29, 255, 10, 29, 3, 30, 3, 30, 5, 30, 259, 10, 30, + 3, 31, 3, 31, 3, 31, 3, 31, 5, 31, 265, 10, 31, 3, 143, 2, 32, 3, 3, 5, + 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, + 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, + 23, 45, 24, 47, 25, 49, 2, 51, 2, 53, 2, 55, 2, 57, 2, 59, 2, 61, 2, 3, + 2, 20, 5, 2, 11, 12, 14, 15, 34, 34, 4, 2, 12, 12, 15, 15, 4, 2, 36, 36, + 94, 94, 6, 2, 12, 12, 15, 15, 94, 94, 98, 98, 4, 2, 11, 11, 34, 34, 6, + 2, 12, 12, 15, 15, 36, 36, 98, 98, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, + 47, 47, 10, 2, 36, 36, 41, 41, 94, 94, 100, 100, 104, 104, 112, 112, 116, + 116, 118, 118, 3, 2, 50, 53, 3, 2, 50, 57, 5, 2, 50, 59, 67, 72, 99, 104, + 3, 2, 50, 59, 4, 2, 50, 59, 97, 97, 6, 2, 38, 38, 67, 92, 97, 97, 99, 124, + 4, 2, 2, 129, 55298, 56321, 3, 2, 55298, 56321, 3, 2, 56322, 57345, 2, + 283, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, + 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, + 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, + 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, + 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, + 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, + 2, 3, 63, 3, 2, 2, 2, 5, 65, 3, 2, 2, 2, 7, 67, 3, 2, 2, 2, 9, 69, 3, 2, + 2, 2, 11, 71, 3, 2, 2, 2, 13, 73, 3, 2, 2, 2, 15, 75, 3, 2, 2, 2, 17, 85, + 3, 2, 2, 2, 19, 87, 3, 2, 2, 2, 21, 89, 3, 2, 2, 2, 23, 91, 3, 2, 2, 2, + 25, 93, 3, 2, 2, 2, 27, 96, 3, 2, 2, 2, 29, 101, 3, 2, 2, 2, 31, 110, 3, + 2, 2, 2, 33, 122, 3, 2, 2, 2, 35, 131, 3, 2, 2, 2, 37, 137, 3, 2, 2, 2, + 39, 151, 3, 2, 2, 2, 41, 162, 3, 2, 2, 2, 43, 172, 3, 2, 2, 2, 45, 181, + 3, 2, 2, 2, 47, 197, 3, 2, 2, 2, 49, 204, 3, 2, 2, 2, 51, 231, 3, 2, 2, + 2, 53, 233, 3, 2, 2, 2, 55, 244, 3, 2, 2, 2, 57, 246, 3, 2, 2, 2, 59, 258, + 3, 2, 2, 2, 61, 264, 3, 2, 2, 2, 63, 64, 7, 63, 2, 2, 64, 4, 3, 2, 2, 2, + 65, 66, 7, 42, 2, 2, 66, 6, 3, 2, 2, 2, 67, 68, 7, 43, 2, 2, 68, 8, 3, + 2, 2, 2, 69, 70, 7, 125, 2, 2, 70, 10, 3, 2, 2, 2, 71, 72, 7, 127, 2, 2, + 72, 12, 3, 2, 2, 2, 73, 74, 7, 44, 2, 2, 74, 14, 3, 2, 2, 2, 75, 76, 7, + 118, 2, 2, 76, 77, 7, 107, 2, 2, 77, 78, 7, 111, 2, 2, 78, 79, 7, 103, + 2, 2, 79, 80, 7, 48, 2, 2, 80, 81, 7, 86, 2, 2, 81, 82, 7, 107, 2, 2, 82, + 83, 7, 111, 2, 2, 83, 84, 7, 103, 2, 2, 84, 16, 3, 2, 2, 2, 85, 86, 7, + 93, 2, 2, 86, 18, 3, 2, 2, 2, 87, 88, 7, 95, 2, 2, 88, 20, 3, 2, 2, 2, + 89, 90, 7, 47, 2, 2, 90, 22, 3, 2, 2, 2, 91, 92, 7, 49, 2, 2, 92, 24, 3, + 2, 2, 2, 93, 94, 7, 49, 2, 2, 94, 95, 7, 60, 2, 2, 95, 26, 3, 2, 2, 2, + 96, 97, 7, 66, 2, 2, 97, 98, 7, 102, 2, 2, 98, 99, 7, 113, 2, 2, 99, 100, + 7, 101, 2, 2, 100, 28, 3, 2, 2, 2, 101, 102, 7, 66, 2, 2, 102, 103, 7, + 106, 2, 2, 103, 104, 7, 99, 2, 2, 104, 105, 7, 112, 2, 2, 105, 106, 7, + 102, 2, 2, 106, 107, 7, 110, 2, 2, 107, 108, 7, 103, 2, 2, 108, 109, 7, + 116, 2, 2, 109, 30, 3, 2, 2, 2, 110, 111, 7, 107, 2, 2, 111, 112, 7, 112, + 2, 2, 112, 113, 7, 118, 2, 2, 113, 114, 7, 103, 2, 2, 114, 115, 7, 116, + 2, 2, 115, 116, 7, 104, 2, 2, 116, 117, 7, 99, 2, 2, 117, 118, 7, 101, + 2, 2, 118, 119, 7, 103, 2, 2, 119, 120, 7, 125, 2, 2, 120, 121, 7, 127, + 2, 2, 121, 32, 3, 2, 2, 2, 122, 123, 7, 66, 2, 2, 123, 124, 7, 117, 2, + 2, 124, 125, 7, 103, 2, 2, 125, 126, 7, 116, 2, 2, 126, 127, 7, 120, 2, + 2, 127, 128, 7, 103, 2, 2, 128, 129, 7, 116, 2, 2, 129, 34, 3, 2, 2, 2, + 130, 132, 9, 2, 2, 2, 131, 130, 3, 2, 2, 2, 132, 133, 3, 2, 2, 2, 133, + 131, 3, 2, 2, 2, 133, 134, 3, 2, 2, 2, 134, 135, 3, 2, 2, 2, 135, 136, + 8, 18, 2, 2, 136, 36, 3, 2, 2, 2, 137, 138, 7, 49, 2, 2, 138, 139, 7, 44, + 2, 2, 139, 143, 3, 2, 2, 2, 140, 142, 11, 2, 2, 2, 141, 140, 3, 2, 2, 2, + 142, 145, 3, 2, 2, 2, 143, 144, 3, 2, 2, 2, 143, 141, 3, 2, 2, 2, 144, + 146, 3, 2, 2, 2, 145, 143, 3, 2, 2, 2, 146, 147, 7, 44, 2, 2, 147, 148, + 7, 49, 2, 2, 148, 149, 3, 2, 2, 2, 149, 150, 8, 19, 3, 2, 150, 38, 3, 2, + 2, 2, 151, 152, 7, 49, 2, 2, 152, 153, 7, 49, 2, 2, 153, 157, 3, 2, 2, + 2, 154, 156, 10, 3, 2, 2, 155, 154, 3, 2, 2, 2, 156, 159, 3, 2, 2, 2, 157, + 155, 3, 2, 2, 2, 157, 158, 3, 2, 2, 2, 158, 160, 3, 2, 2, 2, 159, 157, + 3, 2, 2, 2, 160, 161, 8, 20, 3, 2, 161, 40, 3, 2, 2, 2, 162, 167, 7, 36, + 2, 2, 163, 166, 10, 4, 2, 2, 164, 166, 5, 51, 26, 2, 165, 163, 3, 2, 2, + 2, 165, 164, 3, 2, 2, 2, 166, 169, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2, 167, + 168, 3, 2, 2, 2, 168, 170, 3, 2, 2, 2, 169, 167, 3, 2, 2, 2, 170, 171, + 7, 36, 2, 2, 171, 42, 3, 2, 2, 2, 172, 175, 7, 98, 2, 2, 173, 176, 10, + 5, 2, 2, 174, 176, 5, 51, 26, 2, 175, 173, 3, 2, 2, 2, 175, 174, 3, 2, + 2, 2, 176, 177, 3, 2, 2, 2, 177, 175, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, + 178, 179, 3, 2, 2, 2, 179, 180, 7, 98, 2, 2, 180, 44, 3, 2, 2, 2, 181, + 185, 7, 60, 2, 2, 182, 184, 9, 6, 2, 2, 183, 182, 3, 2, 2, 2, 184, 187, + 3, 2, 2, 2, 185, 183, 3, 2, 2, 2, 185, 186, 3, 2, 2, 2, 186, 195, 3, 2, + 2, 2, 187, 185, 3, 2, 2, 2, 188, 196, 5, 41, 21, 2, 189, 191, 10, 7, 2, + 2, 190, 189, 3, 2, 2, 2, 191, 194, 3, 2, 2, 2, 192, 190, 3, 2, 2, 2, 192, + 193, 3, 2, 2, 2, 193, 196, 3, 2, 2, 2, 194, 192, 3, 2, 2, 2, 195, 188, + 3, 2, 2, 2, 195, 192, 3, 2, 2, 2, 196, 46, 3, 2, 2, 2, 197, 201, 5, 61, + 31, 2, 198, 200, 5, 59, 30, 2, 199, 198, 3, 2, 2, 2, 200, 203, 3, 2, 2, + 2, 201, 199, 3, 2, 2, 2, 201, 202, 3, 2, 2, 2, 202, 48, 3, 2, 2, 2, 203, + 201, 3, 2, 2, 2, 204, 206, 9, 8, 2, 2, 205, 207, 9, 9, 2, 2, 206, 205, + 3, 2, 2, 2, 206, 207, 3, 2, 2, 2, 207, 208, 3, 2, 2, 2, 208, 209, 5, 57, + 29, 2, 209, 50, 3, 2, 2, 2, 210, 211, 7, 94, 2, 2, 211, 232, 9, 10, 2, + 2, 212, 217, 7, 94, 2, 2, 213, 215, 9, 11, 2, 2, 214, 213, 3, 2, 2, 2, + 214, 215, 3, 2, 2, 2, 215, 216, 3, 2, 2, 2, 216, 218, 9, 12, 2, 2, 217, + 214, 3, 2, 2, 2, 217, 218, 3, 2, 2, 2, 218, 219, 3, 2, 2, 2, 219, 232, + 9, 12, 2, 2, 220, 222, 7, 94, 2, 2, 221, 223, 7, 119, 2, 2, 222, 221, 3, + 2, 2, 2, 223, 224, 3, 2, 2, 2, 224, 222, 3, 2, 2, 2, 224, 225, 3, 2, 2, + 2, 225, 226, 3, 2, 2, 2, 226, 227, 5, 55, 28, 2, 227, 228, 5, 55, 28, 2, + 228, 229, 5, 55, 28, 2, 229, 230, 5, 55, 28, 2, 230, 232, 3, 2, 2, 2, 231, + 210, 3, 2, 2, 2, 231, 212, 3, 2, 2, 2, 231, 220, 3, 2, 2, 2, 232, 52, 3, + 2, 2, 2, 233, 242, 5, 55, 28, 2, 234, 237, 5, 55, 28, 2, 235, 237, 7, 97, + 2, 2, 236, 234, 3, 2, 2, 2, 236, 235, 3, 2, 2, 2, 237, 240, 3, 2, 2, 2, + 238, 236, 3, 2, 2, 2, 238, 239, 3, 2, 2, 2, 239, 241, 3, 2, 2, 2, 240, + 238, 3, 2, 2, 2, 241, 243, 5, 55, 28, 2, 242, 238, 3, 2, 2, 2, 242, 243, + 3, 2, 2, 2, 243, 54, 3, 2, 2, 2, 244, 245, 9, 13, 2, 2, 245, 56, 3, 2, + 2, 2, 246, 254, 9, 14, 2, 2, 247, 249, 9, 15, 2, 2, 248, 247, 3, 2, 2, + 2, 249, 252, 3, 2, 2, 2, 250, 248, 3, 2, 2, 2, 250, 251, 3, 2, 2, 2, 251, + 253, 3, 2, 2, 2, 252, 250, 3, 2, 2, 2, 253, 255, 9, 14, 2, 2, 254, 250, + 3, 2, 2, 2, 254, 255, 3, 2, 2, 2, 255, 58, 3, 2, 2, 2, 256, 259, 5, 61, + 31, 2, 257, 259, 9, 14, 2, 2, 258, 256, 3, 2, 2, 2, 258, 257, 3, 2, 2, + 2, 259, 60, 3, 2, 2, 2, 260, 265, 9, 16, 2, 2, 261, 265, 10, 17, 2, 2, + 262, 263, 9, 18, 2, 2, 263, 265, 9, 19, 2, 2, 264, 260, 3, 2, 2, 2, 264, + 261, 3, 2, 2, 2, 264, 262, 3, 2, 2, 2, 265, 62, 3, 2, 2, 2, 26, 2, 133, + 143, 157, 165, 167, 175, 177, 185, 192, 195, 201, 206, 214, 217, 224, 231, + 236, 238, 242, 250, 254, 258, 264, 4, 2, 3, 2, 2, 90, 2, +} + +var lexerChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", +} + +var lexerModeNames = []string{ + "DEFAULT_MODE", +} + +var lexerLiteralNames = []string{ + "", "'='", "'('", "')'", "'{'", "'}'", "'*'", "'time.Time'", "'['", "']'", + "'-'", "'/'", "'/:'", "'@doc'", "'@handler'", "'interface{}'", "'@server'", +} + +var lexerSymbolicNames = []string{ + "", "", "", "", "", "", "", "", "", "", "", "", "", "ATDOC", "ATHANDLER", + "INTERFACE", "ATSERVER", "WS", "COMMENT", "LINE_COMMENT", "STRING", "RAW_STRING", + "LINE_VALUE", "ID", +} + +var lexerRuleNames = []string{ + "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", + "T__9", "T__10", "T__11", "ATDOC", "ATHANDLER", "INTERFACE", "ATSERVER", + "WS", "COMMENT", "LINE_COMMENT", "STRING", "RAW_STRING", "LINE_VALUE", + "ID", "ExponentPart", "EscapeSequence", "HexDigits", "HexDigit", "Digits", + "LetterOrDigit", "Letter", +} + +type ApiParserLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +// NewApiParserLexer produces a new lexer instance for the optional input antlr.CharStream. +// +// The *ApiParserLexer instance produced may be reused by calling the SetInputStream method. +// The initial lexer configuration is expensive to construct, and the object is not thread-safe; +// however, if used within a Golang sync.Pool, the construction cost amortizes well and the +// objects can be used in a thread-safe manner. +func NewApiParserLexer(input antlr.CharStream) *ApiParserLexer { + l := new(ApiParserLexer) + lexerDeserializer := antlr.NewATNDeserializer(nil) + lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn) + lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState)) + for index, ds := range lexerAtn.DecisionToState { + lexerDecisionToDFA[index] = antlr.NewDFA(ds, index) + } + l.BaseLexer = antlr.NewBaseLexer(input) + l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache()) + + l.channelNames = lexerChannelNames + l.modeNames = lexerModeNames + l.RuleNames = lexerRuleNames + l.LiteralNames = lexerLiteralNames + l.SymbolicNames = lexerSymbolicNames + l.GrammarFileName = "ApiParser.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// ApiParserLexer tokens. +const ( + ApiParserLexerT__0 = 1 + ApiParserLexerT__1 = 2 + ApiParserLexerT__2 = 3 + ApiParserLexerT__3 = 4 + ApiParserLexerT__4 = 5 + ApiParserLexerT__5 = 6 + ApiParserLexerT__6 = 7 + ApiParserLexerT__7 = 8 + ApiParserLexerT__8 = 9 + ApiParserLexerT__9 = 10 + ApiParserLexerT__10 = 11 + ApiParserLexerT__11 = 12 + ApiParserLexerATDOC = 13 + ApiParserLexerATHANDLER = 14 + ApiParserLexerINTERFACE = 15 + ApiParserLexerATSERVER = 16 + ApiParserLexerWS = 17 + ApiParserLexerCOMMENT = 18 + ApiParserLexerLINE_COMMENT = 19 + ApiParserLexerSTRING = 20 + ApiParserLexerRAW_STRING = 21 + ApiParserLexerLINE_VALUE = 22 + ApiParserLexerID = 23 +) + +const COMEMNTS = 88 diff --git a/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go b/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go new file mode 100644 index 00000000..d8d896b6 --- /dev/null +++ b/tools/goctl/api/parser/g4/gen/api/apiparser_parser.go @@ -0,0 +1,5688 @@ +// Code generated from tools/goctl/api/parser/g4/ApiParser.g4 by ANTLR 4.9. DO NOT EDIT. + +package api // ApiParser +import ( + "fmt" + "reflect" + "strconv" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = reflect.Copy +var _ = strconv.Itoa + +var parserATN = []uint16{ + 3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 25, 348, + 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, + 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, + 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, + 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, + 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, + 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, + 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 3, 2, 7, + 2, 78, 10, 2, 12, 2, 14, 2, 81, 11, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, + 3, 88, 10, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 5, 5, 98, + 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 6, 7, 108, 10, 7, + 13, 7, 14, 7, 109, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 10, 3, + 10, 3, 10, 3, 10, 6, 10, 123, 10, 10, 13, 10, 14, 10, 124, 3, 10, 3, 10, + 3, 11, 3, 11, 5, 11, 131, 10, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, + 13, 3, 13, 3, 13, 7, 13, 141, 10, 13, 12, 13, 14, 13, 144, 11, 13, 3, 13, + 3, 13, 3, 14, 3, 14, 5, 14, 150, 10, 14, 3, 15, 3, 15, 5, 15, 154, 10, + 15, 3, 16, 3, 16, 3, 16, 5, 16, 159, 10, 16, 3, 16, 3, 16, 7, 16, 163, + 10, 16, 12, 16, 14, 16, 166, 11, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, + 5, 17, 173, 10, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 5, 18, 180, 10, + 18, 3, 18, 3, 18, 7, 18, 184, 10, 18, 12, 18, 14, 18, 187, 11, 18, 3, 18, + 3, 18, 3, 19, 3, 19, 3, 19, 5, 19, 194, 10, 19, 3, 19, 3, 19, 3, 20, 3, + 20, 3, 20, 5, 20, 201, 10, 20, 3, 21, 3, 21, 3, 21, 3, 21, 5, 21, 207, + 10, 21, 3, 22, 5, 22, 210, 10, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, + 23, 3, 23, 3, 23, 3, 23, 3, 23, 5, 23, 222, 10, 23, 3, 24, 3, 24, 3, 24, + 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 26, 3, + 26, 3, 26, 3, 26, 3, 27, 5, 27, 241, 10, 27, 3, 27, 3, 27, 3, 28, 3, 28, + 3, 28, 6, 28, 248, 10, 28, 13, 28, 14, 28, 249, 3, 28, 3, 28, 3, 29, 3, + 29, 3, 29, 3, 29, 3, 29, 7, 29, 259, 10, 29, 12, 29, 14, 29, 262, 11, 29, + 3, 29, 3, 29, 3, 30, 5, 30, 267, 10, 30, 3, 30, 3, 30, 5, 30, 271, 10, + 30, 3, 30, 3, 30, 3, 31, 3, 31, 5, 31, 277, 10, 31, 3, 31, 6, 31, 280, + 10, 31, 13, 31, 14, 31, 281, 3, 31, 5, 31, 285, 10, 31, 3, 31, 5, 31, 288, + 10, 31, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 5, 33, 297, 10, + 33, 3, 33, 5, 33, 300, 10, 33, 3, 33, 5, 33, 303, 10, 33, 3, 34, 3, 34, + 5, 34, 307, 10, 34, 3, 34, 3, 34, 3, 35, 3, 35, 5, 35, 313, 10, 35, 3, + 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 5, 37, 323, 10, 37, + 6, 37, 325, 10, 37, 13, 37, 14, 37, 326, 3, 38, 3, 38, 3, 38, 3, 38, 7, + 38, 333, 10, 38, 12, 38, 14, 38, 336, 11, 38, 3, 38, 3, 38, 3, 38, 3, 38, + 5, 38, 342, 10, 38, 6, 38, 344, 10, 38, 13, 38, 14, 38, 345, 3, 38, 2, + 2, 39, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, + 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, + 72, 74, 2, 2, 2, 357, 2, 79, 3, 2, 2, 2, 4, 87, 3, 2, 2, 2, 6, 89, 3, 2, + 2, 2, 8, 97, 3, 2, 2, 2, 10, 99, 3, 2, 2, 2, 12, 103, 3, 2, 2, 2, 14, 113, + 3, 2, 2, 2, 16, 115, 3, 2, 2, 2, 18, 118, 3, 2, 2, 2, 20, 130, 3, 2, 2, + 2, 22, 132, 3, 2, 2, 2, 24, 136, 3, 2, 2, 2, 26, 149, 3, 2, 2, 2, 28, 153, + 3, 2, 2, 2, 30, 155, 3, 2, 2, 2, 32, 169, 3, 2, 2, 2, 34, 176, 3, 2, 2, + 2, 36, 190, 3, 2, 2, 2, 38, 200, 3, 2, 2, 2, 40, 202, 3, 2, 2, 2, 42, 209, + 3, 2, 2, 2, 44, 221, 3, 2, 2, 2, 46, 223, 3, 2, 2, 2, 48, 227, 3, 2, 2, + 2, 50, 235, 3, 2, 2, 2, 52, 240, 3, 2, 2, 2, 54, 244, 3, 2, 2, 2, 56, 253, + 3, 2, 2, 2, 58, 266, 3, 2, 2, 2, 60, 274, 3, 2, 2, 2, 62, 289, 3, 2, 2, + 2, 64, 292, 3, 2, 2, 2, 66, 304, 3, 2, 2, 2, 68, 310, 3, 2, 2, 2, 70, 316, + 3, 2, 2, 2, 72, 324, 3, 2, 2, 2, 74, 343, 3, 2, 2, 2, 76, 78, 5, 4, 3, + 2, 77, 76, 3, 2, 2, 2, 78, 81, 3, 2, 2, 2, 79, 77, 3, 2, 2, 2, 79, 80, + 3, 2, 2, 2, 80, 3, 3, 2, 2, 2, 81, 79, 3, 2, 2, 2, 82, 88, 5, 6, 4, 2, + 83, 88, 5, 8, 5, 2, 84, 88, 5, 18, 10, 2, 85, 88, 5, 20, 11, 2, 86, 88, + 5, 52, 27, 2, 87, 82, 3, 2, 2, 2, 87, 83, 3, 2, 2, 2, 87, 84, 3, 2, 2, + 2, 87, 85, 3, 2, 2, 2, 87, 86, 3, 2, 2, 2, 88, 5, 3, 2, 2, 2, 89, 90, 8, + 4, 1, 2, 90, 91, 7, 25, 2, 2, 91, 92, 7, 3, 2, 2, 92, 93, 8, 4, 1, 2, 93, + 94, 7, 22, 2, 2, 94, 7, 3, 2, 2, 2, 95, 98, 5, 10, 6, 2, 96, 98, 5, 12, + 7, 2, 97, 95, 3, 2, 2, 2, 97, 96, 3, 2, 2, 2, 98, 9, 3, 2, 2, 2, 99, 100, + 8, 6, 1, 2, 100, 101, 7, 25, 2, 2, 101, 102, 5, 16, 9, 2, 102, 11, 3, 2, + 2, 2, 103, 104, 8, 7, 1, 2, 104, 105, 7, 25, 2, 2, 105, 107, 7, 4, 2, 2, + 106, 108, 5, 14, 8, 2, 107, 106, 3, 2, 2, 2, 108, 109, 3, 2, 2, 2, 109, + 107, 3, 2, 2, 2, 109, 110, 3, 2, 2, 2, 110, 111, 3, 2, 2, 2, 111, 112, + 7, 5, 2, 2, 112, 13, 3, 2, 2, 2, 113, 114, 5, 16, 9, 2, 114, 15, 3, 2, + 2, 2, 115, 116, 8, 9, 1, 2, 116, 117, 7, 22, 2, 2, 117, 17, 3, 2, 2, 2, + 118, 119, 8, 10, 1, 2, 119, 120, 7, 25, 2, 2, 120, 122, 7, 4, 2, 2, 121, + 123, 5, 70, 36, 2, 122, 121, 3, 2, 2, 2, 123, 124, 3, 2, 2, 2, 124, 122, + 3, 2, 2, 2, 124, 125, 3, 2, 2, 2, 125, 126, 3, 2, 2, 2, 126, 127, 7, 5, + 2, 2, 127, 19, 3, 2, 2, 2, 128, 131, 5, 22, 12, 2, 129, 131, 5, 24, 13, + 2, 130, 128, 3, 2, 2, 2, 130, 129, 3, 2, 2, 2, 131, 21, 3, 2, 2, 2, 132, + 133, 8, 12, 1, 2, 133, 134, 7, 25, 2, 2, 134, 135, 5, 26, 14, 2, 135, 23, + 3, 2, 2, 2, 136, 137, 8, 13, 1, 2, 137, 138, 7, 25, 2, 2, 138, 142, 7, + 4, 2, 2, 139, 141, 5, 28, 15, 2, 140, 139, 3, 2, 2, 2, 141, 144, 3, 2, + 2, 2, 142, 140, 3, 2, 2, 2, 142, 143, 3, 2, 2, 2, 143, 145, 3, 2, 2, 2, + 144, 142, 3, 2, 2, 2, 145, 146, 7, 5, 2, 2, 146, 25, 3, 2, 2, 2, 147, 150, + 5, 30, 16, 2, 148, 150, 5, 32, 17, 2, 149, 147, 3, 2, 2, 2, 149, 148, 3, + 2, 2, 2, 150, 27, 3, 2, 2, 2, 151, 154, 5, 34, 18, 2, 152, 154, 5, 36, + 19, 2, 153, 151, 3, 2, 2, 2, 153, 152, 3, 2, 2, 2, 154, 29, 3, 2, 2, 2, + 155, 156, 8, 16, 1, 2, 156, 158, 7, 25, 2, 2, 157, 159, 7, 25, 2, 2, 158, + 157, 3, 2, 2, 2, 158, 159, 3, 2, 2, 2, 159, 160, 3, 2, 2, 2, 160, 164, + 7, 6, 2, 2, 161, 163, 5, 38, 20, 2, 162, 161, 3, 2, 2, 2, 163, 166, 3, + 2, 2, 2, 164, 162, 3, 2, 2, 2, 164, 165, 3, 2, 2, 2, 165, 167, 3, 2, 2, + 2, 166, 164, 3, 2, 2, 2, 167, 168, 7, 7, 2, 2, 168, 31, 3, 2, 2, 2, 169, + 170, 8, 17, 1, 2, 170, 172, 7, 25, 2, 2, 171, 173, 7, 3, 2, 2, 172, 171, + 3, 2, 2, 2, 172, 173, 3, 2, 2, 2, 173, 174, 3, 2, 2, 2, 174, 175, 5, 44, + 23, 2, 175, 33, 3, 2, 2, 2, 176, 177, 8, 18, 1, 2, 177, 179, 7, 25, 2, + 2, 178, 180, 7, 25, 2, 2, 179, 178, 3, 2, 2, 2, 179, 180, 3, 2, 2, 2, 180, + 181, 3, 2, 2, 2, 181, 185, 7, 6, 2, 2, 182, 184, 5, 38, 20, 2, 183, 182, + 3, 2, 2, 2, 184, 187, 3, 2, 2, 2, 185, 183, 3, 2, 2, 2, 185, 186, 3, 2, + 2, 2, 186, 188, 3, 2, 2, 2, 187, 185, 3, 2, 2, 2, 188, 189, 7, 7, 2, 2, + 189, 35, 3, 2, 2, 2, 190, 191, 8, 19, 1, 2, 191, 193, 7, 25, 2, 2, 192, + 194, 7, 3, 2, 2, 193, 192, 3, 2, 2, 2, 193, 194, 3, 2, 2, 2, 194, 195, + 3, 2, 2, 2, 195, 196, 5, 44, 23, 2, 196, 37, 3, 2, 2, 2, 197, 198, 6, 20, + 2, 2, 198, 201, 5, 40, 21, 2, 199, 201, 5, 42, 22, 2, 200, 197, 3, 2, 2, + 2, 200, 199, 3, 2, 2, 2, 201, 39, 3, 2, 2, 2, 202, 203, 8, 21, 1, 2, 203, + 204, 7, 25, 2, 2, 204, 206, 5, 44, 23, 2, 205, 207, 7, 23, 2, 2, 206, 205, + 3, 2, 2, 2, 206, 207, 3, 2, 2, 2, 207, 41, 3, 2, 2, 2, 208, 210, 7, 8, + 2, 2, 209, 208, 3, 2, 2, 2, 209, 210, 3, 2, 2, 2, 210, 211, 3, 2, 2, 2, + 211, 212, 7, 25, 2, 2, 212, 43, 3, 2, 2, 2, 213, 214, 8, 23, 1, 2, 214, + 222, 7, 25, 2, 2, 215, 222, 5, 48, 25, 2, 216, 222, 5, 50, 26, 2, 217, + 222, 7, 17, 2, 2, 218, 222, 7, 9, 2, 2, 219, 222, 5, 46, 24, 2, 220, 222, + 5, 30, 16, 2, 221, 213, 3, 2, 2, 2, 221, 215, 3, 2, 2, 2, 221, 216, 3, + 2, 2, 2, 221, 217, 3, 2, 2, 2, 221, 218, 3, 2, 2, 2, 221, 219, 3, 2, 2, + 2, 221, 220, 3, 2, 2, 2, 222, 45, 3, 2, 2, 2, 223, 224, 7, 8, 2, 2, 224, + 225, 8, 24, 1, 2, 225, 226, 7, 25, 2, 2, 226, 47, 3, 2, 2, 2, 227, 228, + 8, 25, 1, 2, 228, 229, 7, 25, 2, 2, 229, 230, 7, 10, 2, 2, 230, 231, 8, + 25, 1, 2, 231, 232, 7, 25, 2, 2, 232, 233, 7, 11, 2, 2, 233, 234, 5, 44, + 23, 2, 234, 49, 3, 2, 2, 2, 235, 236, 7, 10, 2, 2, 236, 237, 7, 11, 2, + 2, 237, 238, 5, 44, 23, 2, 238, 51, 3, 2, 2, 2, 239, 241, 5, 54, 28, 2, + 240, 239, 3, 2, 2, 2, 240, 241, 3, 2, 2, 2, 241, 242, 3, 2, 2, 2, 242, + 243, 5, 56, 29, 2, 243, 53, 3, 2, 2, 2, 244, 245, 7, 18, 2, 2, 245, 247, + 7, 4, 2, 2, 246, 248, 5, 70, 36, 2, 247, 246, 3, 2, 2, 2, 248, 249, 3, + 2, 2, 2, 249, 247, 3, 2, 2, 2, 249, 250, 3, 2, 2, 2, 250, 251, 3, 2, 2, + 2, 251, 252, 7, 5, 2, 2, 252, 55, 3, 2, 2, 2, 253, 254, 8, 29, 1, 2, 254, + 255, 7, 25, 2, 2, 255, 256, 5, 72, 37, 2, 256, 260, 7, 6, 2, 2, 257, 259, + 5, 58, 30, 2, 258, 257, 3, 2, 2, 2, 259, 262, 3, 2, 2, 2, 260, 258, 3, + 2, 2, 2, 260, 261, 3, 2, 2, 2, 261, 263, 3, 2, 2, 2, 262, 260, 3, 2, 2, + 2, 263, 264, 7, 7, 2, 2, 264, 57, 3, 2, 2, 2, 265, 267, 5, 60, 31, 2, 266, + 265, 3, 2, 2, 2, 266, 267, 3, 2, 2, 2, 267, 270, 3, 2, 2, 2, 268, 271, + 5, 54, 28, 2, 269, 271, 5, 62, 32, 2, 270, 268, 3, 2, 2, 2, 270, 269, 3, + 2, 2, 2, 271, 272, 3, 2, 2, 2, 272, 273, 5, 64, 33, 2, 273, 59, 3, 2, 2, + 2, 274, 276, 7, 15, 2, 2, 275, 277, 7, 4, 2, 2, 276, 275, 3, 2, 2, 2, 276, + 277, 3, 2, 2, 2, 277, 284, 3, 2, 2, 2, 278, 280, 5, 70, 36, 2, 279, 278, + 3, 2, 2, 2, 280, 281, 3, 2, 2, 2, 281, 279, 3, 2, 2, 2, 281, 282, 3, 2, + 2, 2, 282, 285, 3, 2, 2, 2, 283, 285, 7, 22, 2, 2, 284, 279, 3, 2, 2, 2, + 284, 283, 3, 2, 2, 2, 285, 287, 3, 2, 2, 2, 286, 288, 7, 5, 2, 2, 287, + 286, 3, 2, 2, 2, 287, 288, 3, 2, 2, 2, 288, 61, 3, 2, 2, 2, 289, 290, 7, + 16, 2, 2, 290, 291, 7, 25, 2, 2, 291, 63, 3, 2, 2, 2, 292, 293, 8, 33, + 1, 2, 293, 294, 7, 25, 2, 2, 294, 296, 5, 74, 38, 2, 295, 297, 5, 66, 34, + 2, 296, 295, 3, 2, 2, 2, 296, 297, 3, 2, 2, 2, 297, 299, 3, 2, 2, 2, 298, + 300, 7, 25, 2, 2, 299, 298, 3, 2, 2, 2, 299, 300, 3, 2, 2, 2, 300, 302, + 3, 2, 2, 2, 301, 303, 5, 68, 35, 2, 302, 301, 3, 2, 2, 2, 302, 303, 3, + 2, 2, 2, 303, 65, 3, 2, 2, 2, 304, 306, 7, 4, 2, 2, 305, 307, 7, 25, 2, + 2, 306, 305, 3, 2, 2, 2, 306, 307, 3, 2, 2, 2, 307, 308, 3, 2, 2, 2, 308, + 309, 7, 5, 2, 2, 309, 67, 3, 2, 2, 2, 310, 312, 7, 4, 2, 2, 311, 313, 5, + 44, 23, 2, 312, 311, 3, 2, 2, 2, 312, 313, 3, 2, 2, 2, 313, 314, 3, 2, + 2, 2, 314, 315, 7, 5, 2, 2, 315, 69, 3, 2, 2, 2, 316, 317, 7, 25, 2, 2, + 317, 318, 8, 36, 1, 2, 318, 319, 7, 24, 2, 2, 319, 71, 3, 2, 2, 2, 320, + 322, 7, 25, 2, 2, 321, 323, 7, 12, 2, 2, 322, 321, 3, 2, 2, 2, 322, 323, + 3, 2, 2, 2, 323, 325, 3, 2, 2, 2, 324, 320, 3, 2, 2, 2, 325, 326, 3, 2, + 2, 2, 326, 324, 3, 2, 2, 2, 326, 327, 3, 2, 2, 2, 327, 73, 3, 2, 2, 2, + 328, 329, 7, 13, 2, 2, 329, 334, 7, 25, 2, 2, 330, 331, 7, 12, 2, 2, 331, + 333, 7, 25, 2, 2, 332, 330, 3, 2, 2, 2, 333, 336, 3, 2, 2, 2, 334, 332, + 3, 2, 2, 2, 334, 335, 3, 2, 2, 2, 335, 344, 3, 2, 2, 2, 336, 334, 3, 2, + 2, 2, 337, 338, 7, 14, 2, 2, 338, 341, 7, 25, 2, 2, 339, 340, 7, 12, 2, + 2, 340, 342, 7, 25, 2, 2, 341, 339, 3, 2, 2, 2, 341, 342, 3, 2, 2, 2, 342, + 344, 3, 2, 2, 2, 343, 328, 3, 2, 2, 2, 343, 337, 3, 2, 2, 2, 344, 345, + 3, 2, 2, 2, 345, 343, 3, 2, 2, 2, 345, 346, 3, 2, 2, 2, 346, 75, 3, 2, + 2, 2, 41, 79, 87, 97, 109, 124, 130, 142, 149, 153, 158, 164, 172, 179, + 185, 193, 200, 206, 209, 221, 240, 249, 260, 266, 270, 276, 281, 284, 287, + 296, 299, 302, 306, 312, 322, 326, 334, 341, 343, 345, +} +var literalNames = []string{ + "", "'='", "'('", "')'", "'{'", "'}'", "'*'", "'time.Time'", "'['", "']'", + "'-'", "'/'", "'/:'", "'@doc'", "'@handler'", "'interface{}'", "'@server'", +} +var symbolicNames = []string{ + "", "", "", "", "", "", "", "", "", "", "", "", "", "ATDOC", "ATHANDLER", + "INTERFACE", "ATSERVER", "WS", "COMMENT", "LINE_COMMENT", "STRING", "RAW_STRING", + "LINE_VALUE", "ID", +} + +var ruleNames = []string{ + "api", "spec", "syntaxLit", "importSpec", "importLit", "importBlock", "importBlockValue", + "importValue", "infoSpec", "typeSpec", "typeLit", "typeBlock", "typeLitBody", + "typeBlockBody", "typeStruct", "typeAlias", "typeBlockStruct", "typeBlockAlias", + "field", "normalField", "anonymousFiled", "dataType", "pointerType", "mapType", + "arrayType", "serviceSpec", "atServer", "serviceApi", "serviceRoute", "atDoc", + "atHandler", "route", "body", "replybody", "kvLit", "serviceName", "path", +} + +type ApiParserParser struct { + *antlr.BaseParser +} + +// NewApiParserParser produces a new parser instance for the optional input antlr.TokenStream. +// +// The *ApiParserParser instance produced may be reused by calling the SetInputStream method. +// The initial parser configuration is expensive to construct, and the object is not thread-safe; +// however, if used within a Golang sync.Pool, the construction cost amortizes well and the +// objects can be used in a thread-safe manner. +func NewApiParserParser(input antlr.TokenStream) *ApiParserParser { + this := new(ApiParserParser) + deserializer := antlr.NewATNDeserializer(nil) + deserializedATN := deserializer.DeserializeFromUInt16(parserATN) + decisionToDFA := make([]*antlr.DFA, len(deserializedATN.DecisionToState)) + for index, ds := range deserializedATN.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(ds, index) + } + this.BaseParser = antlr.NewBaseParser(input) + + this.Interpreter = antlr.NewParserATNSimulator(this, deserializedATN, decisionToDFA, antlr.NewPredictionContextCache()) + this.RuleNames = ruleNames + this.LiteralNames = literalNames + this.SymbolicNames = symbolicNames + this.GrammarFileName = "ApiParser.g4" + + return this +} + +// ApiParserParser tokens. +const ( + ApiParserParserEOF = antlr.TokenEOF + ApiParserParserT__0 = 1 + ApiParserParserT__1 = 2 + ApiParserParserT__2 = 3 + ApiParserParserT__3 = 4 + ApiParserParserT__4 = 5 + ApiParserParserT__5 = 6 + ApiParserParserT__6 = 7 + ApiParserParserT__7 = 8 + ApiParserParserT__8 = 9 + ApiParserParserT__9 = 10 + ApiParserParserT__10 = 11 + ApiParserParserT__11 = 12 + ApiParserParserATDOC = 13 + ApiParserParserATHANDLER = 14 + ApiParserParserINTERFACE = 15 + ApiParserParserATSERVER = 16 + ApiParserParserWS = 17 + ApiParserParserCOMMENT = 18 + ApiParserParserLINE_COMMENT = 19 + ApiParserParserSTRING = 20 + ApiParserParserRAW_STRING = 21 + ApiParserParserLINE_VALUE = 22 + ApiParserParserID = 23 +) + +// ApiParserParser rules. +const ( + ApiParserParserRULE_api = 0 + ApiParserParserRULE_spec = 1 + ApiParserParserRULE_syntaxLit = 2 + ApiParserParserRULE_importSpec = 3 + ApiParserParserRULE_importLit = 4 + ApiParserParserRULE_importBlock = 5 + ApiParserParserRULE_importBlockValue = 6 + ApiParserParserRULE_importValue = 7 + ApiParserParserRULE_infoSpec = 8 + ApiParserParserRULE_typeSpec = 9 + ApiParserParserRULE_typeLit = 10 + ApiParserParserRULE_typeBlock = 11 + ApiParserParserRULE_typeLitBody = 12 + ApiParserParserRULE_typeBlockBody = 13 + ApiParserParserRULE_typeStruct = 14 + ApiParserParserRULE_typeAlias = 15 + ApiParserParserRULE_typeBlockStruct = 16 + ApiParserParserRULE_typeBlockAlias = 17 + ApiParserParserRULE_field = 18 + ApiParserParserRULE_normalField = 19 + ApiParserParserRULE_anonymousFiled = 20 + ApiParserParserRULE_dataType = 21 + ApiParserParserRULE_pointerType = 22 + ApiParserParserRULE_mapType = 23 + ApiParserParserRULE_arrayType = 24 + ApiParserParserRULE_serviceSpec = 25 + ApiParserParserRULE_atServer = 26 + ApiParserParserRULE_serviceApi = 27 + ApiParserParserRULE_serviceRoute = 28 + ApiParserParserRULE_atDoc = 29 + ApiParserParserRULE_atHandler = 30 + ApiParserParserRULE_route = 31 + ApiParserParserRULE_body = 32 + ApiParserParserRULE_replybody = 33 + ApiParserParserRULE_kvLit = 34 + ApiParserParserRULE_serviceName = 35 + ApiParserParserRULE_path = 36 +) + +// IApiContext is an interface to support dynamic dispatch. +type IApiContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsApiContext differentiates from other interfaces. + IsApiContext() +} + +type ApiContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyApiContext() *ApiContext { + var p = new(ApiContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_api + return p +} + +func (*ApiContext) IsApiContext() {} + +func NewApiContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ApiContext { + var p = new(ApiContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_api + + return p +} + +func (s *ApiContext) GetParser() antlr.Parser { return s.parser } + +func (s *ApiContext) AllSpec() []ISpecContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*ISpecContext)(nil)).Elem()) + var tst = make([]ISpecContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(ISpecContext) + } + } + + return tst +} + +func (s *ApiContext) Spec(i int) ISpecContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ISpecContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(ISpecContext) +} + +func (s *ApiContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ApiContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ApiContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitApi(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) Api() (localctx IApiContext) { + localctx = NewApiContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, ApiParserParserRULE_api) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + p.SetState(77) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for _la == ApiParserParserATSERVER || _la == ApiParserParserID { + { + p.SetState(74) + p.Spec() + } + + p.SetState(79) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + + return localctx +} + +// ISpecContext is an interface to support dynamic dispatch. +type ISpecContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsSpecContext differentiates from other interfaces. + IsSpecContext() +} + +type SpecContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptySpecContext() *SpecContext { + var p = new(SpecContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_spec + return p +} + +func (*SpecContext) IsSpecContext() {} + +func NewSpecContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SpecContext { + var p = new(SpecContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_spec + + return p +} + +func (s *SpecContext) GetParser() antlr.Parser { return s.parser } + +func (s *SpecContext) SyntaxLit() ISyntaxLitContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ISyntaxLitContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ISyntaxLitContext) +} + +func (s *SpecContext) ImportSpec() IImportSpecContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IImportSpecContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IImportSpecContext) +} + +func (s *SpecContext) InfoSpec() IInfoSpecContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IInfoSpecContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IInfoSpecContext) +} + +func (s *SpecContext) TypeSpec() ITypeSpecContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeSpecContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeSpecContext) +} + +func (s *SpecContext) ServiceSpec() IServiceSpecContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IServiceSpecContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IServiceSpecContext) +} + +func (s *SpecContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *SpecContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *SpecContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitSpec(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) Spec() (localctx ISpecContext) { + localctx = NewSpecContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, ApiParserParserRULE_spec) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(85) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 1, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(80) + p.SyntaxLit() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(81) + p.ImportSpec() + } + + case 3: + p.EnterOuterAlt(localctx, 3) + { + p.SetState(82) + p.InfoSpec() + } + + case 4: + p.EnterOuterAlt(localctx, 4) + { + p.SetState(83) + p.TypeSpec() + } + + case 5: + p.EnterOuterAlt(localctx, 5) + { + p.SetState(84) + p.ServiceSpec() + } + + } + + return localctx +} + +// ISyntaxLitContext is an interface to support dynamic dispatch. +type ISyntaxLitContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetSyntaxToken returns the syntaxToken token. + GetSyntaxToken() antlr.Token + + // GetAssign returns the assign token. + GetAssign() antlr.Token + + // GetVersion returns the version token. + GetVersion() antlr.Token + + // SetSyntaxToken sets the syntaxToken token. + SetSyntaxToken(antlr.Token) + + // SetAssign sets the assign token. + SetAssign(antlr.Token) + + // SetVersion sets the version token. + SetVersion(antlr.Token) + + // IsSyntaxLitContext differentiates from other interfaces. + IsSyntaxLitContext() +} + +type SyntaxLitContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + syntaxToken antlr.Token + assign antlr.Token + version antlr.Token +} + +func NewEmptySyntaxLitContext() *SyntaxLitContext { + var p = new(SyntaxLitContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_syntaxLit + return p +} + +func (*SyntaxLitContext) IsSyntaxLitContext() {} + +func NewSyntaxLitContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SyntaxLitContext { + var p = new(SyntaxLitContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_syntaxLit + + return p +} + +func (s *SyntaxLitContext) GetParser() antlr.Parser { return s.parser } + +func (s *SyntaxLitContext) GetSyntaxToken() antlr.Token { return s.syntaxToken } + +func (s *SyntaxLitContext) GetAssign() antlr.Token { return s.assign } + +func (s *SyntaxLitContext) GetVersion() antlr.Token { return s.version } + +func (s *SyntaxLitContext) SetSyntaxToken(v antlr.Token) { s.syntaxToken = v } + +func (s *SyntaxLitContext) SetAssign(v antlr.Token) { s.assign = v } + +func (s *SyntaxLitContext) SetVersion(v antlr.Token) { s.version = v } + +func (s *SyntaxLitContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *SyntaxLitContext) STRING() antlr.TerminalNode { + return s.GetToken(ApiParserParserSTRING, 0) +} + +func (s *SyntaxLitContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *SyntaxLitContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *SyntaxLitContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitSyntaxLit(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) SyntaxLit() (localctx ISyntaxLitContext) { + localctx = NewSyntaxLitContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 4, ApiParserParserRULE_syntaxLit) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "syntax") + { + p.SetState(88) + + var _m = p.Match(ApiParserParserID) + + localctx.(*SyntaxLitContext).syntaxToken = _m + } + { + p.SetState(89) + + var _m = p.Match(ApiParserParserT__0) + + localctx.(*SyntaxLitContext).assign = _m + } + checkVersion(p) + { + p.SetState(91) + + var _m = p.Match(ApiParserParserSTRING) + + localctx.(*SyntaxLitContext).version = _m + } + + return localctx +} + +// IImportSpecContext is an interface to support dynamic dispatch. +type IImportSpecContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsImportSpecContext differentiates from other interfaces. + IsImportSpecContext() +} + +type ImportSpecContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyImportSpecContext() *ImportSpecContext { + var p = new(ImportSpecContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_importSpec + return p +} + +func (*ImportSpecContext) IsImportSpecContext() {} + +func NewImportSpecContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ImportSpecContext { + var p = new(ImportSpecContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_importSpec + + return p +} + +func (s *ImportSpecContext) GetParser() antlr.Parser { return s.parser } + +func (s *ImportSpecContext) ImportLit() IImportLitContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IImportLitContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IImportLitContext) +} + +func (s *ImportSpecContext) ImportBlock() IImportBlockContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IImportBlockContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IImportBlockContext) +} + +func (s *ImportSpecContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ImportSpecContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ImportSpecContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitImportSpec(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ImportSpec() (localctx IImportSpecContext) { + localctx = NewImportSpecContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, ApiParserParserRULE_importSpec) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(95) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 2, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(93) + p.ImportLit() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(94) + p.ImportBlock() + } + + } + + return localctx +} + +// IImportLitContext is an interface to support dynamic dispatch. +type IImportLitContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetImportToken returns the importToken token. + GetImportToken() antlr.Token + + // SetImportToken sets the importToken token. + SetImportToken(antlr.Token) + + // IsImportLitContext differentiates from other interfaces. + IsImportLitContext() +} + +type ImportLitContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + importToken antlr.Token +} + +func NewEmptyImportLitContext() *ImportLitContext { + var p = new(ImportLitContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_importLit + return p +} + +func (*ImportLitContext) IsImportLitContext() {} + +func NewImportLitContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ImportLitContext { + var p = new(ImportLitContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_importLit + + return p +} + +func (s *ImportLitContext) GetParser() antlr.Parser { return s.parser } + +func (s *ImportLitContext) GetImportToken() antlr.Token { return s.importToken } + +func (s *ImportLitContext) SetImportToken(v antlr.Token) { s.importToken = v } + +func (s *ImportLitContext) ImportValue() IImportValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IImportValueContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IImportValueContext) +} + +func (s *ImportLitContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *ImportLitContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ImportLitContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ImportLitContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitImportLit(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ImportLit() (localctx IImportLitContext) { + localctx = NewImportLitContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 8, ApiParserParserRULE_importLit) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "import") + { + p.SetState(98) + + var _m = p.Match(ApiParserParserID) + + localctx.(*ImportLitContext).importToken = _m + } + { + p.SetState(99) + p.ImportValue() + } + + return localctx +} + +// IImportBlockContext is an interface to support dynamic dispatch. +type IImportBlockContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetImportToken returns the importToken token. + GetImportToken() antlr.Token + + // SetImportToken sets the importToken token. + SetImportToken(antlr.Token) + + // IsImportBlockContext differentiates from other interfaces. + IsImportBlockContext() +} + +type ImportBlockContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + importToken antlr.Token +} + +func NewEmptyImportBlockContext() *ImportBlockContext { + var p = new(ImportBlockContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_importBlock + return p +} + +func (*ImportBlockContext) IsImportBlockContext() {} + +func NewImportBlockContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ImportBlockContext { + var p = new(ImportBlockContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_importBlock + + return p +} + +func (s *ImportBlockContext) GetParser() antlr.Parser { return s.parser } + +func (s *ImportBlockContext) GetImportToken() antlr.Token { return s.importToken } + +func (s *ImportBlockContext) SetImportToken(v antlr.Token) { s.importToken = v } + +func (s *ImportBlockContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *ImportBlockContext) AllImportBlockValue() []IImportBlockValueContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IImportBlockValueContext)(nil)).Elem()) + var tst = make([]IImportBlockValueContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IImportBlockValueContext) + } + } + + return tst +} + +func (s *ImportBlockContext) ImportBlockValue(i int) IImportBlockValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IImportBlockValueContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IImportBlockValueContext) +} + +func (s *ImportBlockContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ImportBlockContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ImportBlockContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitImportBlock(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ImportBlock() (localctx IImportBlockContext) { + localctx = NewImportBlockContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 10, ApiParserParserRULE_importBlock) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "import") + { + p.SetState(102) + + var _m = p.Match(ApiParserParserID) + + localctx.(*ImportBlockContext).importToken = _m + } + { + p.SetState(103) + p.Match(ApiParserParserT__1) + } + p.SetState(105) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for ok := true; ok; ok = _la == ApiParserParserSTRING { + { + p.SetState(104) + p.ImportBlockValue() + } + + p.SetState(107) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + { + p.SetState(109) + p.Match(ApiParserParserT__2) + } + + return localctx +} + +// IImportBlockValueContext is an interface to support dynamic dispatch. +type IImportBlockValueContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsImportBlockValueContext differentiates from other interfaces. + IsImportBlockValueContext() +} + +type ImportBlockValueContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyImportBlockValueContext() *ImportBlockValueContext { + var p = new(ImportBlockValueContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_importBlockValue + return p +} + +func (*ImportBlockValueContext) IsImportBlockValueContext() {} + +func NewImportBlockValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ImportBlockValueContext { + var p = new(ImportBlockValueContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_importBlockValue + + return p +} + +func (s *ImportBlockValueContext) GetParser() antlr.Parser { return s.parser } + +func (s *ImportBlockValueContext) ImportValue() IImportValueContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IImportValueContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IImportValueContext) +} + +func (s *ImportBlockValueContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ImportBlockValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ImportBlockValueContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitImportBlockValue(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ImportBlockValue() (localctx IImportBlockValueContext) { + localctx = NewImportBlockValueContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 12, ApiParserParserRULE_importBlockValue) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(111) + p.ImportValue() + } + + return localctx +} + +// IImportValueContext is an interface to support dynamic dispatch. +type IImportValueContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsImportValueContext differentiates from other interfaces. + IsImportValueContext() +} + +type ImportValueContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyImportValueContext() *ImportValueContext { + var p = new(ImportValueContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_importValue + return p +} + +func (*ImportValueContext) IsImportValueContext() {} + +func NewImportValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ImportValueContext { + var p = new(ImportValueContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_importValue + + return p +} + +func (s *ImportValueContext) GetParser() antlr.Parser { return s.parser } + +func (s *ImportValueContext) STRING() antlr.TerminalNode { + return s.GetToken(ApiParserParserSTRING, 0) +} + +func (s *ImportValueContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ImportValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ImportValueContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitImportValue(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ImportValue() (localctx IImportValueContext) { + localctx = NewImportValueContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 14, ApiParserParserRULE_importValue) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + checkImportValue(p) + { + p.SetState(114) + p.Match(ApiParserParserSTRING) + } + + return localctx +} + +// IInfoSpecContext is an interface to support dynamic dispatch. +type IInfoSpecContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetInfoToken returns the infoToken token. + GetInfoToken() antlr.Token + + // GetLp returns the lp token. + GetLp() antlr.Token + + // GetRp returns the rp token. + GetRp() antlr.Token + + // SetInfoToken sets the infoToken token. + SetInfoToken(antlr.Token) + + // SetLp sets the lp token. + SetLp(antlr.Token) + + // SetRp sets the rp token. + SetRp(antlr.Token) + + // IsInfoSpecContext differentiates from other interfaces. + IsInfoSpecContext() +} + +type InfoSpecContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + infoToken antlr.Token + lp antlr.Token + rp antlr.Token +} + +func NewEmptyInfoSpecContext() *InfoSpecContext { + var p = new(InfoSpecContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_infoSpec + return p +} + +func (*InfoSpecContext) IsInfoSpecContext() {} + +func NewInfoSpecContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *InfoSpecContext { + var p = new(InfoSpecContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_infoSpec + + return p +} + +func (s *InfoSpecContext) GetParser() antlr.Parser { return s.parser } + +func (s *InfoSpecContext) GetInfoToken() antlr.Token { return s.infoToken } + +func (s *InfoSpecContext) GetLp() antlr.Token { return s.lp } + +func (s *InfoSpecContext) GetRp() antlr.Token { return s.rp } + +func (s *InfoSpecContext) SetInfoToken(v antlr.Token) { s.infoToken = v } + +func (s *InfoSpecContext) SetLp(v antlr.Token) { s.lp = v } + +func (s *InfoSpecContext) SetRp(v antlr.Token) { s.rp = v } + +func (s *InfoSpecContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *InfoSpecContext) AllKvLit() []IKvLitContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IKvLitContext)(nil)).Elem()) + var tst = make([]IKvLitContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IKvLitContext) + } + } + + return tst +} + +func (s *InfoSpecContext) KvLit(i int) IKvLitContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKvLitContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IKvLitContext) +} + +func (s *InfoSpecContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *InfoSpecContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *InfoSpecContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitInfoSpec(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) InfoSpec() (localctx IInfoSpecContext) { + localctx = NewInfoSpecContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 16, ApiParserParserRULE_infoSpec) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "info") + { + p.SetState(117) + + var _m = p.Match(ApiParserParserID) + + localctx.(*InfoSpecContext).infoToken = _m + } + { + p.SetState(118) + + var _m = p.Match(ApiParserParserT__1) + + localctx.(*InfoSpecContext).lp = _m + } + p.SetState(120) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for ok := true; ok; ok = _la == ApiParserParserID { + { + p.SetState(119) + p.KvLit() + } + + p.SetState(122) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + { + p.SetState(124) + + var _m = p.Match(ApiParserParserT__2) + + localctx.(*InfoSpecContext).rp = _m + } + + return localctx +} + +// ITypeSpecContext is an interface to support dynamic dispatch. +type ITypeSpecContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsTypeSpecContext differentiates from other interfaces. + IsTypeSpecContext() +} + +type TypeSpecContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyTypeSpecContext() *TypeSpecContext { + var p = new(TypeSpecContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeSpec + return p +} + +func (*TypeSpecContext) IsTypeSpecContext() {} + +func NewTypeSpecContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeSpecContext { + var p = new(TypeSpecContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeSpec + + return p +} + +func (s *TypeSpecContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeSpecContext) TypeLit() ITypeLitContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeLitContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeLitContext) +} + +func (s *TypeSpecContext) TypeBlock() ITypeBlockContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeBlockContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeBlockContext) +} + +func (s *TypeSpecContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeSpecContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeSpecContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeSpec(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeSpec() (localctx ITypeSpecContext) { + localctx = NewTypeSpecContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 18, ApiParserParserRULE_typeSpec) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(128) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 5, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(126) + p.TypeLit() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(127) + p.TypeBlock() + } + + } + + return localctx +} + +// ITypeLitContext is an interface to support dynamic dispatch. +type ITypeLitContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetTypeToken returns the typeToken token. + GetTypeToken() antlr.Token + + // SetTypeToken sets the typeToken token. + SetTypeToken(antlr.Token) + + // IsTypeLitContext differentiates from other interfaces. + IsTypeLitContext() +} + +type TypeLitContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + typeToken antlr.Token +} + +func NewEmptyTypeLitContext() *TypeLitContext { + var p = new(TypeLitContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeLit + return p +} + +func (*TypeLitContext) IsTypeLitContext() {} + +func NewTypeLitContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeLitContext { + var p = new(TypeLitContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeLit + + return p +} + +func (s *TypeLitContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeLitContext) GetTypeToken() antlr.Token { return s.typeToken } + +func (s *TypeLitContext) SetTypeToken(v antlr.Token) { s.typeToken = v } + +func (s *TypeLitContext) TypeLitBody() ITypeLitBodyContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeLitBodyContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeLitBodyContext) +} + +func (s *TypeLitContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *TypeLitContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeLitContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeLitContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeLit(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeLit() (localctx ITypeLitContext) { + localctx = NewTypeLitContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 20, ApiParserParserRULE_typeLit) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "type") + { + p.SetState(131) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeLitContext).typeToken = _m + } + { + p.SetState(132) + p.TypeLitBody() + } + + return localctx +} + +// ITypeBlockContext is an interface to support dynamic dispatch. +type ITypeBlockContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetTypeToken returns the typeToken token. + GetTypeToken() antlr.Token + + // GetLp returns the lp token. + GetLp() antlr.Token + + // GetRp returns the rp token. + GetRp() antlr.Token + + // SetTypeToken sets the typeToken token. + SetTypeToken(antlr.Token) + + // SetLp sets the lp token. + SetLp(antlr.Token) + + // SetRp sets the rp token. + SetRp(antlr.Token) + + // IsTypeBlockContext differentiates from other interfaces. + IsTypeBlockContext() +} + +type TypeBlockContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + typeToken antlr.Token + lp antlr.Token + rp antlr.Token +} + +func NewEmptyTypeBlockContext() *TypeBlockContext { + var p = new(TypeBlockContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeBlock + return p +} + +func (*TypeBlockContext) IsTypeBlockContext() {} + +func NewTypeBlockContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeBlockContext { + var p = new(TypeBlockContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeBlock + + return p +} + +func (s *TypeBlockContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeBlockContext) GetTypeToken() antlr.Token { return s.typeToken } + +func (s *TypeBlockContext) GetLp() antlr.Token { return s.lp } + +func (s *TypeBlockContext) GetRp() antlr.Token { return s.rp } + +func (s *TypeBlockContext) SetTypeToken(v antlr.Token) { s.typeToken = v } + +func (s *TypeBlockContext) SetLp(v antlr.Token) { s.lp = v } + +func (s *TypeBlockContext) SetRp(v antlr.Token) { s.rp = v } + +func (s *TypeBlockContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *TypeBlockContext) AllTypeBlockBody() []ITypeBlockBodyContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*ITypeBlockBodyContext)(nil)).Elem()) + var tst = make([]ITypeBlockBodyContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(ITypeBlockBodyContext) + } + } + + return tst +} + +func (s *TypeBlockContext) TypeBlockBody(i int) ITypeBlockBodyContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeBlockBodyContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(ITypeBlockBodyContext) +} + +func (s *TypeBlockContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeBlockContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeBlockContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeBlock(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeBlock() (localctx ITypeBlockContext) { + localctx = NewTypeBlockContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 22, ApiParserParserRULE_typeBlock) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "type") + { + p.SetState(135) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeBlockContext).typeToken = _m + } + { + p.SetState(136) + + var _m = p.Match(ApiParserParserT__1) + + localctx.(*TypeBlockContext).lp = _m + } + p.SetState(140) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for _la == ApiParserParserID { + { + p.SetState(137) + p.TypeBlockBody() + } + + p.SetState(142) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + { + p.SetState(143) + + var _m = p.Match(ApiParserParserT__2) + + localctx.(*TypeBlockContext).rp = _m + } + + return localctx +} + +// ITypeLitBodyContext is an interface to support dynamic dispatch. +type ITypeLitBodyContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsTypeLitBodyContext differentiates from other interfaces. + IsTypeLitBodyContext() +} + +type TypeLitBodyContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyTypeLitBodyContext() *TypeLitBodyContext { + var p = new(TypeLitBodyContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeLitBody + return p +} + +func (*TypeLitBodyContext) IsTypeLitBodyContext() {} + +func NewTypeLitBodyContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeLitBodyContext { + var p = new(TypeLitBodyContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeLitBody + + return p +} + +func (s *TypeLitBodyContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeLitBodyContext) TypeStruct() ITypeStructContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeStructContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeStructContext) +} + +func (s *TypeLitBodyContext) TypeAlias() ITypeAliasContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeAliasContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeAliasContext) +} + +func (s *TypeLitBodyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeLitBodyContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeLitBodyContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeLitBody(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeLitBody() (localctx ITypeLitBodyContext) { + localctx = NewTypeLitBodyContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 24, ApiParserParserRULE_typeLitBody) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(147) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 7, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(145) + p.TypeStruct() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(146) + p.TypeAlias() + } + + } + + return localctx +} + +// ITypeBlockBodyContext is an interface to support dynamic dispatch. +type ITypeBlockBodyContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsTypeBlockBodyContext differentiates from other interfaces. + IsTypeBlockBodyContext() +} + +type TypeBlockBodyContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyTypeBlockBodyContext() *TypeBlockBodyContext { + var p = new(TypeBlockBodyContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeBlockBody + return p +} + +func (*TypeBlockBodyContext) IsTypeBlockBodyContext() {} + +func NewTypeBlockBodyContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeBlockBodyContext { + var p = new(TypeBlockBodyContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeBlockBody + + return p +} + +func (s *TypeBlockBodyContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeBlockBodyContext) TypeBlockStruct() ITypeBlockStructContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeBlockStructContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeBlockStructContext) +} + +func (s *TypeBlockBodyContext) TypeBlockAlias() ITypeBlockAliasContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeBlockAliasContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeBlockAliasContext) +} + +func (s *TypeBlockBodyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeBlockBodyContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeBlockBodyContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeBlockBody(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeBlockBody() (localctx ITypeBlockBodyContext) { + localctx = NewTypeBlockBodyContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 26, ApiParserParserRULE_typeBlockBody) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(151) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 8, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + { + p.SetState(149) + p.TypeBlockStruct() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(150) + p.TypeBlockAlias() + } + + } + + return localctx +} + +// ITypeStructContext is an interface to support dynamic dispatch. +type ITypeStructContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetStructName returns the structName token. + GetStructName() antlr.Token + + // GetStructToken returns the structToken token. + GetStructToken() antlr.Token + + // GetLbrace returns the lbrace token. + GetLbrace() antlr.Token + + // GetRbrace returns the rbrace token. + GetRbrace() antlr.Token + + // SetStructName sets the structName token. + SetStructName(antlr.Token) + + // SetStructToken sets the structToken token. + SetStructToken(antlr.Token) + + // SetLbrace sets the lbrace token. + SetLbrace(antlr.Token) + + // SetRbrace sets the rbrace token. + SetRbrace(antlr.Token) + + // IsTypeStructContext differentiates from other interfaces. + IsTypeStructContext() +} + +type TypeStructContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + structName antlr.Token + structToken antlr.Token + lbrace antlr.Token + rbrace antlr.Token +} + +func NewEmptyTypeStructContext() *TypeStructContext { + var p = new(TypeStructContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeStruct + return p +} + +func (*TypeStructContext) IsTypeStructContext() {} + +func NewTypeStructContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeStructContext { + var p = new(TypeStructContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeStruct + + return p +} + +func (s *TypeStructContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeStructContext) GetStructName() antlr.Token { return s.structName } + +func (s *TypeStructContext) GetStructToken() antlr.Token { return s.structToken } + +func (s *TypeStructContext) GetLbrace() antlr.Token { return s.lbrace } + +func (s *TypeStructContext) GetRbrace() antlr.Token { return s.rbrace } + +func (s *TypeStructContext) SetStructName(v antlr.Token) { s.structName = v } + +func (s *TypeStructContext) SetStructToken(v antlr.Token) { s.structToken = v } + +func (s *TypeStructContext) SetLbrace(v antlr.Token) { s.lbrace = v } + +func (s *TypeStructContext) SetRbrace(v antlr.Token) { s.rbrace = v } + +func (s *TypeStructContext) AllID() []antlr.TerminalNode { + return s.GetTokens(ApiParserParserID) +} + +func (s *TypeStructContext) ID(i int) antlr.TerminalNode { + return s.GetToken(ApiParserParserID, i) +} + +func (s *TypeStructContext) AllField() []IFieldContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IFieldContext)(nil)).Elem()) + var tst = make([]IFieldContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IFieldContext) + } + } + + return tst +} + +func (s *TypeStructContext) Field(i int) IFieldContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IFieldContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IFieldContext) +} + +func (s *TypeStructContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeStructContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeStructContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeStruct(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeStruct() (localctx ITypeStructContext) { + localctx = NewTypeStructContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 28, ApiParserParserRULE_typeStruct) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + var _alt int + + p.EnterOuterAlt(localctx, 1) + checkKeyword(p) + { + p.SetState(154) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeStructContext).structName = _m + } + p.SetState(156) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == ApiParserParserID { + { + p.SetState(155) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeStructContext).structToken = _m + } + + } + { + p.SetState(158) + + var _m = p.Match(ApiParserParserT__3) + + localctx.(*TypeStructContext).lbrace = _m + } + p.SetState(162) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 10, p.GetParserRuleContext()) + + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + { + p.SetState(159) + p.Field() + } + + } + p.SetState(164) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 10, p.GetParserRuleContext()) + } + { + p.SetState(165) + + var _m = p.Match(ApiParserParserT__4) + + localctx.(*TypeStructContext).rbrace = _m + } + + return localctx +} + +// ITypeAliasContext is an interface to support dynamic dispatch. +type ITypeAliasContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetAlias returns the alias token. + GetAlias() antlr.Token + + // GetAssign returns the assign token. + GetAssign() antlr.Token + + // SetAlias sets the alias token. + SetAlias(antlr.Token) + + // SetAssign sets the assign token. + SetAssign(antlr.Token) + + // IsTypeAliasContext differentiates from other interfaces. + IsTypeAliasContext() +} + +type TypeAliasContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + alias antlr.Token + assign antlr.Token +} + +func NewEmptyTypeAliasContext() *TypeAliasContext { + var p = new(TypeAliasContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeAlias + return p +} + +func (*TypeAliasContext) IsTypeAliasContext() {} + +func NewTypeAliasContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeAliasContext { + var p = new(TypeAliasContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeAlias + + return p +} + +func (s *TypeAliasContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeAliasContext) GetAlias() antlr.Token { return s.alias } + +func (s *TypeAliasContext) GetAssign() antlr.Token { return s.assign } + +func (s *TypeAliasContext) SetAlias(v antlr.Token) { s.alias = v } + +func (s *TypeAliasContext) SetAssign(v antlr.Token) { s.assign = v } + +func (s *TypeAliasContext) DataType() IDataTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IDataTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IDataTypeContext) +} + +func (s *TypeAliasContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *TypeAliasContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeAliasContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeAliasContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeAlias(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeAlias() (localctx ITypeAliasContext) { + localctx = NewTypeAliasContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 30, ApiParserParserRULE_typeAlias) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + checkKeyword(p) + { + p.SetState(168) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeAliasContext).alias = _m + } + p.SetState(170) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == ApiParserParserT__0 { + { + p.SetState(169) + + var _m = p.Match(ApiParserParserT__0) + + localctx.(*TypeAliasContext).assign = _m + } + + } + { + p.SetState(172) + p.DataType() + } + + return localctx +} + +// ITypeBlockStructContext is an interface to support dynamic dispatch. +type ITypeBlockStructContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetStructName returns the structName token. + GetStructName() antlr.Token + + // GetStructToken returns the structToken token. + GetStructToken() antlr.Token + + // GetLbrace returns the lbrace token. + GetLbrace() antlr.Token + + // GetRbrace returns the rbrace token. + GetRbrace() antlr.Token + + // SetStructName sets the structName token. + SetStructName(antlr.Token) + + // SetStructToken sets the structToken token. + SetStructToken(antlr.Token) + + // SetLbrace sets the lbrace token. + SetLbrace(antlr.Token) + + // SetRbrace sets the rbrace token. + SetRbrace(antlr.Token) + + // IsTypeBlockStructContext differentiates from other interfaces. + IsTypeBlockStructContext() +} + +type TypeBlockStructContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + structName antlr.Token + structToken antlr.Token + lbrace antlr.Token + rbrace antlr.Token +} + +func NewEmptyTypeBlockStructContext() *TypeBlockStructContext { + var p = new(TypeBlockStructContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeBlockStruct + return p +} + +func (*TypeBlockStructContext) IsTypeBlockStructContext() {} + +func NewTypeBlockStructContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeBlockStructContext { + var p = new(TypeBlockStructContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeBlockStruct + + return p +} + +func (s *TypeBlockStructContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeBlockStructContext) GetStructName() antlr.Token { return s.structName } + +func (s *TypeBlockStructContext) GetStructToken() antlr.Token { return s.structToken } + +func (s *TypeBlockStructContext) GetLbrace() antlr.Token { return s.lbrace } + +func (s *TypeBlockStructContext) GetRbrace() antlr.Token { return s.rbrace } + +func (s *TypeBlockStructContext) SetStructName(v antlr.Token) { s.structName = v } + +func (s *TypeBlockStructContext) SetStructToken(v antlr.Token) { s.structToken = v } + +func (s *TypeBlockStructContext) SetLbrace(v antlr.Token) { s.lbrace = v } + +func (s *TypeBlockStructContext) SetRbrace(v antlr.Token) { s.rbrace = v } + +func (s *TypeBlockStructContext) AllID() []antlr.TerminalNode { + return s.GetTokens(ApiParserParserID) +} + +func (s *TypeBlockStructContext) ID(i int) antlr.TerminalNode { + return s.GetToken(ApiParserParserID, i) +} + +func (s *TypeBlockStructContext) AllField() []IFieldContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IFieldContext)(nil)).Elem()) + var tst = make([]IFieldContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IFieldContext) + } + } + + return tst +} + +func (s *TypeBlockStructContext) Field(i int) IFieldContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IFieldContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IFieldContext) +} + +func (s *TypeBlockStructContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeBlockStructContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeBlockStructContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeBlockStruct(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeBlockStruct() (localctx ITypeBlockStructContext) { + localctx = NewTypeBlockStructContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 32, ApiParserParserRULE_typeBlockStruct) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + var _alt int + + p.EnterOuterAlt(localctx, 1) + checkKeyword(p) + { + p.SetState(175) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeBlockStructContext).structName = _m + } + p.SetState(177) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == ApiParserParserID { + { + p.SetState(176) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeBlockStructContext).structToken = _m + } + + } + { + p.SetState(179) + + var _m = p.Match(ApiParserParserT__3) + + localctx.(*TypeBlockStructContext).lbrace = _m + } + p.SetState(183) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 13, p.GetParserRuleContext()) + + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + { + p.SetState(180) + p.Field() + } + + } + p.SetState(185) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 13, p.GetParserRuleContext()) + } + { + p.SetState(186) + + var _m = p.Match(ApiParserParserT__4) + + localctx.(*TypeBlockStructContext).rbrace = _m + } + + return localctx +} + +// ITypeBlockAliasContext is an interface to support dynamic dispatch. +type ITypeBlockAliasContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetAlias returns the alias token. + GetAlias() antlr.Token + + // GetAssign returns the assign token. + GetAssign() antlr.Token + + // SetAlias sets the alias token. + SetAlias(antlr.Token) + + // SetAssign sets the assign token. + SetAssign(antlr.Token) + + // IsTypeBlockAliasContext differentiates from other interfaces. + IsTypeBlockAliasContext() +} + +type TypeBlockAliasContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + alias antlr.Token + assign antlr.Token +} + +func NewEmptyTypeBlockAliasContext() *TypeBlockAliasContext { + var p = new(TypeBlockAliasContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_typeBlockAlias + return p +} + +func (*TypeBlockAliasContext) IsTypeBlockAliasContext() {} + +func NewTypeBlockAliasContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TypeBlockAliasContext { + var p = new(TypeBlockAliasContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_typeBlockAlias + + return p +} + +func (s *TypeBlockAliasContext) GetParser() antlr.Parser { return s.parser } + +func (s *TypeBlockAliasContext) GetAlias() antlr.Token { return s.alias } + +func (s *TypeBlockAliasContext) GetAssign() antlr.Token { return s.assign } + +func (s *TypeBlockAliasContext) SetAlias(v antlr.Token) { s.alias = v } + +func (s *TypeBlockAliasContext) SetAssign(v antlr.Token) { s.assign = v } + +func (s *TypeBlockAliasContext) DataType() IDataTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IDataTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IDataTypeContext) +} + +func (s *TypeBlockAliasContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *TypeBlockAliasContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *TypeBlockAliasContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *TypeBlockAliasContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitTypeBlockAlias(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) TypeBlockAlias() (localctx ITypeBlockAliasContext) { + localctx = NewTypeBlockAliasContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 34, ApiParserParserRULE_typeBlockAlias) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + checkKeyword(p) + { + p.SetState(189) + + var _m = p.Match(ApiParserParserID) + + localctx.(*TypeBlockAliasContext).alias = _m + } + p.SetState(191) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == ApiParserParserT__0 { + { + p.SetState(190) + + var _m = p.Match(ApiParserParserT__0) + + localctx.(*TypeBlockAliasContext).assign = _m + } + + } + { + p.SetState(193) + p.DataType() + } + + return localctx +} + +// IFieldContext is an interface to support dynamic dispatch. +type IFieldContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsFieldContext differentiates from other interfaces. + IsFieldContext() +} + +type FieldContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFieldContext() *FieldContext { + var p = new(FieldContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_field + return p +} + +func (*FieldContext) IsFieldContext() {} + +func NewFieldContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FieldContext { + var p = new(FieldContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_field + + return p +} + +func (s *FieldContext) GetParser() antlr.Parser { return s.parser } + +func (s *FieldContext) NormalField() INormalFieldContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*INormalFieldContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(INormalFieldContext) +} + +func (s *FieldContext) AnonymousFiled() IAnonymousFiledContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IAnonymousFiledContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IAnonymousFiledContext) +} + +func (s *FieldContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FieldContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FieldContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitField(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) Field() (localctx IFieldContext) { + localctx = NewFieldContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 36, ApiParserParserRULE_field) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(198) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 15, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + p.SetState(195) + + if !(isNormal(p)) { + panic(antlr.NewFailedPredicateException(p, "isNormal(p)", "")) + } + { + p.SetState(196) + p.NormalField() + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(197) + p.AnonymousFiled() + } + + } + + return localctx +} + +// INormalFieldContext is an interface to support dynamic dispatch. +type INormalFieldContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetFieldName returns the fieldName token. + GetFieldName() antlr.Token + + // GetTag returns the tag token. + GetTag() antlr.Token + + // SetFieldName sets the fieldName token. + SetFieldName(antlr.Token) + + // SetTag sets the tag token. + SetTag(antlr.Token) + + // IsNormalFieldContext differentiates from other interfaces. + IsNormalFieldContext() +} + +type NormalFieldContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + fieldName antlr.Token + tag antlr.Token +} + +func NewEmptyNormalFieldContext() *NormalFieldContext { + var p = new(NormalFieldContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_normalField + return p +} + +func (*NormalFieldContext) IsNormalFieldContext() {} + +func NewNormalFieldContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *NormalFieldContext { + var p = new(NormalFieldContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_normalField + + return p +} + +func (s *NormalFieldContext) GetParser() antlr.Parser { return s.parser } + +func (s *NormalFieldContext) GetFieldName() antlr.Token { return s.fieldName } + +func (s *NormalFieldContext) GetTag() antlr.Token { return s.tag } + +func (s *NormalFieldContext) SetFieldName(v antlr.Token) { s.fieldName = v } + +func (s *NormalFieldContext) SetTag(v antlr.Token) { s.tag = v } + +func (s *NormalFieldContext) DataType() IDataTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IDataTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IDataTypeContext) +} + +func (s *NormalFieldContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *NormalFieldContext) RAW_STRING() antlr.TerminalNode { + return s.GetToken(ApiParserParserRAW_STRING, 0) +} + +func (s *NormalFieldContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *NormalFieldContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *NormalFieldContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitNormalField(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) NormalField() (localctx INormalFieldContext) { + localctx = NewNormalFieldContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 38, ApiParserParserRULE_normalField) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + checkKeyword(p) + { + p.SetState(201) + + var _m = p.Match(ApiParserParserID) + + localctx.(*NormalFieldContext).fieldName = _m + } + { + p.SetState(202) + p.DataType() + } + p.SetState(204) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 16, p.GetParserRuleContext()) == 1 { + { + p.SetState(203) + + var _m = p.Match(ApiParserParserRAW_STRING) + + localctx.(*NormalFieldContext).tag = _m + } + + } + + return localctx +} + +// IAnonymousFiledContext is an interface to support dynamic dispatch. +type IAnonymousFiledContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetStar returns the star token. + GetStar() antlr.Token + + // SetStar sets the star token. + SetStar(antlr.Token) + + // IsAnonymousFiledContext differentiates from other interfaces. + IsAnonymousFiledContext() +} + +type AnonymousFiledContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + star antlr.Token +} + +func NewEmptyAnonymousFiledContext() *AnonymousFiledContext { + var p = new(AnonymousFiledContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_anonymousFiled + return p +} + +func (*AnonymousFiledContext) IsAnonymousFiledContext() {} + +func NewAnonymousFiledContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AnonymousFiledContext { + var p = new(AnonymousFiledContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_anonymousFiled + + return p +} + +func (s *AnonymousFiledContext) GetParser() antlr.Parser { return s.parser } + +func (s *AnonymousFiledContext) GetStar() antlr.Token { return s.star } + +func (s *AnonymousFiledContext) SetStar(v antlr.Token) { s.star = v } + +func (s *AnonymousFiledContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *AnonymousFiledContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AnonymousFiledContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *AnonymousFiledContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitAnonymousFiled(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) AnonymousFiled() (localctx IAnonymousFiledContext) { + localctx = NewAnonymousFiledContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 40, ApiParserParserRULE_anonymousFiled) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + p.SetState(207) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == ApiParserParserT__5 { + { + p.SetState(206) + + var _m = p.Match(ApiParserParserT__5) + + localctx.(*AnonymousFiledContext).star = _m + } + + } + { + p.SetState(209) + p.Match(ApiParserParserID) + } + + return localctx +} + +// IDataTypeContext is an interface to support dynamic dispatch. +type IDataTypeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetInter returns the inter token. + GetInter() antlr.Token + + // GetTime returns the time token. + GetTime() antlr.Token + + // SetInter sets the inter token. + SetInter(antlr.Token) + + // SetTime sets the time token. + SetTime(antlr.Token) + + // IsDataTypeContext differentiates from other interfaces. + IsDataTypeContext() +} + +type DataTypeContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + inter antlr.Token + time antlr.Token +} + +func NewEmptyDataTypeContext() *DataTypeContext { + var p = new(DataTypeContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_dataType + return p +} + +func (*DataTypeContext) IsDataTypeContext() {} + +func NewDataTypeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *DataTypeContext { + var p = new(DataTypeContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_dataType + + return p +} + +func (s *DataTypeContext) GetParser() antlr.Parser { return s.parser } + +func (s *DataTypeContext) GetInter() antlr.Token { return s.inter } + +func (s *DataTypeContext) GetTime() antlr.Token { return s.time } + +func (s *DataTypeContext) SetInter(v antlr.Token) { s.inter = v } + +func (s *DataTypeContext) SetTime(v antlr.Token) { s.time = v } + +func (s *DataTypeContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *DataTypeContext) MapType() IMapTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IMapTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IMapTypeContext) +} + +func (s *DataTypeContext) ArrayType() IArrayTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IArrayTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IArrayTypeContext) +} + +func (s *DataTypeContext) INTERFACE() antlr.TerminalNode { + return s.GetToken(ApiParserParserINTERFACE, 0) +} + +func (s *DataTypeContext) PointerType() IPointerTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IPointerTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IPointerTypeContext) +} + +func (s *DataTypeContext) TypeStruct() ITypeStructContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*ITypeStructContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(ITypeStructContext) +} + +func (s *DataTypeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *DataTypeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *DataTypeContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitDataType(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) DataType() (localctx IDataTypeContext) { + localctx = NewDataTypeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 42, ApiParserParserRULE_dataType) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.SetState(219) + p.GetErrorHandler().Sync(p) + switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 18, p.GetParserRuleContext()) { + case 1: + p.EnterOuterAlt(localctx, 1) + isInterface(p) + { + p.SetState(212) + p.Match(ApiParserParserID) + } + + case 2: + p.EnterOuterAlt(localctx, 2) + { + p.SetState(213) + p.MapType() + } + + case 3: + p.EnterOuterAlt(localctx, 3) + { + p.SetState(214) + p.ArrayType() + } + + case 4: + p.EnterOuterAlt(localctx, 4) + { + p.SetState(215) + + var _m = p.Match(ApiParserParserINTERFACE) + + localctx.(*DataTypeContext).inter = _m + } + + case 5: + p.EnterOuterAlt(localctx, 5) + { + p.SetState(216) + + var _m = p.Match(ApiParserParserT__6) + + localctx.(*DataTypeContext).time = _m + } + + case 6: + p.EnterOuterAlt(localctx, 6) + { + p.SetState(217) + p.PointerType() + } + + case 7: + p.EnterOuterAlt(localctx, 7) + { + p.SetState(218) + p.TypeStruct() + } + + } + + return localctx +} + +// IPointerTypeContext is an interface to support dynamic dispatch. +type IPointerTypeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetStar returns the star token. + GetStar() antlr.Token + + // SetStar sets the star token. + SetStar(antlr.Token) + + // IsPointerTypeContext differentiates from other interfaces. + IsPointerTypeContext() +} + +type PointerTypeContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + star antlr.Token +} + +func NewEmptyPointerTypeContext() *PointerTypeContext { + var p = new(PointerTypeContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_pointerType + return p +} + +func (*PointerTypeContext) IsPointerTypeContext() {} + +func NewPointerTypeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PointerTypeContext { + var p = new(PointerTypeContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_pointerType + + return p +} + +func (s *PointerTypeContext) GetParser() antlr.Parser { return s.parser } + +func (s *PointerTypeContext) GetStar() antlr.Token { return s.star } + +func (s *PointerTypeContext) SetStar(v antlr.Token) { s.star = v } + +func (s *PointerTypeContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *PointerTypeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *PointerTypeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *PointerTypeContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitPointerType(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) PointerType() (localctx IPointerTypeContext) { + localctx = NewPointerTypeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 44, ApiParserParserRULE_pointerType) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(221) + + var _m = p.Match(ApiParserParserT__5) + + localctx.(*PointerTypeContext).star = _m + } + checkKeyword(p) + { + p.SetState(223) + p.Match(ApiParserParserID) + } + + return localctx +} + +// IMapTypeContext is an interface to support dynamic dispatch. +type IMapTypeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetMapToken returns the mapToken token. + GetMapToken() antlr.Token + + // GetLbrack returns the lbrack token. + GetLbrack() antlr.Token + + // GetKey returns the key token. + GetKey() antlr.Token + + // GetRbrack returns the rbrack token. + GetRbrack() antlr.Token + + // SetMapToken sets the mapToken token. + SetMapToken(antlr.Token) + + // SetLbrack sets the lbrack token. + SetLbrack(antlr.Token) + + // SetKey sets the key token. + SetKey(antlr.Token) + + // SetRbrack sets the rbrack token. + SetRbrack(antlr.Token) + + // GetValue returns the value rule contexts. + GetValue() IDataTypeContext + + // SetValue sets the value rule contexts. + SetValue(IDataTypeContext) + + // IsMapTypeContext differentiates from other interfaces. + IsMapTypeContext() +} + +type MapTypeContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + mapToken antlr.Token + lbrack antlr.Token + key antlr.Token + rbrack antlr.Token + value IDataTypeContext +} + +func NewEmptyMapTypeContext() *MapTypeContext { + var p = new(MapTypeContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_mapType + return p +} + +func (*MapTypeContext) IsMapTypeContext() {} + +func NewMapTypeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *MapTypeContext { + var p = new(MapTypeContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_mapType + + return p +} + +func (s *MapTypeContext) GetParser() antlr.Parser { return s.parser } + +func (s *MapTypeContext) GetMapToken() antlr.Token { return s.mapToken } + +func (s *MapTypeContext) GetLbrack() antlr.Token { return s.lbrack } + +func (s *MapTypeContext) GetKey() antlr.Token { return s.key } + +func (s *MapTypeContext) GetRbrack() antlr.Token { return s.rbrack } + +func (s *MapTypeContext) SetMapToken(v antlr.Token) { s.mapToken = v } + +func (s *MapTypeContext) SetLbrack(v antlr.Token) { s.lbrack = v } + +func (s *MapTypeContext) SetKey(v antlr.Token) { s.key = v } + +func (s *MapTypeContext) SetRbrack(v antlr.Token) { s.rbrack = v } + +func (s *MapTypeContext) GetValue() IDataTypeContext { return s.value } + +func (s *MapTypeContext) SetValue(v IDataTypeContext) { s.value = v } + +func (s *MapTypeContext) AllID() []antlr.TerminalNode { + return s.GetTokens(ApiParserParserID) +} + +func (s *MapTypeContext) ID(i int) antlr.TerminalNode { + return s.GetToken(ApiParserParserID, i) +} + +func (s *MapTypeContext) DataType() IDataTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IDataTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IDataTypeContext) +} + +func (s *MapTypeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *MapTypeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *MapTypeContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitMapType(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) MapType() (localctx IMapTypeContext) { + localctx = NewMapTypeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 46, ApiParserParserRULE_mapType) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "map") + { + p.SetState(226) + + var _m = p.Match(ApiParserParserID) + + localctx.(*MapTypeContext).mapToken = _m + } + { + p.SetState(227) + + var _m = p.Match(ApiParserParserT__7) + + localctx.(*MapTypeContext).lbrack = _m + } + checkKey(p) + { + p.SetState(229) + + var _m = p.Match(ApiParserParserID) + + localctx.(*MapTypeContext).key = _m + } + { + p.SetState(230) + + var _m = p.Match(ApiParserParserT__8) + + localctx.(*MapTypeContext).rbrack = _m + } + { + p.SetState(231) + + var _x = p.DataType() + + localctx.(*MapTypeContext).value = _x + } + + return localctx +} + +// IArrayTypeContext is an interface to support dynamic dispatch. +type IArrayTypeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetLbrack returns the lbrack token. + GetLbrack() antlr.Token + + // GetRbrack returns the rbrack token. + GetRbrack() antlr.Token + + // SetLbrack sets the lbrack token. + SetLbrack(antlr.Token) + + // SetRbrack sets the rbrack token. + SetRbrack(antlr.Token) + + // IsArrayTypeContext differentiates from other interfaces. + IsArrayTypeContext() +} + +type ArrayTypeContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + lbrack antlr.Token + rbrack antlr.Token +} + +func NewEmptyArrayTypeContext() *ArrayTypeContext { + var p = new(ArrayTypeContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_arrayType + return p +} + +func (*ArrayTypeContext) IsArrayTypeContext() {} + +func NewArrayTypeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ArrayTypeContext { + var p = new(ArrayTypeContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_arrayType + + return p +} + +func (s *ArrayTypeContext) GetParser() antlr.Parser { return s.parser } + +func (s *ArrayTypeContext) GetLbrack() antlr.Token { return s.lbrack } + +func (s *ArrayTypeContext) GetRbrack() antlr.Token { return s.rbrack } + +func (s *ArrayTypeContext) SetLbrack(v antlr.Token) { s.lbrack = v } + +func (s *ArrayTypeContext) SetRbrack(v antlr.Token) { s.rbrack = v } + +func (s *ArrayTypeContext) DataType() IDataTypeContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IDataTypeContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IDataTypeContext) +} + +func (s *ArrayTypeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ArrayTypeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ArrayTypeContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitArrayType(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ArrayType() (localctx IArrayTypeContext) { + localctx = NewArrayTypeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 48, ApiParserParserRULE_arrayType) + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(233) + + var _m = p.Match(ApiParserParserT__7) + + localctx.(*ArrayTypeContext).lbrack = _m + } + { + p.SetState(234) + + var _m = p.Match(ApiParserParserT__8) + + localctx.(*ArrayTypeContext).rbrack = _m + } + { + p.SetState(235) + p.DataType() + } + + return localctx +} + +// IServiceSpecContext is an interface to support dynamic dispatch. +type IServiceSpecContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // IsServiceSpecContext differentiates from other interfaces. + IsServiceSpecContext() +} + +type ServiceSpecContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyServiceSpecContext() *ServiceSpecContext { + var p = new(ServiceSpecContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_serviceSpec + return p +} + +func (*ServiceSpecContext) IsServiceSpecContext() {} + +func NewServiceSpecContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ServiceSpecContext { + var p = new(ServiceSpecContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_serviceSpec + + return p +} + +func (s *ServiceSpecContext) GetParser() antlr.Parser { return s.parser } + +func (s *ServiceSpecContext) ServiceApi() IServiceApiContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IServiceApiContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IServiceApiContext) +} + +func (s *ServiceSpecContext) AtServer() IAtServerContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IAtServerContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IAtServerContext) +} + +func (s *ServiceSpecContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ServiceSpecContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ServiceSpecContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitServiceSpec(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ServiceSpec() (localctx IServiceSpecContext) { + localctx = NewServiceSpecContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 50, ApiParserParserRULE_serviceSpec) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + p.SetState(238) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + if _la == ApiParserParserATSERVER { + { + p.SetState(237) + p.AtServer() + } + + } + { + p.SetState(240) + p.ServiceApi() + } + + return localctx +} + +// IAtServerContext is an interface to support dynamic dispatch. +type IAtServerContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetLp returns the lp token. + GetLp() antlr.Token + + // GetRp returns the rp token. + GetRp() antlr.Token + + // SetLp sets the lp token. + SetLp(antlr.Token) + + // SetRp sets the rp token. + SetRp(antlr.Token) + + // IsAtServerContext differentiates from other interfaces. + IsAtServerContext() +} + +type AtServerContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + lp antlr.Token + rp antlr.Token +} + +func NewEmptyAtServerContext() *AtServerContext { + var p = new(AtServerContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_atServer + return p +} + +func (*AtServerContext) IsAtServerContext() {} + +func NewAtServerContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AtServerContext { + var p = new(AtServerContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_atServer + + return p +} + +func (s *AtServerContext) GetParser() antlr.Parser { return s.parser } + +func (s *AtServerContext) GetLp() antlr.Token { return s.lp } + +func (s *AtServerContext) GetRp() antlr.Token { return s.rp } + +func (s *AtServerContext) SetLp(v antlr.Token) { s.lp = v } + +func (s *AtServerContext) SetRp(v antlr.Token) { s.rp = v } + +func (s *AtServerContext) ATSERVER() antlr.TerminalNode { + return s.GetToken(ApiParserParserATSERVER, 0) +} + +func (s *AtServerContext) AllKvLit() []IKvLitContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IKvLitContext)(nil)).Elem()) + var tst = make([]IKvLitContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IKvLitContext) + } + } + + return tst +} + +func (s *AtServerContext) KvLit(i int) IKvLitContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IKvLitContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IKvLitContext) +} + +func (s *AtServerContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *AtServerContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *AtServerContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitAtServer(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) AtServer() (localctx IAtServerContext) { + localctx = NewAtServerContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 52, ApiParserParserRULE_atServer) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(242) + p.Match(ApiParserParserATSERVER) + } + { + p.SetState(243) + + var _m = p.Match(ApiParserParserT__1) + + localctx.(*AtServerContext).lp = _m + } + p.SetState(245) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for ok := true; ok; ok = _la == ApiParserParserID { + { + p.SetState(244) + p.KvLit() + } + + p.SetState(247) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + } + { + p.SetState(249) + + var _m = p.Match(ApiParserParserT__2) + + localctx.(*AtServerContext).rp = _m + } + + return localctx +} + +// IServiceApiContext is an interface to support dynamic dispatch. +type IServiceApiContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // GetServiceToken returns the serviceToken token. + GetServiceToken() antlr.Token + + // GetLbrace returns the lbrace token. + GetLbrace() antlr.Token + + // GetRbrace returns the rbrace token. + GetRbrace() antlr.Token + + // SetServiceToken sets the serviceToken token. + SetServiceToken(antlr.Token) + + // SetLbrace sets the lbrace token. + SetLbrace(antlr.Token) + + // SetRbrace sets the rbrace token. + SetRbrace(antlr.Token) + + // IsServiceApiContext differentiates from other interfaces. + IsServiceApiContext() +} + +type ServiceApiContext struct { + *antlr.BaseParserRuleContext + parser antlr.Parser + serviceToken antlr.Token + lbrace antlr.Token + rbrace antlr.Token +} + +func NewEmptyServiceApiContext() *ServiceApiContext { + var p = new(ServiceApiContext) + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1) + p.RuleIndex = ApiParserParserRULE_serviceApi + return p +} + +func (*ServiceApiContext) IsServiceApiContext() {} + +func NewServiceApiContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ServiceApiContext { + var p = new(ServiceApiContext) + + p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState) + + p.parser = parser + p.RuleIndex = ApiParserParserRULE_serviceApi + + return p +} + +func (s *ServiceApiContext) GetParser() antlr.Parser { return s.parser } + +func (s *ServiceApiContext) GetServiceToken() antlr.Token { return s.serviceToken } + +func (s *ServiceApiContext) GetLbrace() antlr.Token { return s.lbrace } + +func (s *ServiceApiContext) GetRbrace() antlr.Token { return s.rbrace } + +func (s *ServiceApiContext) SetServiceToken(v antlr.Token) { s.serviceToken = v } + +func (s *ServiceApiContext) SetLbrace(v antlr.Token) { s.lbrace = v } + +func (s *ServiceApiContext) SetRbrace(v antlr.Token) { s.rbrace = v } + +func (s *ServiceApiContext) ServiceName() IServiceNameContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IServiceNameContext)(nil)).Elem(), 0) + + if t == nil { + return nil + } + + return t.(IServiceNameContext) +} + +func (s *ServiceApiContext) ID() antlr.TerminalNode { + return s.GetToken(ApiParserParserID, 0) +} + +func (s *ServiceApiContext) AllServiceRoute() []IServiceRouteContext { + var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IServiceRouteContext)(nil)).Elem()) + var tst = make([]IServiceRouteContext, len(ts)) + + for i, t := range ts { + if t != nil { + tst[i] = t.(IServiceRouteContext) + } + } + + return tst +} + +func (s *ServiceApiContext) ServiceRoute(i int) IServiceRouteContext { + var t = s.GetTypedRuleContext(reflect.TypeOf((*IServiceRouteContext)(nil)).Elem(), i) + + if t == nil { + return nil + } + + return t.(IServiceRouteContext) +} + +func (s *ServiceApiContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ServiceApiContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ServiceApiContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { + switch t := visitor.(type) { + case ApiParserVisitor: + return t.VisitServiceApi(s) + + default: + return t.VisitChildren(s) + } +} + +func (p *ApiParserParser) ServiceApi() (localctx IServiceApiContext) { + localctx = NewServiceApiContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 54, ApiParserParserRULE_serviceApi) + var _la int + + defer func() { + p.ExitRule() + }() + + defer func() { + if err := recover(); err != nil { + if v, ok := err.(antlr.RecognitionException); ok { + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + } else { + panic(err) + } + } + }() + + p.EnterOuterAlt(localctx, 1) + match(p, "service") + { + p.SetState(252) + + var _m = p.Match(ApiParserParserID) + + localctx.(*ServiceApiContext).serviceToken = _m + } + { + p.SetState(253) + p.ServiceName() + } + { + p.SetState(254) + + var _m = p.Match(ApiParserParserT__3) + + localctx.(*ServiceApiContext).lbrace = _m + } + p.SetState(258) + p.GetErrorHandler().Sync(p) + _la = p.GetTokenStream().LA(1) + + for ((_la)&-(0x1f+1)) == 0 && ((1<" { + continue + } + if strings.TrimSpace(text) == "" { + continue + } + list = append(list, text) + } + } + if len(list) == 1 { + t := strings.TrimPrefix(list[0], "*") + if IsGolangKeyWord(t) { + notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", t)) + } + } + if len(list) > 1 { + if list[0] == "*" { + t := strings.TrimPrefix(list[1], "*") + if IsGolangKeyWord(t) { + notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", t)) + } + return false + } + } + return len(list) > 1 +} + +func MatchTag(v string) bool { + return matchRegex(v, tagRegex) +} + +func isInterface(p *ApiParserParser) { + v := getCurrentTokenText(p) + if IsGolangKeyWord(v) { + notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", v)) + } +} + +func getCurrentTokenText(p *ApiParserParser) string { + token := p.GetCurrentToken() + if token == nil { + return "" + } + + return token.GetText() +} + +func setCurrentTokenText(p *ApiParserParser, text string) { + token := p.GetCurrentToken() + if token == nil { + return + } + + token.SetText(text) +} + +func notifyErrorListeners(p *ApiParserParser, msg string) { + p.NotifyErrorListeners(msg, nil, nil) +} + +func matchRegex(text, str string) bool { + re := regexp.MustCompile(str) + v := re.FindString(text) + text = strings.TrimFunc(text, func(r rune) bool { + return unicode.IsSpace(r) + }) + return v == text +} + +func expecting(expecting, found string) string { + return fmt.Sprintf(`expecting '%s', found input '%s'`, expecting, found) +} + +func mismatched(expecting, found string) string { + return fmt.Sprintf(`mismatched '%s', found input '%s'`, expecting, found) +} diff --git a/tools/goctl/api/parser/g4/gen/api/baseparser_test.go b/tools/goctl/api/parser/g4/gen/api/baseparser_test.go new file mode 100644 index 00000000..d50ec323 --- /dev/null +++ b/tools/goctl/api/parser/g4/gen/api/baseparser_test.go @@ -0,0 +1,11 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMatch(t *testing.T) { + assert.False(t, matchRegex("v1ddd", versionRegex)) +} diff --git a/tools/goctl/api/parser/g4/test.api b/tools/goctl/api/parser/g4/test.api new file mode 100644 index 00000000..a70584e8 --- /dev/null +++ b/tools/goctl/api/parser/g4/test.api @@ -0,0 +1,99 @@ +// only one +syntax = "v1" + +// import +import "foo.api" + +// import group +import( + "foo.api" + "foo/bar.api" +) + +// only one +info( + title: "foo title" + desc: "foo +desc" + author: "foo author" + email: "foo email" + version: "foo version" +) + +// ignore the following duplicate name + +type Foo int + +// type single +type Foo { + Bar string +} + +type Foo { + Bar string `json:"bar"` + Inline +} +// go struct +type Foo struct { + Bar string `json:"bar"` +} + +// type group +type ( + Foo int + + // go struct + Foo struct { + Bar string `json:"bar"` + } + + Foo { + VString string `json:"vString"` + VBool bool `json:"vBool"` + VInt8 int8 `json:"vInt8"` + VInt16 int16 `json:"vInt16"` + VInt32 int32 `json:"vInt32"` + VInt64 int64 `json:"vInt64"` + VInt int `json:"vInt"` + VUInt8 uint8 `json:"vUInt8"` + VUInt16 uint16 `json:"vUInt16"` + VUInt32 uint32 `json:"vUInt32"` + VUInt64 uint64 `json:"vUInt64"` + VFloat32 float32 `json:"vFloat32"` + VFloat64 float64 `json:"vFloat64"` + VByte byte `json:"vByte"` + VRune rune `json:"vRune"` + VMap map[string]int `json:"vMap"` + VArray []int `json:"vArray"` + VStruct Foo `json:"vStruct"` + VStructPointer *Foo `json:"vStructPointer"` + VInterface interface{} `json:"vInterface"` + T time.Time + } +) + +@server( + jwt: Foo + group: foo/bar + anotherKey: anotherValue +) +service example-api { + @doc( + summary: "foo1" + ) + @server( + handler: fooHandler1 + anotherKey: anotherValue + ) + post /api/foo1 (SingleExample) + + @doc "foo2" + @handler fooHandler2 + get /api/foo2 (SingleExample) returns (SingleExample2) + + @handler fooHandler3 + post /api/foo3/:id returns (SingleExample2) + + @handler fooHandler4 + get /api/foo4 +} diff --git a/tools/goctl/api/parser/g4/test/apiparser_test.go b/tools/goctl/api/parser/g4/test/apiparser_test.go new file mode 100644 index 00000000..f23f8d69 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apiparser_test.go @@ -0,0 +1,369 @@ +package test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" +) + +var ( + normalApi = ` + syntax="v1" + + info ( + foo: bar + ) + + type Foo { + Bar int + } + + @server( + foo: bar + ) + service foo-api{ + @doc("foo") + @handler foo + post /foo (Foo) returns ([]int) + } +` + missDeclarationApi = ` + @server( + foo: bar + ) + service foo-api{ + @doc("foo") + @handler foo + post /foo (Foo) returns (Foo) + } + ` + + missDeclarationInArrayApi = ` + @server( + foo: bar + ) + service foo-api{ + @doc("foo") + @handler foo + post /foo returns ([]Foo) + } + ` + + missDeclarationInArrayApi2 = ` + @server( + foo: bar + ) + service foo-api{ + @doc("foo") + @handler foo + post /foo returns ([]*Foo) + } + ` + + nestedApiImport = ` + import "foo.api" + ` + + ambiguousSyntax = ` + syntax = "v2" + ` + + ambiguousService = ` + service bar-api{ + @handler foo + post /foo + } + ` + duplicateHandler = ` + service bar-api{ + @handler foo + post /foo + } + ` + + duplicateRoute = ` + service bar-api{ + @handler bar + post /foo + } + ` + + duplicateType = ` + type Foo int + ` +) + +func TestApiParser(t *testing.T) { + t.Run("missDeclarationApi", func(t *testing.T) { + _, err := parser.ParseContent(missDeclarationApi) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("missDeclarationApi", func(t *testing.T) { + _, err := parser.ParseContent(missDeclarationInArrayApi) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("missDeclarationApi", func(t *testing.T) { + _, err := parser.ParseContent(missDeclarationInArrayApi2) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("nestedImport", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "foo.api") + err := ioutil.WriteFile(file, []byte(nestedApiImport), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(`import "%s"`, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("duplicateImport", func(t *testing.T) { + _, err := parser.ParseContent(` + import "foo.api" + import "foo.api" + `) + assert.Error(t, err) + }) + + t.Run("duplicateKey", func(t *testing.T) { + _, err := parser.ParseContent(` + info ( + foo: bar + foo: bar + ) + `) + assert.Error(t, err) + }) + + t.Run("ambiguousSyntax", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "foo.api") + err := ioutil.WriteFile(file, []byte(ambiguousSyntax), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(` + syntax = "v1" + import "%s"`, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("ambiguousSyntax", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "foo.api") + err := ioutil.WriteFile(file, []byte(ambiguousSyntax), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(` + syntax = "v1" + import "%s"`, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("ambiguousService", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "foo.api") + err := ioutil.WriteFile(file, []byte(ambiguousService), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(` + import "%s" + + service foo-api{ + @handler foo + post /foo + } + `, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("duplicateHandler", func(t *testing.T) { + _, err := parser.ParseContent(` + service foo-api{ + @handler foo + post /foo + + @handler foo + post /bar + } + `) + assert.Error(t, err) + + file := filepath.Join(t.TempDir(), "foo.api") + err = ioutil.WriteFile(file, []byte(duplicateHandler), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(` + import "%s" + service bar-api{ + @handler foo + post /foo + } + `, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("duplicateRoute", func(t *testing.T) { + _, err := parser.ParseContent(` + service foo-api{ + @handler foo + post /foo + + @handler bar + post /foo + } + `) + assert.Error(t, err) + + file := filepath.Join(t.TempDir(), "foo.api") + err = ioutil.WriteFile(file, []byte(duplicateRoute), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(` + import "%s" + service bar-api{ + @handler foo + post /foo + } + `, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("duplicateType", func(t *testing.T) { + _, err := parser.ParseContent(` + type Foo int + type Foo bool + `) + assert.Error(t, err) + + file := filepath.Join(t.TempDir(), "foo.api") + err = ioutil.WriteFile(file, []byte(duplicateType), os.ModePerm) + if err != nil { + return + } + + _, err = parser.ParseContent(fmt.Sprintf(` + import "%s" + + type Foo bool + `, file)) + assert.Error(t, err) + fmt.Printf("%+v\n", err) + }) + + t.Run("normal", func(t *testing.T) { + v, err := parser.ParseContent(normalApi) + assert.Nil(t, err) + body := &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + } + + assert.True(t, v.Equal(&ast.Api{ + Syntax: &ast.SyntaxExpr{ + Syntax: ast.NewTextExpr("syntax"), + Assign: ast.NewTextExpr("="), + Version: ast.NewTextExpr(`"v1"`), + }, + Info: &ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo"), + Value: ast.NewTextExpr("bar"), + }, + }, + }, + Type: []ast.TypeExpr{ + &ast.TypeStruct{ + Name: ast.NewTextExpr("Foo"), + LBrace: ast.NewTextExpr("{"), + RBrace: ast.NewTextExpr("}"), + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("Bar"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + }, + }, + }, + }, + Service: []*ast.Service{ + { + AtServer: &ast.AtServer{ + AtServerToken: ast.NewTextExpr("@server"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo"), + Value: ast.NewTextExpr("bar"), + }, + }, + }, + ServiceApi: &ast.ServiceApi{ + ServiceToken: ast.NewTextExpr("service"), + Name: ast.NewTextExpr("foo-api"), + Lbrace: ast.NewTextExpr("{"), + Rbrace: ast.NewTextExpr("}"), + ServiceRoute: []*ast.ServiceRoute{ + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo"), + Req: body, + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Array{ + ArrayExpr: ast.NewTextExpr("[]int"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Literal{Literal: ast.NewTextExpr("int")}, + }, + }, + }, + }, + }, + }, + }, + }, + })) + }) +} diff --git a/tools/goctl/api/parser/g4/test/apis/empty.api b/tools/goctl/api/parser/g4/test/apis/empty.api new file mode 100644 index 00000000..e69de29b diff --git a/tools/goctl/api/parser/g4/test/apis/example.api b/tools/goctl/api/parser/g4/test/apis/example.api new file mode 100644 index 00000000..50e1746c --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apis/example.api @@ -0,0 +1,72 @@ +// syntax: specify the api syntax version, +// through this version can be a good control +// api syntax upgrade incompatibility issues +syntax = "v1" + +// Info block is a key-value pair description body, +// you can add some descriptions of the current api +// file through this description body, you can add +// any key-value pair, which does not participate in the api generation +info( + title: sample of api + desc: "you can add a newline +description by quotes" + author: songmeizi + anyAnotherKey: anyTnotherValue +) + +// The structure in the api evolved from the structure of golang, +// and it is also reserved to support the structure of golang. + +// a golang structure +type Foo struct{ + Foo int +} + +// api structure +type Bar { + Bar int +} + +// structure group +type ( + FooBar { + Foo int + Bar bool + } +) + +// Like the info block, @server can define any key-value pair. +// The difference is that @server is a description of the service +// block or route, which will participate in the api file generation. +// There are several important keys that need to be understood, +// which have special meanings. The jwt key is to declare that code +// generation needs to include jwt authentication logic. The group key +// is to declare that the files generated by the code need to be grouped +// according to the value corresponding to the group. The handler key +// determines the handler in golang. Layer file logic generation +@server( + jwt: Auth + group: foo + anyAnotherKey: anyTnotherValue +) + +// service block is the description of the api service, +// including @doc block, @handler and api routing information +service foo-api { + // shortening doc declaration + @doc("foo") + // shortening handler declaration + @handler foo + // route + get /foo (Foo) returns (Bar) + + + @doc( + summary: foo + ) + @server( + handler: bar + ) + post /bar (Foo) +} diff --git a/tools/goctl/api/parser/g4/test/apis/info.api b/tools/goctl/api/parser/g4/test/apis/info.api new file mode 100644 index 00000000..c1c5438e --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apis/info.api @@ -0,0 +1,6 @@ +info( + author: songmeizi + desc: "the sample of +info" + date: "2020-01-06" +) diff --git a/tools/goctl/api/parser/g4/test/apis/service.api b/tools/goctl/api/parser/g4/test/apis/service.api new file mode 100644 index 00000000..c725f478 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apis/service.api @@ -0,0 +1,29 @@ +type Foo {} + +@server( + foo: foo + bar: "bar" + fooBar: "foo +bar" +) +service foo-api { + @doc("foo") + @handler foo + get /foo (Foo) returns (Foo) + @handler bar + post /foo (Foo) + @handler fooBar + post /foo/bar + @server( + handler: getFoo + ) + post /foo/:id returns(Foo) +} + +service foo-api { + @doc( + summary:"post foo" + ) + @handler postFoo + post /foo/bar/post (Foo) +} diff --git a/tools/goctl/api/parser/g4/test/apis/syntax.api b/tools/goctl/api/parser/g4/test/apis/syntax.api new file mode 100644 index 00000000..c32253f5 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apis/syntax.api @@ -0,0 +1 @@ +syntax = "v1" \ No newline at end of file diff --git a/tools/goctl/api/parser/g4/test/apis/test.api b/tools/goctl/api/parser/g4/test/apis/test.api new file mode 100644 index 00000000..3061ed39 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apis/test.api @@ -0,0 +1,111 @@ +// syntax doc +syntax = "v1" // syntax comment + +// import doc +import "foo.api" // import comment + +import( + // import group doc + "bar.api" // import group comment +) + +// info doc +info(// info comment + // author doc + author: "songmeizi" // author comment + // date doc + date: 2020-01-04 // date comment + // desc doc + desc: "break line + desc" // desc comment +) + + +type ( + FooBar struct{ + Foo int + } + // remove struct + Bar { + // vString + VString string `json:"vString"` + // vBool + VBool bool `json:"vBool"` + // vInt8 + VInt8 int8 `json:"vInt8"` + // vInt16 + VInt16 int16 `json:"vInt16"` + // vInt32 + VInt32 int32 `json:"vInt32"` + // vInt64 + VInt64 int64 `json:"vInt64"` + // vInt + VInt int `json:"vInt"` + // vUInt8 + VUInt8 uint8 `json:"vUInt8"` + // vUInt16 + VUInt16 uint16 `json:"vUInt16"` + // vUInt32 + VUInt32 uint32 `json:"vUInt32"` + // vUInt64 + VUInt64 uint64 `json:"vUInt64"` + // vFloat32 + VFloat32 float32 `json:"vFloat32"` + // vFloat64 + VFloat64 float64 `json:"vFloat64"` + // vByte + VByte byte `json:"vByte"` + // vRune + VRune rune `json:"vRune"` + // vMap + VMap map[string]int `json:"vMap"` + // vArray + VArray []int `json:"vArray"` + // vStruct + VStruct FooBar `json:"vStruct"` + // vStructPointer + VStructPointer *FooBar `json:"vStructPointer"` + // vInterface + VInterface interface{} `json:"vInterface"` + // inline + FooBar + } +) + +@server( + host: 0.0.0.0 + port: 8080 + annotation: "break line + desc" +) +service foo-api{ + @doc("foo") + @handler postFoo + // foo + post /foo (FooBar) returns (FooBar) + + @doc( + summary: bar + ) + @server( + handler: postBar + ) + post /bar (FooBar) + + @doc("foobar") + @handler postFooBar + /** + * httpmethod: post + * path: /foo/bar + * reply: FooBar + */ + post /foo/bar returns (FooBar) + + @doc("barfoo") + @handler postBarFoo + post /bar/foo // post:/bar/foo + + @doc("barfoo") + @handler getBarFoo + get /bar/foo returns (FooBar) +} \ No newline at end of file diff --git a/tools/goctl/api/parser/g4/test/apis/types.api b/tools/goctl/api/parser/g4/test/apis/types.api new file mode 100644 index 00000000..9f68db12 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/apis/types.api @@ -0,0 +1,23 @@ +type Foo{ +} + +type Bar struct{ +} + +type FooBar { + Foo int + Bar bool + Map map[string]int + Map1 map[string]Bar + Map2 map[string]*Bar + Map3 map[string][]int + Map4 map[string][]Bar + Map5 map[string][]*Bar + Map6 map[string]map[string]int + Array []int + Array1 []*Bar + Array2 []Bar + Pointer *Bar + Bar +} + diff --git a/tools/goctl/api/parser/g4/test/ast_test.go b/tools/goctl/api/parser/g4/test/ast_test.go new file mode 100644 index 00000000..1884b92f --- /dev/null +++ b/tools/goctl/api/parser/g4/test/ast_test.go @@ -0,0 +1,457 @@ +package test + +import ( + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +var parser = ast.NewParser(ast.WithParserPrefix("test.api"), ast.WithParserDebug()) + +func TestApi(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.Api().Accept(visitor) + } + content, err := ioutil.ReadFile("./apis/test.api") + assert.Nil(t, err) + + v, err := parser.Accept(fn, string(content)) + assert.Nil(t, err) + api := v.(*ast.Api) + body := &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("FooBar")}, + } + + returns := ast.NewTextExpr("returns") + assert.True(t, api.Equal(&ast.Api{ + Syntax: &ast.SyntaxExpr{ + Syntax: ast.NewTextExpr("syntax"), + Assign: ast.NewTextExpr("="), + Version: ast.NewTextExpr(`"v1"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// syntax doc"), + }, + CommentExpr: ast.NewTextExpr("// syntax comment"), + }, + Import: []*ast.ImportExpr{ + { + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"foo.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// import doc"), + }, + CommentExpr: ast.NewTextExpr("// import comment"), + }, + { + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"bar.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// import group doc"), + }, + CommentExpr: ast.NewTextExpr("// import group comment"), + }, + }, + Info: &ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("author"), + Value: ast.NewTextExpr(`"songmeizi"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// author doc"), + }, + CommentExpr: ast.NewTextExpr("// author comment"), + }, + { + Key: ast.NewTextExpr("date"), + Value: ast.NewTextExpr(`2020-01-04`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// date doc"), + }, + CommentExpr: ast.NewTextExpr("// date comment"), + }, + { + Key: ast.NewTextExpr("desc"), + Value: ast.NewTextExpr(`"break line + desc"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// desc doc"), + }, + CommentExpr: ast.NewTextExpr("// desc comment"), + }, + }, + }, + Type: []ast.TypeExpr{ + &ast.TypeStruct{ + Name: ast.NewTextExpr("FooBar"), + Struct: ast.NewTextExpr("struct"), + LBrace: ast.NewTextExpr("{"), + RBrace: ast.NewTextExpr("}"), + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("Foo"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + }, + }, + }, + &ast.TypeStruct{ + Name: ast.NewTextExpr("Bar"), + LBrace: ast.NewTextExpr("{"), + RBrace: ast.NewTextExpr("}"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// remove struct"), + }, + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("VString"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("string")}, + Tag: ast.NewTextExpr("`json:\"vString\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vString"), + }, + }, + { + Name: ast.NewTextExpr("VBool"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("bool")}, + Tag: ast.NewTextExpr("`json:\"vBool\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vBool"), + }, + }, + { + Name: ast.NewTextExpr("VInt8"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int8")}, + Tag: ast.NewTextExpr("`json:\"vInt8\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vInt8"), + }, + }, + { + Name: ast.NewTextExpr("VInt16"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int16")}, + Tag: ast.NewTextExpr("`json:\"vInt16\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vInt16"), + }, + }, + { + Name: ast.NewTextExpr("VInt32"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int32")}, + Tag: ast.NewTextExpr("`json:\"vInt32\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vInt32"), + }, + }, + { + Name: ast.NewTextExpr("VInt64"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int64")}, + Tag: ast.NewTextExpr("`json:\"vInt64\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vInt64"), + }, + }, + { + Name: ast.NewTextExpr("VInt"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + Tag: ast.NewTextExpr("`json:\"vInt\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vInt"), + }, + }, + { + Name: ast.NewTextExpr("VUInt8"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("uint8")}, + Tag: ast.NewTextExpr("`json:\"vUInt8\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vUInt8"), + }, + }, + { + Name: ast.NewTextExpr("VUInt16"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("uint16")}, + Tag: ast.NewTextExpr("`json:\"vUInt16\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vUInt16"), + }, + }, + { + Name: ast.NewTextExpr("VUInt32"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("uint32")}, + Tag: ast.NewTextExpr("`json:\"vUInt32\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vUInt32"), + }, + }, + { + Name: ast.NewTextExpr("VUInt64"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("uint64")}, + Tag: ast.NewTextExpr("`json:\"vUInt64\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vUInt64"), + }, + }, + { + Name: ast.NewTextExpr("VFloat32"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("float32")}, + Tag: ast.NewTextExpr("`json:\"vFloat32\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vFloat32"), + }, + }, + { + Name: ast.NewTextExpr("VFloat64"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("float64")}, + Tag: ast.NewTextExpr("`json:\"vFloat64\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vFloat64"), + }, + }, + { + Name: ast.NewTextExpr("VByte"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("byte")}, + Tag: ast.NewTextExpr("`json:\"vByte\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vByte"), + }, + }, + { + Name: ast.NewTextExpr("VRune"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("rune")}, + Tag: ast.NewTextExpr("`json:\"vRune\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vRune"), + }, + }, + { + Name: ast.NewTextExpr("VMap"), + DataType: &ast.Map{ + MapExpr: ast.NewTextExpr("map[string]int"), + Map: ast.NewTextExpr("map"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Key: ast.NewTextExpr("string"), + Value: &ast.Literal{Literal: ast.NewTextExpr("int")}, + }, + Tag: ast.NewTextExpr("`json:\"vMap\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vMap"), + }, + }, + { + Name: ast.NewTextExpr("VArray"), + DataType: &ast.Array{ + ArrayExpr: ast.NewTextExpr("[]int"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Literal{Literal: ast.NewTextExpr("int")}, + }, + Tag: ast.NewTextExpr("`json:\"vArray\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vArray"), + }, + }, + { + Name: ast.NewTextExpr("VStruct"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("FooBar")}, + Tag: ast.NewTextExpr("`json:\"vStruct\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vStruct"), + }, + }, + { + Name: ast.NewTextExpr("VStructPointer"), + DataType: &ast.Pointer{ + PointerExpr: ast.NewTextExpr("*FooBar"), + Star: ast.NewTextExpr("*"), + Name: ast.NewTextExpr("FooBar"), + }, + Tag: ast.NewTextExpr("`json:\"vStructPointer\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vStructPointer"), + }, + }, + { + Name: ast.NewTextExpr("VInterface"), + DataType: &ast.Interface{Literal: ast.NewTextExpr("interface{}")}, + Tag: ast.NewTextExpr("`json:\"vInterface\"`"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// vInterface"), + }, + }, + { + IsAnonymous: true, + DataType: &ast.Literal{Literal: ast.NewTextExpr("FooBar")}, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// inline"), + }, + }, + }, + }, + }, + Service: []*ast.Service{ + { + AtServer: &ast.AtServer{ + AtServerToken: ast.NewTextExpr("@server"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("host"), + Value: ast.NewTextExpr("0.0.0.0"), + }, + { + Key: ast.NewTextExpr("port"), + Value: ast.NewTextExpr("8080"), + }, + { + Key: ast.NewTextExpr("annotation"), + Value: ast.NewTextExpr(`"break line + desc"`), + }, + }, + }, + ServiceApi: &ast.ServiceApi{ + ServiceToken: ast.NewTextExpr("service"), + Name: ast.NewTextExpr("foo-api"), + Lbrace: ast.NewTextExpr("{"), + Rbrace: ast.NewTextExpr("}"), + ServiceRoute: []*ast.ServiceRoute{ + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("postFoo"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo"), + Req: body, + ReturnToken: returns, + Reply: body, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + }, + }, + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("summary"), + Value: ast.NewTextExpr("bar"), + }, + }, + }, + AtServer: &ast.AtServer{ + AtServerToken: ast.NewTextExpr("@server"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("handler"), + Value: ast.NewTextExpr("postBar"), + }, + }, + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/bar"), + Req: body, + }, + }, + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foobar"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("postFooBar"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/bar"), + ReturnToken: returns, + Reply: body, + DocExpr: []ast.Expr{ + ast.NewTextExpr(`/** + * httpmethod: post + * path: /foo/bar + * reply: FooBar + */`), + }, + }, + }, + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"barfoo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("postBarFoo"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/bar/foo"), + ReturnToken: returns, + Reply: body, + CommentExpr: ast.NewTextExpr("// post:/bar/foo"), + }, + }, + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"barfoo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("getBarFoo"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("get"), + Path: ast.NewTextExpr("/bar/foo"), + ReturnToken: returns, + Reply: body, + }, + }, + }, + }, + }, + }, + })) +} + +func TestApiSyntax(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.Api().Accept(visitor) + } + parser.Accept(fn, ` + // doc 1 + // doc 2 + syntax = "v1" // comment 1 + // comment 2 + import "foo.api" + `) +} diff --git a/tools/goctl/api/parser/g4/test/grammar_test.go b/tools/goctl/api/parser/g4/test/grammar_test.go new file mode 100644 index 00000000..4202a855 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/grammar_test.go @@ -0,0 +1,25 @@ +package test + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var files = []string{ + "example", + "empty", + "syntax", + "info", + "types", + "service", +} + +func TestGrammar(t *testing.T) { + for _, file := range files { + t.Run(file, func(t *testing.T) { + _, err := parser.Parse("./apis/" + file + ".api") + assert.Nil(t, err) + }) + } +} diff --git a/tools/goctl/api/parser/g4/test/import_test.go b/tools/goctl/api/parser/g4/test/import_test.go new file mode 100644 index 00000000..e136d7b5 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/import_test.go @@ -0,0 +1,143 @@ +package test + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +var importAccept = func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.ImportSpec().Accept(visitor) +} + +func TestImport(t *testing.T) { + t.Run("matched", func(t *testing.T) { + v, err := parser.Accept(importAccept, `import "foo.api"`) + assert.Nil(t, err) + + list := v.([]*ast.ImportExpr) + for _, each := range list { + assert.True(t, each.Equal(&ast.ImportExpr{ + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"foo.api"`), + })) + } + }) + + t.Run("matched block", func(t *testing.T) { + v, err := parser.Accept(importAccept, ` + import ( + /**foo*/ + "foo.api" + /**bar*/ + "bar.api" + /**foobar*/ + "foo/bar.api"/**foobar*/ + ) + `) + assert.Nil(t, err) + + list := v.([]*ast.ImportExpr) + expected := []*ast.ImportExpr{ + { + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"foo.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**foo*/"), + }, + }, + { + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"bar.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**bar*/"), + }, + }, + { + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"foo/bar.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**foobar*/"), + }, + CommentExpr: ast.NewTextExpr("/**foobar*/"), + }, + } + + sort.Slice(list, func(i, j int) bool { + return list[i].Value.Line() < list[j].Value.Line() + }) + sort.Slice(expected, func(i, j int) bool { + return expected[i].Value.Line() < expected[j].Value.Line() + }) + + assert.True(t, len(list) == len(expected)) + for index, each := range list { + assert.True(t, each.Equal(expected[index])) + } + }) + + t.Run("matched doc", func(t *testing.T) { + v, err := parser.Accept(importAccept, ` + /**doc*/ + import "foo.api" /**line doc*/`) + assert.Nil(t, err) + + list := v.([]*ast.ImportExpr) + for _, each := range list { + assert.True(t, each.Equal(&ast.ImportExpr{ + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"foo.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**doc*/"), + }, + CommentExpr: ast.NewTextExpr("/**line doc*/"), + })) + } + }) + + t.Run("matched comment", func(t *testing.T) { + v, err := parser.Accept(importAccept, ` + // comment block + import "foo.api" // line comment`) + assert.Nil(t, err) + + list := v.([]*ast.ImportExpr) + for _, each := range list { + assert.True(t, each.Equal(&ast.ImportExpr{ + Import: ast.NewTextExpr("import"), + Value: ast.NewTextExpr(`"foo.api"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// comment block"), + }, + CommentExpr: ast.NewTextExpr("// line comment"), + })) + } + }) + + t.Run("mismatched import", func(t *testing.T) { + _, err := parser.Accept(importAccept, ` + "foo.api"`) + assert.Error(t, err) + + _, err = parser.Accept(importAccept, ` + impor "foo.api"`) + assert.Error(t, err) + }) + + t.Run("mismatched value", func(t *testing.T) { + _, err := parser.Accept(importAccept, ` + import "foo"`) + assert.Error(t, err) + + _, err = parser.Accept(importAccept, ` + import ""`) + assert.Error(t, err) + + _, err = parser.Accept(importAccept, ` + import `) + assert.Error(t, err) + }) +} diff --git a/tools/goctl/api/parser/g4/test/info_test.go b/tools/goctl/api/parser/g4/test/info_test.go new file mode 100644 index 00000000..52f3a771 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/info_test.go @@ -0,0 +1,186 @@ +package test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +var infoAccept = func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.InfoSpec().Accept(visitor) +} + +func TestInfo(t *testing.T) { + t.Run("matched", func(t *testing.T) { + v, err := parser.Accept(infoAccept, ` + info( + title: foo + ) + `) + + assert.Nil(t, err) + info := v.(*ast.InfoExpr) + assert.True(t, info.Equal(&ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("title"), + Value: ast.NewTextExpr("foo"), + }, + }, + })) + + v, err = parser.Accept(infoAccept, ` + info( + title: 中文(bar) + ) + `) + assert.Nil(t, err) + info = v.(*ast.InfoExpr) + assert.True(t, info.Equal(&ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("title"), + Value: ast.NewTextExpr("中文(bar)"), + }, + }, + })) + + v, err = parser.Accept(infoAccept, ` + info( + foo: "new +line" + ) + `) + assert.Nil(t, err) + info = v.(*ast.InfoExpr) + assert.True(t, info.Equal(&ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo"), + Value: ast.NewTextExpr(`"new +line"`), + }, + }, + })) + }) + + t.Run("matched doc", func(t *testing.T) { + v, err := parser.Accept(infoAccept, ` + // doc + info( // comment + // foo + title: foo // bar + ) + `) + assert.Nil(t, err) + info := v.(*ast.InfoExpr) + assert.True(t, info.Equal(&ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("title"), + Value: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + }, + })) + + v, err = parser.Accept(infoAccept, ` + /**doc block*/ + info( /**line block*/ + /**foo*/ + title: foo /*bar**/ + ) + `) + assert.Nil(t, err) + info = v.(*ast.InfoExpr) + assert.True(t, info.Equal(&ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("title"), + Value: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**foo*/"), + }, + CommentExpr: ast.NewTextExpr("/*bar**/"), + }, + }, + })) + + v, err = parser.Accept(infoAccept, ` + info( + // doc + title: foo + // doc + author: bar + ) + `) + assert.Nil(t, err) + info = v.(*ast.InfoExpr) + assert.True(t, info.Equal(&ast.InfoExpr{ + Info: ast.NewTextExpr("info"), + Kvs: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("title"), + Value: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// doc"), + }, + }, + { + Key: ast.NewTextExpr("author"), + Value: ast.NewTextExpr("bar"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// doc"), + }, + }, + }, + })) + + }) + + t.Run("mismatched", func(t *testing.T) { + _, err := parser.Accept(infoAccept, ` + info( + title + ) + `) + assert.Error(t, err) + + _, err = parser.Accept(infoAccept, ` + info( + :title + ) + `) + assert.Error(t, err) + + _, err = parser.Accept(infoAccept, ` + info( + foo bar + ) + `) + assert.Error(t, err) + + _, err = parser.Accept(infoAccept, ` + info( + foo : new +line + ) + `) + assert.Error(t, err) + + _, err = parser.Accept(infoAccept, ` + info() + `) + assert.Error(t, err) + }) +} diff --git a/tools/goctl/api/parser/g4/test/service_test.go b/tools/goctl/api/parser/g4/test/service_test.go new file mode 100644 index 00000000..f69c8d10 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/service_test.go @@ -0,0 +1,686 @@ +package test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +func TestBody(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.Body().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, `(Foo)`) + assert.Nil(t, err) + body := v.(*ast.Body) + assert.True(t, body.Equal(&ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `(var)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `()`) + assert.Nil(t, err) + }) +} + +func TestRoute(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.Route().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, `post /foo/foo-bar/:bar (Foo) returns (Bar)`) + assert.Nil(t, err) + route := v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + Req: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + }, + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + })) + + v, err = parser.Accept(fn, `post /foo/foo-bar/:bar (Foo)`) + assert.Nil(t, err) + route = v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + Req: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + }, + })) + + v, err = parser.Accept(fn, `post /foo/foo-bar/:bar returns (Bar)`) + assert.Nil(t, err) + route = v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + })) + + v, err = parser.Accept(fn, `post /foo/foo-bar/:bar returns ([]Bar)`) + assert.Nil(t, err) + route = v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Array{ + ArrayExpr: ast.NewTextExpr("[]Bar"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + }, + })) + + v, err = parser.Accept(fn, `post /foo/foo-bar/:bar returns ([]*Bar)`) + assert.Nil(t, err) + route = v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Array{ + ArrayExpr: ast.NewTextExpr("[]*Bar"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Pointer{ + PointerExpr: ast.NewTextExpr("*Bar"), + Star: ast.NewTextExpr("*"), + Name: ast.NewTextExpr("Bar"), + }}, + }, + })) + + v, err = parser.Accept(fn, `post /foo/foo-bar/:bar`) + assert.Nil(t, err) + route = v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + })) + + v, err = parser.Accept(fn, ` + // foo + post /foo/foo-bar/:bar // bar`) + assert.Nil(t, err) + route = v.(*ast.Route) + assert.True(t, route.Equal(&ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo/foo-bar/:bar"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `posts /foo`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `gets /foo`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `post /foo/:`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `post /foo/`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `post foo/bar`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `post /foo/bar return (Bar)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` /foo/bar returns (Bar)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` post returns (Bar)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` post /foo/bar returns (int)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` post /foo/bar returns (*int)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` post /foo/bar returns ([]var)`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` post /foo/bar returns (const)`) + assert.Error(t, err) + }) +} + +func TestAtHandler(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.AtHandler().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, `@handler foo`) + assert.Nil(t, err) + atHandler := v.(*ast.AtHandler) + assert.True(t, atHandler.Equal(&ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + })) + + v, err = parser.Accept(fn, ` + // foo + @handler foo // bar`) + assert.Nil(t, err) + atHandler = v.(*ast.AtHandler) + assert.True(t, atHandler.Equal(&ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, ``) + assert.Error(t, err) + + _, err = parser.Accept(fn, `@handler`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `@handler "foo"`) + assert.Error(t, err) + }) + +} + +func TestAtDoc(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.AtDoc().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, `@doc "foo"`) + assert.Nil(t, err) + atDoc := v.(*ast.AtDoc) + assert.True(t, atDoc.Equal(&ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + LineDoc: ast.NewTextExpr(`"foo"`), + })) + + v, err = parser.Accept(fn, `@doc("foo")`) + assert.Nil(t, err) + atDoc = v.(*ast.AtDoc) + assert.True(t, atDoc.Equal(&ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + })) + + v, err = parser.Accept(fn, `@doc( + foo: bar + )`) + assert.Nil(t, err) + atDoc = v.(*ast.AtDoc) + assert.True(t, atDoc.Equal(&ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo"), + Value: ast.NewTextExpr("bar"), + }, + }, + })) + + v, err = parser.Accept(fn, `@doc( + // foo + foo: bar // bar + )`) + assert.Nil(t, err) + atDoc = v.(*ast.AtDoc) + assert.True(t, atDoc.Equal(&ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo"), + Value: ast.NewTextExpr("bar"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + }, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `@doc("foo"`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `@doc "foo")`) + assert.Error(t, err) + }) +} + +func TestServiceRoute(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.ServiceRoute().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, ` + @doc("foo") + // foo/bar + // foo + @handler foo // bar + // foo/bar + // foo + post /foo (Foo) returns (Bar) // bar + `) + assert.Nil(t, err) + sr := v.(*ast.ServiceRoute) + assert.True(t, sr.Equal(&ast.ServiceRoute{ + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo"), + Req: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + }, + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `post /foo (Foo) returns (Bar) // bar`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `@handler foo`) + assert.Error(t, err) + }) +} + +func TestServiceApi(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.ServiceApi().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, ` + service foo-api{ + @doc("foo") + // foo/bar + // foo + @handler foo // bar + // foo/bar + // foo + post /foo (Foo) returns (Bar) // bar + } + `) + assert.Nil(t, err) + api := v.(*ast.ServiceApi) + assert.True(t, api.Equal(&ast.ServiceApi{ + ServiceToken: ast.NewTextExpr("service"), + Name: ast.NewTextExpr("foo-api"), + Lbrace: ast.NewTextExpr("{"), + Rbrace: ast.NewTextExpr("}"), + ServiceRoute: []*ast.ServiceRoute{ + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo"), + Req: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + }, + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + }, + }, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `services foo-api{}`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `service foo-api{`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `service foo-api{ + post /foo + }`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `service foo-api{ + @handler foo + }`) + assert.Error(t, err) + }) +} + +func TestAtServer(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.AtServer().Accept(v) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, ` + @server( + // foo + foo1: bar1 // bar + // foo + foo2: "bar2" // bar + /**foo*/ + foo3: "foo + bar" /**bar*/ + ) + `) + assert.Nil(t, err) + as := v.(*ast.AtServer) + assert.True(t, as.Equal(&ast.AtServer{ + AtServerToken: ast.NewTextExpr("@server"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo1"), + Value: ast.NewTextExpr("bar1"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + { + Key: ast.NewTextExpr("foo2"), + Value: ast.NewTextExpr(`"bar2"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + { + Key: ast.NewTextExpr("foo3"), + Value: ast.NewTextExpr(`"foo + bar"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**foo*/"), + }, + CommentExpr: ast.NewTextExpr("/**bar*/"), + }, + }, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `server ( + foo:bar + )`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `@server ()`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `@server ( + foo: bar + `) + assert.Error(t, err) + }) +} + +func TestServiceSpec(t *testing.T) { + fn := func(p *api.ApiParserParser, v *ast.ApiVisitor) interface{} { + return p.ServiceSpec().Accept(v) + } + t.Run("normal", func(t *testing.T) { + _, err := parser.Accept(fn, ` + service foo-api{ + @handler foo + post /foo returns ([]int) + } + `) + assert.Nil(t, err) + + v, err := parser.Accept(fn, ` + @server( + // foo + foo1: bar1 // bar + // foo + foo2: "bar2" // bar + /**foo*/ + foo3: "foo + bar" /**bar*/ + ) + service foo-api{ + @doc("foo") + // foo/bar + // foo + @handler foo // bar + // foo/bar + // foo + post /foo (Foo) returns (Bar) // bar + } + `) + assert.Nil(t, err) + service := v.(*ast.Service) + assert.True(t, service.Equal(&ast.Service{ + AtServer: &ast.AtServer{ + AtServerToken: ast.NewTextExpr("@server"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Kv: []*ast.KvExpr{ + { + Key: ast.NewTextExpr("foo1"), + Value: ast.NewTextExpr("bar1"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + { + Key: ast.NewTextExpr("foo2"), + Value: ast.NewTextExpr(`"bar2"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + { + Key: ast.NewTextExpr("foo3"), + Value: ast.NewTextExpr(`"foo + bar"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("/**foo*/"), + }, + CommentExpr: ast.NewTextExpr("/**bar*/"), + }, + }, + }, + ServiceApi: &ast.ServiceApi{ + ServiceToken: ast.NewTextExpr("service"), + Name: ast.NewTextExpr("foo-api"), + Lbrace: ast.NewTextExpr("{"), + Rbrace: ast.NewTextExpr("}"), + ServiceRoute: []*ast.ServiceRoute{ + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo"), + Req: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + }, + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + }, + }, + }, + })) + + v, err = parser.Accept(fn, ` + service foo-api{ + @doc("foo") + // foo/bar + // foo + @handler foo // bar + // foo/bar + // foo + post /foo (Foo) returns (Bar) // bar + } + `) + assert.Nil(t, err) + service = v.(*ast.Service) + assert.True(t, service.Equal(&ast.Service{ + ServiceApi: &ast.ServiceApi{ + ServiceToken: ast.NewTextExpr("service"), + Name: ast.NewTextExpr("foo-api"), + Lbrace: ast.NewTextExpr("{"), + Rbrace: ast.NewTextExpr("}"), + ServiceRoute: []*ast.ServiceRoute{ + { + AtDoc: &ast.AtDoc{ + AtDocToken: ast.NewTextExpr("@doc"), + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + LineDoc: ast.NewTextExpr(`"foo"`), + }, + AtHandler: &ast.AtHandler{ + AtHandlerToken: ast.NewTextExpr("@handler"), + Name: ast.NewTextExpr("foo"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + Route: &ast.Route{ + Method: ast.NewTextExpr("post"), + Path: ast.NewTextExpr("/foo"), + Req: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")}, + }, + ReturnToken: ast.NewTextExpr("returns"), + Reply: &ast.Body{ + Lp: ast.NewTextExpr("("), + Rp: ast.NewTextExpr(")"), + Name: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// foo"), + }, + CommentExpr: ast.NewTextExpr("// bar"), + }, + }, + }, + }, + })) + }) +} diff --git a/tools/goctl/api/parser/g4/test/syntax_test.go b/tools/goctl/api/parser/g4/test/syntax_test.go new file mode 100644 index 00000000..67349e83 --- /dev/null +++ b/tools/goctl/api/parser/g4/test/syntax_test.go @@ -0,0 +1,75 @@ +package test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +var syntaxAccept = func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.SyntaxLit().Accept(visitor) +} + +func TestSyntax(t *testing.T) { + t.Run("matched", func(t *testing.T) { + v, err := parser.Accept(syntaxAccept, `syntax = "v1"`) + assert.Nil(t, err) + + syntax := v.(*ast.SyntaxExpr) + assert.True(t, syntax.Equal(&ast.SyntaxExpr{ + Syntax: ast.NewTextExpr("syntax"), + Assign: ast.NewTextExpr("="), + Version: ast.NewTextExpr(`"v1"`), + })) + }) + + t.Run("expecting syntax", func(t *testing.T) { + _, err := parser.Accept(syntaxAccept, `= "v1"`) + assert.Error(t, err) + + _, err = parser.Accept(syntaxAccept, `syn = "v1"`) + assert.Error(t, err) + }) + + t.Run("missing assign", func(t *testing.T) { + _, err := parser.Accept(syntaxAccept, `syntax "v1"`) + assert.Error(t, err) + + _, err = parser.Accept(syntaxAccept, `syntax + "v1"`) + assert.Error(t, err) + }) + + t.Run("mismatched version", func(t *testing.T) { + _, err := parser.Accept(syntaxAccept, `syntax="v0"`) + assert.Error(t, err) + + _, err = parser.Accept(syntaxAccept, `syntax = "v1a"`) + assert.Error(t, err) + + _, err = parser.Accept(syntaxAccept, `syntax = "vv1"`) + assert.Error(t, err) + + _, err = parser.Accept(syntaxAccept, `syntax = "1"`) + assert.Error(t, err) + }) + + t.Run("with comment", func(t *testing.T) { + v, err := parser.Accept(syntaxAccept, ` + // doc + syntax="v1" // line comment`) + assert.Nil(t, err) + + syntax := v.(*ast.SyntaxExpr) + assert.True(t, syntax.Equal(&ast.SyntaxExpr{ + Syntax: ast.NewTextExpr("syntax"), + Assign: ast.NewTextExpr("="), + Version: ast.NewTextExpr(`"v1"`), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// doc"), + }, + CommentExpr: ast.NewTextExpr("// line comment"), + })) + }) +} diff --git a/tools/goctl/api/parser/g4/test/type_test.go b/tools/goctl/api/parser/g4/test/type_test.go new file mode 100644 index 00000000..9d8b457c --- /dev/null +++ b/tools/goctl/api/parser/g4/test/type_test.go @@ -0,0 +1,467 @@ +package test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" +) + +var fieldAccept = func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.Field().Accept(visitor) +} + +func TestField(t *testing.T) { + t.Run("anonymous", func(t *testing.T) { + v, err := parser.Accept(fieldAccept, `User`) + assert.Nil(t, err) + f := v.(*ast.TypeField) + assert.True(t, f.Equal(&ast.TypeField{ + IsAnonymous: true, + DataType: &ast.Literal{Literal: ast.NewTextExpr("User")}, + })) + + v, err = parser.Accept(fieldAccept, `*User`) + assert.Nil(t, err) + f = v.(*ast.TypeField) + assert.True(t, f.Equal(&ast.TypeField{ + IsAnonymous: true, + DataType: &ast.Pointer{ + PointerExpr: ast.NewTextExpr("*User"), + Star: ast.NewTextExpr("*"), + Name: ast.NewTextExpr("User"), + }, + })) + + v, err = parser.Accept(fieldAccept, ` + // anonymous user + *User // pointer type`) + assert.Nil(t, err) + f = v.(*ast.TypeField) + assert.True(t, f.Equal(&ast.TypeField{ + IsAnonymous: true, + DataType: &ast.Pointer{ + PointerExpr: ast.NewTextExpr("*User"), + Star: ast.NewTextExpr("*"), + Name: ast.NewTextExpr("User"), + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// anonymous user"), + }, + CommentExpr: ast.NewTextExpr("// pointer type"), + })) + + _, err = parser.Accept(fieldAccept, `interface`) + assert.Error(t, err) + + _, err = parser.Accept(fieldAccept, `map`) + assert.Error(t, err) + }) + + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fieldAccept, `User int`) + assert.Nil(t, err) + f := v.(*ast.TypeField) + assert.True(t, f.Equal(&ast.TypeField{ + Name: ast.NewTextExpr("User"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + })) + v, err = parser.Accept(fieldAccept, `Foo Bar`) + assert.Nil(t, err) + f = v.(*ast.TypeField) + assert.True(t, f.Equal(&ast.TypeField{ + Name: ast.NewTextExpr("Foo"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + })) + + v, err = parser.Accept(fieldAccept, `Foo map[int]Bar`) + assert.Nil(t, err) + f = v.(*ast.TypeField) + assert.True(t, f.Equal(&ast.TypeField{ + Name: ast.NewTextExpr("Foo"), + DataType: &ast.Map{ + MapExpr: ast.NewTextExpr("map[int]Bar"), + Map: ast.NewTextExpr("map"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Key: ast.NewTextExpr("int"), + Value: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + }, + })) + }) +} + +func TestDataType_ID(t *testing.T) { + dt := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.DataType().Accept(visitor) + } + t.Run("Struct", func(t *testing.T) { + v, err := parser.Accept(dt, `Foo`) + assert.Nil(t, err) + id := v.(ast.DataType) + assert.True(t, id.Equal(&ast.Literal{Literal: ast.NewTextExpr("Foo")})) + }) + + t.Run("basic", func(t *testing.T) { + v, err := parser.Accept(dt, `int`) + assert.Nil(t, err) + id := v.(ast.DataType) + assert.True(t, id.Equal(&ast.Literal{Literal: ast.NewTextExpr("int")})) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(dt, `map`) + assert.Error(t, err) + }) +} + +func TestDataType_Map(t *testing.T) { + dt := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.MapType().Accept(visitor) + } + t.Run("basicKey", func(t *testing.T) { + v, err := parser.Accept(dt, `map[int]Bar`) + assert.Nil(t, err) + m := v.(ast.DataType) + assert.True(t, m.Equal(&ast.Map{ + MapExpr: ast.NewTextExpr("map[int]Bar"), + Map: ast.NewTextExpr("map"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Key: ast.NewTextExpr("int"), + Value: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(dt, `map[var]Bar`) + assert.Error(t, err) + + _, err = parser.Accept(dt, `map[*User]Bar`) + assert.Error(t, err) + + _, err = parser.Accept(dt, `map[User]Bar`) + assert.Error(t, err) + }) +} + +func TestDataType_Array(t *testing.T) { + dt := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.ArrayType().Accept(visitor) + } + t.Run("basic", func(t *testing.T) { + v, err := parser.Accept(dt, `[]int`) + assert.Nil(t, err) + array := v.(ast.DataType) + assert.True(t, array.Equal(&ast.Array{ + ArrayExpr: ast.NewTextExpr("[]int"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Literal{Literal: ast.NewTextExpr("int")}, + })) + }) + + t.Run("pointer", func(t *testing.T) { + v, err := parser.Accept(dt, `[]*User`) + assert.Nil(t, err) + array := v.(ast.DataType) + assert.True(t, array.Equal(&ast.Array{ + ArrayExpr: ast.NewTextExpr("[]*User"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Pointer{ + PointerExpr: ast.NewTextExpr("*User"), + Star: ast.NewTextExpr("*"), + Name: ast.NewTextExpr("User"), + }, + })) + }) + + t.Run("interface{}", func(t *testing.T) { + v, err := parser.Accept(dt, `[]interface{}`) + assert.Nil(t, err) + array := v.(ast.DataType) + assert.True(t, array.Equal(&ast.Array{ + ArrayExpr: ast.NewTextExpr("[]interface{}"), + LBrack: ast.NewTextExpr("["), + RBrack: ast.NewTextExpr("]"), + Literal: &ast.Interface{Literal: ast.NewTextExpr("interface{}")}, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(dt, `[]var`) + assert.Error(t, err) + + _, err = parser.Accept(dt, `[]interface`) + assert.Error(t, err) + }) +} + +func TestDataType_Interface(t *testing.T) { + dt := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.DataType().Accept(visitor) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(dt, `interface{}`) + assert.Nil(t, err) + inter := v.(ast.DataType) + assert.True(t, inter.Equal(&ast.Interface{Literal: ast.NewTextExpr("interface{}")})) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(dt, `interface`) + assert.Error(t, err) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(dt, `interface{`) + assert.Error(t, err) + }) +} + +func TestDataType_Time(t *testing.T) { + dt := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.DataType().Accept(visitor) + } + t.Run("normal", func(t *testing.T) { + _, err := parser.Accept(dt, `time.Time`) + assert.Error(t, err) + }) +} + +func TestDataType_Pointer(t *testing.T) { + dt := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.PointerType().Accept(visitor) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(dt, `*int`) + assert.Nil(t, err) + assert.True(t, v.(ast.DataType).Equal(&ast.Pointer{ + PointerExpr: ast.NewTextExpr("*int"), + Star: ast.NewTextExpr("*"), + Name: ast.NewTextExpr("int"), + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(dt, `int`) + assert.Error(t, err) + }) +} + +func TestAlias(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.TypeAlias().Accept(visitor) + } + t.Run("normal", func(t *testing.T) { + _, err := parser.Accept(fn, `Foo int`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `Foo=int`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` + Foo int // comment`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` + Foo int /**comment*/`) + assert.Error(t, err) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `Foo var`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `Foo 2`) + assert.Error(t, err) + }) +} + +func TestTypeStruct(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.TypeStruct().Accept(visitor) + } + + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, "Foo {\n\t\t\tFoo string\n\t\t\tBar int `json:\"bar\"``\n\t\t}") + assert.Nil(t, err) + s := v.(*ast.TypeStruct) + assert.True(t, s.Equal(&ast.TypeStruct{ + Name: ast.NewTextExpr("Foo"), + LBrace: ast.NewTextExpr("{"), + RBrace: ast.NewTextExpr("}"), + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("Foo"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("string")}, + }, + { + Name: ast.NewTextExpr("Bar"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + Tag: ast.NewTextExpr("`json:\"bar\"`"), + }, + }, + })) + + v, err = parser.Accept(fn, "Foo struct{\n\t\t\tFoo string\n\t\t\tBar int `json:\"bar\"``\n\t\t}") + assert.Nil(t, err) + s = v.(*ast.TypeStruct) + assert.True(t, s.Equal(&ast.TypeStruct{ + Name: ast.NewTextExpr("Foo"), + LBrace: ast.NewTextExpr("{"), + RBrace: ast.NewTextExpr("}"), + Struct: ast.NewTextExpr("struct"), + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("Foo"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("string")}, + }, + { + Name: ast.NewTextExpr("Bar"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + Tag: ast.NewTextExpr("`json:\"bar\"`"), + }, + }, + })) + }) +} + +func TestTypeBlock(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.TypeBlock().Accept(visitor) + } + t.Run("normal", func(t *testing.T) { + v, err := parser.Accept(fn, `type( + // doc + Foo int + )`) + assert.Error(t, err) + + v, err = parser.Accept(fn, `type ( + // doc + Foo { + Bar int + } + )`) + assert.Nil(t, err) + st := v.([]ast.TypeExpr) + assert.True(t, st[0].Equal(&ast.TypeStruct{ + Name: ast.NewTextExpr("Foo"), + LBrace: ast.NewTextExpr("{"), + RBrace: ast.NewTextExpr("}"), + DocExpr: []ast.Expr{ + ast.NewTextExpr("// doc"), + }, + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("Bar"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + }, + }, + })) + }) +} + +func TestTypeLit(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.TypeLit().Accept(visitor) + } + t.Run("normal", func(t *testing.T) { + _, err := parser.Accept(fn, `type Foo int`) + assert.Error(t, err) + + _, err = parser.Accept(fn, `type Foo = int`) + assert.Error(t, err) + + _, err = parser.Accept(fn, ` + // doc + type Foo = int // comment`) + assert.Error(t, err) + + v, err := parser.Accept(fn, ` + // doc + type Foo {// comment + Bar int + }`) + assert.Nil(t, err) + st := v.(*ast.TypeStruct) + assert.True(t, st.Equal(&ast.TypeStruct{ + Name: ast.NewTextExpr("Foo"), + Fields: []*ast.TypeField{ + { + Name: ast.NewTextExpr("Bar"), + DataType: &ast.Literal{Literal: ast.NewTextExpr("int")}, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// comment"), + }, + }, + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// doc"), + }, + })) + + v, err = parser.Accept(fn, ` + // doc + type Foo {// comment + Bar + }`) + assert.Nil(t, err) + st = v.(*ast.TypeStruct) + assert.True(t, st.Equal(&ast.TypeStruct{ + Name: ast.NewTextExpr("Foo"), + Fields: []*ast.TypeField{ + { + IsAnonymous: true, + DataType: &ast.Literal{Literal: ast.NewTextExpr("Bar")}, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// comment"), + }, + }, + }, + DocExpr: []ast.Expr{ + ast.NewTextExpr("// doc"), + }, + })) + }) + + t.Run("wrong", func(t *testing.T) { + _, err := parser.Accept(fn, `type Foo`) + assert.Error(t, err) + }) +} + +func TestTypeUnExported(t *testing.T) { + fn := func(p *api.ApiParserParser, visitor *ast.ApiVisitor) interface{} { + return p.TypeSpec().Accept(visitor) + } + + t.Run("type", func(t *testing.T) { + _, err := parser.Accept(fn, `type foo {}`) + assert.Nil(t, err) + }) + + t.Run("field", func(t *testing.T) { + _, err := parser.Accept(fn, `type Foo { + name int + }`) + assert.Nil(t, err) + + _, err = parser.Accept(fn, `type Foo { + Name int + }`) + assert.Nil(t, err) + }) + + t.Run("filedDataType", func(t *testing.T) { + _, err := parser.Accept(fn, `type Foo { + Foo *foo + Bar []bar + FooBar map[int]fooBar + }`) + assert.Nil(t, err) + }) +} diff --git a/tools/goctl/api/parser/infostate.go b/tools/goctl/api/parser/infostate.go deleted file mode 100644 index 6432fd83..00000000 --- a/tools/goctl/api/parser/infostate.go +++ /dev/null @@ -1,62 +0,0 @@ -package parser - -import ( - "fmt" - "strings" - - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -const ( - titleTag = "title" - descTag = "desc" - versionTag = "version" - authorTag = "author" - emailTag = "email" -) - -type infoState struct { - *baseState - innerState int -} - -func newInfoState(st *baseState) state { - return &infoState{ - baseState: st, - innerState: startState, - } -} - -func (s *infoState) process(api *spec.ApiSpec) (state, error) { - attrs, err := s.parseProperties() - if err != nil { - return nil, err - } - - if err := s.writeInfo(api, attrs); err != nil { - return nil, err - } - - return newRootState(s.r, s.lineNumber), nil -} - -func (s *infoState) writeInfo(api *spec.ApiSpec, attrs map[string]string) error { - for k, v := range attrs { - switch k { - case titleTag: - api.Info.Title = strings.TrimSpace(v) - case descTag: - api.Info.Desc = strings.TrimSpace(v) - case versionTag: - api.Info.Version = strings.TrimSpace(v) - case authorTag: - api.Info.Author = strings.TrimSpace(v) - case emailTag: - api.Info.Email = strings.TrimSpace(v) - default: - return fmt.Errorf("unknown directive %q in %q section", k, infoDirective) - } - } - - return nil -} diff --git a/tools/goctl/api/parser/parser.go b/tools/goctl/api/parser/parser.go index ce6f079c..96e92ddb 100644 --- a/tools/goctl/api/parser/parser.go +++ b/tools/goctl/api/parser/parser.go @@ -1,103 +1,292 @@ package parser import ( - "bufio" - "bytes" "errors" "fmt" - "io" - "io/ioutil" "path/filepath" - "strings" + "unicode" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/ast" + "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api" "github.com/tal-tech/go-zero/tools/goctl/api/spec" - "github.com/tal-tech/go-zero/tools/goctl/util" ) -type Parser struct { - r *bufio.Reader - api *ApiStruct +type parser struct { + ast *ast.Api + spec *spec.ApiSpec } -func NewParser(filename string) (*Parser, error) { - apiAbsPath, err := filepath.Abs(filename) +func Parse(filename string) (*spec.ApiSpec, error) { + astParser := ast.NewParser(ast.WithParserPrefix(filepath.Base(filename))) + ast, err := astParser.Parse(filename) if err != nil { return nil, err } - api, err := ioutil.ReadFile(filename) + spec := new(spec.ApiSpec) + p := parser{ast: ast, spec: spec} + err = p.convert2Spec() if err != nil { return nil, err } - apiStruct, err := ParseApi(string(api)) + return spec, nil +} + +func ParseContent(content string) (*spec.ApiSpec, error) { + astParser := ast.NewParser() + ast, err := astParser.ParseContent(content) if err != nil { return nil, err } - for _, item := range strings.Split(apiStruct.Imports, "\n") { - importLine := strings.TrimSpace(item) - if len(importLine) > 0 { - item := strings.TrimPrefix(importLine, "import") - item = strings.TrimSpace(item) - item = strings.TrimPrefix(item, `"`) - item = strings.TrimSuffix(item, `"`) - var path = item - if !util.FileExists(item) { - path = filepath.Join(filepath.Dir(apiAbsPath), item) + spec := new(spec.ApiSpec) + p := parser{ast: ast, spec: spec} + err = p.convert2Spec() + if err != nil { + return nil, err + } + + return spec, nil +} + +func (p parser) convert2Spec() error { + p.fillInfo() + p.fillSyntax() + p.fillImport() + err := p.fillTypes() + if err != nil { + return err + } + return p.fillService() +} + +func (p parser) fillInfo() { + var properties = make(map[string]string, 0) + if p.ast.Info != nil { + p.spec.Info = spec.Info{} + for _, kv := range p.ast.Info.Kvs { + properties[kv.Key.Text()] = kv.Value.Text() + } + } + p.spec.Info.Properties = properties +} + +func (p parser) fillSyntax() { + if p.ast.Syntax != nil { + p.spec.Syntax = spec.ApiSyntax{Version: p.ast.Syntax.Version.Text()} + } +} + +func (p parser) fillImport() { + if len(p.ast.Import) > 0 { + for _, item := range p.ast.Import { + p.spec.Imports = append(p.spec.Imports, spec.Import{Value: item.Value.Text()}) + } + } +} + +func (p parser) fillTypes() error { + for _, item := range p.ast.Type { + switch v := (item).(type) { + case *ast.TypeStruct: + var members []spec.Member + for _, item := range v.Fields { + members = append(members, p.fieldToMember(item)) } - content, err := ioutil.ReadFile(path) + p.spec.Types = append(p.spec.Types, spec.DefineStruct{ + RawName: v.Name.Text(), + Members: members, + Docs: p.stringExprs(v.Doc()), + }) + default: + return errors.New(fmt.Sprintf("unknown type %+v", v)) + } + } + + for _, item := range p.spec.Types { + switch v := (item).(type) { + case spec.DefineStruct: + for _, member := range v.Members { + switch v := member.Type.(type) { + case spec.DefineStruct: + tp, err := p.findDefinedType(v.RawName) + if err != nil { + return err + } else { + member.Type = *tp + } + } + } + default: + return errors.New(fmt.Sprintf("unknown type %+v", v)) + } + } + return nil +} + +func (p parser) findDefinedType(name string) (*spec.Type, error) { + for _, item := range p.spec.Types { + if _, ok := item.(spec.DefineStruct); ok { + if item.Name() == name { + return &item, nil + } + } + } + return nil, errors.New(fmt.Sprintf("type %s not defined", name)) +} + +func (p parser) fieldToMember(field *ast.TypeField) spec.Member { + var name = "" + var tag = "" + if !field.IsAnonymous { + name = field.Name.Text() + tag = field.Tag.Text() + } + return spec.Member{ + Name: name, + Type: p.astTypeToSpec(field.DataType), + Tag: tag, + Comment: p.commentExprs(field.Comment()), + Docs: p.stringExprs(field.Doc()), + IsInline: field.IsAnonymous, + } +} + +func (p parser) astTypeToSpec(in ast.DataType) spec.Type { + switch v := (in).(type) { + case *ast.Literal: + raw := v.Literal.Text() + if api.IsBasicType(raw) { + return spec.PrimitiveType{RawName: raw} + } else { + return spec.DefineStruct{RawName: raw} + } + case *ast.Interface: + return spec.InterfaceType{RawName: v.Literal.Text()} + case *ast.Map: + return spec.MapType{RawName: v.MapExpr.Text(), Key: v.Key.Text(), Value: p.astTypeToSpec(v.Value)} + case *ast.Array: + return spec.ArrayType{RawName: v.ArrayExpr.Text(), Value: p.astTypeToSpec(v.Literal)} + case *ast.Pointer: + raw := v.Name.Text() + if api.IsBasicType(raw) { + return spec.PointerType{RawName: v.PointerExpr.Text(), Type: spec.PrimitiveType{RawName: raw}} + } else { + return spec.PointerType{RawName: v.PointerExpr.Text(), Type: spec.DefineStruct{RawName: raw}} + } + } + panic(fmt.Sprintf("unspported type %+v", in)) +} + +func (p parser) stringExprs(docs []ast.Expr) []string { + var result []string + for _, item := range docs { + result = append(result, item.Text()) + } + return result +} + +func (p parser) commentExprs(comment ast.Expr) string { + if comment == nil { + return "" + } + return comment.Text() +} + +func (p parser) fillService() error { + var groups []spec.Group + for _, item := range p.ast.Service { + var group spec.Group + if item.AtServer != nil { + var properties = make(map[string]string, 0) + for _, kv := range item.AtServer.Kv { + properties[kv.Key.Text()] = kv.Value.Text() + } + group.Annotation.Properties = properties + } + + for _, astRoute := range item.ServiceApi.ServiceRoute { + route := spec.Route{ + Annotation: spec.Annotation{}, + Method: astRoute.Route.Method.Text(), + Path: astRoute.Route.Path.Text(), + } + if astRoute.AtHandler != nil { + route.Handler = astRoute.AtHandler.Name.Text() + } + + if astRoute.AtServer != nil { + var properties = make(map[string]string, 0) + for _, kv := range astRoute.AtServer.Kv { + properties[kv.Key.Text()] = kv.Value.Text() + } + route.Annotation.Properties = properties + if len(route.Handler) == 0 { + route.Handler = properties["handler"] + } + if len(route.Handler) == 0 { + return fmt.Errorf("missing handler annotation for %q", route.Path) + } + + for _, char := range route.Handler { + if !unicode.IsDigit(char) && !unicode.IsLetter(char) { + return errors.New(fmt.Sprintf("route [%s] handler [%s] invalid, handler name should only contains letter or digit", + route.Path, route.Handler)) + } + } + } + + if astRoute.Route.Req != nil { + route.RequestType = p.astTypeToSpec(astRoute.Route.Req.Name) + } + if astRoute.Route.Reply != nil { + route.ResponseType = p.astTypeToSpec(astRoute.Route.Reply.Name) + } + + err := p.fillRouteType(&route) if err != nil { - return nil, errors.New("import api file not exist: " + item) + return err } - importStruct, err := ParseApi(string(content)) + group.Routes = append(group.Routes, route) + + name := item.ServiceApi.Name.Text() + if len(p.spec.Service.Name) > 0 && p.spec.Service.Name != name { + return errors.New(fmt.Sprintf("mulit service name defined %s and %s", name, p.spec.Service.Name)) + } + p.spec.Service.Name = name + } + groups = append(groups, group) + } + p.spec.Service.Groups = groups + + return nil +} + +func (p parser) fillRouteType(route *spec.Route) error { + if route.RequestType != nil { + switch route.RequestType.(type) { + case spec.DefineStruct: + tp, err := p.findDefinedType(route.RequestType.Name()) if err != nil { - return nil, err + return err } - if len(importStruct.Imports) > 0 { - return nil, errors.New("import api should not import another api file recursive") + route.RequestType = *tp + } + } + + if route.ResponseType != nil { + switch route.ResponseType.(type) { + case spec.DefineStruct: + tp, err := p.findDefinedType(route.ResponseType.Name()) + if err != nil { + return err } - apiStruct.Type += "\n" + importStruct.Type - apiStruct.Service += "\n" + importStruct.Service - } - } - - if len(strings.TrimSpace(apiStruct.Service)) == 0 { - return nil, errors.New("api has no service defined") - } - - var buffer = new(bytes.Buffer) - buffer.WriteString(apiStruct.Service) - return &Parser{ - r: bufio.NewReader(buffer), - api: apiStruct, - }, nil -} - -func (p *Parser) Parse() (api *spec.ApiSpec, err error) { - api = new(spec.ApiSpec) - var sp = StructParser{Src: p.api.Type} - types, err := sp.Parse() - if err != nil { - return nil, err - } - - api.Types = types - var lineNumber = p.api.serviceBeginLine - st := newRootState(p.r, &lineNumber) - for { - st, err = st.process(api) - if err == io.EOF { - return api, p.validate(api) - } - if err != nil { - return nil, fmt.Errorf("near line: %d, %s", lineNumber, err.Error()) - } - if st == nil { - return api, p.validate(api) + route.ResponseType = *tp } } + return nil } diff --git a/tools/goctl/api/parser/readme.md b/tools/goctl/api/parser/readme.md new file mode 100644 index 00000000..ca5af250 --- /dev/null +++ b/tools/goctl/api/parser/readme.md @@ -0,0 +1,682 @@ +# Api语法描述 + +## api示例 + +``` golang +/** + * api语法示例及语法说明 + */ + +// api语法版本 +syntax = "v1" + +// import literal +import "foo.api" + +// import group +import ( + "bar.api" + "foo/bar.api" +) +info( + author: "songmeizi" + date: "2020-01-08" + desc: "api语法示例及语法说明" +) + +// type literal + +type Foo{ + Foo int `json:"foo"` +} + +// type group + +type( + Bar{ + Bar int `json:"bar"` + } +) + +// service block +@server( + jwt: Auth + group: foo +) +service foo-api{ + @doc "foo" + @handler foo + post /foo (Foo) returns (Bar) +} +``` + +## api语法结构 + +* syntax语法声明 +* import语法块 +* info语法块 +* type语法块 +* service语法块 +* 隐藏通道 + +> ### 温馨提示️ +> 在以上语法结构中,各个语法块从语法上来说,按照语法块为单位,可以在.api文件中任意位置声明, +> 但是为了提高阅读效率,我们建议按照以上顺序进行声明,因为在将来可能会通过严格模式来控制语法块的顺序。 + +### syntax语法声明 + +syntax是新加入的语法结构,该语法的引入可以解决: + +* 快速针对api版本定位存在问题的语法结构 +* 针对版本做语法解析 +* 防止api语法大版本升级导致前后不能向前兼容 + +> ### 警告 ⚠️ +> 被import的api必须要和main api的syntax版本一致。 + +**语法定义** + +``` antlrv4 +'syntax'={checkVersion(p)}STRING +``` + +**语法说明** +> syntax:固定token,标志一个syntax语法结构的开始 +> +> checkVersion:自定义go方法,检测`STRING`是否为一个合法的版本号,目前检测逻辑为,STRING必须是满足`(?m)"v[1-9][0-9]*"`正则。 +> +> STRING:一串英文双引号包裹的字符串,如"v1" +> +> 一个api语法文件只能有0或者1个syntax语法声明,如果没有syntax,则默认为v1版本 +> + + +**正确语法示例** ✅ + +eg1:不规范写法 + +``` api +syntax="v1" +``` + +eg2:规范写法(推荐) + +``` api +syntax = "v2" +``` + +**错误语法示例** ❌ + +eg1: + +``` api +syntax = "v0" +``` + +eg2: + +``` api +syntax = v1 +``` + +eg3: + +``` api +syntax = "V1" +``` + +## import语法块 + +随着业务规模增大,api中定义的结构体和服务越来越多,所有的语法描述均为一个api文件,这是多么糟糕的一个问题, 其会大大增加了阅读难度和维护难度,import语法块可以帮助我们解决这个问题,通过拆分api文件, +不同的api文件按照一定规则声明,可以降低阅读难度和维护难度。 + +> ### 警告 ⚠️ +> 这里import不像golang那样包含package声明,仅仅是一个文件路径的引入,最终解析后会把所有的声明都汇聚到一个spec.Spec中。 +> 不能import多个相同路径,否则会解析错误。 + +**语法定义** + +``` antlrv4 +'import' {checkImportValue(p)}STRING +|'import' '(' ({checkImportValue(p)}STRING)+ ')' +``` + +**语法说明** +> import:固定token,标志一个import语法的开始 +> +> checkImportValue:自定义go方法,检测`STRING`是否为一个合法的文件路径,目前检测逻辑为,STRING必须是满足`(?m)"(/?[a-zA-Z0-9_#-])+\.api"`正则。 +> +> STRING:一串英文双引号包裹的字符串,如"foo.api" +> + + +**正确语法示例** ✅ + +eg: + +``` api +import "foo.api" +import "foo/bar.api" + +import( + "bar.api" + "foo/bar/foo.api" +) +``` + +**错误语法示例** ❌ + +eg: + +``` api +import foo.api +import "foo.txt" +import ( + bar.api + bar.api +) +``` + +## info语法块 + +info语法块是一个包含了多个键值对的语法体,其作用相当于一个api服务的描述,解析器会将其映射到spec.Spec中, 以备用于翻译成其他语言(golang、java等) +时需要携带的meta元素。如果仅仅是对当前api的一个说明,而不考虑其翻译 时传递到其他语言,则使用简单的多行注释或者java风格的文档注释即可,关于注释说明请参考下文的 **隐藏通道**。 + +> ### 警告 ⚠️ +> 不能使用重复的key,每个api文件只能有0或者1个info语法块 + +**语法定义** + +``` antlrv4 +'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' +``` + +**语法说明** +> info:固定token,标志一个info语法块的开始 +> +> checkKeyValue:自定义go方法,检测`VALUE`是否为一个合法值。 +> +> VALUE:key对应的值,可以为单行的除'\r','\n','/'后的任意字符,多行请以""包裹,不过强烈建议所有都以""包裹 +> + +**正确语法示例** ✅ + +eg1:不规范写法 + +``` api +info( +foo: foo value +bar:"bar value" + desc:"long long long long +long long text" +) +``` + +eg2:规范写法(推荐) + +``` api +info( + foo: "foo value" + bar: "bar value" + desc: "long long long long long long text" +) +``` + +**错误语法示例** ❌ + +eg1:没有key-value内容 + +``` api +info() +``` + +eg2:不包含冒号 + +``` api +info( + foo value +) +``` + +eg3:key-value没有换行 + +``` api +info(foo:"value") +``` + +eg4:没有key + +``` api +info( + : "value" +) +``` + +eg5:非法的key + +``` api +info( + 12: "value" +) +``` + +eg6:移除旧版本多行语法 + +``` api +info( + foo: > + some text + < +) +``` + +## type语法块 + +在api服务中,我们需要用到一个结构体(类)来作为请求体,响应体的载体,因此我们需要声明一些结构体来完成这件事情, type语法块由golang的type演变而来,当然也保留着一些golang type的特性,沿用golang特性有: + +* 保留了golang内置数据类型`bool`,`int`,`int8`,`int16`,`int32`,`int64`,`uint`,`uint8`,`uint16`,`uint32`,`uint64`,`uintptr` + ,`float32`,`float64`,`complex64`,`complex128`,`string`,`byte`,`rune`, +* 兼容golang struct风格声明 +* 保留golang关键字 + +> ### 警告 ⚠️ +> * 不支持alias +> * 不支持time.Time数据类型 +> * 结构体名称、字段名称、不能为golang关键字 + +**语法定义** +> 由于其和golang相似,因此不做详细说明,具体语法定义请在[ApiParser.g4](g4/ApiParser.g4)中查看typeSpec定义。 + +**语法说明** + +> 参考golang写法 + +**正确语法示例** ✅ + +eg1:不规范写法 + +``` api +type Foo struct{ + Id int `path:"id"` // ① + Foo int `json:"foo"` +} + +type Bar struct{ + // 非导出型字段 + bar int `form:"bar"` +} + +type( + // 非导出型结构体 + fooBar struct{ + FooBar int + } +) +``` + +eg2:规范写法(推荐) + +``` api +type Foo{ + Id int `path:"id"` + Foo int `json:"foo"` +} + +type Bar{ + Bar int `form:"bar"` +} + +type( + FooBar{ + FooBar int + } +) +``` + +**错误语法示例** ❌ + +eg + +``` api +type Gender int // 不支持 + +// 非struct token +type Foo structure{ + CreateTime time.Time // 不支持time.Time +} + +// golang关键字 var +type var{} + +type Foo{ + // golang关键字 interface + Foo interface +} + + +type Foo{ + foo int + // map key必须要golang内置数据类型 + m map[Bar]string +} +``` + +**① tag说明** +> tag定义和golang中json tag语法一样,除了json tag外,go-zero还提供了另外一些tag来实现对字段的描述, +> 详情见下表。 + +* tag表 + + |tag key |描述 |提供方 |有效范围 |示例 | + |:--- |:--- |:--- |:--- |:--- | + |json|json序列化tag|golang|request、response|`json:"fooo"`| + |path|路由path,如`/foo/:id`|go-zero|request|`path:"id"`| + |form|标志请求体是一个form(POST方法时)或者一个query(GET方法时`/search?name=keyword`)|go-zero|request|`form:"name"`| + +* tag修饰符 + > 常见参数校验描述 + + |tag key |描述 |提供方 |有效范围 |示例 | + |:--- |:--- |:--- |:--- |:--- | + |optional|定义当前字段为可选参数|go-zero|request|`json:"name,optional"`| + |options|定义当前字段的枚举值,多个以竖线②隔开|go-zero|request|`json:"gender,options=male"`| + |default|定义当前字段默认值|go-zero|request|`json:"gender,default=male"`| + |range|定义当前字段数值范围|go-zero|request|`json:"age,range=[0:120]"`| + + ② 竖线:| + > ### 温馨提示 + > tag修饰符需要在tag value后以引文逗号,隔开 + +## service语法块 + +service语法块用于定义api服务,包含服务名称,服务metadata,中间件声明,路由,handler等。 + +> ### 警告 ⚠️ +> * main api和被import的api服务名称必须一致,不能出现服务名称歧义。 +> * handler名称不能重复 +> * 路由(请求方法+请求path)名称不能重复 +> * 请求体必须声明为普通(非指针)struct,响应体做了一些向前兼容处理,详请见下文说明 +> + +**语法定义** + +``` antlrv4 +serviceSpec: atServer? serviceApi; +atServer: '@server' lp='(' kvLit+ rp=')'; +serviceApi: {match(p,"service")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; +serviceRoute: atDoc? (atServer|atHandler) route; +atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; +atHandler: '@handler' ID; +route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; +body: lp='(' (ID)? rp=')'; +replybody: lp='(' dataType? rp=')'; +// kv +kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; + +serviceName: (ID '-'?)+; +path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; +``` + +**语法说明** + +> serviceSpec:包含了一个可选语法块`atServer`和`serviceApi`语法块,其遵循序列模式(编写service必须要按照顺序,否则会解析出错) +> +> atServer: 可选语法块,定义key-value结构的server metadata,'@server'表示这一个server语法块的开始,其可以用于描述serviceApi或者route语法块,其用于描述不同语法块时有一些特殊关键key +> 需要值得注意,见 **atServer关键key描述说明**。 +> +> serviceApi:包含了1到多个`serviceRoute`语法块 +> +> serviceRoute:按照序列模式包含了`atDoc`,handler和`route` +> +> atDoc:可选语法块,一个路由的key-value描述,其在解析后会传递到spec.Spec结构体,如果不关心传递到spec.Spec, +> 推荐用单行注释替代。 +> +> handler:是对路由的handler层描述,可以通过atServer指定`handler` key来指定handler名称, +> 也可以直接用atHandler语法块来定义handler名称 +> +> atHandler:'@handler' 固定token,后接一个遵循正则`[_a-zA-Z][a-zA-Z_-]*`)的值,用于声明一个handler名称 +> +> route:路由,有`httpMethod`、`path`、可选`request`、可选`response`组成,`httpMethod`是必须是小写。 +> +> body:api请求体语法定义,必须要由()包裹的可选的ID值 +> +> replyBody:api响应体语法定义,必须由()包裹的struct、~~array(向前兼容处理,后续可能会废弃,强烈推荐以struct包裹,不要直接用array作为响应体)~~ +> +> kvLit: 同info key-value +> +> serviceName: 可以有多个'-'join的ID值 +> +> path:api请求路径,必须以'/'或者'/:'开头,切不能以'/'结尾,中间可包含ID或者多个以'-'join的ID字符串 + +**atServer关键key描述说明** + +修饰service时 + +|key|描述|示例| +|:---|:---|:---| +|jwt|声明当前service下所有路由需要jwt鉴权,且会自动生成包含jwt逻辑的代码|`jwt: Auth`| +|group|声明当前service或者路由文件分组|`group: login`| +|middleware|声明当前service需要开启中间件|`middleware: AuthMiddleware`| + +修饰route时 + +|key|描述|示例| +|:---|:---|:---| +|handler|声明一个handler|-| + +**正确语法示例** ✅ + +eg1:不规范写法 + +``` api +@server( + jwt: Auth + group: foo + middleware: AuthMiddleware +) +service foo-api{ + @doc( + summary: foo + ) + @server( + handler: foo + ) + // 非导出型body + post /foo/:id (foo) returns (bar) + + @doc "bar" + @handler bar + post /bar returns ([]int)// 不推荐数组作为响应体 + + @handler fooBar + post /foo/bar (Foo) returns // 可以省略'returns' +} +``` + +eg2:规范写法(推荐) + +``` api +@server( + jwt: Auth + group: foo + middleware: AuthMiddleware +) +service foo-api{ + @doc "foo" + @handler: foo + post /foo/:id (Foo) returns (Bar) +} + +service foo-api{ + @handler ping + get /ping + + @doc "foo" + @handler: bar + post /bar/:id (Foo) +} + +``` + +**错误语法示例** ❌ + +``` api +// 不支持空的server语法块 +@server( +) +// 不支持空的service语法块 +service foo-api{ +} + +service foo-api{ + @doc kkkk // 简版doc必须用英文双引号引起来 + @handler foo + post /foo + + @handler foo // 重复的handler + post /bar + + @handler fooBar + post /bar // 重复的路由 + + // @handler和@doc顺序错误 + @handler someHandler + @doc "some doc" + post /some/path + + // handler缺失 + post /some/path/:id + + @handler reqTest + post /foo/req (*Foo) // 不支持除普通结构体外的其他数据类型作为请求体 + + @handler replyTest + post /foo/reply returns (*Foo) // 不支持除普通结构体、数组(向前兼容,后续考虑废弃)外的其他数据类型作为响应体 +} +``` + +## 隐藏通道 + +隐藏通道目前主要为空百符号,换行符号以及注释,这里我们只说注释,因为空白符号和换行符号我们目前拿来也无用。 + +### 单行注释 + +**语法定义** + +``` antlrv4 +'//' ~[\r\n]* +``` + +**语法说明** +由语法定义可知道,单行注释必须要以`//`开头,内容为不能包含换行符 + +**正确语法示例** ✅ + +``` api +// doc +// comment +``` + +**错误语法示例** ❌ + +``` api +// break +line comments +``` + +### java风格文档注释 + +**语法定义** + +``` antlrv4 +'/*' .*? '*/' +``` + +**语法说明** + +由语法定义可知道,单行注释必须要以`/*`开头,`*/`结尾的任意字符。 + +**正确语法示例** ✅ + +``` api +/** + * java-style doc + */ +``` + +**错误语法示例** ❌ + +``` api +/* + * java-style doc */ + */ +``` + +## Doc&Comment + +如果想获取某一个元素的doc或者comment开发人员需要怎么定义? + +**Doc** +> 我们规定上一个语法块(非隐藏通道内容)的行数line+1到当前语法块第一个元素前的所有注释(当行,或者多行)均为doc, 且保留了`//`、`/*`、`*/`原始标记。 + +**Comment** +> 我们规定当前语法块最后一个元素所在行开始的一个注释块(当行,或者多行)为comment 且保留了`//`、`/*`、`*/`原始标记。 + +语法块Doc和Comment的支持情况 + +|语法块|parent语法块|Doc|Comment| +|:---|:---|:---|:---| +|syntaxLit|api|✅|✅| +|kvLit|infoSpec|✅|✅| +|importLit|importSpec|✅|✅| +|typeLit|api|✅|❌| +|typeLit|typeBlock|✅|❌| +|field|typeLit|✅|✅| +|key-value|atServer|✅|✅| +|atHandler|serviceRoute|✅|✅| +|route|serviceRoute|✅|✅| + +以下为对应语法块解析后细带doc和comment的写法 +``` api +// syntaxLit doc +syntax = "v1" // syntaxLit commnet + +info( + // kvLit doc + author: songmeizi // kvLit comment +) + +// typeLit doc +type Foo {} + +type( + // typeLit doc + Bar{} + + FooBar{ + // filed doc + Name int // filed comment + } +) + +@server( + /** + * kvLit doc + * 开启jwt鉴权 + */ + jwt: Auth /**kvLit comment*/ +) +service foo-api{ + // atHandler doc + @handler foo //atHandler comment + + /* + * route doc + * post请求 + * path为 /foo + * 请求体:Foo + * 响应体:Foo + */ + post /foo (Foo) returns (Foo) // route comment +} +``` \ No newline at end of file diff --git a/tools/goctl/api/parser/rootstate.go b/tools/goctl/api/parser/rootstate.go deleted file mode 100644 index 2c8ba8e7..00000000 --- a/tools/goctl/api/parser/rootstate.go +++ /dev/null @@ -1,113 +0,0 @@ -package parser - -import ( - "bufio" - "fmt" - "strings" - - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -type rootState struct { - *baseState -} - -func newRootState(r *bufio.Reader, lineNumber *int) state { - var state = newBaseState(r, lineNumber) - return rootState{ - baseState: state, - } -} - -func (s rootState) process(api *spec.ApiSpec) (state, error) { - var annos []spec.Annotation - var builder strings.Builder - for { - ch, err := s.readSkipComment() - if err != nil { - return nil, err - } - - switch { - case isSpace(ch): - if builder.Len() == 0 { - continue - } - - token := builder.String() - builder.Reset() - return s.processToken(token, annos) - case ch == at: - if builder.Len() > 0 { - return nil, fmt.Errorf("%q before %q", builder.String(), at) - } - - var annoName string - annoLoop: - for { - next, err := s.readSkipComment() - if err != nil { - return nil, err - } - - switch { - case isSpace(next): - if builder.Len() > 0 { - annoName = builder.String() - builder.Reset() - } - case next == leftParenthesis: - if err := s.unread(); err != nil { - return nil, err - } - - if builder.Len() > 0 { - annoName = builder.String() - builder.Reset() - } - attrs, err := s.parseProperties() - if err != nil { - return nil, err - } - - annos = append(annos, spec.Annotation{ - Name: annoName, - Properties: attrs, - }) - break annoLoop - default: - builder.WriteRune(next) - } - } - case ch == leftParenthesis: - if builder.Len() == 0 { - return nil, fmt.Errorf("incorrect %q at the beginning of the line", leftParenthesis) - } - - if err := s.unread(); err != nil { - return nil, err - } - - token := builder.String() - builder.Reset() - return s.processToken(token, annos) - case isLetterDigit(ch): - builder.WriteRune(ch) - case isNewline(ch): - if builder.Len() > 0 { - return nil, fmt.Errorf("incorrect newline after %q", builder.String()) - } - } - } -} - -func (s rootState) processToken(token string, annos []spec.Annotation) (state, error) { - switch token { - case infoDirective: - return newInfoState(s.baseState), nil - case serviceDirective: - return newServiceState(s.baseState, annos), nil - default: - return nil, fmt.Errorf("wrong directive %q", token) - } -} diff --git a/tools/goctl/api/parser/servicestate.go b/tools/goctl/api/parser/servicestate.go deleted file mode 100644 index fc8a928b..00000000 --- a/tools/goctl/api/parser/servicestate.go +++ /dev/null @@ -1,130 +0,0 @@ -package parser - -import ( - "bufio" - "bytes" - "fmt" - "io" - "strings" - - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -type serviceState struct { - *baseState - annos []spec.Annotation -} - -func newServiceState(state *baseState, annos []spec.Annotation) state { - return &serviceState{ - baseState: state, - annos: annos, - } -} - -func (s *serviceState) process(api *spec.ApiSpec) (state, error) { - var name string - var routes []spec.Route - parser := &serviceEntityParser{ - acceptName: func(n string) { - name = n - }, - acceptRoute: func(route spec.Route) { - routes = append(routes, route) - }, - } - ent := newEntity(s.baseState, api, parser) - if err := ent.process(); err != nil { - return nil, err - } - - api.Service = spec.Service{ - Name: name, - Groups: append(api.Service.Groups, spec.Group{ - Annotations: s.annos, - Routes: routes, - }), - } - - return newRootState(s.r, s.lineNumber), nil -} - -type serviceEntityParser struct { - acceptName func(name string) - acceptRoute func(route spec.Route) -} - -func (p *serviceEntityParser) parseLine(line string, api *spec.ApiSpec, annos []spec.Annotation) error { - var defaultErr = fmt.Errorf("wrong line %q, %q", line, routeSyntax) - - line = strings.TrimSpace(line) - var buffer = new(bytes.Buffer) - buffer.WriteString(line) - reader := bufio.NewReader(buffer) - var builder strings.Builder - var fields = make([]string, 0) - for { - ch, _, err := reader.ReadRune() - if err != nil { - if err == io.EOF { - if builder.Len() > 0 { - token := strings.TrimSpace(builder.String()) - if len(token) > 0 && token != returnsTag { - fields = append(fields, token) - } - } - break - } - return err - } - - switch { - case isSpace(ch), ch == leftParenthesis, ch == rightParenthesis, ch == semicolon: - if builder.Len() == 0 { - continue - } - token := builder.String() - builder.Reset() - fields = append(fields, token) - default: - builder.WriteRune(ch) - } - } - - if len(fields) < 2 { - return defaultErr - } - method := fields[0] - path := fields[1] - var req string - var resp string - - if len(fields) > 2 { - req = fields[2] - } - if stringx.Contains(fields, returnsTag) { - if fields[len(fields)-1] != returnsTag { - resp = fields[len(fields)-1] - } else { - return defaultErr - } - if fields[2] == returnsTag { - req = "" - } - } - - p.acceptRoute(spec.Route{ - Annotations: annos, - Method: method, - Path: path, - RequestType: GetType(api, req), - ResponseType: GetType(api, resp), - }) - - return nil -} - -func (p *serviceEntityParser) setEntityName(name string) { - p.acceptName(name) -} diff --git a/tools/goctl/api/parser/state.go b/tools/goctl/api/parser/state.go deleted file mode 100644 index 9af51320..00000000 --- a/tools/goctl/api/parser/state.go +++ /dev/null @@ -1,7 +0,0 @@ -package parser - -import "github.com/tal-tech/go-zero/tools/goctl/api/spec" - -type state interface { - process(api *spec.ApiSpec) (state, error) -} diff --git a/tools/goctl/api/parser/typeparser.go b/tools/goctl/api/parser/typeparser.go deleted file mode 100644 index b528452f..00000000 --- a/tools/goctl/api/parser/typeparser.go +++ /dev/null @@ -1,339 +0,0 @@ -package parser - -import ( - "errors" - "fmt" - "go/ast" - "go/parser" - "go/token" - "sort" - "strings" - - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -var ( - ErrStructNotFound = errors.New("struct not found") - ErrUnSupportInlineType = errors.New("unsupport inline type") - interfaceExpr = `interface{}` - objectM = make(map[string]*spec.Type) -) - -const ( - golangF = `package ast - %s -` - pkgPrefix = "package" -) - -type StructParser struct { - Src string -} - -func (sp *StructParser) Parse() ([]spec.Type, error) { - if !strings.HasPrefix(sp.Src, pkgPrefix) { - sp.Src = fmt.Sprintf(golangF, sp.Src) - } - - fSet := token.NewFileSet() - f, err := parser.ParseFile(fSet, "", sp.Src, parser.ParseComments) - if err != nil { - return nil, err - } - commentMap := ast.NewCommentMap(fSet, f, f.Comments) - f.Comments = commentMap.Filter(f).Comments() - scope := f.Scope - if scope == nil { - return nil, ErrStructNotFound - } - objects := scope.Objects - structs := make([]*spec.Type, 0) - for structName, obj := range objects { - st, err := sp.parseObject(structName, obj) - if err != nil { - return nil, err - } - structs = append(structs, st) - } - sort.Slice(structs, func(i, j int) bool { - return structs[i].Name < structs[j].Name - }) - resp := make([]spec.Type, 0) - for _, item := range structs { - resp = append(resp, *item) - } - return resp, nil -} - -func (sp *StructParser) parseObject(structName string, obj *ast.Object) (*spec.Type, error) { - if data, ok := objectM[structName]; ok { - return data, nil - } - var st spec.Type - st.Name = structName - if obj.Decl == nil { - objectM[structName] = &st - return &st, nil - } - decl, ok := obj.Decl.(*ast.TypeSpec) - if !ok { - objectM[structName] = &st - return &st, nil - } - if decl.Type == nil { - objectM[structName] = &st - return &st, nil - } - tp, ok := decl.Type.(*ast.StructType) - if !ok { - objectM[structName] = &st - return &st, nil - } - fields := tp.Fields - if fields == nil { - objectM[structName] = &st - return &st, nil - } - fieldList := fields.List - members, err := sp.parseFields(fieldList) - if err != nil { - return nil, err - } - st.Members = members - objectM[structName] = &st - return &st, nil -} - -func (sp *StructParser) parseFields(fields []*ast.Field) ([]spec.Member, error) { - members := make([]spec.Member, 0) - for _, field := range fields { - docs := parseCommentOrDoc(field.Doc) - comments := parseCommentOrDoc(field.Comment) - name := parseName(field.Names) - tp, stringExpr, err := sp.parseType(field.Type) - if err != nil { - return nil, err - } - tag := parseTag(field.Tag) - isInline := name == "" - if isInline { - var err error - name, err = sp.getInlineName(tp) - if err != nil { - return nil, err - } - } - members = append(members, spec.Member{ - Name: name, - Type: stringExpr, - Expr: tp, - Tag: tag, - Comments: comments, - Docs: docs, - IsInline: isInline, - }) - - } - return members, nil -} - -func (sp *StructParser) getInlineName(tp interface{}) (string, error) { - switch v := tp.(type) { - case *spec.Type: - return v.Name, nil - case *spec.PointerType: - return sp.getInlineName(v.Star) - case *spec.StructType: - return v.StringExpr, nil - default: - return "", ErrUnSupportInlineType - } -} - -func (sp *StructParser) getInlineTypePrefix(tp interface{}) (string, error) { - if tp == nil { - return "", nil - } - switch tp.(type) { - case *ast.Ident: - return "", nil - case *ast.StarExpr: - return "*", nil - case *ast.TypeSpec: - return "", nil - default: - return "", ErrUnSupportInlineType - } -} - -func parseTag(basicLit *ast.BasicLit) string { - if basicLit == nil { - return "" - } - return basicLit.Value -} - -// returns -// resp1: type can convert to *spec.PointerType|*spec.BasicType|*spec.MapType|*spec.ArrayType|*spec.InterfaceType -// resp2: type's string expression,like int、string、[]int64、map[string]User、*User -// resp3: error -func (sp *StructParser) parseType(expr ast.Expr) (interface{}, string, error) { - if expr == nil { - return nil, "", errors.New("parse error " + sp.Src) - } - exprStr := sp.Src[expr.Pos():expr.End()] - switch v := expr.(type) { - case *ast.StarExpr: - star, stringExpr, err := sp.parseType(v.X) - if err != nil { - return nil, "", err - } - e := fmt.Sprintf("*%s", stringExpr) - return &spec.PointerType{Star: star, StringExpr: e}, e, nil - case *ast.Ident: - if isBasicType(v.Name) { - return &spec.BasicType{Name: v.Name, StringExpr: v.Name}, v.Name, nil - } else if v.Obj != nil { - obj := v.Obj - if obj.Name != v.Name { // 防止引用自己而无限递归 - specType, err := sp.parseObject(v.Name, v.Obj) - if err != nil { - return nil, "", err - } else { - return specType, v.Obj.Name, nil - } - } else { - inlineType, err := sp.getInlineTypePrefix(obj.Decl) - if err != nil { - return nil, "", err - } - return &spec.StructType{ - StringExpr: fmt.Sprintf("%s%s", inlineType, v.Name), - }, v.Name, nil - } - } else { - return nil, "", fmt.Errorf(" [%s] - member is not exist, expr is %s", v.Name, exprStr) - } - case *ast.MapType: - key, keyStringExpr, err := sp.parseType(v.Key) - if err != nil { - return nil, "", err - } - - value, valueStringExpr, err := sp.parseType(v.Value) - if err != nil { - return nil, "", err - } - - keyType, ok := key.(*spec.BasicType) - if !ok { - return nil, "", fmt.Errorf("[%+v] - unsupported type of map key, expr is %s", v.Key, exprStr) - } - - e := fmt.Sprintf("map[%s]%s", keyStringExpr, valueStringExpr) - return &spec.MapType{ - Key: keyType.Name, - Value: value, - StringExpr: e, - }, e, nil - case *ast.ArrayType: - arrayType, stringExpr, err := sp.parseType(v.Elt) - if err != nil { - return nil, "", err - } - - e := fmt.Sprintf("[]%s", stringExpr) - return &spec.ArrayType{ArrayType: arrayType, StringExpr: e}, e, nil - case *ast.InterfaceType: - return &spec.InterfaceType{StringExpr: interfaceExpr}, interfaceExpr, nil - case *ast.ChanType: - return nil, "", errors.New("[chan] - unsupported type, expr is " + exprStr) - case *ast.FuncType: - return nil, "", errors.New("[func] - unsupported type, expr is " + exprStr) - case *ast.StructType: // todo can optimize - return nil, "", errors.New("[struct] - unsupported inline struct type, expr is " + exprStr) - case *ast.SelectorExpr: - x := v.X - sel := v.Sel - xIdent, ok := x.(*ast.Ident) - if ok { - name := xIdent.Name - if name != "time" && sel.Name != "Time" { - return nil, "", fmt.Errorf("[outter package] - package: %s, unsupport type", exprStr) - } - - tm := fmt.Sprintf("time.Time") - return &spec.TimeType{ - StringExpr: tm, - }, tm, nil - } - return nil, "", errors.New("parse error " + exprStr) - default: - return nil, "", errors.New("parse error " + exprStr) - } -} - -func isBasicType(tp string) bool { - switch tp { - case - "bool", - "uint8", - "uint16", - "uint32", - "uint64", - "int8", - "int16", - "int32", - "int64", - "float32", - "float64", - "complex64", - "complex128", - "string", - "int", - "uint", - "uintptr", - "byte", - "rune", - "Type", - "Type1", - "IntegerType", - "FloatType", - "ComplexType": - return true - default: - return false - } -} -func parseName(names []*ast.Ident) string { - if len(names) == 0 { - return "" - } - name := names[0] - return parseIdent(name) -} - -func parseIdent(ident *ast.Ident) string { - if ident == nil { - return "" - } - return ident.Name -} - -func parseCommentOrDoc(cg *ast.CommentGroup) []string { - if cg == nil { - return nil - } - comments := make([]string, 0) - for _, comment := range cg.List { - if comment == nil { - continue - } - text := strings.TrimSpace(comment.Text) - if text == "" { - continue - } - comments = append(comments, text) - } - return comments -} diff --git a/tools/goctl/api/parser/util.go b/tools/goctl/api/parser/util.go index 37aebaea..7a0ebdff 100644 --- a/tools/goctl/api/parser/util.go +++ b/tools/goctl/api/parser/util.go @@ -1,65 +1,7 @@ package parser import ( - "bufio" - "github.com/tal-tech/go-zero/tools/goctl/api/spec" ) var emptyType spec.Type - -func GetType(api *spec.ApiSpec, t string) spec.Type { - for _, tp := range api.Types { - if tp.Name == t { - return tp - } - } - - return emptyType -} - -func isLetterDigit(r rune) bool { - return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || ('0' <= r && r <= '9') -} - -func isSpace(r rune) bool { - return r == ' ' || r == '\t' -} - -func isSlash(r rune) bool { - return r == '/' -} - -func isNewline(r rune) bool { - return r == '\n' || r == '\r' -} - -func read(r *bufio.Reader) (rune, error) { - ch, _, err := r.ReadRune() - return ch, err -} - -func readLine(r *bufio.Reader) (string, error) { - line, _, err := r.ReadLine() - if err != nil { - return "", err - } else { - return string(line), nil - } -} - -func skipSpaces(r *bufio.Reader) error { - for { - next, err := read(r) - if err != nil { - return err - } - if !isSpace(next) { - return unread(r) - } - } -} - -func unread(r *bufio.Reader) error { - return r.UnreadRune() -} diff --git a/tools/goctl/api/parser/validator.go b/tools/goctl/api/parser/validator.go deleted file mode 100644 index 95e5ff8e..00000000 --- a/tools/goctl/api/parser/validator.go +++ /dev/null @@ -1,55 +0,0 @@ -package parser - -import ( - "errors" - "fmt" - "strings" - - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/api/spec" - "github.com/tal-tech/go-zero/tools/goctl/api/util" -) - -func (p *Parser) validate(api *spec.ApiSpec) (err error) { - var builder strings.Builder - for _, tp := range api.Types { - if ok, name := p.validateDuplicateProperty(tp); !ok { - fmt.Fprintf(&builder, `duplicate property "%s" of type "%s"`+"\n", name, tp.Name) - } - } - if ok, info := p.validateDuplicateRouteHandler(api); !ok { - fmt.Fprintf(&builder, info) - } - if len(builder.String()) > 0 { - return errors.New(builder.String()) - } - return nil -} - -func (p *Parser) validateDuplicateProperty(tp spec.Type) (bool, string) { - var names []string - for _, member := range tp.Members { - if stringx.Contains(names, member.Name) { - return false, member.Name - } else { - names = append(names, member.Name) - } - } - return true, "" -} - -func (p *Parser) validateDuplicateRouteHandler(api *spec.ApiSpec) (bool, string) { - var names []string - for _, r := range api.Service.Routes() { - handler, ok := util.GetAnnotationValue(r.Annotations, "server", "handler") - if !ok { - return false, fmt.Sprintf("missing handler annotation for %s", r.Path) - } - if stringx.Contains(names, handler) { - return false, fmt.Sprintf(`duplicated handler for name "%s"`, handler) - } else { - names = append(names, handler) - } - } - return true, "" -} diff --git a/tools/goctl/api/parser/vars.go b/tools/goctl/api/parser/vars.go deleted file mode 100644 index f7811b86..00000000 --- a/tools/goctl/api/parser/vars.go +++ /dev/null @@ -1,20 +0,0 @@ -package parser - -const ( - infoDirective = "info" - serviceDirective = "service" - typeDirective = "type" - typeStruct = "struct" - routeSyntax = "route syntax: [get/post/delete] /path(request) returns[(response)]" - returnsTag = "returns" - at = '@' - colon = ':' - leftParenthesis = '(' - rightParenthesis = ')' - leftBrace = "{" - rightBrace = '}' - multilineBeginTag = '>' - multilineEndTag = '<' - semicolon = ';' - newline = "\n" -) diff --git a/tools/goctl/api/spec/fn.go b/tools/goctl/api/spec/fn.go index 8477b59f..7d044615 100644 --- a/tools/goctl/api/spec/fn.go +++ b/tools/goctl/api/spec/fn.go @@ -9,8 +9,9 @@ import ( ) const bodyTagKey = "json" +const formTagKey = "form" -var definedKeys = []string{"json", "form", "path"} +var definedKeys = []string{bodyTagKey, formTagKey, "path"} func (s Service) Routes() []Route { var result []Route @@ -45,6 +46,22 @@ func (m Member) IsOptional() bool { return false } +func (m Member) IsOmitEmpty() bool { + if !m.IsBodyMember() { + return false + } + + tag := m.Tags() + for _, item := range tag { + if item.Key == bodyTagKey { + if stringx.Contains(item.Options, "omitempty") { + return true + } + } + } + return false +} + func (m Member) IsOmitempty() bool { if !m.IsBodyMember() { return false @@ -76,7 +93,7 @@ func (m Member) GetPropertyName() (string, error) { } func (m Member) GetComment() string { - return strings.TrimSpace(strings.Join(m.Comments, "; ")) + return strings.TrimSpace(m.Comment) } func (m Member) IsBodyMember() bool { @@ -93,7 +110,21 @@ func (m Member) IsBodyMember() bool { return false } -func (t Type) GetBodyMembers() []Member { +func (m Member) IsFormMember() bool { + if m.IsInline { + return false + } + + tags := m.Tags() + for _, tag := range tags { + if tag.Key == formTagKey { + return true + } + } + return false +} + +func (t DefineStruct) GetBodyMembers() []Member { var result []Member for _, member := range t.Members { if member.IsBodyMember() { @@ -103,7 +134,17 @@ func (t Type) GetBodyMembers() []Member { return result } -func (t Type) GetNonBodyMembers() []Member { +func (t DefineStruct) GetFormMembers() []Member { + var result []Member + for _, member := range t.Members { + if member.IsFormMember() { + result = append(result, member) + } + } + return result +} + +func (t DefineStruct) GetNonBodyMembers() []Member { var result []Member for _, member := range t.Members { if !member.IsBodyMember() { @@ -112,3 +153,37 @@ func (t Type) GetNonBodyMembers() []Member { } return result } + +func (r Route) JoinedDoc() string { + return strings.Join(r.Docs, " ") +} + +func (r Route) GetAnnotation(key string) string { + if r.Annotation.Properties == nil { + return "" + } + return r.Annotation.Properties[key] +} + +func (g Group) GetAnnotation(key string) string { + if g.Annotation.Properties == nil { + return "" + } + return g.Annotation.Properties[key] +} + +func (r Route) ResponseTypeName() string { + if r.ResponseType == nil { + return "" + } + + return r.ResponseType.Name() +} + +func (r Route) RequestTypeName() string { + if r.RequestType == nil { + return "" + } + + return r.RequestType.Name() +} diff --git a/tools/goctl/api/spec/name.go b/tools/goctl/api/spec/name.go new file mode 100644 index 00000000..0f0a53c5 --- /dev/null +++ b/tools/goctl/api/spec/name.go @@ -0,0 +1,25 @@ +package spec + +func (t PrimitiveType) Name() string { + return t.RawName +} + +func (t DefineStruct) Name() string { + return t.RawName +} + +func (t MapType) Name() string { + return t.RawName +} + +func (t ArrayType) Name() string { + return t.RawName +} + +func (t PointerType) Name() string { + return t.RawName +} + +func (t InterfaceType) Name() string { + return t.RawName +} diff --git a/tools/goctl/api/spec/spec.go b/tools/goctl/api/spec/spec.go index 26227bf4..72d4a0ec 100644 --- a/tools/goctl/api/spec/spec.go +++ b/tools/goctl/api/spec/spec.go @@ -1,59 +1,66 @@ package spec type ( + Doc []string + Annotation struct { - Name string Properties map[string]string - Value string + } + + ApiSyntax struct { + Version string } ApiSpec struct { Info Info + Syntax ApiSyntax + Imports []Import Types []Type Service Service } + Import struct { + Value string + } + Group struct { - Annotations []Annotation - Routes []Route + Annotation Annotation + Routes []Route } Info struct { - Title string - Desc string + // Deprecated: use Properties instead + Title string + // Deprecated: use Properties instead + Desc string + // Deprecated: use Properties instead Version string - Author string - Email string + // Deprecated: use Properties instead + Author string + // Deprecated: use Properties instead + Email string + Properties map[string]string } Member struct { - Annotations []Annotation - Name string + Name string // 数据类型字面值,如:string、map[int]string、[]int64、[]*User - Type string - // it can be asserted as BasicType: int、bool、 - // PointerType: *string、*User、 - // MapType: map[${BasicType}]interface、 - // ArrayType:[]int、[]User、[]*User - // InterfaceType: interface{} - // Type - Expr interface{} - Tag string - // Deprecated - Comment string // 换成标准struct中将废弃 - // 成员尾部注释说明 - Comments []string + Type Type + Tag string + Comment string // 成员头顶注释说明 - Docs []string + Docs Doc IsInline bool } Route struct { - Annotations []Annotation + Annotation Annotation Method string Path string RequestType Type ResponseType Type + Docs Doc + Handler string } Service struct { @@ -61,71 +68,45 @@ type ( Groups []Group } - Type struct { - Name string - Annotations []Annotation - Members []Member + Type interface { + Name() string } - // 系统预设基本数据类型 - BasicType struct { - StringExpr string - Name string + DefineStruct struct { + RawName string + Members []Member + Docs Doc } - PointerType struct { - StringExpr string - // it can be asserted as BasicType: int、bool、 - // PointerType: *string、*User、 - // MapType: map[${BasicType}]interface、 - // ArrayType:[]int、[]User、[]*User - // InterfaceType: interface{} - // Type - Star interface{} + // 系统预设基本数据类型 bool int32 int64 float32 + PrimitiveType struct { + RawName string } MapType struct { - StringExpr string - // only support the BasicType + RawName string + // only support the PrimitiveType Key string - // it can be asserted as BasicType: int、bool、 + // it can be asserted as PrimitiveType: int、bool、 // PointerType: *string、*User、 - // MapType: map[${BasicType}]interface、 + // MapType: map[${PrimitiveType}]interface、 // ArrayType:[]int、[]User、[]*User // InterfaceType: interface{} // Type - Value interface{} + Value Type } + ArrayType struct { - StringExpr string - // it can be asserted as BasicType: int、bool、 - // PointerType: *string、*User、 - // MapType: map[${BasicType}]interface、 - // ArrayType:[]int、[]User、[]*User - // InterfaceType: interface{} - // Type - ArrayType interface{} + RawName string + Value Type } + InterfaceType struct { - StringExpr string - // do nothing,just for assert + RawName string } - TimeType struct { - StringExpr string - } - StructType struct { - StringExpr string + + PointerType struct { + RawName string + Type Type } ) - -func (spec *ApiSpec) ContainsTime() bool { - for _, item := range spec.Types { - members := item.Members - for _, member := range members { - if _, ok := member.Expr.(*TimeType); ok { - return true - } - } - } - return false -} diff --git a/tools/goctl/api/tsgen/gen.go b/tools/goctl/api/tsgen/gen.go index 579378d9..3b341e3b 100644 --- a/tools/goctl/api/tsgen/gen.go +++ b/tools/goctl/api/tsgen/gen.go @@ -20,15 +20,12 @@ func TsCommand(c *cli.Context) error { if len(apiFile) == 0 { return errors.New("missing -api") } + if len(dir) == 0 { return errors.New("missing -dir") } - p, err := parser.NewParser(apiFile) - if err != nil { - return err - } - api, err := p.Parse() + api, err := parser.Parse(apiFile) if err != nil { fmt.Println(aurora.Red("Failed")) return err diff --git a/tools/goctl/api/tsgen/gencomponents.go b/tools/goctl/api/tsgen/gencomponents.go index 0606eb82..1373fd37 100644 --- a/tools/goctl/api/tsgen/gencomponents.go +++ b/tools/goctl/api/tsgen/gencomponents.go @@ -1,7 +1,6 @@ package tsgen import ( - "errors" "path" "strings" "text/template" @@ -19,19 +18,12 @@ const ( ) func genComponents(dir string, api *spec.ApiSpec) error { - types := apiutil.GetSharedTypes(api) + types := api.Types if len(types) == 0 { return nil } - val, err := buildTypes(types, func(name string) (*spec.Type, error) { - for _, ty := range api.Types { - if strings.ToLower(ty.Name) == strings.ToLower(name) { - return &ty, nil - } - } - return nil, errors.New("inline type " + name + " not exist, please correct api file") - }) + val, err := buildTypes(types) if err != nil { return err } @@ -57,7 +49,7 @@ func genComponents(dir string, api *spec.ApiSpec) error { }) } -func buildTypes(types []spec.Type, inlineType func(string) (*spec.Type, error)) (string, error) { +func buildTypes(types []spec.Type) (string, error) { var builder strings.Builder first := true for _, tp := range types { @@ -66,12 +58,8 @@ func buildTypes(types []spec.Type, inlineType func(string) (*spec.Type, error)) } else { builder.WriteString("\n") } - if err := writeType(&builder, tp, func(name string) (*spec.Type, error) { - return inlineType(name) - }, func(tp string) string { - return "" - }); err != nil { - return "", apiutil.WrapErr(err, "Type "+tp.Name+" generate error") + if err := writeType(&builder, tp); err != nil { + return "", apiutil.WrapErr(err, "Type "+tp.Name()+" generate error") } } diff --git a/tools/goctl/api/tsgen/genpacket.go b/tools/goctl/api/tsgen/genpacket.go index 3dc189ee..4d81bdfd 100644 --- a/tools/goctl/api/tsgen/genpacket.go +++ b/tools/goctl/api/tsgen/genpacket.go @@ -1,7 +1,6 @@ package tsgen import ( - "errors" "fmt" "path" "strings" @@ -15,8 +14,6 @@ import ( const ( handlerTemplate = `{{.imports}} -{{.types}} - {{.apis}} ` ) @@ -35,36 +32,6 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e } defer fp.Close() - var localTypes []spec.Type - for _, route := range api.Service.Routes() { - rts := apiutil.GetLocalTypes(api, route) - localTypes = append(localTypes, rts...) - } - - var prefixForType = func(ty string) string { - if _, pri := primitiveType(ty); pri { - return "" - } - for _, item := range localTypes { - if util.Title(item.Name) == ty { - return "" - } - } - return packagePrefix - } - - types, err := genTypes(localTypes, func(name string) (*spec.Type, error) { - for _, ty := range api.Types { - if strings.ToLower(ty.Name) == strings.ToLower(name) { - return &ty, nil - } - } - return nil, errors.New("inline type " + name + " not exist, please correct api file") - }, prefixForType) - if err != nil { - return err - } - imports := "" if len(caller) == 0 { caller = "webapi" @@ -76,8 +43,8 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e if len(webApi) > 0 { imports += `import ` + importCaller + ` from ` + "\"" + webApi + "\"" } - shardTypes := apiutil.GetSharedTypes(api) - if len(shardTypes) != 0 { + + if len(api.Types) != 0 { if len(imports) > 0 { imports += "\n" } @@ -85,7 +52,7 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e imports += fmt.Sprintf(`import * as components from "%s"`, "./"+outputFile) } - apis, err := genApi(api, caller, prefixForType) + apis, err := genApi(api, caller) if err != nil { return err } @@ -93,54 +60,35 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e t := template.Must(template.New("handlerTemplate").Parse(handlerTemplate)) return t.Execute(fp, map[string]string{ "webApi": webApi, - "types": strings.TrimSpace(types), "imports": imports, "apis": strings.TrimSpace(apis), }) } -func genTypes(localTypes []spec.Type, inlineType func(string) (*spec.Type, error), prefixForType func(string) string) (string, error) { - var builder strings.Builder - var first bool - - for _, tp := range localTypes { - if first { - first = false - } else { - fmt.Fprintln(&builder) - } - if err := writeType(&builder, tp, func(name string) (s *spec.Type, err error) { - return inlineType(name) - }, prefixForType); err != nil { - return "", err - } - } - types := builder.String() - return types, nil -} - -func genApi(api *spec.ApiSpec, caller string, prefixForType func(string) string) (string, error) { +func genApi(api *spec.ApiSpec, caller string) (string, error) { var builder strings.Builder for _, group := range api.Service.Groups { for _, route := range group.Routes { - handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") - if !ok { + handler := route.Handler + if len(handler) == 0 { return "", fmt.Errorf("missing handler annotation for route %q", route.Path) } + handler = util.Untitle(handler) handler = strings.Replace(handler, "Handler", "", 1) comment := commentForRoute(route) if len(comment) > 0 { fmt.Fprintf(&builder, "%s\n", comment) } - fmt.Fprintf(&builder, "export function %s(%s) {\n", handler, paramsForRoute(route, prefixForType)) + fmt.Fprintf(&builder, "export function %s(%s) {\n", handler, paramsForRoute(route)) writeIndent(&builder, 1) responseGeneric := "" - if len(route.ResponseType.Name) > 0 { - val, err := goTypeToTs(route.ResponseType.Name, prefixForType) + if len(route.ResponseTypeName()) > 0 { + val, err := goTypeToTs(route.ResponseType, true) if err != nil { return "", err } + responseGeneric = fmt.Sprintf("<%s>", val) } fmt.Fprintf(&builder, `return %s.%s%s(%s)`, caller, strings.ToLower(route.Method), @@ -153,14 +101,18 @@ func genApi(api *spec.ApiSpec, caller string, prefixForType func(string) string) return apis, nil } -func paramsForRoute(route spec.Route, prefixForType func(string) string) string { +func paramsForRoute(route spec.Route) string { + if route.RequestType == nil { + return "" + } hasParams := pathHasParams(route) hasBody := hasRequestBody(route) - rt, err := goTypeToTs(route.RequestType.Name, prefixForType) + rt, err := goTypeToTs(route.RequestType, true) if err != nil { fmt.Println(err.Error()) return "" } + if hasParams && hasBody { return fmt.Sprintf("params: %s, req: %s", rt+"Params", rt) } else if hasParams { @@ -173,7 +125,7 @@ func paramsForRoute(route spec.Route, prefixForType func(string) string) string func commentForRoute(route spec.Route) string { var builder strings.Builder - comment, _ := apiutil.GetAnnotationValue(route.Annotations, "doc", "summary") + comment := route.JoinedDoc() builder.WriteString("/**") builder.WriteString("\n * @description " + comment) hasParams := pathHasParams(route) @@ -200,24 +152,35 @@ func callParamsForRoute(route spec.Route, group spec.Group) string { } else if hasBody { return fmt.Sprintf("%s, %s", pathForRoute(route, group), "req") } + return pathForRoute(route, group) } func pathForRoute(route spec.Route, group spec.Group) string { - value, ok := apiutil.GetAnnotationValue(group.Annotations, "server", pathPrefix) - if !ok { + prefix := group.GetAnnotation("pathPrefix") + if len(prefix) == 0 { return "\"" + route.Path + "\"" } else { - value = strings.TrimPrefix(value, `"`) - value = strings.TrimSuffix(value, `"`) - return fmt.Sprintf(`"%s/%s"`, value, strings.TrimPrefix(route.Path, "/")) + prefix = strings.TrimPrefix(prefix, `"`) + prefix = strings.TrimSuffix(prefix, `"`) + return fmt.Sprintf(`"%s/%s"`, prefix, strings.TrimPrefix(route.Path, "/")) } } func pathHasParams(route spec.Route) bool { - return len(route.RequestType.Members) != len(route.RequestType.GetBodyMembers()) + ds, ok := route.RequestType.(spec.DefineStruct) + if !ok { + return false + } + + return len(ds.Members) != len(ds.GetBodyMembers()) } func hasRequestBody(route spec.Route) bool { - return len(route.RequestType.Name) > 0 && len(route.RequestType.GetBodyMembers()) > 0 + ds, ok := route.RequestType.(spec.DefineStruct) + if !ok { + return false + } + + return len(route.RequestTypeName()) > 0 && len(ds.GetBodyMembers()) > 0 } diff --git a/tools/goctl/api/tsgen/util.go b/tools/goctl/api/tsgen/util.go index 94b4cd0e..9f664151 100644 --- a/tools/goctl/api/tsgen/util.go +++ b/tools/goctl/api/tsgen/util.go @@ -1,6 +1,7 @@ package tsgen import ( + "errors" "fmt" "io" "strings" @@ -10,9 +11,9 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util" ) -func writeProperty(writer io.Writer, member spec.Member, indent int, prefixForType func(string) string) error { +func writeProperty(writer io.Writer, member spec.Member, indent int) error { writeIndent(writer, indent) - ty, err := goTypeToTs(member.Type, prefixForType) + ty, err := goTypeToTs(member.Type, false) if err != nil { return err } @@ -45,68 +46,56 @@ func writeIndent(writer io.Writer, indent int) { } } -func goTypeToTs(tp string, prefixForType func(string) string) (string, error) { - if val, pri := primitiveType(tp); pri { - return val, nil +func goTypeToTs(tp spec.Type, fromPacket bool) (string, error) { + switch v := tp.(type) { + case spec.DefineStruct: + return addPrefix(tp, fromPacket), nil + case spec.PrimitiveType: + r, ok := primitiveType(tp.Name()) + if !ok { + return "", errors.New("unsupported primitive type " + tp.Name()) + } + + return r, nil + case spec.MapType: + valueType, err := goTypeToTs(v.Value, fromPacket) + if err != nil { + return "", err + } + + return fmt.Sprintf("{ [key: string]: %s }", valueType), nil + case spec.ArrayType: + if tp.Name() == "[]byte" { + return "Blob", nil + } + + valueType, err := goTypeToTs(v.Value, fromPacket) + if err != nil { + return "", err + } + + return fmt.Sprintf("Array<%s>", valueType), nil + case spec.InterfaceType: + return "any", nil + case spec.PointerType: + return goTypeToTs(v.Type, fromPacket) } - if tp == "[]byte" { - return "Blob", nil - } else if strings.HasPrefix(tp, "[][]") { - tys, err := apiutil.DecomposeType(tp) - if err != nil { - return "", err - } - if len(tys) == 0 { - return "", fmt.Errorf("%s tp parse error", tp) - } - innerType, err := goTypeToTs(tys[0], prefixForType) - if err != nil { - return "", err - } - return fmt.Sprintf("Array>", innerType), nil - } else if strings.HasPrefix(tp, "[]") { - tys, err := apiutil.DecomposeType(tp) - if err != nil { - return "", err - } - if len(tys) == 0 { - return "", fmt.Errorf("%s tp parse error", tp) - } - innerType, err := goTypeToTs(tys[0], prefixForType) - if err != nil { - return "", err - } - return fmt.Sprintf("Array<%s>", innerType), nil - } else if strings.HasPrefix(tp, "map") { - tys, err := apiutil.DecomposeType(tp) - if err != nil { - return "", err - } - if len(tys) != 2 { - return "", fmt.Errorf("%s tp parse error", tp) - } - innerType, err := goTypeToTs(tys[1], prefixForType) - if err != nil { - return "", err - } - return fmt.Sprintf("{ [key: string]: %s }", innerType), nil - } - return addPrefixIfNeed(util.Title(tp), prefixForType), nil + + return "", errors.New("unsupported type " + tp.Name()) } -func addPrefixIfNeed(tp string, prefixForType func(string) string) string { - if val, pri := primitiveType(tp); pri { - return val +func addPrefix(tp spec.Type, fromPacket bool) string { + if fromPacket { + return packagePrefix + util.Title(tp.Name()) } - tp = strings.Replace(tp, "*", "", 1) - return prefixForType(tp) + util.Title(tp) + return util.Title(tp.Name()) } func primitiveType(tp string) (string, bool) { switch tp { case "string": return "string", true - case "int", "int8", "int32", "int64": + case "int", "int8", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": return "number", true case "float", "float32", "float64": return "number", true @@ -120,52 +109,56 @@ func primitiveType(tp string) (string, bool) { return "", false } -func writeType(writer io.Writer, tp spec.Type, inlineType func(string) (*spec.Type, error), prefixForType func(string) string) error { - fmt.Fprintf(writer, "export interface %s {\n", util.Title(tp.Name)) - if err := genMembers(writer, tp, false, inlineType, prefixForType); err != nil { +func writeType(writer io.Writer, tp spec.Type) error { + fmt.Fprintf(writer, "export interface %s {\n", util.Title(tp.Name())) + if err := genMembers(writer, tp, false); err != nil { return err } + fmt.Fprintf(writer, "}\n") - err := genParamsTypesIfNeed(writer, tp, inlineType, prefixForType) - if err != nil { - return err - } - return nil + return genParamsTypesIfNeed(writer, tp) } -func genParamsTypesIfNeed(writer io.Writer, tp spec.Type, inlineType func(string) (*spec.Type, error), prefixForType func(string) string) error { - members := tp.GetNonBodyMembers() +func genParamsTypesIfNeed(writer io.Writer, tp spec.Type) error { + definedType, ok := tp.(spec.DefineStruct) + if !ok { + return errors.New("no members of type " + tp.Name()) + } + + members := definedType.GetNonBodyMembers() if len(members) == 0 { return nil } fmt.Fprintf(writer, "\n") - fmt.Fprintf(writer, "export interface %sParams {\n", util.Title(tp.Name)) - if err := genMembers(writer, tp, true, inlineType, prefixForType); err != nil { + fmt.Fprintf(writer, "export interface %sParams {\n", util.Title(tp.Name())) + if err := genMembers(writer, tp, true); err != nil { return err } + fmt.Fprintf(writer, "}\n") return nil } -func genMembers(writer io.Writer, tp spec.Type, isParam bool, inlineType func(string) (*spec.Type, error), prefixForType func(string) string) error { - members := tp.GetBodyMembers() +func genMembers(writer io.Writer, tp spec.Type, isParam bool) error { + definedType, ok := tp.(spec.DefineStruct) + if !ok { + return errors.New("no members of type " + tp.Name()) + } + + members := definedType.GetBodyMembers() if isParam { - members = tp.GetNonBodyMembers() + members = definedType.GetNonBodyMembers() } for _, member := range members { if member.IsInline { - // 获取inline类型的成员然后添加到type中 - it, err := inlineType(strings.TrimPrefix(member.Type, "*")) - if err != nil { - return err - } - if err := genMembers(writer, *it, isParam, inlineType, prefixForType); err != nil { + if err := genMembers(writer, member.Type, isParam); err != nil { return err } continue } - if err := writeProperty(writer, member, 1, prefixForType); err != nil { - return apiutil.WrapErr(err, " type "+tp.Name) + + if err := writeProperty(writer, member, 1); err != nil { + return apiutil.WrapErr(err, " type "+tp.Name()) } } return nil diff --git a/tools/goctl/api/util/annotation.go b/tools/goctl/api/util/annotation.go deleted file mode 100644 index 196c6b6f..00000000 --- a/tools/goctl/api/util/annotation.go +++ /dev/null @@ -1,20 +0,0 @@ -package util - -import ( - "strings" - - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -func GetAnnotationValue(annos []spec.Annotation, key, field string) (string, bool) { - for _, anno := range annos { - if anno.Name == field && len(anno.Value) > 0 { - return anno.Value, true - } - if anno.Name == key { - value, ok := anno.Properties[field] - return strings.TrimSpace(value), ok - } - } - return "", false -} diff --git a/tools/goctl/api/util/case.go b/tools/goctl/api/util/case.go index 5a93b4eb..64c330c2 100644 --- a/tools/goctl/api/util/case.go +++ b/tools/goctl/api/util/case.go @@ -1,5 +1,10 @@ package util +import ( + "strings" + "unicode" +) + func IsUpperCase(r rune) bool { if r >= 'A' && r <= 'Z' { return true @@ -15,7 +20,7 @@ func IsLowerCase(r rune) bool { } func ToSnakeCase(s string) string { - out := []rune{} + var out []rune for index, r := range s { if index == 0 { out = append(out, ToLowerCase(r)) @@ -77,7 +82,7 @@ func ToUpperCase(r rune) rune { } func ToLower(s string) string { - out := []rune{} + var out []rune for _, r := range s { out = append(out, ToLowerCase(r)) } @@ -85,7 +90,7 @@ func ToLower(s string) string { } func ToUpper(s string) string { - out := []rune{} + var out []rune for _, r := range s { out = append(out, ToUpperCase(r)) } @@ -105,3 +110,18 @@ func UpperFirst(s string) string { } return ToUpper(s[:1]) + s[1:] } + +func UnExport(text string) bool { + var flag bool + str := strings.Map(func(r rune) rune { + if flag { + return r + } + if unicode.IsLetter(r) { + flag = true + return unicode.ToLower(r) + } + return r + }, text) + return str == text +} diff --git a/tools/goctl/api/util/types.go b/tools/goctl/api/util/types.go deleted file mode 100644 index 34a360d6..00000000 --- a/tools/goctl/api/util/types.go +++ /dev/null @@ -1,159 +0,0 @@ -package util - -import ( - "fmt" - "strings" - - "github.com/tal-tech/go-zero/tools/goctl/api/spec" -) - -func DecomposeType(t string) (result []string, err error) { - add := func(tp string) error { - ret, err := DecomposeType(tp) - if err != nil { - return err - } - - result = append(result, ret...) - return nil - } - if strings.HasPrefix(t, "map") { - t = strings.ReplaceAll(t, "map", "") - if t[0] == '[' { - pos := strings.Index(t, "]") - if pos > 1 { - if err = add(t[1:pos]); err != nil { - return - } - if len(t) > pos+1 { - err = add(t[pos+1:]) - return - } - } - } - } else if strings.HasPrefix(t, "[]") { - if len(t) > 2 { - err = add(t[2:]) - return - } - } else if strings.HasPrefix(t, "*") { - err = add(t[1:]) - return - } else { - result = append(result, t) - return - } - - err = fmt.Errorf("bad type %q", t) - return -} - -func GetAllTypes(api *spec.ApiSpec, route spec.Route) []spec.Type { - var rts []spec.Type - types := api.Types - getTypeRecursive(route.RequestType, types, &rts) - getTypeRecursive(route.ResponseType, types, &rts) - return rts -} - -func GetLocalTypes(api *spec.ApiSpec, route spec.Route) []spec.Type { - sharedTypes := GetSharedTypes(api) - isSharedType := func(ty spec.Type) bool { - for _, item := range sharedTypes { - if item.Name == ty.Name { - return true - } - } - return false - } - - var rts = GetAllTypes(api, route) - - var result []spec.Type - for _, item := range rts { - if !isSharedType(item) { - result = append(result, item) - } - } - return result -} - -func getTypeRecursive(ty spec.Type, allTypes []spec.Type, result *[]spec.Type) { - isCustomType := func(name string) (*spec.Type, bool) { - for _, item := range allTypes { - if item.Name == name { - return &item, true - } - } - return nil, false - } - if len(ty.Name) > 0 { - *result = append(*result, ty) - } - for _, member := range ty.Members { - decomposedItems, _ := DecomposeType(member.Type) - if len(decomposedItems) == 0 { - continue - } - var customTypes []spec.Type - for _, item := range decomposedItems { - c, e := isCustomType(item) - if e { - customTypes = append(customTypes, *c) - } - } - for _, ty := range customTypes { - hasAppend := false - for _, item := range *result { - if ty.Name == item.Name { - hasAppend = true - break - } - - } - if !hasAppend { - getTypeRecursive(ty, allTypes, result) - } - } - } -} - -func GetSharedTypes(api *spec.ApiSpec) []spec.Type { - types := api.Types - var result []spec.Type - var container []spec.Type - hasInclude := func(all []spec.Type, ty spec.Type) bool { - for _, item := range all { - if item.Name == ty.Name { - return true - } - } - return false - } - for _, route := range api.Service.Routes() { - var rts []spec.Type - getTypeRecursive(route.RequestType, types, &rts) - getTypeRecursive(route.ResponseType, types, &rts) - for _, item := range rts { - if len(item.Name) == 0 { - continue - } - if hasInclude(container, item) { - hasAppend := false - for _, r := range result { - if item.Name == r.Name { - hasAppend = true - break - } - - } - if !hasAppend { - result = append(result, item) - } - } else { - container = append(container, item) - } - } - } - return result -} diff --git a/tools/goctl/api/validate/validate.go b/tools/goctl/api/validate/validate.go index 4b06c515..1f1769ef 100644 --- a/tools/goctl/api/validate/validate.go +++ b/tools/goctl/api/validate/validate.go @@ -16,11 +16,7 @@ func GoValidateApi(c *cli.Context) error { return errors.New("missing -api") } - p, err := parser.NewParser(apiFile) - if err != nil { - return err - } - _, err = p.Parse() + _, err := parser.Parse(apiFile) if err == nil { fmt.Println(aurora.Green("api format ok")) } diff --git a/tools/goctl/goctl.go b/tools/goctl/goctl.go index 0ee4ab73..2406b3ad 100644 --- a/tools/goctl/goctl.go +++ b/tools/goctl/goctl.go @@ -23,12 +23,18 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/plugin" rpc "github.com/tal-tech/go-zero/tools/goctl/rpc/cli" "github.com/tal-tech/go-zero/tools/goctl/tpl" + "github.com/tal-tech/go-zero/tools/goctl/upgrade" "github.com/urfave/cli" ) var ( BuildVersion = "1.1.3" commands = []cli.Command{ + { + Name: "upgrade", + Usage: "upgrade goctl to latest version", + Action: upgrade.Upgrade, + }, { Name: "api", Usage: "generate api related files", diff --git a/tools/goctl/plugin/plugin.go b/tools/goctl/plugin/plugin.go index 2718f88b..5dd2c311 100644 --- a/tools/goctl/plugin/plugin.go +++ b/tools/goctl/plugin/plugin.go @@ -20,9 +20,7 @@ import ( "github.com/urfave/cli" ) -const ( - pluginArg = "_plugin" -) +const pluginArg = "_plugin" type Plugin struct { Api *spec.ApiSpec @@ -74,12 +72,7 @@ func prepareArgs(c *cli.Context) ([]byte, error) { var transferData Plugin if len(apiPath) > 0 && util.FileExists(apiPath) { - p, err := parser.NewParser(apiPath) - if err != nil { - return nil, err - } - - api, err := p.Parse() + api, err := parser.Parse(apiPath) if err != nil { return nil, err } @@ -183,6 +176,7 @@ func getPluginAndArgs(arg string) (string, string) { return trimQuote(arg[:i]), trimQuote(arg[i+1:]) } + func trimQuote(in string) string { in = strings.Trim(in, `"`) in = strings.Trim(in, `'`) diff --git a/tools/goctl/upgrade/upgrade.go b/tools/goctl/upgrade/upgrade.go new file mode 100644 index 00000000..daed3d10 --- /dev/null +++ b/tools/goctl/upgrade/upgrade.go @@ -0,0 +1,18 @@ +package upgrade + +import ( + "fmt" + + "github.com/tal-tech/go-zero/tools/goctl/rpc/execx" + "github.com/urfave/cli" +) + +func Upgrade(_ *cli.Context) error { + info, err := execx.Run("GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl", "") + if err != nil { + return err + } + + fmt.Print(info) + return nil +}