You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-zero/tools/goctl/api/parser/g4/ast/ast.go

341 lines
6.4 KiB
Go

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 defines a token
TokenStream interface {
GetStart() antlr.Token
GetStop() antlr.Token
GetParser() antlr.Parser
}
// ApiVisitor wraps api.BaseApiParserVisitor to call methods which has prefix Visit to
// visit node from the api syntax
ApiVisitor struct {
api.BaseApiParserVisitor
debug bool
log console.Console
prefix string
infoFlag bool
}
// VisitorOption defines a function with argument ApiVisitor
VisitorOption func(v *ApiVisitor)
// Spec describes api spec
Spec interface {
Doc() []Expr
Comment() Expr
Format() error
Equal(v interface{}) bool
}
// Expr describes ast expression
Expr interface {
Prefix() string
Line() int
Column() int
Text() string
SetText(text string)
Start() int
Stop() int
Equal(expr Expr) bool
IsNotNil() bool
}
)
// NewApiVisitor creates an instance for ApiVisitor
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)
}
// WithVisitorPrefix returns a VisitorOption wrap with specified prefix
func WithVisitorPrefix(prefix string) VisitorOption {
return func(v *ApiVisitor) {
v.prefix = prefix
}
}
// WithVisitorDebug returns a debug VisitorOption
func WithVisitorDebug() VisitorOption {
return func(v *ApiVisitor) {
v.debug = true
}
}
type defaultExpr struct {
prefix, v string
line, column int
start, stop int
}
// NewTextExpr creates a default instance for Expr
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
}
// EqualDoc compares whether the element literals in two Spec are equal
func EqualDoc(spec1, spec2 Spec) bool {
if spec1 == nil {
return spec2 == nil
}
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()))
}
}