apiparser.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. package ast
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "path/filepath"
  6. "strings"
  7. "github.com/tal-tech/go-zero/tools/goctl/api/parser/g4/gen/api"
  8. "github.com/tal-tech/go-zero/tools/goctl/util/console"
  9. "github.com/zeromicro/antlr"
  10. )
  11. type (
  12. // Parser provides api parsing capabilities
  13. Parser struct {
  14. linePrefix string
  15. debug bool
  16. log console.Console
  17. antlr.DefaultErrorListener
  18. src string
  19. }
  20. // ParserOption defines an function with argument Parser
  21. ParserOption func(p *Parser)
  22. )
  23. // NewParser creates an instance for Parser
  24. func NewParser(options ...ParserOption) *Parser {
  25. p := &Parser{
  26. log: console.NewColorConsole(),
  27. }
  28. for _, opt := range options {
  29. opt(p)
  30. }
  31. return p
  32. }
  33. // Accept can parse any terminalNode of api tree by fn.
  34. // -- for debug
  35. func (p *Parser) Accept(fn func(p *api.ApiParserParser, visitor *ApiVisitor) interface{}, content string) (v interface{}, err error) {
  36. defer func() {
  37. p := recover()
  38. if p != nil {
  39. switch e := p.(type) {
  40. case error:
  41. err = e
  42. default:
  43. err = fmt.Errorf("%+v", p)
  44. }
  45. }
  46. }()
  47. inputStream := antlr.NewInputStream(content)
  48. lexer := api.NewApiParserLexer(inputStream)
  49. lexer.RemoveErrorListeners()
  50. tokens := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel)
  51. apiParser := api.NewApiParserParser(tokens)
  52. apiParser.RemoveErrorListeners()
  53. apiParser.AddErrorListener(p)
  54. var visitorOptions []VisitorOption
  55. visitorOptions = append(visitorOptions, WithVisitorPrefix(p.linePrefix))
  56. if p.debug {
  57. visitorOptions = append(visitorOptions, WithVisitorDebug())
  58. }
  59. visitor := NewApiVisitor(visitorOptions...)
  60. v = fn(apiParser, visitor)
  61. return
  62. }
  63. // Parse is used to parse the api from the specified file name
  64. func (p *Parser) Parse(filename string) (*Api, error) {
  65. data, err := p.readContent(filename)
  66. if err != nil {
  67. return nil, err
  68. }
  69. return p.parse(filename, data)
  70. }
  71. // ParseContent is used to parse the api from the specified content
  72. func (p *Parser) ParseContent(content string) (*Api, error) {
  73. return p.parse("", content)
  74. }
  75. // parse is used to parse api from the content
  76. // filename is only used to mark the file where the error is located
  77. func (p *Parser) parse(filename, content string) (*Api, error) {
  78. root, err := p.invoke(filename, content)
  79. if err != nil {
  80. return nil, err
  81. }
  82. var apiAstList []*Api
  83. apiAstList = append(apiAstList, root)
  84. for _, imp := range root.Import {
  85. dir := filepath.Dir(p.src)
  86. path := filepath.Join(dir, imp.Value.Text())
  87. data, err := p.readContent(path)
  88. if err != nil {
  89. return nil, err
  90. }
  91. nestedApi, err := p.invoke(path, data)
  92. if err != nil {
  93. return nil, err
  94. }
  95. err = p.valid(root, nestedApi)
  96. if err != nil {
  97. return nil, err
  98. }
  99. apiAstList = append(apiAstList, nestedApi)
  100. }
  101. err = p.checkTypeDeclaration(apiAstList)
  102. if err != nil {
  103. return nil, err
  104. }
  105. allApi := p.memberFill(apiAstList)
  106. return allApi, nil
  107. }
  108. func (p *Parser) invoke(linePrefix, content string) (v *Api, err error) {
  109. defer func() {
  110. p := recover()
  111. if p != nil {
  112. switch e := p.(type) {
  113. case error:
  114. err = e
  115. default:
  116. err = fmt.Errorf("%+v", p)
  117. }
  118. }
  119. }()
  120. if linePrefix != "" {
  121. p.linePrefix = linePrefix
  122. }
  123. inputStream := antlr.NewInputStream(content)
  124. lexer := api.NewApiParserLexer(inputStream)
  125. lexer.RemoveErrorListeners()
  126. tokens := antlr.NewCommonTokenStream(lexer, antlr.LexerDefaultTokenChannel)
  127. apiParser := api.NewApiParserParser(tokens)
  128. apiParser.RemoveErrorListeners()
  129. apiParser.AddErrorListener(p)
  130. var visitorOptions []VisitorOption
  131. visitorOptions = append(visitorOptions, WithVisitorPrefix(p.linePrefix))
  132. if p.debug {
  133. visitorOptions = append(visitorOptions, WithVisitorDebug())
  134. }
  135. visitor := NewApiVisitor(visitorOptions...)
  136. v = apiParser.Api().Accept(visitor).(*Api)
  137. v.LinePrefix = p.linePrefix
  138. return
  139. }
  140. func (p *Parser) valid(mainApi *Api, nestedApi *Api) error {
  141. err := p.nestedApiCheck(mainApi, nestedApi)
  142. if err != nil {
  143. return err
  144. }
  145. mainHandlerMap := make(map[string]PlaceHolder)
  146. mainRouteMap := make(map[string]PlaceHolder)
  147. mainTypeMap := make(map[string]PlaceHolder)
  148. routeMap := func(list []*ServiceRoute) (map[string]PlaceHolder, map[string]PlaceHolder) {
  149. handlerMap := make(map[string]PlaceHolder)
  150. routeMap := make(map[string]PlaceHolder)
  151. for _, g := range list {
  152. handler := g.GetHandler()
  153. if handler.IsNotNil() {
  154. handlerName := handler.Text()
  155. handlerMap[handlerName] = Holder
  156. path := fmt.Sprintf("%s://%s", g.Route.Method.Text(), g.Route.Path.Text())
  157. routeMap[path] = Holder
  158. }
  159. }
  160. return handlerMap, routeMap
  161. }
  162. for _, each := range mainApi.Service {
  163. h, r := routeMap(each.ServiceApi.ServiceRoute)
  164. for k, v := range h {
  165. mainHandlerMap[k] = v
  166. }
  167. for k, v := range r {
  168. mainRouteMap[k] = v
  169. }
  170. }
  171. for _, each := range mainApi.Type {
  172. mainTypeMap[each.NameExpr().Text()] = Holder
  173. }
  174. // duplicate route check
  175. err = p.duplicateRouteCheck(nestedApi, mainHandlerMap, mainRouteMap)
  176. if err != nil {
  177. return err
  178. }
  179. // duplicate type check
  180. for _, each := range nestedApi.Type {
  181. if _, ok := mainTypeMap[each.NameExpr().Text()]; ok {
  182. return fmt.Errorf("%s line %d:%d duplicate type declaration '%s'",
  183. nestedApi.LinePrefix, each.NameExpr().Line(), each.NameExpr().Column(), each.NameExpr().Text())
  184. }
  185. }
  186. return nil
  187. }
  188. func (p *Parser) duplicateRouteCheck(nestedApi *Api, mainHandlerMap map[string]PlaceHolder, mainRouteMap map[string]PlaceHolder) error {
  189. for _, each := range nestedApi.Service {
  190. for _, r := range each.ServiceApi.ServiceRoute {
  191. handler := r.GetHandler()
  192. if !handler.IsNotNil() {
  193. return fmt.Errorf("%s handler not exist near line %d", nestedApi.LinePrefix, r.Route.Method.Line())
  194. }
  195. if _, ok := mainHandlerMap[handler.Text()]; ok {
  196. return fmt.Errorf("%s line %d:%d duplicate handler '%s'",
  197. nestedApi.LinePrefix, handler.Line(), handler.Column(), handler.Text())
  198. }
  199. path := fmt.Sprintf("%s://%s", r.Route.Method.Text(), r.Route.Path.Text())
  200. if _, ok := mainRouteMap[path]; ok {
  201. return fmt.Errorf("%s line %d:%d duplicate route '%s'",
  202. nestedApi.LinePrefix, r.Route.Method.Line(), r.Route.Method.Column(), r.Route.Method.Text()+" "+r.Route.Path.Text())
  203. }
  204. }
  205. }
  206. return nil
  207. }
  208. func (p *Parser) nestedApiCheck(mainApi *Api, nestedApi *Api) error {
  209. if len(nestedApi.Import) > 0 {
  210. importToken := nestedApi.Import[0].Import
  211. return fmt.Errorf("%s line %d:%d the nested api does not support import",
  212. nestedApi.LinePrefix, importToken.Line(), importToken.Column())
  213. }
  214. if mainApi.Syntax != nil && nestedApi.Syntax != nil {
  215. if mainApi.Syntax.Version.Text() != nestedApi.Syntax.Version.Text() {
  216. syntaxToken := nestedApi.Syntax.Syntax
  217. return fmt.Errorf("%s line %d:%d multiple syntax declaration, expecting syntax '%s', but found '%s'",
  218. nestedApi.LinePrefix, syntaxToken.Line(), syntaxToken.Column(), mainApi.Syntax.Version.Text(), nestedApi.Syntax.Version.Text())
  219. }
  220. }
  221. if len(mainApi.Service) > 0 {
  222. mainService := mainApi.Service[0]
  223. for _, service := range nestedApi.Service {
  224. if mainService.ServiceApi.Name.Text() != service.ServiceApi.Name.Text() {
  225. return fmt.Errorf("%s multiple service name declaration, expecting service name '%s', but found '%s'",
  226. nestedApi.LinePrefix, mainService.ServiceApi.Name.Text(), service.ServiceApi.Name.Text())
  227. }
  228. }
  229. }
  230. return nil
  231. }
  232. func (p *Parser) memberFill(apiList []*Api) *Api {
  233. var root Api
  234. for index, each := range apiList {
  235. if index == 0 {
  236. root.Syntax = each.Syntax
  237. root.Info = each.Info
  238. root.Import = each.Import
  239. }
  240. root.Type = append(root.Type, each.Type...)
  241. root.Service = append(root.Service, each.Service...)
  242. }
  243. return &root
  244. }
  245. // checkTypeDeclaration checks whether a struct type has been declared in context
  246. func (p *Parser) checkTypeDeclaration(apiList []*Api) error {
  247. types := make(map[string]TypeExpr)
  248. for _, root := range apiList {
  249. for _, each := range root.Type {
  250. types[each.NameExpr().Text()] = each
  251. }
  252. }
  253. for _, apiItem := range apiList {
  254. linePrefix := apiItem.LinePrefix
  255. err := p.checkTypes(apiItem, linePrefix, types)
  256. if err != nil {
  257. return err
  258. }
  259. err = p.checkServices(apiItem, types, linePrefix)
  260. if err != nil {
  261. return err
  262. }
  263. }
  264. return nil
  265. }
  266. func (p *Parser) checkServices(apiItem *Api, types map[string]TypeExpr, linePrefix string) error {
  267. for _, service := range apiItem.Service {
  268. for _, each := range service.ServiceApi.ServiceRoute {
  269. route := each.Route
  270. err := p.checkRequestBody(route, types, linePrefix)
  271. if err != nil {
  272. return err
  273. }
  274. if route.Reply != nil && route.Reply.Name.IsNotNil() && route.Reply.Name.Expr().IsNotNil() {
  275. reply := route.Reply.Name
  276. var structName string
  277. switch tp := reply.(type) {
  278. case *Literal:
  279. structName = tp.Literal.Text()
  280. case *Array:
  281. switch innerTp := tp.Literal.(type) {
  282. case *Literal:
  283. structName = innerTp.Literal.Text()
  284. case *Pointer:
  285. structName = innerTp.Name.Text()
  286. }
  287. }
  288. if api.IsBasicType(structName) {
  289. continue
  290. }
  291. _, ok := types[structName]
  292. if !ok {
  293. return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context",
  294. linePrefix, route.Reply.Name.Expr().Line(), route.Reply.Name.Expr().Column(), structName)
  295. }
  296. }
  297. }
  298. }
  299. return nil
  300. }
  301. func (p *Parser) checkRequestBody(route *Route, types map[string]TypeExpr, linePrefix string) error {
  302. if route.Req != nil && route.Req.Name.IsNotNil() && route.Req.Name.Expr().IsNotNil() {
  303. _, ok := types[route.Req.Name.Expr().Text()]
  304. if !ok {
  305. return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context",
  306. linePrefix, route.Req.Name.Expr().Line(), route.Req.Name.Expr().Column(), route.Req.Name.Expr().Text())
  307. }
  308. }
  309. return nil
  310. }
  311. func (p *Parser) checkTypes(apiItem *Api, linePrefix string, types map[string]TypeExpr) error {
  312. for _, each := range apiItem.Type {
  313. tp, ok := each.(*TypeStruct)
  314. if !ok {
  315. continue
  316. }
  317. for _, member := range tp.Fields {
  318. err := p.checkType(linePrefix, types, member.DataType)
  319. if err != nil {
  320. return err
  321. }
  322. }
  323. }
  324. return nil
  325. }
  326. func (p *Parser) checkType(linePrefix string, types map[string]TypeExpr, expr DataType) error {
  327. if expr == nil {
  328. return nil
  329. }
  330. switch v := expr.(type) {
  331. case *Literal:
  332. name := v.Literal.Text()
  333. if api.IsBasicType(name) {
  334. return nil
  335. }
  336. _, ok := types[name]
  337. if !ok {
  338. return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context",
  339. linePrefix, v.Literal.Line(), v.Literal.Column(), name)
  340. }
  341. case *Pointer:
  342. name := v.Name.Text()
  343. if api.IsBasicType(name) {
  344. return nil
  345. }
  346. _, ok := types[name]
  347. if !ok {
  348. return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context",
  349. linePrefix, v.Name.Line(), v.Name.Column(), name)
  350. }
  351. case *Map:
  352. return p.checkType(linePrefix, types, v.Value)
  353. case *Array:
  354. return p.checkType(linePrefix, types, v.Literal)
  355. default:
  356. return nil
  357. }
  358. return nil
  359. }
  360. func (p *Parser) readContent(filename string) (string, error) {
  361. filename = strings.ReplaceAll(filename, `"`, "")
  362. abs, err := filepath.Abs(filename)
  363. if err != nil {
  364. return "", err
  365. }
  366. p.src = abs
  367. data, err := ioutil.ReadFile(abs)
  368. if err != nil {
  369. return "", err
  370. }
  371. return string(data), nil
  372. }
  373. // SyntaxError accepts errors and panic it
  374. func (p *Parser) SyntaxError(_ antlr.Recognizer, _ interface{}, line, column int, msg string, _ antlr.RecognitionException) {
  375. str := fmt.Sprintf(`%s line %d:%d %s`, p.linePrefix, line, column, msg)
  376. if p.debug {
  377. p.log.Error(str)
  378. }
  379. panic(str)
  380. }
  381. // WithParserDebug returns a debug ParserOption
  382. func WithParserDebug() ParserOption {
  383. return func(p *Parser) {
  384. p.debug = true
  385. }
  386. }
  387. // WithParserPrefix returns a prefix ParserOption
  388. func WithParserPrefix(prefix string) ParserOption {
  389. return func(p *Parser) {
  390. p.linePrefix = prefix
  391. }
  392. }