apifileparser.go 4.4 KB

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