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:
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
|
||||
}
|
||||
Reference in New Issue
Block a user