feat(goctl): Add api parser (#2585)

This commit is contained in:
anqiansong
2023-03-28 23:45:26 +08:00
committed by GitHub
parent 455a6c8f97
commit 50bc361430
59 changed files with 11633 additions and 6 deletions

View File

@@ -0,0 +1,223 @@
package ast
import (
"fmt"
"io"
"strings"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
"github.com/zeromicro/go-zero/tools/goctl/util"
)
// Node represents a node in the AST.
type Node interface {
// Pos returns the position of the first character belonging to the node.
Pos() token.Position
// End returns the position of the first character immediately after the node.
End() token.Position
// Format returns the node's text after format.
Format(...string) string
// HasHeadCommentGroup returns true if the node has head comment group.
HasHeadCommentGroup() bool
// HasLeadingCommentGroup returns true if the node has leading comment group.
HasLeadingCommentGroup() bool
// CommentGroup returns the node's head comment group and leading comment group.
CommentGroup() (head, leading CommentGroup)
}
// Stmt represents a statement in the AST.
type Stmt interface {
Node
stmtNode()
}
// Expr represents an expression in the AST.
type Expr interface {
Node
exprNode()
}
// AST represents a parsed API file.
type AST struct {
Filename string
Stmts []Stmt
readPosition int
}
// TokenNode represents a token node in the AST.
type TokenNode struct {
// HeadCommentGroup are the comments in prev lines.
HeadCommentGroup CommentGroup
// Token is the token of the node.
Token token.Token
// LeadingCommentGroup are the tail comments in same line.
LeadingCommentGroup CommentGroup
// headFlag and leadingFlag is a comment flag only used in transfer another Node to TokenNode,
// headFlag's value is true do not represent HeadCommentGroup is not empty,
// leadingFlag's values is true do not represent LeadingCommentGroup is not empty.
headFlag, leadingFlag bool
}
// NewTokenNode creates and returns a new TokenNode.
func NewTokenNode(tok token.Token) *TokenNode {
return &TokenNode{Token: tok}
}
// IsEmptyString returns true if the node is empty string.
func (t *TokenNode) IsEmptyString() bool {
return t.Equal("")
}
// IsZeroString returns true if the node is zero string.
func (t *TokenNode) IsZeroString() bool {
return t.Equal(`""`) || t.Equal("``")
}
// Equal returns true if the node's text is equal to the given text.
func (t *TokenNode) Equal(s string) bool {
return t.Token.Text == s
}
// SetLeadingCommentGroup sets the node's leading comment group.
func (t *TokenNode) SetLeadingCommentGroup(cg CommentGroup) {
t.LeadingCommentGroup = cg
}
func (t *TokenNode) HasLeadingCommentGroup() bool {
return t.LeadingCommentGroup.Valid() || t.leadingFlag
}
func (t *TokenNode) HasHeadCommentGroup() bool {
return t.HeadCommentGroup.Valid() || t.headFlag
}
func (t *TokenNode) CommentGroup() (head, leading CommentGroup) {
return t.HeadCommentGroup, t.LeadingCommentGroup
}
// PeekFirstLeadingComment returns the first leading comment of the node.
func (t *TokenNode) PeekFirstLeadingComment() *CommentStmt {
if len(t.LeadingCommentGroup) > 0 {
return t.LeadingCommentGroup[0]
}
return nil
}
// PeekFirstHeadComment returns the first head comment of the node.
func (t *TokenNode) PeekFirstHeadComment() *CommentStmt {
if len(t.HeadCommentGroup) > 0 {
return t.HeadCommentGroup[0]
}
return nil
}
func (t *TokenNode) Format(prefix ...string) string {
p := peekOne(prefix)
var textList []string
for _, v := range t.HeadCommentGroup {
textList = append(textList, v.Format(p))
}
var tokenText = p + t.Token.Text
var validLeadingCommentGroup CommentGroup
for _, e := range t.LeadingCommentGroup {
if util.IsEmptyStringOrWhiteSpace(e.Comment.Text) {
continue
}
validLeadingCommentGroup = append(validLeadingCommentGroup, e)
}
if len(validLeadingCommentGroup) > 0 {
tokenText = tokenText + WhiteSpace + t.LeadingCommentGroup.Join(WhiteSpace)
}
textList = append(textList, tokenText)
return strings.Join(textList, NewLine)
}
func (t *TokenNode) Pos() token.Position {
if len(t.HeadCommentGroup) > 0 {
return t.PeekFirstHeadComment().Pos()
}
return t.Token.Position
}
func (t *TokenNode) End() token.Position {
if len(t.LeadingCommentGroup) > 0 {
return t.LeadingCommentGroup[len(t.LeadingCommentGroup)-1].End()
}
return t.Token.Position
}
// Format formats the AST.
func (a *AST) Format(w io.Writer) {
fw := NewWriter(w)
defer fw.Flush()
for idx, e := range a.Stmts {
if e.Format() == NilIndent {
continue
}
fw.Write(withNode(e))
fw.NewLine()
switch e.(type) {
case *SyntaxStmt:
fw.NewLine()
case *ImportGroupStmt:
fw.NewLine()
case *ImportLiteralStmt:
if idx < len(a.Stmts)-1 {
_, ok := a.Stmts[idx+1].(*ImportLiteralStmt)
if !ok {
fw.NewLine()
}
}
case *InfoStmt:
fw.NewLine()
case *ServiceStmt:
fw.NewLine()
case *TypeGroupStmt:
fw.NewLine()
case *TypeLiteralStmt:
fw.NewLine()
case *CommentStmt:
}
}
}
// FormatForUnitTest formats the AST for unit test.
func (a *AST) FormatForUnitTest(w io.Writer) {
fw := NewWriter(w)
defer fw.Flush()
for _, e := range a.Stmts {
text := e.Format()
if text == NilIndent {
continue
}
fw.WriteText(text)
}
}
// Print prints the AST.
func (a *AST) Print() {
_ = Print(a)
}
// SyntaxError represents a syntax error.
func SyntaxError(pos token.Position, format string, v ...interface{}) error {
return fmt.Errorf("syntax error: %s %s", pos.String(), fmt.Sprintf(format, v...))
}
// DuplicateStmtError represents a duplicate statement error.
func DuplicateStmtError(pos token.Position, msg string) error {
return fmt.Errorf("duplicate declaration: %s %s", pos.String(), msg)
}
func peekOne(list []string) string {
if len(list) == 0 {
return ""
}
return list[0]
}

View File

@@ -0,0 +1,75 @@
package ast
import (
"strings"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
"github.com/zeromicro/go-zero/tools/goctl/util"
)
// CommentGroup represents a list of comments.
type CommentGroup []*CommentStmt
// List returns the list of comments.
func (cg CommentGroup) List() []string {
var list = make([]string, 0, len(cg))
for _, v := range cg {
comment := v.Comment.Text
if util.IsEmptyStringOrWhiteSpace(comment) {
continue
}
list = append(list, comment)
}
return list
}
// String joins and returns the comment text.
func (cg CommentGroup) String() string {
return cg.Join(" ")
}
// Join joins the comments with the given separator.
func (cg CommentGroup) Join(sep string) string {
if !cg.Valid() {
return ""
}
list := cg.List()
return strings.Join(list, sep)
}
// Valid returns true if the comment is valid.
func (cg CommentGroup) Valid() bool {
return len(cg) > 0
}
// CommentStmt represents a comment statement.
type CommentStmt struct {
// Comment is the comment token.
Comment token.Token
}
func (c *CommentStmt) HasHeadCommentGroup() bool {
return false
}
func (c *CommentStmt) HasLeadingCommentGroup() bool {
return false
}
func (c *CommentStmt) CommentGroup() (head, leading CommentGroup) {
return
}
func (c *CommentStmt) stmtNode() {}
func (c *CommentStmt) Pos() token.Position {
return c.Comment.Position
}
func (c *CommentStmt) End() token.Position {
return c.Comment.Position
}
func (c *CommentStmt) Format(prefix ...string) string {
return peekOne(prefix) + c.Comment.Text
}

View File

@@ -0,0 +1,111 @@
package ast
import "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
// ImportStmt represents an import statement.
type ImportStmt interface {
Stmt
importNode()
}
// ImportLiteralStmt represents an import literal statement.
type ImportLiteralStmt struct {
// Import is the import token.
Import *TokenNode
// Value is the import value.
Value *TokenNode
}
func (i *ImportLiteralStmt) HasHeadCommentGroup() bool {
return i.Import.HasHeadCommentGroup()
}
func (i *ImportLiteralStmt) HasLeadingCommentGroup() bool {
return i.Value.HasLeadingCommentGroup()
}
func (i *ImportLiteralStmt) CommentGroup() (head, leading CommentGroup) {
return i.Import.HeadCommentGroup, i.Value.LeadingCommentGroup
}
func (i *ImportLiteralStmt) Format(prefix ...string) (result string) {
if i.Value.IsZeroString() {
return ""
}
w := NewBufferWriter()
importNode := transferTokenNode(i.Import, ignoreLeadingComment(), withTokenNodePrefix(prefix...))
w.Write(withNode(importNode, i.Value), withMode(ModeExpectInSameLine))
return w.String()
}
func (i *ImportLiteralStmt) End() token.Position {
return i.Value.End()
}
func (i *ImportLiteralStmt) importNode() {}
func (i *ImportLiteralStmt) Pos() token.Position {
return i.Import.Pos()
}
func (i *ImportLiteralStmt) stmtNode() {}
type ImportGroupStmt struct {
// Import is the import token.
Import *TokenNode
// LParen is the left parenthesis token.
LParen *TokenNode
// Values is the import values.
Values []*TokenNode
// RParen is the right parenthesis token.
RParen *TokenNode
}
func (i *ImportGroupStmt) HasHeadCommentGroup() bool {
return i.Import.HasHeadCommentGroup()
}
func (i *ImportGroupStmt) HasLeadingCommentGroup() bool {
return i.RParen.HasLeadingCommentGroup()
}
func (i *ImportGroupStmt) CommentGroup() (head, leading CommentGroup) {
return i.Import.HeadCommentGroup, i.RParen.LeadingCommentGroup
}
func (i *ImportGroupStmt) Format(prefix ...string) string {
var textList []string
for _, v := range i.Values {
if v.IsZeroString() {
continue
}
textList = append(textList, v.Format(Indent))
}
if len(textList) == 0 {
return ""
}
importNode := transferTokenNode(i.Import, ignoreLeadingComment(), withTokenNodePrefix(prefix...))
w := NewBufferWriter()
w.Write(withNode(importNode, i.LParen), expectSameLine())
w.NewLine()
for _, v := range i.Values {
node := transferTokenNode(v, withTokenNodePrefix(peekOne(prefix)+Indent))
w.Write(withNode(node), expectSameLine())
w.NewLine()
}
w.Write(withNode(transferTokenNode(i.RParen, withTokenNodePrefix(prefix...))))
return w.String()
}
func (i *ImportGroupStmt) End() token.Position {
return i.RParen.End()
}
func (i *ImportGroupStmt) importNode() {}
func (i *ImportGroupStmt) Pos() token.Position {
return i.Import.Pos()
}
func (i *ImportGroupStmt) stmtNode() {}

View File

@@ -0,0 +1,65 @@
package ast
import "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
// InfoStmt is the info statement.
type InfoStmt struct {
// Info is the info keyword.
Info *TokenNode
// LParen is the left parenthesis.
LParen *TokenNode
// Values is the info values.
Values []*KVExpr
// RParen is the right parenthesis.
RParen *TokenNode
}
func (i *InfoStmt) HasHeadCommentGroup() bool {
return i.Info.HasHeadCommentGroup()
}
func (i *InfoStmt) HasLeadingCommentGroup() bool {
return i.RParen.HasLeadingCommentGroup()
}
func (i *InfoStmt) CommentGroup() (head, leading CommentGroup) {
return i.Info.HeadCommentGroup, i.RParen.LeadingCommentGroup
}
func (i *InfoStmt) Format(prefix ...string) string {
if len(i.Values) == 0 {
return ""
}
var textList []string
for _, v := range i.Values {
if v.Value.IsZeroString() {
continue
}
textList = append(textList, v.Format(Indent))
}
if len(textList) == 0 {
return ""
}
w := NewBufferWriter()
infoNode := transferTokenNode(i.Info, withTokenNodePrefix(prefix...), ignoreLeadingComment())
w.Write(withNode(infoNode, i.LParen))
w.NewLine()
for _, v := range i.Values {
node := transferTokenNode(v.Key, withTokenNodePrefix(peekOne(prefix)+Indent), ignoreLeadingComment())
w.Write(withNode(node, v.Value), expectIndentInfix(), expectSameLine())
w.NewLine()
}
w.Write(withNode(transferTokenNode(i.RParen, withTokenNodePrefix(prefix...))))
return w.String()
}
func (i *InfoStmt) End() token.Position {
return i.RParen.End()
}
func (i *InfoStmt) Pos() token.Position {
return i.Info.Pos()
}
func (i *InfoStmt) stmtNode() {}

View File

@@ -0,0 +1,39 @@
package ast
import "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
// KVExpr is a key value expression.
type KVExpr struct {
// Key is the key of the key value expression.
Key *TokenNode
// Value is the value of the key value expression.
Value *TokenNode
}
func (i *KVExpr) HasHeadCommentGroup() bool {
return i.Key.HasHeadCommentGroup()
}
func (i *KVExpr) HasLeadingCommentGroup() bool {
return i.Value.HasLeadingCommentGroup()
}
func (i *KVExpr) CommentGroup() (head, leading CommentGroup) {
return i.Key.HeadCommentGroup, i.Value.LeadingCommentGroup
}
func (i *KVExpr) Format(prefix ...string) string {
w := NewBufferWriter()
w.Write(withNode(i.Key, i.Value), withPrefix(prefix...), withInfix(Indent), withRawText())
return w.String()
}
func (i *KVExpr) End() token.Position {
return i.Value.End()
}
func (i *KVExpr) Pos() token.Position {
return i.Key.Pos()
}
func (i *KVExpr) exprNode() {}

View File

@@ -0,0 +1,237 @@
package ast
import (
"fmt"
"go/token"
"io"
"os"
"reflect"
apitoken "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
)
// A FieldFilter may be provided to Fprint to control the output.
type FieldFilter func(name string, value reflect.Value) bool
// NotNilFilter returns true for field values that are not nil,
// it returns false otherwise.
func NotNilFilter(_ string, v reflect.Value) bool {
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return !v.IsNil()
}
return true
}
// Fprint prints the value of x to the writer w.
func Fprint(w io.Writer, x interface{}, f FieldFilter) error {
return fprint(w, x, f)
}
func fprint(w io.Writer, x interface{}, f FieldFilter) (err error) {
// setup printer
p := printer{
output: w,
filter: f,
ptrmap: make(map[interface{}]int),
last: '\n', // force printing of line number on first line
}
// install error handler
defer func() {
if e := recover(); e != nil {
err = e.(localError).err // re-panics if it's not a localError
}
}()
// print x
if x == nil {
p.printf("nil\n")
return
}
p.print(reflect.ValueOf(x))
p.printf("\n")
return
}
func Print(x interface{}) error {
return Fprint(os.Stdout, x, NotNilFilter)
}
type printer struct {
output io.Writer
filter FieldFilter
ptrmap map[interface{}]int // *T -> line number
prefixIndent int // current indentation level
last byte // the last byte processed by Write
line int // current line number
}
var prefixIndent = []byte(". ")
// Write implements io.Writer.
func (p *printer) Write(data []byte) (n int, err error) {
var m int
for i, b := range data {
// invariant: data[0:n] has been written
if b == '\n' {
m, err = p.output.Write(data[n : i+1])
n += m
if err != nil {
return
}
p.line++
} else if p.last == '\n' {
_, err = fmt.Fprintf(p.output, "%6d ", p.line)
if err != nil {
return
}
for j := p.prefixIndent; j > 0; j-- {
_, err = p.output.Write(prefixIndent)
if err != nil {
return
}
}
}
p.last = b
}
if len(data) > n {
m, err = p.output.Write(data[n:])
n += m
}
return
}
// localError wraps locally caught errors so we can distinguish
// them from genuine panics which we don't want to return as errors.
type localError struct {
err error
}
// printf is a convenience wrapper that takes care of print errors.
func (p *printer) printf(format string, args ...interface{}) {
if _, err := fmt.Fprintf(p, format, args...); err != nil {
panic(localError{err})
}
}
// Implementation note: Print is written for AST nodes but could be
// used to print arbitrary data structures; such a version should
// probably be in a different package.
//
// Note: This code detects (some) cycles created via pointers but
// not cycles that are created via slices or maps containing the
// same slice or map. Code for general data structures probably
// should catch those as well.
func (p *printer) print(x reflect.Value) {
if !NotNilFilter("", x) {
p.printf("nil")
return
}
switch x.Kind() {
case reflect.Interface:
p.print(x.Elem())
case reflect.Map:
p.printf("%s (len = %d) {", x.Type(), x.Len())
if x.Len() > 0 {
p.prefixIndent++
p.printf("\n")
for _, key := range x.MapKeys() {
p.print(key)
p.printf(": ")
p.print(x.MapIndex(key))
p.printf("\n")
}
p.prefixIndent--
}
p.printf("}")
case reflect.Pointer:
p.printf("*")
// type-checked ASTs may contain cycles - use ptrmap
// to keep track of objects that have been printed
// already and print the respective line number instead
ptr := x.Interface()
if line, exists := p.ptrmap[ptr]; exists {
p.printf("(obj @ %d)", line)
} else {
p.ptrmap[ptr] = p.line
p.print(x.Elem())
}
case reflect.Array:
p.printf("%s {", x.Type())
if x.Len() > 0 {
p.prefixIndent++
p.printf("\n")
for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
p.print(x.Index(i))
p.printf("\n")
}
p.prefixIndent--
}
p.printf("}")
case reflect.Slice:
if s, ok := x.Interface().([]byte); ok {
p.printf("%#q", s)
return
}
p.printf("%s (len = %d) {", x.Type(), x.Len())
if x.Len() > 0 {
p.prefixIndent++
p.printf("\n")
for i, n := 0, x.Len(); i < n; i++ {
p.printf("%d: ", i)
p.print(x.Index(i))
p.printf("\n")
}
p.prefixIndent--
}
p.printf("}")
case reflect.Struct:
if val, ok := x.Interface().(apitoken.Position); ok {
p.printf(val.String())
return
}
t := x.Type()
p.printf("%s {", t)
p.prefixIndent++
first := true
for i, n := 0, t.NumField(); i < n; i++ {
// exclude non-exported fields because their
// values cannot be accessed via reflection
if name := t.Field(i).Name; token.IsExported(name) {
value := x.Field(i)
if p.filter == nil || p.filter(name, value) {
if first {
p.printf("\n")
first = false
}
p.printf("%s: ", name)
p.print(value)
p.printf("\n")
}
}
}
p.prefixIndent--
p.printf("}")
default:
v := x.Interface()
switch v := v.(type) {
case string:
// print strings in quotes
p.printf("%q", v)
return
}
// default
p.printf("%v", v)
}
}

