feature: refactor api parse to g4 (#365)

* feature: refactor api parse to g4

* new g4 parser

* add CHANGE_LOG.MD

* refactor

* fix byte bug

* refactor

* optimized

* optimized

* revert

* update readme.md

* update readme.md

* update readme.md

* update readme.md

* remove no need

* fix java gen

* add upgrade

* resolve confilits

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
This commit is contained in:
kingxt
2021-01-11 15:10:51 +08:00
committed by GitHub
parent b0ccfb8eb4
commit ee19fb736b
88 changed files with 13641 additions and 2458 deletions

View File

@@ -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")},
},
},
},
},
},
},
},
},
}))
})
}

View File

@@ -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)
}

View File

@@ -0,0 +1,6 @@
info(
author: songmeizi
desc: "the sample of
info"
date: "2020-01-06"
)

View File

@@ -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)
}

View File

@@ -0,0 +1 @@
syntax = "v1"

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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"
`)
}

View File

@@ -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)
})
}
}

View File

@@ -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)
})
}

View File

@@ -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)
})
}

View File

@@ -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"),
},
},
},
},
}))
})
}

View File

@@ -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"),
}))
})
}

View File

@@ -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)
})
}