123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- package parser
- import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "io"
- "strings"
- )
- const (
- tokenInfo = "info"
- tokenImport = "import"
- tokenType = "type"
- tokenService = "service"
- tokenServiceAnnotation = "@server"
- )
- type (
- ApiStruct struct {
- Info string
- Type string
- Service string
- Imports string
- serviceBeginLine int
- }
- apiFileState interface {
- process(api *ApiStruct, token string) (apiFileState, error)
- }
- apiRootState struct {
- *baseState
- }
- apiInfoState struct {
- *baseState
- }
- apiImportState struct {
- *baseState
- }
- apiTypeState struct {
- *baseState
- }
- apiServiceState struct {
- *baseState
- }
- )
- func ParseApi(src string) (*ApiStruct, error) {
- var buffer = new(bytes.Buffer)
- buffer.WriteString(src)
- api := new(ApiStruct)
- var lineNumber = api.serviceBeginLine
- apiFile := baseState{r: bufio.NewReader(buffer), lineNumber: &lineNumber}
- st := apiRootState{&apiFile}
- for {
- st, err := st.process(api, "")
- if err == io.EOF {
- return api, nil
- }
- if err != nil {
- return nil, fmt.Errorf("near line: %d, %s", lineNumber, err.Error())
- }
- if st == nil {
- return api, nil
- }
- }
- }
- func (s *apiRootState) process(api *ApiStruct, token string) (apiFileState, error) {
- var builder strings.Builder
- for {
- ch, err := s.readSkipComment()
- if err != nil {
- return nil, err
- }
- switch {
- case isSpace(ch) || isNewline(ch) || ch == leftParenthesis:
- token := builder.String()
- token = strings.TrimSpace(token)
- if len(token) == 0 {
- continue
- }
- builder.Reset()
- switch token {
- case tokenInfo:
- info := apiInfoState{s.baseState}
- return info.process(api, token+string(ch))
- case tokenImport:
- tp := apiImportState{s.baseState}
- return tp.process(api, token+string(ch))
- case tokenType:
- ty := apiTypeState{s.baseState}
- return ty.process(api, token+string(ch))
- case tokenService:
- server := apiServiceState{s.baseState}
- return server.process(api, token+string(ch))
- case tokenServiceAnnotation:
- server := apiServiceState{s.baseState}
- return server.process(api, token+string(ch))
- default:
- if strings.HasPrefix(token, "//") {
- continue
- }
- return nil, errors.New(fmt.Sprintf("invalid token %s at line %d", token, *s.lineNumber))
- }
- default:
- builder.WriteRune(ch)
- }
- }
- }
- func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, error) {
- for {
- line, err := s.readLine()
- if err != nil {
- return nil, err
- }
- api.Info += "\n" + token + line
- token = ""
- if strings.TrimSpace(line) == string(rightParenthesis) {
- return &apiRootState{s.baseState}, nil
- }
- }
- }
- func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, error) {
- line, err := s.readLine()
- if err != nil {
- return nil, err
- }
- line = token + line
- line = removeComment(line)
- if len(strings.Fields(line)) != 2 {
- return nil, errors.New("import syntax error: " + line)
- }
- api.Imports += "\n" + line
- return &apiRootState{s.baseState}, nil
- }
- func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
- var blockCount = 0
- for {
- line, err := s.readLine()
- if err != nil {
- return nil, err
- }
- api.Type += "\n\n" + token + line
- token = ""
- line = strings.TrimSpace(line)
- line = removeComment(line)
- line = strings.TrimSpace(line)
- if strings.HasSuffix(line, leftBrace) {
- blockCount++
- }
- if strings.HasSuffix(line, string(leftParenthesis)) {
- blockCount++
- }
- if strings.HasSuffix(line, string(rightBrace)) {
- blockCount--
- }
- if strings.HasSuffix(line, string(rightParenthesis)) {
- blockCount--
- }
- if blockCount == 0 {
- return &apiRootState{s.baseState}, nil
- }
- }
- }
- func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, error) {
- var blockCount = 0
- for {
- line, err := s.readLineSkipComment()
- if err != nil {
- return nil, err
- }
- line = token + line
- token = ""
- api.Service += "\n" + line
- line = strings.TrimSpace(line)
- line = removeComment(line)
- line = strings.TrimSpace(line)
- if strings.HasSuffix(line, leftBrace) {
- blockCount++
- }
- if strings.HasSuffix(line, string(leftParenthesis)) {
- blockCount++
- }
- if line == string(rightBrace) {
- blockCount--
- }
- if line == string(rightParenthesis) {
- blockCount--
- }
- if blockCount == 0 {
- return &apiRootState{s.baseState}, nil
- }
- }
- }
- func removeComment(line string) string {
- var commentIdx = strings.Index(line, "//")
- if commentIdx >= 0 {
- return line[:commentIdx]
- }
- return line
- }
|