apifileparser.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. line = removeComment(line)
  126. if len(strings.Fields(line)) != 2 {
  127. return nil, errors.New("import syntax error: " + line)
  128. }
  129. api.Imports += "\n" + line
  130. return &apiRootState{s.baseState}, nil
  131. }
  132. func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
  133. var blockCount = 0
  134. for {
  135. line, err := s.readLine()
  136. if err != nil {
  137. return nil, err
  138. }
  139. api.Type += "\n\n" + token + line
  140. token = ""
  141. line = strings.TrimSpace(line)
  142. line = removeComment(line)
  143. line = strings.TrimSpace(line)
  144. if strings.HasSuffix(line, leftBrace) {
  145. blockCount++
  146. }
  147. if strings.HasSuffix(line, string(leftParenthesis)) {
  148. blockCount++
  149. }
  150. if strings.HasSuffix(line, string(rightBrace)) {
  151. blockCount--
  152. }
  153. if strings.HasSuffix(line, string(rightParenthesis)) {
  154. blockCount--
  155. }
  156. if blockCount == 0 {
  157. return &apiRootState{s.baseState}, nil
  158. }
  159. }
  160. }
  161. func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, error) {
  162. var blockCount = 0
  163. for {
  164. line, err := s.readLineSkipComment()
  165. if err != nil {
  166. return nil, err
  167. }
  168. line = token + line
  169. token = ""
  170. api.Service += "\n" + line
  171. line = strings.TrimSpace(line)
  172. line = removeComment(line)
  173. line = strings.TrimSpace(line)
  174. if strings.HasSuffix(line, leftBrace) {
  175. blockCount++
  176. }
  177. if strings.HasSuffix(line, string(leftParenthesis)) {
  178. blockCount++
  179. }
  180. if line == string(rightBrace) {
  181. blockCount--
  182. }
  183. if line == string(rightParenthesis) {
  184. blockCount--
  185. }
  186. if blockCount == 0 {
  187. return &apiRootState{s.baseState}, nil
  188. }
  189. }
  190. }
  191. func removeComment(line string) string {
  192. var commentIdx = strings.Index(line, "//")
  193. if commentIdx >= 0 {
  194. return line[:commentIdx]
  195. }
  196. return line
  197. }