View File

@@ -0,0 +1,577 @@
package ast
import "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
// AtServerStmt represents @server statement.
type AtServerStmt struct {
// AtServer is the @server token.
AtServer *TokenNode
// LParen is the left parenthesis token.
LParen *TokenNode
// Values is the key-value pairs.
Values []*KVExpr
// RParen is the right parenthesis token.
RParen *TokenNode
}
func (a *AtServerStmt) HasHeadCommentGroup() bool {
return a.AtServer.HasHeadCommentGroup()
}
func (a *AtServerStmt) HasLeadingCommentGroup() bool {
return a.RParen.HasLeadingCommentGroup()
}
func (a *AtServerStmt) CommentGroup() (head, leading CommentGroup) {
return a.AtServer.HeadCommentGroup, a.RParen.LeadingCommentGroup
}
func (a *AtServerStmt) Format(prefix ...string) string {
if len(a.Values) == 0 {
return ""
}
var textList []string
for _, v := range a.Values {
if v.Value.IsZeroString() {
continue
}
textList = append(textList, v.Format())
}
if len(textList) == 0 {
return ""
}
w := NewBufferWriter()
atServerNode := transferTokenNode(a.AtServer, withTokenNodePrefix(prefix...), ignoreLeadingComment())
w.Write(withNode(atServerNode, a.LParen), expectSameLine())
w.NewLine()
for _, v := range a.Values {
node := transferTokenNode(v.Key, withTokenNodePrefix(peekOne(prefix)+Indent), ignoreLeadingComment())
w.Write(withNode(node, v.Value), expectIndentInfix(), expectSameLine())
w.NewLine()
}
w.Write(withNode(transferTokenNode(a.RParen, withTokenNodePrefix(prefix...))))
return w.String()
}
func (a *AtServerStmt) End() token.Position {
return a.RParen.End()
}
func (a *AtServerStmt) Pos() token.Position {
return a.AtServer.Pos()
}
func (a *AtServerStmt) stmtNode() {}
type AtDocStmt interface {
Stmt
atDocNode()
}
type AtDocLiteralStmt struct {
AtDoc *TokenNode
Value *TokenNode
}
func (a *AtDocLiteralStmt) HasHeadCommentGroup() bool {
return a.AtDoc.HasHeadCommentGroup()
}
func (a *AtDocLiteralStmt) HasLeadingCommentGroup() bool {
return a.Value.HasLeadingCommentGroup()
}
func (a *AtDocLiteralStmt) CommentGroup() (head, leading CommentGroup) {
return a.AtDoc.HeadCommentGroup, a.Value.LeadingCommentGroup
}
func (a *AtDocLiteralStmt) Format(prefix ...string) string {
if a.Value.IsZeroString() {
return ""
}
w := NewBufferWriter()
atDocNode := transferTokenNode(a.AtDoc, withTokenNodePrefix(prefix...), ignoreLeadingComment())
valueNode := transferTokenNode(a.Value, ignoreHeadComment())
w.Write(withNode(atDocNode, valueNode), expectSameLine())
return w.String()
}
func (a *AtDocLiteralStmt) End() token.Position {
return a.Value.End()
}
func (a *AtDocLiteralStmt) atDocNode() {}
func (a *AtDocLiteralStmt) Pos() token.Position {
return a.AtDoc.Pos()
}
func (a *AtDocLiteralStmt) stmtNode() {}
type AtDocGroupStmt struct {
AtDoc *TokenNode
LParen *TokenNode
Values []*KVExpr
RParen *TokenNode
}
func (a *AtDocGroupStmt) HasHeadCommentGroup() bool {
return a.AtDoc.HasHeadCommentGroup()
}
func (a *AtDocGroupStmt) HasLeadingCommentGroup() bool {
return a.RParen.HasLeadingCommentGroup()
}
func (a *AtDocGroupStmt) CommentGroup() (head, leading CommentGroup) {
return a.AtDoc.HeadCommentGroup, a.RParen.LeadingCommentGroup
}
func (a *AtDocGroupStmt) Format(prefix ...string) string {
if len(a.Values) == 0 {
return ""
}
var textList []string
for _, v := range a.Values {
if v.Value.IsZeroString() {
continue
}
textList = append(textList, v.Format(peekOne(prefix)+Indent))
}
if len(textList) == 0 {
return ""
}
w := NewBufferWriter()
atDocNode := transferTokenNode(a.AtDoc, withTokenNodePrefix(prefix...), ignoreLeadingComment())
w.Write(withNode(atDocNode, a.LParen), expectSameLine())
w.NewLine()
for _, v := range a.Values {
node := transferTokenNode(v.Key, withTokenNodePrefix(peekOne(prefix)+Indent), ignoreLeadingComment())
w.Write(withNode(node, v.Value), expectIndentInfix(), expectSameLine())
w.NewLine()
}
w.Write(withNode(transferTokenNode(a.RParen, withTokenNodePrefix(prefix...))))
return w.String()
}
func (a *AtDocGroupStmt) End() token.Position {
return a.RParen.End()
}
func (a *AtDocGroupStmt) atDocNode() {}
func (a *AtDocGroupStmt) Pos() token.Position {
return a.AtDoc.Pos()
}
func (a *AtDocGroupStmt) stmtNode() {}
type ServiceStmt struct {
AtServerStmt *AtServerStmt
Service *TokenNode
Name *ServiceNameExpr
LBrace *TokenNode
Routes []*ServiceItemStmt
RBrace *TokenNode
}
func (s *ServiceStmt) HasHeadCommentGroup() bool {
if s.AtServerStmt != nil {
return s.AtServerStmt.HasHeadCommentGroup()
}
return s.Service.HasHeadCommentGroup()
}
func (s *ServiceStmt) HasLeadingCommentGroup() bool {
return s.RBrace.HasLeadingCommentGroup()
}
func (s *ServiceStmt) CommentGroup() (head, leading CommentGroup) {
if s.AtServerStmt != nil {
head, _ = s.AtServerStmt.CommentGroup()
return head, s.RBrace.LeadingCommentGroup
}
return s.Service.HeadCommentGroup, s.RBrace.LeadingCommentGroup
}
func (s *ServiceStmt) Format(prefix ...string) string {
w := NewBufferWriter()
if s.AtServerStmt != nil {
text := s.AtServerStmt.Format()
if len(text) > 0 {
w.WriteText(text)
w.NewLine()
}
}
serviceNode := transferTokenNode(s.Service, withTokenNodePrefix(prefix...))
w.Write(withNode(serviceNode, s.Name, s.LBrace), expectSameLine())
if len(s.Routes) == 0 {
w.Write(withNode(transferTokenNode(s.RBrace, withTokenNodePrefix(prefix...))))
return w.String()
}
w.NewLine()
for idx, route := range s.Routes {
routeNode := transfer2TokenNode(route, false, withTokenNodePrefix(peekOne(prefix)+Indent))
w.Write(withNode(routeNode))
if idx < len(s.Routes)-1 {
w.NewLine()
}
}
w.Write(withNode(transferTokenNode(s.RBrace, withTokenNodePrefix(prefix...))))
return w.String()
}
func (s *ServiceStmt) End() token.Position {
return s.RBrace.End()
}
func (s *ServiceStmt) Pos() token.Position {
if s.AtServerStmt != nil {
return s.AtServerStmt.Pos()
}
return s.Service.Pos()
}
func (s *ServiceStmt) stmtNode() {}
type ServiceNameExpr struct {
Name *TokenNode
}
func (s *ServiceNameExpr) HasHeadCommentGroup() bool {
return s.Name.HasHeadCommentGroup()
}
func (s *ServiceNameExpr) HasLeadingCommentGroup() bool {
return s.Name.HasLeadingCommentGroup()
}
func (s *ServiceNameExpr) CommentGroup() (head, leading CommentGroup) {
return s.Name.HeadCommentGroup, s.Name.LeadingCommentGroup
}
func (s *ServiceNameExpr) Format(...string) string {
w := NewBufferWriter()
w.WriteText(s.Name.Format())
return w.String()
}
func (s *ServiceNameExpr) End() token.Position {
return s.Name.End()
}
func (s *ServiceNameExpr) Pos() token.Position {
return s.Name.Pos()
}
func (s *ServiceNameExpr) exprNode() {}
type AtHandlerStmt struct {
AtHandler *TokenNode
Name *TokenNode
}
func (a *AtHandlerStmt) HasHeadCommentGroup() bool {
return a.AtHandler.HasHeadCommentGroup()
}
func (a *AtHandlerStmt) HasLeadingCommentGroup() bool {
return a.Name.HasLeadingCommentGroup()
}
func (a *AtHandlerStmt) CommentGroup() (head, leading CommentGroup) {
return a.AtHandler.HeadCommentGroup, a.Name.LeadingCommentGroup
}
func (a *AtHandlerStmt) Format(prefix ...string) string {
w := NewBufferWriter()
atDocNode := transferTokenNode(a.AtHandler, withTokenNodePrefix(prefix...), ignoreLeadingComment())
nameNode := transferTokenNode(a.Name, ignoreHeadComment())
w.Write(withNode(atDocNode, nameNode), expectSameLine())
return w.String()
}
func (a *AtHandlerStmt) End() token.Position {
return a.Name.End()
}
func (a *AtHandlerStmt) Pos() token.Position {
return a.AtHandler.Pos()
}
func (a *AtHandlerStmt) stmtNode() {}
type ServiceItemStmt struct {
AtDoc AtDocStmt
AtHandler *AtHandlerStmt
Route *RouteStmt
}
func (s *ServiceItemStmt) HasHeadCommentGroup() bool {
if s.AtDoc != nil {
return s.AtDoc.HasHeadCommentGroup()
}
return s.AtHandler.HasHeadCommentGroup()
}
func (s *ServiceItemStmt) HasLeadingCommentGroup() bool {
return s.Route.HasLeadingCommentGroup()
}
func (s *ServiceItemStmt) CommentGroup() (head, leading CommentGroup) {
_, leading = s.Route.CommentGroup()
if s.AtDoc != nil {
head, _ = s.AtDoc.CommentGroup()
return head, leading
}
head, _ = s.AtHandler.CommentGroup()
return head, leading
}
func (s *ServiceItemStmt) Format(prefix ...string) string {
w := NewBufferWriter()
if s.AtDoc != nil {
w.WriteText(s.AtDoc.Format(prefix...))
w.NewLine()
}
w.WriteText(s.AtHandler.Format(prefix...))
w.NewLine()
routeNode := transfer2TokenNode(s.Route, false, withTokenNodePrefix(prefix...))
w.Write(withNode(routeNode))
w.NewLine()
return w.String()
}
func (s *ServiceItemStmt) End() token.Position {
return s.Route.End()
}
func (s *ServiceItemStmt) Pos() token.Position {
if s.AtDoc != nil {
return s.AtDoc.Pos()
}
return s.AtHandler.Pos()
}
func (s *ServiceItemStmt) stmtNode() {}
type RouteStmt struct {
Method *TokenNode
Path *PathExpr
Request *BodyStmt
Returns *TokenNode
Response *BodyStmt
}
func (r *RouteStmt) HasHeadCommentGroup() bool {
return r.Method.HasHeadCommentGroup()
}
func (r *RouteStmt) HasLeadingCommentGroup() bool {
if r.Response != nil {
return r.Response.HasLeadingCommentGroup()
} else if r.Returns != nil {
return r.Returns.HasLeadingCommentGroup()
} else if r.Request != nil {
return r.Request.HasLeadingCommentGroup()
}
return r.Path.HasLeadingCommentGroup()
}
func (r *RouteStmt) CommentGroup() (head, leading CommentGroup) {
head, _ = r.Method.CommentGroup()
if r.Response != nil {
_, leading = r.Response.CommentGroup()
} else if r.Returns != nil {
_, leading = r.Returns.CommentGroup()
} else if r.Request != nil {
_, leading = r.Request.CommentGroup()
}
return head, leading
}
func (r *RouteStmt) Format(prefix ...string) string {
w := NewBufferWriter()
methodNode := transferTokenNode(r.Method, withTokenNodePrefix(prefix...), ignoreLeadingComment())
if r.Response != nil {
if r.Response.Body == nil {
r.Response.RParen = transferTokenNode(r.Response.RParen, ignoreHeadComment())
if r.Request != nil {
w.Write(withNode(methodNode, r.Path, r.Request), expectSameLine())
} else {
w.Write(withNode(methodNode, r.Path), expectSameLine())
}
} else {
r.Response.RParen = transferTokenNode(r.Response.RParen, ignoreHeadComment())
if r.Request != nil {
w.Write(withNode(methodNode, r.Path, r.Request, r.Returns, r.Response), expectSameLine())
} else {
w.Write(withNode(methodNode, r.Path, r.Returns, r.Response), expectSameLine())
}
}
} else if r.Request != nil {
r.Request.RParen = transferTokenNode(r.Request.RParen, ignoreHeadComment())
w.Write(withNode(methodNode, r.Path, r.Request), expectSameLine())
} else {
pathNode := transferTokenNode(r.Path.Value, ignoreHeadComment())
w.Write(withNode(methodNode, pathNode), expectSameLine())
}
return w.String()
}
func (r *RouteStmt) End() token.Position {
if r.Response != nil {
return r.Response.End()
}
if r.Returns != nil {
return r.Returns.Pos()
}
if r.Request != nil {
return r.Request.End()
}
return r.Path.End()
}
func (r *RouteStmt) Pos() token.Position {
return r.Method.Pos()
}
func (r *RouteStmt) stmtNode() {}
type PathExpr struct {
Value *TokenNode
}
func (p *PathExpr) HasHeadCommentGroup() bool {
return p.Value.HasHeadCommentGroup()
}
func (p *PathExpr) HasLeadingCommentGroup() bool {
return p.Value.HasLeadingCommentGroup()
}
func (p *PathExpr) CommentGroup() (head, leading CommentGroup) {
return p.Value.CommentGroup()
}
func (p *PathExpr) Format(prefix ...string) string {
pathNode := transferTokenNode(p.Value, ignoreComment())
return pathNode.Format(prefix...)
}
func (p *PathExpr) End() token.Position {
return p.Value.End()
}
func (p *PathExpr) Pos() token.Position {
return p.Value.Pos()
}
func (p *PathExpr) exprNode() {}
type BodyStmt struct {
LParen *TokenNode
Body *BodyExpr
RParen *TokenNode
}
func (b *BodyStmt) HasHeadCommentGroup() bool {
return b.LParen.HasHeadCommentGroup()
}
func (b *BodyStmt) HasLeadingCommentGroup() bool {
return b.RParen.HasLeadingCommentGroup()
}
func (b *BodyStmt) CommentGroup() (head, leading CommentGroup) {
return b.LParen.HeadCommentGroup, b.RParen.LeadingCommentGroup
}
func (b *BodyStmt) Format(...string) string {
w := NewBufferWriter()
if b.Body == nil {
return ""
}
w.Write(withNode(b.LParen, b.Body, b.RParen), withInfix(NilIndent), expectSameLine())
return w.String()
}
func (b *BodyStmt) End() token.Position {
return b.RParen.End()
}
func (b *BodyStmt) Pos() token.Position {
return b.LParen.Pos()
}
func (b *BodyStmt) stmtNode() {}
type BodyExpr struct {
LBrack *TokenNode
RBrack *TokenNode
Star *TokenNode
Value *TokenNode
}
func (e *BodyExpr) HasHeadCommentGroup() bool {
if e.LBrack != nil {
return e.LBrack.HasHeadCommentGroup()
} else if e.Star != nil {
return e.Star.HasHeadCommentGroup()
} else {
return e.Value.HasHeadCommentGroup()
}
}
func (e *BodyExpr) HasLeadingCommentGroup() bool {
return e.Value.HasLeadingCommentGroup()
}
func (e *BodyExpr) CommentGroup() (head, leading CommentGroup) {
if e.LBrack != nil {
head = e.LBrack.HeadCommentGroup
} else if e.Star != nil {
head = e.Star.HeadCommentGroup
} else {
head = e.Value.HeadCommentGroup
}
return head, e.Value.LeadingCommentGroup
}
func (e *BodyExpr) End() token.Position {
return e.Value.End()
}
func (e *BodyExpr) Format(...string) string {
w := NewBufferWriter()
if e.LBrack != nil {
lbrackNode := transferTokenNode(e.LBrack, ignoreComment())
rbrackNode := transferTokenNode(e.RBrack, ignoreComment())
if e.Star != nil {
starNode := transferTokenNode(e.Star, ignoreComment())
w.Write(withNode(lbrackNode, rbrackNode, starNode, e.Value), withInfix(NilIndent), expectSameLine())
} else {
w.Write(withNode(lbrackNode, rbrackNode, e.Value), withInfix(NilIndent), expectSameLine())
}
} else if e.Star != nil {
starNode := transferTokenNode(e.Star, ignoreComment())
w.Write(withNode(starNode, e.Value), withInfix(NilIndent), expectSameLine())
} else {
w.Write(withNode(e.Value))
}
return w.String()
}
func (e *BodyExpr) Pos() token.Position {
if e.LBrack != nil {
return e.LBrack.Pos()
}
if e.Star != nil {
return e.Star.Pos()
}
return e.Value.Pos()
}
func (e *BodyExpr) exprNode() {}

