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.
341 lines
6.4 KiB
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()))
|
|
}
|
|
}
|