Skip to content

Commit

Permalink
refactor parser and remove deprecated code (#204)
Browse files Browse the repository at this point in the history
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

Co-authored-by: kim <[email protected]>
  • Loading branch information
kingxt and kim authored Nov 13, 2020
1 parent ef4d496 commit 16bfb1b
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 123 deletions.
2 changes: 1 addition & 1 deletion tools/goctl/api/docgen/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func genDoc(api *spec.ApiSpec, dir string, filename string) error {
defer fp.Close()

var builder strings.Builder
for index, route := range api.Service.Routes {
for index, route := range api.Service.Routes() {
routeComment, _ := util.GetAnnotationValue(route.Annotations, "doc", "summary")
if len(routeComment) == 0 {
routeComment = "N/A"
Expand Down
2 changes: 1 addition & 1 deletion tools/goctl/api/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func apiFormat(data string) (string, error) {
return data, nil
}

fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.StructBody)))
fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.Type)))
if err != nil {
str := err.Error()
lineNumber := strings.Index(str, ":")
Expand Down
23 changes: 10 additions & 13 deletions tools/goctl/api/gogen/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ info(
)
type Request struct {
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // }
}
type Response struct {
Expand Down Expand Up @@ -292,13 +292,13 @@ func TestParser(t *testing.T) {
assert.Nil(t, err)

assert.Equal(t, len(api.Types), 2)
assert.Equal(t, len(api.Service.Routes), 2)
assert.Equal(t, len(api.Service.Routes()), 2)

assert.Equal(t, api.Service.Routes[0].Path, "/greet/from/:name")
assert.Equal(t, api.Service.Routes[1].Path, "/greet/get")
assert.Equal(t, api.Service.Routes()[0].Path, "/greet/from/:name")
assert.Equal(t, api.Service.Routes()[1].Path, "/greet/get")

assert.Equal(t, api.Service.Routes[1].RequestType.Name, "Request")
assert.Equal(t, api.Service.Routes[1].ResponseType.Name, "")
assert.Equal(t, api.Service.Routes()[1].RequestType.Name, "Request")
assert.Equal(t, api.Service.Routes()[1].ResponseType.Name, "")

validate(t, filename)
}
Expand All @@ -315,7 +315,7 @@ func TestMultiService(t *testing.T) {
api, err := parser.Parse()
assert.Nil(t, err)

assert.Equal(t, len(api.Service.Routes), 2)
assert.Equal(t, len(api.Service.Routes()), 2)
assert.Equal(t, len(api.Service.Groups), 2)

validate(t, filename)
Expand All @@ -342,10 +342,7 @@ func TestInvalidApiFile(t *testing.T) {
assert.Nil(t, err)
defer os.Remove(filename)

parser, err := parser.NewParser(filename)
assert.Nil(t, err)

_, err = parser.Parse()
_, err = parser.NewParser(filename)
assert.NotNil(t, err)
}

Expand All @@ -361,8 +358,8 @@ func TestAnonymousAnnotation(t *testing.T) {
api, err := parser.Parse()
assert.Nil(t, err)

assert.Equal(t, len(api.Service.Routes), 1)
assert.Equal(t, api.Service.Routes[0].Annotations[0].Value, "GreetHandler")
assert.Equal(t, len(api.Service.Routes()), 1)
assert.Equal(t, api.Service.Routes()[0].Annotations[0].Value, "GreetHandler")

validate(t, filename)
}
Expand Down
4 changes: 2 additions & 2 deletions tools/goctl/api/gogen/genetc.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ func genEtc(dir string, api *spec.ApiSpec) error {
defer fp.Close()

service := api.Service
host, ok := util.GetAnnotationValue(service.Annotations, "server", "host")
host, ok := util.GetAnnotationValue(service.Groups[0].Annotations, "server", "host")
if !ok {
host = "0.0.0.0"
}
port, ok := util.GetAnnotationValue(service.Annotations, "server", "port")
port, ok := util.GetAnnotationValue(service.Groups[0].Annotations, "server", "port")
if !ok {
port = strconv.Itoa(defaultPort)
}
Expand Down
2 changes: 1 addition & 1 deletion tools/goctl/api/javagen/genpacket.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class {{.packetName}} extends HttpRequestPacket<{{.packetName}}.{{.packet
`

func genPacket(dir, packetName string, api *spec.ApiSpec) error {
for _, route := range api.Service.Routes {
for _, route := range api.Service.Routes() {
if err := createWith(dir, api, route, packetName); err != nil {
return err
}
Expand Down
219 changes: 219 additions & 0 deletions tools/goctl/api/parser/apifileparser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
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
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)
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)
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
}
28 changes: 23 additions & 5 deletions tools/goctl/api/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package parser
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -34,10 +35,11 @@ func NewParser(filename string) (*Parser, error) {
if err != nil {
return nil, err
}

for _, item := range strings.Split(apiStruct.Imports, "\n") {
ip := strings.TrimSpace(item)
if len(ip) > 0 {
item := strings.TrimPrefix(item, "import")
importLine := strings.TrimSpace(item)
if len(importLine) > 0 {
item := strings.TrimPrefix(importLine, "import")
item = strings.TrimSpace(item)
item = strings.TrimPrefix(item, `"`)
item = strings.TrimSuffix(item, `"`)
Expand All @@ -46,18 +48,33 @@ func NewParser(filename string) (*Parser, error) {
path = filepath.Join(filepath.Dir(apiAbsPath), item)
}
content, err := ioutil.ReadFile(path)
if err != nil {
return nil, errors.New("import api file not exist: " + item)
}

importStruct, err := ParseApi(string(content))
if err != nil {
return nil, err
}
apiStruct.StructBody += "\n" + string(content)

if len(importStruct.Imports) > 0 {
return nil, errors.New("import api should not import another api file recursive")
}

apiStruct.Type += "\n" + importStruct.Type
apiStruct.Service += "\n" + importStruct.Service
}
}

if len(strings.TrimSpace(apiStruct.Service)) == 0 {
return nil, errors.New("api has no service defined")
}

var buffer = new(bytes.Buffer)
buffer.WriteString(apiStruct.Service)
return &Parser{
r: bufio.NewReader(buffer),
typeDef: apiStruct.StructBody,
typeDef: apiStruct.Type,
api: apiStruct,
}, nil
}
Expand All @@ -69,6 +86,7 @@ func (p *Parser) Parse() (api *spec.ApiSpec, err error) {
if err != nil {
return nil, err
}

api.Types = types
var lineNumber = p.api.serviceBeginLine
st := newRootState(p.r, &lineNumber)
Expand Down
4 changes: 1 addition & 3 deletions tools/goctl/api/parser/servicestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ func (s *serviceState) process(api *spec.ApiSpec) (state, error) {
}

api.Service = spec.Service{
Name: name,
Annotations: append(api.Service.Annotations, s.annos...),
Routes: append(api.Service.Routes, routes...),
Name: name,
Groups: append(api.Service.Groups, spec.Group{
Annotations: s.annos,
Routes: routes,
Expand Down
Loading

0 comments on commit 16bfb1b

Please sign in to comment.