mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-10 08:29:58 +08:00
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:
46
tools/goctl/api/parser/g4/ApiLexer.g4
Normal file
46
tools/goctl/api/parser/g4/ApiLexer.g4
Normal file
@@ -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
|
||||
;
|
||||
73
tools/goctl/api/parser/g4/ApiParser.g4
Normal file
73
tools/goctl/api/parser/g4/ApiParser.g4
Normal file
@@ -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)?)))+;
|
||||
251
tools/goctl/api/parser/g4/ast/api.go
Normal file
251
tools/goctl/api/parser/g4/ast/api.go
Normal file
@@ -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
|
||||
}
|
||||
405
tools/goctl/api/parser/g4/ast/apiparser.go
Normal file
405
tools/goctl/api/parser/g4/ast/apiparser.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
325
tools/goctl/api/parser/g4/ast/ast.go
Normal file
325
tools/goctl/api/parser/g4/ast/ast.go
Normal file
@@ -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()))
|
||||
}
|
||||
}
|
||||
96
tools/goctl/api/parser/g4/ast/import.go
Normal file
96
tools/goctl/api/parser/g4/ast/import.go
Normal file
@@ -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
|
||||
}
|
||||
67
tools/goctl/api/parser/g4/ast/info.go
Normal file
67
tools/goctl/api/parser/g4/ast/info.go
Normal file
@@ -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
|
||||
}
|
||||
79
tools/goctl/api/parser/g4/ast/kv.go
Normal file
79
tools/goctl/api/parser/g4/ast/kv.go
Normal file
@@ -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
|
||||
}
|
||||
5
tools/goctl/api/parser/g4/ast/placeholder.go
Normal file
5
tools/goctl/api/parser/g4/ast/placeholder.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package ast
|
||||
|
||||
var Holder PlaceHolder
|
||||
|
||||
type PlaceHolder struct{}
|
||||
603
tools/goctl/api/parser/g4/ast/service.go
Normal file
603
tools/goctl/api/parser/g4/ast/service.go
Normal file
@@ -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
|
||||
}
|
||||
58
tools/goctl/api/parser/g4/ast/syntax.go
Normal file
58
tools/goctl/api/parser/g4/ast/syntax.go
Normal file
@@ -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
|
||||
}
|
||||
677
tools/goctl/api/parser/g4/ast/type.go
Normal file
677
tools/goctl/api/parser/g4/ast/type.go
Normal file
@@ -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
|
||||
}
|
||||
156
tools/goctl/api/parser/g4/gen/api/apiparser_base_visitor.go
Normal file
156
tools/goctl/api/parser/g4/gen/api/apiparser_base_visitor.go
Normal file
@@ -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)
|
||||
}
|
||||
234
tools/goctl/api/parser/g4/gen/api/apiparser_lexer.go
Normal file
234
tools/goctl/api/parser/g4/gen/api/apiparser_lexer.go
Normal file
@@ -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
|
||||
5688
tools/goctl/api/parser/g4/gen/api/apiparser_parser.go
Normal file
5688
tools/goctl/api/parser/g4/gen/api/apiparser_parser.go
Normal file
File diff suppressed because it is too large
Load Diff
120
tools/goctl/api/parser/g4/gen/api/apiparser_visitor.go
Normal file
120
tools/goctl/api/parser/g4/gen/api/apiparser_visitor.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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"
|
||||
|
||||
// A complete Visitor for a parse tree produced by ApiParserParser.
|
||||
type ApiParserVisitor interface {
|
||||
antlr.ParseTreeVisitor
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#api.
|
||||
VisitApi(ctx *ApiContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#spec.
|
||||
VisitSpec(ctx *SpecContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#syntaxLit.
|
||||
VisitSyntaxLit(ctx *SyntaxLitContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#importSpec.
|
||||
VisitImportSpec(ctx *ImportSpecContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#importLit.
|
||||
VisitImportLit(ctx *ImportLitContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#importBlock.
|
||||
VisitImportBlock(ctx *ImportBlockContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#importBlockValue.
|
||||
VisitImportBlockValue(ctx *ImportBlockValueContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#importValue.
|
||||
VisitImportValue(ctx *ImportValueContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#infoSpec.
|
||||
VisitInfoSpec(ctx *InfoSpecContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeSpec.
|
||||
VisitTypeSpec(ctx *TypeSpecContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeLit.
|
||||
VisitTypeLit(ctx *TypeLitContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeBlock.
|
||||
VisitTypeBlock(ctx *TypeBlockContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeLitBody.
|
||||
VisitTypeLitBody(ctx *TypeLitBodyContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeBlockBody.
|
||||
VisitTypeBlockBody(ctx *TypeBlockBodyContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeStruct.
|
||||
VisitTypeStruct(ctx *TypeStructContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeAlias.
|
||||
VisitTypeAlias(ctx *TypeAliasContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeBlockStruct.
|
||||
VisitTypeBlockStruct(ctx *TypeBlockStructContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#typeBlockAlias.
|
||||
VisitTypeBlockAlias(ctx *TypeBlockAliasContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#field.
|
||||
VisitField(ctx *FieldContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#normalField.
|
||||
VisitNormalField(ctx *NormalFieldContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#anonymousFiled.
|
||||
VisitAnonymousFiled(ctx *AnonymousFiledContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#dataType.
|
||||
VisitDataType(ctx *DataTypeContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#pointerType.
|
||||
VisitPointerType(ctx *PointerTypeContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#mapType.
|
||||
VisitMapType(ctx *MapTypeContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#arrayType.
|
||||
VisitArrayType(ctx *ArrayTypeContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#serviceSpec.
|
||||
VisitServiceSpec(ctx *ServiceSpecContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#atServer.
|
||||
VisitAtServer(ctx *AtServerContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#serviceApi.
|
||||
VisitServiceApi(ctx *ServiceApiContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#serviceRoute.
|
||||
VisitServiceRoute(ctx *ServiceRouteContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#atDoc.
|
||||
VisitAtDoc(ctx *AtDocContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#atHandler.
|
||||
VisitAtHandler(ctx *AtHandlerContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#route.
|
||||
VisitRoute(ctx *RouteContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#body.
|
||||
VisitBody(ctx *BodyContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#replybody.
|
||||
VisitReplybody(ctx *ReplybodyContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#kvLit.
|
||||
VisitKvLit(ctx *KvLitContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#serviceName.
|
||||
VisitServiceName(ctx *ServiceNameContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by ApiParserParser#path.
|
||||
VisitPath(ctx *PathContext) interface{}
|
||||
}
|
||||
222
tools/goctl/api/parser/g4/gen/api/baseparser.go
Normal file
222
tools/goctl/api/parser/g4/gen/api/baseparser.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
)
|
||||
|
||||
const (
|
||||
versionRegex = `(?m)"v[1-9][0-9]*"`
|
||||
importValueRegex = `(?m)"(/?[a-zA-Z0-9_#-])+\.api"`
|
||||
tagRegex = `(?m)\x60[a-z]+:".+"\x60`
|
||||
)
|
||||
|
||||
var holder = struct{}{}
|
||||
var kind = map[string]struct{}{
|
||||
"bool": holder,
|
||||
"int": holder,
|
||||
"int8": holder,
|
||||
"int16": holder,
|
||||
"int32": holder,
|
||||
"int64": holder,
|
||||
"uint": holder,
|
||||
"uint8": holder,
|
||||
"uint16": holder,
|
||||
"uint32": holder,
|
||||
"uint64": holder,
|
||||
"uintptr": holder,
|
||||
"float32": holder,
|
||||
"float64": holder,
|
||||
"complex64": holder,
|
||||
"complex128": holder,
|
||||
"string": holder,
|
||||
"byte": holder,
|
||||
"rune": holder,
|
||||
}
|
||||
|
||||
func match(p *ApiParserParser, text string) {
|
||||
v := getCurrentTokenText(p)
|
||||
|
||||
if v != text {
|
||||
notifyErrorListeners(p, expecting(text, v))
|
||||
}
|
||||
}
|
||||
|
||||
func checkVersion(p *ApiParserParser) {
|
||||
v := getCurrentTokenText(p)
|
||||
if !matchRegex(v, versionRegex) {
|
||||
notifyErrorListeners(p, mismatched("version", v))
|
||||
}
|
||||
}
|
||||
|
||||
func checkImportValue(p *ApiParserParser) {
|
||||
v := getCurrentTokenText(p)
|
||||
if !matchRegex(v, importValueRegex) {
|
||||
notifyErrorListeners(p, mismatched("import value", v))
|
||||
}
|
||||
}
|
||||
|
||||
func checkKeyValue(p *ApiParserParser) {
|
||||
v := getCurrentTokenText(p)
|
||||
if !strings.HasPrefix(v, ":") {
|
||||
notifyErrorListeners(p, mismatched(":", v))
|
||||
}
|
||||
|
||||
v = strings.TrimPrefix(v, ":")
|
||||
v = strings.TrimFunc(v, func(r rune) bool {
|
||||
return unicode.IsSpace(r)
|
||||
})
|
||||
setCurrentTokenText(p, v)
|
||||
}
|
||||
|
||||
func checkHttpMethod(p *ApiParserParser) {
|
||||
method := getCurrentTokenText(p)
|
||||
uppler := strings.ToUpper(method)
|
||||
switch uppler {
|
||||
case http.MethodPost, http.MethodGet, http.MethodHead,
|
||||
http.MethodPut, http.MethodPatch, http.MethodDelete,
|
||||
http.MethodConnect, http.MethodOptions, http.MethodTrace:
|
||||
if method != strings.ToLower(method) {
|
||||
notifyErrorListeners(p, expecting("http method lower case", method))
|
||||
}
|
||||
default:
|
||||
notifyErrorListeners(p, expecting("http method", method))
|
||||
}
|
||||
}
|
||||
|
||||
func checkKeyword(p *ApiParserParser) {
|
||||
v := getCurrentTokenText(p)
|
||||
if IsGolangKeyWord(v) {
|
||||
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", v))
|
||||
}
|
||||
}
|
||||
|
||||
func checkKey(p *ApiParserParser) {
|
||||
v := getCurrentTokenText(p)
|
||||
if IsGolangKeyWord(v) {
|
||||
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", v))
|
||||
}
|
||||
|
||||
if _, ok := kind[v]; !ok {
|
||||
notifyErrorListeners(p, fmt.Sprintf("expecting golang basic type, found : '%s'", v))
|
||||
}
|
||||
}
|
||||
|
||||
func IsBasicType(text string) bool {
|
||||
_, ok := kind[text]
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsGolangKeyWord(text string, excepts ...string) bool {
|
||||
for _, each := range excepts {
|
||||
if text == each {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch text {
|
||||
case "var", "const", "package", "import", "func", "return",
|
||||
"defer", "go", "select", "interface", "struct", "break", "case",
|
||||
"continue", "for", "fallthrough", "else", "if", "switch", "goto",
|
||||
"default", "chan", "type", "map", "range":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isNormal(p *ApiParserParser) bool {
|
||||
ct := p.GetTokenStream().(*antlr.CommonTokenStream)
|
||||
line := p.GetCurrentToken().GetLine()
|
||||
tokens := ct.GetAllTokens()
|
||||
var list []string
|
||||
for _, token := range tokens {
|
||||
if token.GetLine() == line {
|
||||
text := token.GetText()
|
||||
if strings.HasPrefix(text, "//") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(text, "/*") {
|
||||
continue
|
||||
}
|
||||
if text == "<EOF>" {
|
||||
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)
|
||||
}
|
||||
11
tools/goctl/api/parser/g4/gen/api/baseparser_test.go
Normal file
11
tools/goctl/api/parser/g4/gen/api/baseparser_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
assert.False(t, matchRegex("v1ddd", versionRegex))
|
||||
}
|
||||
99
tools/goctl/api/parser/g4/test.api
Normal file
99
tools/goctl/api/parser/g4/test.api
Normal file
@@ -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
|
||||
}
|
||||
369
tools/goctl/api/parser/g4/test/apiparser_test.go
Normal file
369
tools/goctl/api/parser/g4/test/apiparser_test.go
Normal 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")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
0
tools/goctl/api/parser/g4/test/apis/empty.api
Normal file
0
tools/goctl/api/parser/g4/test/apis/empty.api
Normal file
72
tools/goctl/api/parser/g4/test/apis/example.api
Normal file
72
tools/goctl/api/parser/g4/test/apis/example.api
Normal 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)
|
||||
}
|
||||
6
tools/goctl/api/parser/g4/test/apis/info.api
Normal file
6
tools/goctl/api/parser/g4/test/apis/info.api
Normal file
@@ -0,0 +1,6 @@
|
||||
info(
|
||||
author: songmeizi
|
||||
desc: "the sample of
|
||||
info"
|
||||
date: "2020-01-06"
|
||||
)
|
||||
29
tools/goctl/api/parser/g4/test/apis/service.api
Normal file
29
tools/goctl/api/parser/g4/test/apis/service.api
Normal 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)
|
||||
}
|
||||
1
tools/goctl/api/parser/g4/test/apis/syntax.api
Normal file
1
tools/goctl/api/parser/g4/test/apis/syntax.api
Normal file
@@ -0,0 +1 @@
|
||||
syntax = "v1"
|
||||
111
tools/goctl/api/parser/g4/test/apis/test.api
Normal file
111
tools/goctl/api/parser/g4/test/apis/test.api
Normal 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)
|
||||
}
|
||||
23
tools/goctl/api/parser/g4/test/apis/types.api
Normal file
23
tools/goctl/api/parser/g4/test/apis/types.api
Normal 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
|
||||
}
|
||||
|
||||
457
tools/goctl/api/parser/g4/test/ast_test.go
Normal file
457
tools/goctl/api/parser/g4/test/ast_test.go
Normal 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"
|
||||
`)
|
||||
}
|
||||
25
tools/goctl/api/parser/g4/test/grammar_test.go
Normal file
25
tools/goctl/api/parser/g4/test/grammar_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
143
tools/goctl/api/parser/g4/test/import_test.go
Normal file
143
tools/goctl/api/parser/g4/test/import_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
186
tools/goctl/api/parser/g4/test/info_test.go
Normal file
186
tools/goctl/api/parser/g4/test/info_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
686
tools/goctl/api/parser/g4/test/service_test.go
Normal file
686
tools/goctl/api/parser/g4/test/service_test.go
Normal 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"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
75
tools/goctl/api/parser/g4/test/syntax_test.go
Normal file
75
tools/goctl/api/parser/g4/test/syntax_test.go
Normal 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"),
|
||||
}))
|
||||
})
|
||||
}
|
||||
467
tools/goctl/api/parser/g4/test/type_test.go
Normal file
467
tools/goctl/api/parser/g4/test/type_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user