ast.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package ast
  2. import (
  3. "fmt"
  4. "io"
  5. "strings"
  6. "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
  7. "github.com/zeromicro/go-zero/tools/goctl/util"
  8. )
  9. // Node represents a node in the AST.
  10. type Node interface {
  11. // Pos returns the position of the first character belonging to the node.
  12. Pos() token.Position
  13. // End returns the position of the first character immediately after the node.
  14. End() token.Position
  15. // Format returns the node's text after format.
  16. Format(...string) string
  17. // HasHeadCommentGroup returns true if the node has head comment group.
  18. HasHeadCommentGroup() bool
  19. // HasLeadingCommentGroup returns true if the node has leading comment group.
  20. HasLeadingCommentGroup() bool
  21. // CommentGroup returns the node's head comment group and leading comment group.
  22. CommentGroup() (head, leading CommentGroup)
  23. }
  24. // Stmt represents a statement in the AST.
  25. type Stmt interface {
  26. Node
  27. stmtNode()
  28. }
  29. // Expr represents an expression in the AST.
  30. type Expr interface {
  31. Node
  32. exprNode()
  33. }
  34. // AST represents a parsed API file.
  35. type AST struct {
  36. Filename string
  37. Stmts []Stmt
  38. readPosition int
  39. }
  40. // TokenNode represents a token node in the AST.
  41. type TokenNode struct {
  42. // HeadCommentGroup are the comments in prev lines.
  43. HeadCommentGroup CommentGroup
  44. // Token is the token of the node.
  45. Token token.Token
  46. // LeadingCommentGroup are the tail comments in same line.
  47. LeadingCommentGroup CommentGroup
  48. // headFlag and leadingFlag is a comment flag only used in transfer another Node to TokenNode,
  49. // headFlag's value is true do not represent HeadCommentGroup is not empty,
  50. // leadingFlag's values is true do not represent LeadingCommentGroup is not empty.
  51. headFlag, leadingFlag bool
  52. }
  53. // NewTokenNode creates and returns a new TokenNode.
  54. func NewTokenNode(tok token.Token) *TokenNode {
  55. return &TokenNode{Token: tok}
  56. }
  57. // IsEmptyString returns true if the node is empty string.
  58. func (t *TokenNode) IsEmptyString() bool {
  59. return t.Equal("")
  60. }
  61. // IsZeroString returns true if the node is zero string.
  62. func (t *TokenNode) IsZeroString() bool {
  63. return t.Equal(`""`) || t.Equal("``")
  64. }
  65. // Equal returns true if the node's text is equal to the given text.
  66. func (t *TokenNode) Equal(s string) bool {
  67. return t.Token.Text == s
  68. }
  69. // SetLeadingCommentGroup sets the node's leading comment group.
  70. func (t *TokenNode) SetLeadingCommentGroup(cg CommentGroup) {
  71. t.LeadingCommentGroup = cg
  72. }
  73. func (t *TokenNode) HasLeadingCommentGroup() bool {
  74. return t.LeadingCommentGroup.Valid() || t.leadingFlag
  75. }
  76. func (t *TokenNode) HasHeadCommentGroup() bool {
  77. return t.HeadCommentGroup.Valid() || t.headFlag
  78. }
  79. func (t *TokenNode) CommentGroup() (head, leading CommentGroup) {
  80. return t.HeadCommentGroup, t.LeadingCommentGroup
  81. }
  82. // PeekFirstLeadingComment returns the first leading comment of the node.
  83. func (t *TokenNode) PeekFirstLeadingComment() *CommentStmt {
  84. if len(t.LeadingCommentGroup) > 0 {
  85. return t.LeadingCommentGroup[0]
  86. }
  87. return nil
  88. }
  89. // PeekFirstHeadComment returns the first head comment of the node.
  90. func (t *TokenNode) PeekFirstHeadComment() *CommentStmt {
  91. if len(t.HeadCommentGroup) > 0 {
  92. return t.HeadCommentGroup[0]
  93. }
  94. return nil
  95. }
  96. func (t *TokenNode) Format(prefix ...string) string {
  97. p := peekOne(prefix)
  98. var textList []string
  99. for _, v := range t.HeadCommentGroup {
  100. textList = append(textList, v.Format(p))
  101. }
  102. var tokenText = p + t.Token.Text
  103. var validLeadingCommentGroup CommentGroup
  104. for _, e := range t.LeadingCommentGroup {
  105. if util.IsEmptyStringOrWhiteSpace(e.Comment.Text) {
  106. continue
  107. }
  108. validLeadingCommentGroup = append(validLeadingCommentGroup, e)
  109. }
  110. if len(validLeadingCommentGroup) > 0 {
  111. tokenText = tokenText + WhiteSpace + t.LeadingCommentGroup.Join(WhiteSpace)
  112. }
  113. textList = append(textList, tokenText)
  114. return strings.Join(textList, NewLine)
  115. }
  116. func (t *TokenNode) Pos() token.Position {
  117. if len(t.HeadCommentGroup) > 0 {
  118. return t.PeekFirstHeadComment().Pos()
  119. }
  120. return t.Token.Position
  121. }
  122. func (t *TokenNode) End() token.Position {
  123. if len(t.LeadingCommentGroup) > 0 {
  124. return t.LeadingCommentGroup[len(t.LeadingCommentGroup)-1].End()
  125. }
  126. return t.Token.Position
  127. }
  128. // Format formats the AST.
  129. func (a *AST) Format(w io.Writer) {
  130. fw := NewWriter(w)
  131. defer fw.Flush()
  132. for idx, e := range a.Stmts {
  133. if e.Format() == NilIndent {
  134. continue
  135. }
  136. fw.Write(withNode(e))
  137. fw.NewLine()
  138. switch e.(type) {
  139. case *SyntaxStmt:
  140. fw.NewLine()
  141. case *ImportGroupStmt:
  142. fw.NewLine()
  143. case *ImportLiteralStmt:
  144. if idx < len(a.Stmts)-1 {
  145. _, ok := a.Stmts[idx+1].(*ImportLiteralStmt)
  146. if !ok {
  147. fw.NewLine()
  148. }
  149. }
  150. case *InfoStmt:
  151. fw.NewLine()
  152. case *ServiceStmt:
  153. fw.NewLine()
  154. case *TypeGroupStmt:
  155. fw.NewLine()
  156. case *TypeLiteralStmt:
  157. fw.NewLine()
  158. case *CommentStmt:
  159. }
  160. }
  161. }
  162. // FormatForUnitTest formats the AST for unit test.
  163. func (a *AST) FormatForUnitTest(w io.Writer) {
  164. fw := NewWriter(w)
  165. defer fw.Flush()
  166. for _, e := range a.Stmts {
  167. text := e.Format()
  168. if text == NilIndent {
  169. continue
  170. }
  171. fw.WriteText(text)
  172. }
  173. }
  174. // Print prints the AST.
  175. func (a *AST) Print() {
  176. _ = Print(a)
  177. }
  178. // SyntaxError represents a syntax error.
  179. func SyntaxError(pos token.Position, format string, v ...interface{}) error {
  180. return fmt.Errorf("syntax error: %s %s", pos.String(), fmt.Sprintf(format, v...))
  181. }
  182. // DuplicateStmtError represents a duplicate statement error.
  183. func DuplicateStmtError(pos token.Position, msg string) error {
  184. return fmt.Errorf("duplicate declaration: %s %s", pos.String(), msg)
  185. }
  186. func peekOne(list []string) string {
  187. if len(list) == 0 {
  188. return ""
  189. }
  190. return list[0]
  191. }