123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- 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]
- }
|