feature: refactor api parse to g4 (#365)

* feature: refactor api parse to g4

* new g4 parser

* add CHANGE_LOG.MD

* refactor

* fix byte bug

* refactor

* optimized

* optimized

* revert

* update readme.md

* update readme.md

* update readme.md

* update readme.md

* remove no need

* fix java gen

* add upgrade

* resolve confilits

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

View File

@@ -0,0 +1,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
}

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

View 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()))
}
}

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

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

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

View File

@@ -0,0 +1,5 @@
package ast
var Holder PlaceHolder
type PlaceHolder struct{}

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

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

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