View File

@@ -0,0 +1,44 @@
package ast
import "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
// SyntaxStmt represents a syntax statement.
type SyntaxStmt struct {
// Syntax is the syntax token.
Syntax *TokenNode
// Assign is the assign token.
Assign *TokenNode
// Value is the syntax value.
Value *TokenNode
}
func (s *SyntaxStmt) HasHeadCommentGroup() bool {
return s.Syntax.HasHeadCommentGroup()
}
func (s *SyntaxStmt) HasLeadingCommentGroup() bool {
return s.Value.HasLeadingCommentGroup()
}
func (s *SyntaxStmt) CommentGroup() (head, leading CommentGroup) {
return s.Syntax.HeadCommentGroup, s.Syntax.LeadingCommentGroup
}
func (s *SyntaxStmt) Format(prefix ...string) string {
w := NewBufferWriter()
syntaxNode := transferTokenNode(s.Syntax,
withTokenNodePrefix(prefix...), ignoreLeadingComment())
assignNode := transferTokenNode(s.Assign, ignoreLeadingComment())
w.Write(withNode(syntaxNode, assignNode, s.Value), withPrefix(prefix...), expectSameLine())
return w.String()
}
func (s *SyntaxStmt) End() token.Position {
return s.Value.End()
}
func (s *SyntaxStmt) Pos() token.Position {
return s.Syntax.Pos()
}
func (s *SyntaxStmt) stmtNode() {}

