feat(goctl): Add api parser (#2585)
This commit is contained in:
223
tools/goctl/pkg/parser/api/ast/ast.go
Normal file
223
tools/goctl/pkg/parser/api/ast/ast.go
Normal 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]
|
||||
}
|
||||
75
tools/goctl/pkg/parser/api/ast/comment.go
Normal file
75
tools/goctl/pkg/parser/api/ast/comment.go
Normal 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
|
||||
}
|
||||
111
tools/goctl/pkg/parser/api/ast/importstatement.go
Normal file
111
tools/goctl/pkg/parser/api/ast/importstatement.go
Normal 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() {}
|
||||
65
tools/goctl/pkg/parser/api/ast/infostatement.go
Normal file
65
tools/goctl/pkg/parser/api/ast/infostatement.go
Normal 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() {}
|
||||
39
tools/goctl/pkg/parser/api/ast/kvexpression.go
Normal file
39
tools/goctl/pkg/parser/api/ast/kvexpression.go
Normal 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() {}
|
||||
237
tools/goctl/pkg/parser/api/ast/print.go
Normal file
237
tools/goctl/pkg/parser/api/ast/print.go
Normal 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)
|
||||
}
|
||||
}
|
||||
577
tools/goctl/pkg/parser/api/ast/servicestatement.go
Normal file
577
tools/goctl/pkg/parser/api/ast/servicestatement.go
Normal 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() {}
|
||||
44
tools/goctl/pkg/parser/api/ast/syntaxstatement.go
Normal file
44
tools/goctl/pkg/parser/api/ast/syntaxstatement.go
Normal 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() {}
|
||||
797
tools/goctl/pkg/parser/api/ast/typestatement.go
Normal file
797
tools/goctl/pkg/parser/api/ast/typestatement.go
Normal 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********************/
|
||||
403
tools/goctl/pkg/parser/api/ast/writer.go
Normal file
403
tools/goctl/pkg/parser/api/ast/writer.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user