apifileparser.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package parser
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "strings"
  9. "github.com/tal-tech/go-zero/core/stringx"
  10. "github.com/tal-tech/go-zero/tools/goctl/api/util"
  11. )
  12. const (
  13. tokenInfo = "info"
  14. tokenImport = "import"
  15. tokenType = "type"
  16. tokenService = "service"
  17. tokenServiceAnnotation = "@server"
  18. tokenStruct = "struct"
  19. )
  20. type (
  21. ApiStruct struct {
  22. Info string
  23. Type string
  24. Service string
  25. Imports string
  26. serviceBeginLine int
  27. }
  28. apiFileState interface {
  29. process(api *ApiStruct, token string) (apiFileState, error)
  30. }
  31. apiRootState struct {
  32. *baseState
  33. }
  34. apiInfoState struct {
  35. *baseState
  36. }
  37. apiImportState struct {
  38. *baseState
  39. }
  40. apiTypeState struct {
  41. *baseState
  42. }
  43. apiServiceState struct {
  44. *baseState
  45. }
  46. )
  47. func ParseApi(src string) (*ApiStruct, error) {
  48. var buffer = new(bytes.Buffer)
  49. buffer.WriteString(src)
  50. api := new(ApiStruct)
  51. var lineNumber = api.serviceBeginLine
  52. apiFile := baseState{r: bufio.NewReader(buffer), lineNumber: &lineNumber}
  53. st := apiRootState{&apiFile}
  54. for {
  55. st, err := st.process(api, "")
  56. if err == io.EOF {
  57. return api, nil
  58. }
  59. if err != nil {
  60. return nil, fmt.Errorf("near line: %d, %s", lineNumber, err.Error())
  61. }
  62. if st == nil {
  63. return api, nil
  64. }
  65. }
  66. }
  67. func (s *apiRootState) process(api *ApiStruct, _ string) (apiFileState, error) {
  68. var builder strings.Builder
  69. for {
  70. ch, err := s.readSkipComment()
  71. if err != nil {
  72. return nil, err
  73. }
  74. switch {
  75. case isSpace(ch) || isNewline(ch) || ch == leftParenthesis:
  76. token := builder.String()
  77. token = strings.TrimSpace(token)
  78. if len(token) == 0 {
  79. continue
  80. }
  81. builder.Reset()
  82. switch token {
  83. case tokenInfo:
  84. info := apiInfoState{s.baseState}
  85. return info.process(api, token+string(ch))
  86. case tokenImport:
  87. tp := apiImportState{s.baseState}
  88. return tp.process(api, token+string(ch))
  89. case tokenType:
  90. ty := apiTypeState{s.baseState}
  91. return ty.process(api, token+string(ch))
  92. case tokenService:
  93. server := apiServiceState{s.baseState}
  94. return server.process(api, token+string(ch))
  95. case tokenServiceAnnotation:
  96. server := apiServiceState{s.baseState}
  97. return server.process(api, token+string(ch))
  98. default:
  99. if strings.HasPrefix(token, "//") {
  100. continue
  101. }
  102. return nil, errors.New(fmt.Sprintf("invalid token %s at line %d", token, *s.lineNumber))
  103. }
  104. default:
  105. builder.WriteRune(ch)
  106. }
  107. }
  108. }
  109. func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, error) {
  110. for {
  111. line, err := s.readLine()
  112. if err != nil {
  113. return nil, err
  114. }
  115. api.Info += newline + token + line
  116. token = ""
  117. if strings.TrimSpace(line) == string(rightParenthesis) {
  118. return &apiRootState{s.baseState}, nil
  119. }
  120. }
  121. }
  122. func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, error) {
  123. line, err := s.readLine()
  124. if err != nil {
  125. return nil, err
  126. }
  127. line = token + line
  128. line = util.RemoveComment(line)
  129. if len(strings.Fields(line)) != 2 {
  130. return nil, errors.New("import syntax error: " + line)
  131. }
  132. api.Imports += newline + line
  133. return &apiRootState{s.baseState}, nil
  134. }
  135. func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
  136. var blockCount = 0
  137. var braceCount = 0
  138. for {
  139. line, err := s.readLine()
  140. if err != nil {
  141. return nil, err
  142. }
  143. line = token + line
  144. if braceCount == 0 {
  145. line = mayInsertStructKeyword(line)
  146. }
  147. api.Type += newline + newline + line
  148. line = strings.TrimSpace(line)
  149. line = util.RemoveComment(line)
  150. token = ""
  151. if strings.HasSuffix(line, leftBrace) {
  152. blockCount++
  153. braceCount++
  154. }
  155. if strings.HasSuffix(line, string(leftParenthesis)) {
  156. blockCount++
  157. }
  158. if strings.HasSuffix(line, string(rightBrace)) {
  159. blockCount--
  160. braceCount--
  161. }
  162. if strings.HasSuffix(line, string(rightParenthesis)) {
  163. blockCount--
  164. }
  165. if braceCount >= 2 {
  166. return nil, errors.New("nested type not supported: " + line)
  167. }
  168. if braceCount < 0 {
  169. line = strings.TrimSuffix(line, string(rightBrace))
  170. line = strings.TrimSpace(line)
  171. if strings.HasSuffix(line, leftBrace) {
  172. blockCount++
  173. braceCount++
  174. }
  175. }
  176. if blockCount == 0 {
  177. return &apiRootState{s.baseState}, nil
  178. }
  179. }
  180. }
  181. func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, error) {
  182. var blockCount = 0
  183. for {
  184. line, err := s.readLineSkipComment()
  185. if err != nil {
  186. return nil, err
  187. }
  188. line = token + line
  189. token = ""
  190. api.Service += newline + line
  191. line = strings.TrimSpace(line)
  192. line = util.RemoveComment(line)
  193. if strings.HasSuffix(line, leftBrace) {
  194. blockCount++
  195. }
  196. if strings.HasSuffix(line, string(leftParenthesis)) {
  197. blockCount++
  198. }
  199. if line == string(rightBrace) {
  200. blockCount--
  201. }
  202. if line == string(rightParenthesis) {
  203. blockCount--
  204. }
  205. if blockCount == 0 {
  206. return &apiRootState{s.baseState}, nil
  207. }
  208. }
  209. }
  210. func mayInsertStructKeyword(line string) string {
  211. line = util.RemoveComment(line)
  212. if !strings.HasSuffix(line, leftBrace) && !strings.HasSuffix(line, string(rightBrace)) {
  213. return line
  214. }
  215. fields := strings.Fields(line)
  216. if stringx.Contains(fields, tokenStruct) ||
  217. stringx.Contains(fields, tokenStruct+leftBrace) ||
  218. stringx.Contains(fields, tokenStruct+leftBrace+string(rightBrace)) ||
  219. len(fields) <= 1 {
  220. return line
  221. }
  222. var insertIndex int
  223. if fields[0] == tokenType {
  224. insertIndex = 2
  225. } else {
  226. insertIndex = 1
  227. }
  228. if insertIndex >= len(fields) {
  229. return line
  230. }
  231. var result []string
  232. result = append(result, fields[:insertIndex]...)
  233. result = append(result, tokenStruct)
  234. result = append(result, fields[insertIndex:]...)
  235. return strings.Join(result, " ")
  236. }