View File

@@ -0,0 +1,797 @@
package ast
import "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
/*******************TypeStmt Begin********************/
// TypeStmt is the interface for type statement.
type TypeStmt interface {
Stmt
typeNode()
}
// TypeLiteralStmt is the type statement for type literal.
type TypeLiteralStmt struct {
// Type is the type keyword.
Type *TokenNode
// Expr is the type expression.
Expr *TypeExpr
}
func (t *TypeLiteralStmt) HasHeadCommentGroup() bool {
return t.Type.HasHeadCommentGroup()
}
func (t *TypeLiteralStmt) HasLeadingCommentGroup() bool {
return t.Expr.HasLeadingCommentGroup()
}
func (t *TypeLiteralStmt) CommentGroup() (head, leading CommentGroup) {
_, leading = t.Expr.CommentGroup()
return t.Type.HeadCommentGroup, leading
}
func (t *TypeLiteralStmt) Format(prefix ...string) string {
w := NewBufferWriter()
w.Write(withNode(t.Type, t.Expr), withPrefix(prefix...), expectSameLine())
return w.String()
}
func (t *TypeLiteralStmt) End() token.Position {
return t.Expr.End()
}
func (t *TypeLiteralStmt) Pos() token.Position {
return t.Type.Pos()
}
func (t *TypeLiteralStmt) stmtNode() {}
func (t *TypeLiteralStmt) typeNode() {}
// TypeGroupStmt is the type statement for type group.
type TypeGroupStmt struct {
// Type is the type keyword.
Type *TokenNode
// LParen is the left parenthesis.
LParen *TokenNode
// ExprList is the type expression list.
ExprList []*TypeExpr
// RParen is the right parenthesis.
RParen *TokenNode
}
func (t *TypeGroupStmt) HasHeadCommentGroup() bool {
return t.Type.HasHeadCommentGroup()
}
func (t *TypeGroupStmt) HasLeadingCommentGroup() bool {
return t.RParen.HasLeadingCommentGroup()
}
func (t *TypeGroupStmt) CommentGroup() (head, leading CommentGroup) {
return t.Type.HeadCommentGroup, t.RParen.LeadingCommentGroup
}
func (t *TypeGroupStmt) Format(prefix ...string) string {
if len(t.ExprList) == 0 {
return ""
}
w := NewBufferWriter()
typeNode := transferTokenNode(t.Type, withTokenNodePrefix(prefix...))
w.Write(withNode(typeNode, t.LParen), expectSameLine())
w.NewLine()
for _, e := range t.ExprList {
w.Write(withNode(e), withPrefix(peekOne(prefix)+Indent))
w.NewLine()
}
w.WriteText(t.RParen.Format(prefix...))
return w.String()
}
func (t *TypeGroupStmt) End() token.Position {
return t.RParen.End()
}
func (t *TypeGroupStmt) Pos() token.Position {
return t.Type.Pos()
}
func (t *TypeGroupStmt) stmtNode() {}
func (t *TypeGroupStmt) typeNode() {}
/*******************TypeStmt End********************/
/*******************TypeExpr Begin********************/
// TypeExpr is the type expression.
type TypeExpr struct {
// Name is the type name.
Name *TokenNode
// Assign is the assign operator.
Assign *TokenNode
// DataType is the data type.
DataType DataType
}
func (e *TypeExpr) HasHeadCommentGroup() bool {
return e.Name.HasHeadCommentGroup()
}
func (e *TypeExpr) HasLeadingCommentGroup() bool {
return e.DataType.HasLeadingCommentGroup()
}
func (e *TypeExpr) CommentGroup() (head, leading CommentGroup) {
_, leading = e.DataType.CommentGroup()
return e.Name.HeadCommentGroup, leading
}
func (e *TypeExpr) Format(prefix ...string) string {
w := NewBufferWriter()
nameNode := transferTokenNode(e.Name, withTokenNodePrefix(prefix...))
dataTypeNode := transfer2TokenNode(e.DataType, false, withTokenNodePrefix(prefix...))
if e.Assign != nil {
w.Write(withNode(nameNode, e.Assign, dataTypeNode), expectSameLine())
} else {
w.Write(withNode(nameNode, dataTypeNode), expectSameLine())
}
return w.String()
}
func (e *TypeExpr) End() token.Position {
return e.DataType.End()
}
func (e *TypeExpr) Pos() token.Position {
return e.Name.Pos()
}
func (e *TypeExpr) exprNode() {}
func (e *TypeExpr) isStruct() bool {
return e.DataType.ContainsStruct()
}
/*******************TypeExpr Begin********************/
/*******************Elem Begin********************/
// ElemExpr is the element expression.
type ElemExpr struct {
// Name is the field element name.
Name []*TokenNode
// DataType is the field data type.
DataType DataType
// Tag is the field tag.
Tag *TokenNode
}
// IsAnonymous returns true if the element is anonymous.
func (e *ElemExpr) IsAnonymous() bool {
return len(e.Name) == 0
}
func (e *ElemExpr) HasHeadCommentGroup() bool {
if e.IsAnonymous() {
return e.DataType.HasHeadCommentGroup()
}
return e.Name[0].HasHeadCommentGroup()
}
func (e *ElemExpr) HasLeadingCommentGroup() bool {
if e.Tag != nil {
return e.Tag.HasLeadingCommentGroup()
}
return e.DataType.HasLeadingCommentGroup()
}
func (e *ElemExpr) CommentGroup() (head, leading CommentGroup) {
if e.Tag != nil {
leading = e.Tag.LeadingCommentGroup
} else {
_, leading = e.DataType.CommentGroup()
}
if e.IsAnonymous() {
head, _ := e.DataType.CommentGroup()
return head, leading
}
return e.Name[0].HeadCommentGroup, leading
}
func (e *ElemExpr) Format(prefix ...string) string {
w := NewBufferWriter()
var nameNodeList []*TokenNode
for idx, n := range e.Name {
if idx == 0 {
nameNodeList = append(nameNodeList,
transferTokenNode(n, ignoreLeadingComment()))
} else if idx < len(e.Name)-1 {
nameNodeList = append(nameNodeList,
transferTokenNode(n, ignoreLeadingComment(), ignoreHeadComment()))
} else {
nameNodeList = append(nameNodeList, transferTokenNode(n, ignoreHeadComment()))
}
}
var dataTypeOption []tokenNodeOption
if e.DataType.ContainsStruct() {
dataTypeOption = append(dataTypeOption, withTokenNodePrefix(peekOne(prefix)+Indent))
} else {
dataTypeOption = append(dataTypeOption, withTokenNodePrefix(prefix...))
}
dataTypeNode := transfer2TokenNode(e.DataType, false, dataTypeOption...)
if len(nameNodeList) > 0 {
nameNode := transferNilInfixNode(nameNodeList,
withTokenNodePrefix(prefix...), withTokenNodeInfix(", "))
if e.Tag != nil {
w.Write(withNode(nameNode, dataTypeNode, e.Tag), expectIndentInfix(), expectSameLine())
} else {
w.Write(withNode(nameNode, dataTypeNode), expectIndentInfix(), expectSameLine())
}
} else {
if e.Tag != nil {
w.Write(withNode(dataTypeNode, e.Tag), expectIndentInfix(), expectSameLine())
} else {
w.Write(withNode(dataTypeNode), expectIndentInfix(), expectSameLine())
}
}
return w.String()
}
func (e *ElemExpr) End() token.Position {
if e.Tag != nil {
return e.Tag.End()
}
return e.DataType.End()
}
func (e *ElemExpr) Pos() token.Position {
if len(e.Name) > 0 {
return e.Name[0].Pos()
}
return token.IllegalPosition
}
func (e *ElemExpr) exprNode() {}
/*******************Elem End********************/
/*******************ElemExprList Begin********************/
// ElemExprList is the element expression list.
type ElemExprList []*ElemExpr
/*******************ElemExprList Begin********************/
/*******************DataType Begin********************/
// DataType represents the data type.
type DataType interface {
Expr
dataTypeNode()
// CanEqual returns true if the data type can be equal.
CanEqual() bool
// ContainsStruct returns true if the data type contains struct.
ContainsStruct() bool
// RawText returns the raw text of the data type.
RawText() string
}
// AnyDataType is the any data type.
type AnyDataType struct {
// Any is the any token node.
Any *TokenNode
isChild bool
}
func (t *AnyDataType) HasHeadCommentGroup() bool {
return t.Any.HasHeadCommentGroup()
}
func (t *AnyDataType) HasLeadingCommentGroup() bool {
return t.Any.HasLeadingCommentGroup()
}
func (t *AnyDataType) CommentGroup() (head, leading CommentGroup) {
return t.Any.HeadCommentGroup, t.Any.LeadingCommentGroup
}
func (t *AnyDataType) Format(prefix ...string) string {
return t.Any.Format(prefix...)
}
func (t *AnyDataType) End() token.Position {
return t.Any.End()
}
func (t *AnyDataType) RawText() string {
return t.Any.Token.Text
}
func (t *AnyDataType) ContainsStruct() bool {
return false
}
func (t *AnyDataType) Pos() token.Position {
return t.Any.Pos()
}
func (t *AnyDataType) exprNode() {}
func (t *AnyDataType) dataTypeNode() {}
func (t *AnyDataType) CanEqual() bool {
return true
}
// ArrayDataType is the array data type.
type ArrayDataType struct {
// LB is the left bracket token node.
LBrack *TokenNode
// Len is the array length.
Length *TokenNode
// RB is the right bracket token node.
RBrack *TokenNode
// DataType is the array data type.
DataType DataType
isChild bool
}
func (t *ArrayDataType) HasHeadCommentGroup() bool {
return t.LBrack.HasHeadCommentGroup()
}
func (t *ArrayDataType) HasLeadingCommentGroup() bool {
return t.DataType.HasLeadingCommentGroup()
}
func (t *ArrayDataType) CommentGroup() (head, leading CommentGroup) {
_, leading = t.DataType.CommentGroup()
return t.LBrack.HeadCommentGroup, leading
}
func (t *ArrayDataType) Format(prefix ...string) string {
w := NewBufferWriter()
lbrack := transferTokenNode(t.LBrack, ignoreLeadingComment())
lengthNode := transferTokenNode(t.Length, ignoreLeadingComment())
rbrack := transferTokenNode(t.RBrack, ignoreHeadComment())
var dataType *TokenNode
var options []tokenNodeOption
options = append(options, withTokenNodePrefix(prefix...))
if t.isChild {
options = append(options, ignoreComment())
} else {
options = append(options, ignoreHeadComment())
}
dataType = transfer2TokenNode(t.DataType, false, options...)
node := transferNilInfixNode([]*TokenNode{lbrack, lengthNode, rbrack, dataType})
w.Write(withNode(node))
return w.String()
}
func (t *ArrayDataType) End() token.Position {
return t.DataType.End()
}
func (t *ArrayDataType) RawText() string {
return t.Format("")
}
func (t *ArrayDataType) ContainsStruct() bool {
return t.DataType.ContainsStruct()
}
func (t *ArrayDataType) CanEqual() bool {
return t.DataType.CanEqual()
}
func (t *ArrayDataType) Pos() token.Position {
return t.LBrack.Pos()
}
func (t *ArrayDataType) exprNode() {}
func (t *ArrayDataType) dataTypeNode() {}
// BaseDataType is a common id type which contains bool, uint8, uint16, uint32,
// uint64, int8, int16, int32, int64, float32, float64, complex64, complex128,
// string, int, uint, uintptr, byte, rune, any.
type BaseDataType struct {
// Base is the base token node.
Base *TokenNode
isChild bool
}
func (t *BaseDataType) HasHeadCommentGroup() bool {
return t.Base.HasHeadCommentGroup()
}
func (t *BaseDataType) HasLeadingCommentGroup() bool {
return t.Base.HasLeadingCommentGroup()
}
func (t *BaseDataType) CommentGroup() (head, leading CommentGroup) {
return t.Base.HeadCommentGroup, t.Base.LeadingCommentGroup
}
func (t *BaseDataType) Format(prefix ...string) string {
return t.Base.Format(prefix...)
}
func (t *BaseDataType) End() token.Position {
return t.Base.End()
}
func (t *BaseDataType) RawText() string {
return t.Base.Token.Text
}
func (t *BaseDataType) ContainsStruct() bool {
return false
}
func (t *BaseDataType) CanEqual() bool {
return true
}
func (t *BaseDataType) Pos() token.Position {
return t.Base.Pos()
}
func (t *BaseDataType) exprNode() {}
func (t *BaseDataType) dataTypeNode() {}
// InterfaceDataType is the interface data type.
type InterfaceDataType struct {
// Interface is the interface token node.
Interface *TokenNode
isChild bool
}
func (t *InterfaceDataType) HasHeadCommentGroup() bool {
return t.Interface.HasHeadCommentGroup()
}
func (t *InterfaceDataType) HasLeadingCommentGroup() bool {
return t.Interface.HasLeadingCommentGroup()
}
func (t *InterfaceDataType) CommentGroup() (head, leading CommentGroup) {
return t.Interface.HeadCommentGroup, t.Interface.LeadingCommentGroup
}
func (t *InterfaceDataType) Format(prefix ...string) string {
return t.Interface.Format(prefix...)
}
func (t *InterfaceDataType) End() token.Position {
return t.Interface.End()
}
func (t *InterfaceDataType) RawText() string {
return t.Interface.Token.Text
}
func (t *InterfaceDataType) ContainsStruct() bool {
return false
}
func (t *InterfaceDataType) CanEqual() bool {
return true
}
func (t *InterfaceDataType) Pos() token.Position {
return t.Interface.Pos()
}
func (t *InterfaceDataType) exprNode() {}
func (t *InterfaceDataType) dataTypeNode() {}
// MapDataType is the map data type.
type MapDataType struct {
// Map is the map token node.
Map *TokenNode
// Lbrack is the left bracket token node.
LBrack *TokenNode
// Key is the map key data type.
Key DataType
// Rbrack is the right bracket token node.
RBrack *TokenNode
// Value is the map value data type.
Value DataType
isChild bool
}
func (t *MapDataType) HasHeadCommentGroup() bool {
return t.Map.HasHeadCommentGroup()
}
func (t *MapDataType) HasLeadingCommentGroup() bool {
return t.Value.HasLeadingCommentGroup()
}
func (t *MapDataType) CommentGroup() (head, leading CommentGroup) {
_, leading = t.Value.CommentGroup()
return t.Map.HeadCommentGroup, leading
}
func (t *MapDataType) Format(prefix ...string) string {
w := NewBufferWriter()
mapNode := transferTokenNode(t.Map, ignoreLeadingComment())
lbrack := transferTokenNode(t.LBrack, ignoreLeadingComment())
rbrack := transferTokenNode(t.RBrack, ignoreComment())
var keyOption, valueOption []tokenNodeOption
keyOption = append(keyOption, ignoreComment())
valueOption = append(valueOption, withTokenNodePrefix(prefix...))
if t.isChild {
valueOption = append(valueOption, ignoreComment())
} else {
valueOption = append(valueOption, ignoreHeadComment())
}
keyDataType := transfer2TokenNode(t.Key, true, keyOption...)
valueDataType := transfer2TokenNode(t.Value, false, valueOption...)
node := transferNilInfixNode([]*TokenNode{mapNode, lbrack, keyDataType, rbrack, valueDataType})
w.Write(withNode(node))
return w.String()
}
func (t *MapDataType) End() token.Position {
return t.Value.End()
}
func (t *MapDataType) RawText() string {
return t.Format("")
}
func (t *MapDataType) ContainsStruct() bool {
return t.Key.ContainsStruct() || t.Value.ContainsStruct()
}
func (t *MapDataType) CanEqual() bool {
return false
}
func (t *MapDataType) Pos() token.Position {
return t.Map.Pos()
}
func (t *MapDataType) exprNode() {}
func (t *MapDataType) dataTypeNode() {}
// PointerDataType is the pointer data type.
type PointerDataType struct {
// Star is the star token node.
Star *TokenNode
// DataType is the pointer data type.
DataType DataType
isChild bool
}
func (t *PointerDataType) HasHeadCommentGroup() bool {
return t.Star.HasHeadCommentGroup()
}
func (t *PointerDataType) HasLeadingCommentGroup() bool {
return t.DataType.HasLeadingCommentGroup()
}
func (t *PointerDataType) CommentGroup() (head, leading CommentGroup) {
_, leading = t.DataType.CommentGroup()
return t.Star.HeadCommentGroup, leading
}
func (t *PointerDataType) Format(prefix ...string) string {
w := NewBufferWriter()
star := transferTokenNode(t.Star, ignoreLeadingComment(), withTokenNodePrefix(prefix...))
var dataTypeOption []tokenNodeOption
dataTypeOption = append(dataTypeOption, ignoreHeadComment())
dataType := transfer2TokenNode(t.DataType, false, dataTypeOption...)
node := transferNilInfixNode([]*TokenNode{star, dataType})
w.Write(withNode(node))
return w.String()
}
func (t *PointerDataType) End() token.Position {
return t.DataType.End()
}
func (t *PointerDataType) RawText() string {
return t.Format("")
}
func (t *PointerDataType) ContainsStruct() bool {
return t.DataType.ContainsStruct()
}
func (t *PointerDataType) CanEqual() bool {
return t.DataType.CanEqual()
}
func (t *PointerDataType) Pos() token.Position {
return t.Star.Pos()
}
func (t *PointerDataType) exprNode() {}
func (t *PointerDataType) dataTypeNode() {}
// SliceDataType is the slice data type.
type SliceDataType struct {
// Lbrack is the left bracket token node.
LBrack *TokenNode
// Rbrack is the right bracket token node.
RBrack *TokenNode
// DataType is the slice data type.
DataType DataType
isChild bool
}
func (t *SliceDataType) HasHeadCommentGroup() bool {
return t.LBrack.HasHeadCommentGroup()
}
func (t *SliceDataType) HasLeadingCommentGroup() bool {
return t.DataType.HasLeadingCommentGroup()
}
func (t *SliceDataType) CommentGroup() (head, leading CommentGroup) {
_, leading = t.DataType.CommentGroup()
return t.LBrack.HeadCommentGroup, leading
}
func (t *SliceDataType) Format(prefix ...string) string {
w := NewBufferWriter()
lbrack := transferTokenNode(t.LBrack, ignoreLeadingComment())
rbrack := transferTokenNode(t.RBrack, ignoreHeadComment())
dataType := transfer2TokenNode(t.DataType, false, withTokenNodePrefix(prefix...), ignoreHeadComment())
node := transferNilInfixNode([]*TokenNode{lbrack, rbrack, dataType})
w.Write(withNode(node))
return w.String()
}
func (t *SliceDataType) End() token.Position {
return t.DataType.End()
}
func (t *SliceDataType) RawText() string {
return t.Format("")
}
func (t *SliceDataType) ContainsStruct() bool {
return t.DataType.ContainsStruct()
}
func (t *SliceDataType) CanEqual() bool {
return false
}
func (t *SliceDataType) Pos() token.Position {
return t.LBrack.Pos()
}
func (t *SliceDataType) exprNode() {}
func (t *SliceDataType) dataTypeNode() {}
// StructDataType is the structure data type.
type StructDataType struct {
// Lbrace is the left brace token node.
LBrace *TokenNode
// Elements is the structure elements.
Elements ElemExprList
// Rbrace is the right brace token node.
RBrace *TokenNode
isChild bool
}
func (t *StructDataType) HasHeadCommentGroup() bool {
return t.LBrace.HasHeadCommentGroup()
}
func (t *StructDataType) HasLeadingCommentGroup() bool {
return t.RBrace.HasLeadingCommentGroup()
}
func (t *StructDataType) CommentGroup() (head, leading CommentGroup) {
return t.LBrace.HeadCommentGroup, t.RBrace.LeadingCommentGroup
}
func (t *StructDataType) Format(prefix ...string) string {
w := NewBufferWriter()
if len(t.Elements) == 0 {
lbrace := transferTokenNode(t.LBrace, withTokenNodePrefix(prefix...), ignoreLeadingComment())
rbrace := transferTokenNode(t.RBrace, ignoreHeadComment())
brace := transferNilInfixNode([]*TokenNode{lbrace, rbrace})
w.Write(withNode(brace), expectSameLine())
return w.String()
}
w.WriteText(t.LBrace.Format(NilIndent))
w.NewLine()
for _, e := range t.Elements {
var nameNodeList []*TokenNode
if len(e.Name) > 0 {
for idx, n := range e.Name {
if idx == 0 {
nameNodeList = append(nameNodeList,
transferTokenNode(n, withTokenNodePrefix(peekOne(prefix)+Indent), ignoreLeadingComment()))
} else if idx < len(e.Name)-1 {
nameNodeList = append(nameNodeList,
transferTokenNode(n, ignoreLeadingComment(), ignoreHeadComment()))
} else {
nameNodeList = append(nameNodeList, transferTokenNode(n, ignoreHeadComment()))
}
}
}
var dataTypeOption []tokenNodeOption
if e.DataType.ContainsStruct() || e.IsAnonymous() {
dataTypeOption = append(dataTypeOption, withTokenNodePrefix(peekOne(prefix)+Indent))
} else {
dataTypeOption = append(dataTypeOption, withTokenNodePrefix(prefix...))
}
dataTypeNode := transfer2TokenNode(e.DataType, false, dataTypeOption...)
if len(nameNodeList) > 0 {
nameNode := transferNilInfixNode(nameNodeList, withTokenNodeInfix(", "))
if e.Tag != nil {
if e.DataType.ContainsStruct() {
w.Write(withNode(nameNode, dataTypeNode, e.Tag), expectSameLine())
} else {
w.Write(withNode(nameNode, e.DataType, e.Tag), expectIndentInfix(), expectSameLine())
}
} else {
if e.DataType.ContainsStruct() {
w.Write(withNode(nameNode, dataTypeNode), expectSameLine())
} else {
w.Write(withNode(nameNode, e.DataType), expectIndentInfix(), expectSameLine())
}
}
} else {
if e.Tag != nil {
if e.DataType.ContainsStruct() {
w.Write(withNode(dataTypeNode, e.Tag), expectSameLine())
} else {
w.Write(withNode(e.DataType, e.Tag), expectIndentInfix(), expectSameLine())
}
} else {
if e.DataType.ContainsStruct() {
w.Write(withNode(dataTypeNode), expectSameLine())
} else {
w.Write(withNode(dataTypeNode), expectIndentInfix(), expectSameLine())
}
}
}
w.NewLine()
}
w.WriteText(t.RBrace.Format(prefix...))
return w.String()
}
func (t *StructDataType) End() token.Position {
return t.RBrace.End()
}
func (t *StructDataType) RawText() string {
return t.Format("")
}
func (t *StructDataType) ContainsStruct() bool {
return true
}
func (t *StructDataType) CanEqual() bool {
for _, v := range t.Elements {
if !v.DataType.CanEqual() {
return false
}
}
return true
}
func (t *StructDataType) Pos() token.Position {
return t.LBrace.Pos()
}
func (t *StructDataType) exprNode() {}
func (t *StructDataType) dataTypeNode() {}
/*******************DataType End********************/

