apiparser.go 14 KB

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