View File

@@ -0,0 +1,403 @@
package ast
import (
"bytes"
"fmt"
"io"
"strings"
"text/tabwriter"
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
"github.com/zeromicro/go-zero/tools/goctl/util"
)
const (
NilIndent = ""
WhiteSpace = " "
Indent = "\t"
NewLine = "\n"
)
const (
_ WriteMode = 1 << iota
// ModeAuto is the default mode, which will automatically
//determine whether to write a newline.
ModeAuto
// ModeExpectInSameLine will write in the same line.
ModeExpectInSameLine
)
type Option func(o *option)
type option struct {
prefix string
infix string
mode WriteMode
nodes []Node
rawText bool
}
type tokenNodeOption func(o *tokenNodeOpt)
type tokenNodeOpt struct {
prefix string
infix string
ignoreHeadComment bool
ignoreLeadingComment bool
}
// WriteMode is the mode of writing.
type WriteMode int
// Writer is the writer of ast.
type Writer struct {
tw *tabwriter.Writer
writer io.Writer
}
func transfer2TokenNode(node Node, isChild bool, opt ...tokenNodeOption) *TokenNode {
option := new(tokenNodeOpt)
for _, o := range opt {
o(option)
}
var copyOpt = append([]tokenNodeOption(nil), opt...)
var tn *TokenNode
switch val := node.(type) {
case *AnyDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.Any, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.Any = tn
case *ArrayDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.LBrack, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.LBrack = tn
case *BaseDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.Base, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.Base = tn
case *InterfaceDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.Interface, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.Interface = tn
case *MapDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.Map, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.Map = tn
case *PointerDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.Star, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.Star = tn
case *SliceDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.LBrack, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.LBrack = tn
case *StructDataType:
copyOpt = append(copyOpt, withTokenNodePrefix(NilIndent))
tn = transferTokenNode(val.LBrace, copyOpt...)
if option.ignoreHeadComment {
tn.HeadCommentGroup = nil
}
if option.ignoreLeadingComment {
tn.LeadingCommentGroup = nil
}
val.isChild = isChild
val.LBrace = tn
default:
}
return &TokenNode{
headFlag: node.HasHeadCommentGroup(),
leadingFlag: node.HasLeadingCommentGroup(),
Token: token.Token{
Text: node.Format(option.prefix),
Position: node.Pos(),
},
LeadingCommentGroup: CommentGroup{
{
token.Token{Position: node.End()},
},
},
}
}
func transferNilInfixNode(nodes []*TokenNode, opt ...tokenNodeOption) *TokenNode {
result := &TokenNode{}
var option = new(tokenNodeOpt)
for _, o := range opt {
o(option)
}
var list []string
for _, n := range nodes {
list = append(list, n.Token.Text)
}
result.Token = token.Token{
Text: option.prefix + strings.Join(list, option.infix),
Position: nodes[0].Pos(),
}
if !option.ignoreHeadComment {
result.HeadCommentGroup = nodes[0].HeadCommentGroup
}
if !option.ignoreLeadingComment {
result.LeadingCommentGroup = nodes[len(nodes)-1].LeadingCommentGroup
}
return result
}
func transferTokenNode(node *TokenNode, opt ...tokenNodeOption) *TokenNode {
result := &TokenNode{}
var option = new(tokenNodeOpt)
for _, o := range opt {
o(option)
}
result.Token = token.Token{
Type: node.Token.Type,
Text: option.prefix + node.Token.Text,
Position: node.Token.Position,
}
if !option.ignoreHeadComment {
for _, v := range node.HeadCommentGroup {
result.HeadCommentGroup = append(result.HeadCommentGroup,
&CommentStmt{Comment: token.Token{
Type: v.Comment.Type,
Text: option.prefix + v.Comment.Text,
Position: v.Comment.Position,
}})
}
}
if !option.ignoreLeadingComment {
for _, v := range node.LeadingCommentGroup {
result.LeadingCommentGroup = append(result.LeadingCommentGroup, v)
}
}
return result
}
func ignoreHeadComment() tokenNodeOption {
return func(o *tokenNodeOpt) {
o.ignoreHeadComment = true
}
}
func ignoreLeadingComment() tokenNodeOption {
return func(o *tokenNodeOpt) {
o.ignoreLeadingComment = true
}
}
func ignoreComment() tokenNodeOption {
return func(o *tokenNodeOpt) {
o.ignoreHeadComment = true
o.ignoreLeadingComment = true
}
}
func withTokenNodePrefix(prefix ...string) tokenNodeOption {
return func(o *tokenNodeOpt) {
for _, p := range prefix {
o.prefix = p
}
}
}
func withTokenNodeInfix(infix string) tokenNodeOption {
return func(o *tokenNodeOpt) {
o.infix = infix
}
}
func expectSameLine() Option {
return func(o *option) {
o.mode = ModeExpectInSameLine
}
}
func expectIndentInfix() Option {
return func(o *option) {
o.infix = Indent
}
}
func withNode(nodes ...Node) Option {
return func(o *option) {
o.nodes = nodes
}
}
func withMode(mode WriteMode) Option {
return func(o *option) {
o.mode = mode
}
}
func withPrefix(prefix ...string) Option {
return func(o *option) {
for _, p := range prefix {
o.prefix = p
}
}
}
func withInfix(infix string) Option {
return func(o *option) {
o.infix = infix
}
}
func withRawText() Option {
return func(o *option) {
o.rawText = true
}
}
// NewWriter returns a new Writer.
func NewWriter(writer io.Writer) *Writer {
return &Writer{
tw: tabwriter.NewWriter(writer, 1, 8, 1, ' ', tabwriter.TabIndent),
writer: writer,
}
}
// NewBufferWriter returns a new buffer Writer.
func NewBufferWriter() *Writer {
writer := bytes.NewBuffer(nil)
return &Writer{
tw: tabwriter.NewWriter(writer, 1, 8, 1, ' ', tabwriter.TabIndent),
writer: writer,
}
}
// String returns the string of the buffer.
func (w *Writer) String() string {
buffer, ok := w.writer.(*bytes.Buffer)
if !ok {
return ""
}
w.Flush()
return buffer.String()
}
// Flush flushes the buffer.
func (w *Writer) Flush() {
_ = w.tw.Flush()
}
// NewLine writes a new line.
func (w *Writer) NewLine() {
_, _ = fmt.Fprint(w.tw, NewLine)
}
// Write writes the node.
func (w *Writer) Write(opts ...Option) {
if len(opts) == 0 {
return
}
var opt = new(option)
opt.mode = ModeAuto
opt.prefix = NilIndent
opt.infix = WhiteSpace
for _, v := range opts {
v(opt)
}
w.write(opt)
}
// WriteText writes the text.
func (w *Writer) WriteText(text string) {
_, _ = fmt.Fprintf(w.tw, text)
}
func (w *Writer) write(opt *option) {
if len(opt.nodes) == 0 {
return
}
var textList []string
line := opt.nodes[0].End().Line
for idx, node := range opt.nodes {
mode := opt.mode
preIdx := idx - 1
var preNodeHasLeading bool
if preIdx > -1 && preIdx < len(opt.nodes) {
preNode := opt.nodes[preIdx]
preNodeHasLeading = preNode.HasLeadingCommentGroup()
}
if node.HasHeadCommentGroup() || preNodeHasLeading {
mode = ModeAuto
}
if mode == ModeAuto && node.Pos().Line > line {
textList = append(textList, NewLine)
}
line = node.End().Line
if util.TrimWhiteSpace(node.Format()) == "" {
continue
}
textList = append(textList, node.Format(opt.prefix))
}
text := strings.Join(textList, opt.infix)
text = strings.ReplaceAll(text, " \n", "\n")
text = strings.ReplaceAll(text, "\n ", "\n")
if opt.rawText {
_, _ = fmt.Fprint(w.writer, text)
return
}
_, _ = fmt.Fprint(w.tw, text)
}