visitor.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package parser
  2. import (
  3. "go/ast"
  4. "go/token"
  5. "strings"
  6. )
  7. type Visitor struct {
  8. // map[locale][]MessageStruct
  9. // example:
  10. //{
  11. // "English": [
  12. // {
  13. // "PkgName": "x/i18n",
  14. // "StructName": "Messages_en_US"
  15. // }
  16. // ],
  17. //}
  18. RigisterMap map[string][]MessageStruct
  19. // map[locale][]MessageStruct
  20. // example:
  21. //{
  22. // "Messages_en_US": "English",
  23. // "Messages_zh_CN": "SimplifiedChinese"
  24. //}
  25. LocalesMap map[string]string
  26. // map[pkgName][]structs
  27. // example:
  28. //{
  29. // "x/i18n": []*ast.GenDecl,
  30. //}
  31. Variables map[string][]*ast.GenDecl
  32. // the current package path
  33. // example:
  34. // when visit file "x/i18n/i18n_test.go", currentPkgPath = "x/i18n"
  35. currentPkgPath string
  36. // the current import map
  37. // example:
  38. // when visit file "x/i18n/i18n_test.go", currentImportMap =
  39. //{
  40. // "fmt": "fmt",
  41. // "http": "net/http",
  42. // "httptest": "net/http/httptest",
  43. // "strings": "strings",
  44. // "testing": "testing"
  45. // "i18n": "github.com/qor5/x/i18n",
  46. // "testingutils": "github.com/theplant/testingutils",
  47. // "language": "golang.org/x/text/language",
  48. //}
  49. currentImportMap map[string]string
  50. projectParentPath string
  51. fset *token.FileSet
  52. }
  53. type MessageStruct struct {
  54. PkgName string
  55. StructName string
  56. }
  57. func (v *Visitor) Visit(node ast.Node) ast.Visitor {
  58. // find all global variables and insert it into v.Variables
  59. if f, ok := node.(*ast.File); ok {
  60. var temp = strings.Split(v.fset.File(f.Package).Name(), "/")
  61. pkgName := strings.TrimPrefix(strings.Join(temp[:len(temp)-1], "/"), strings.TrimSuffix(v.projectParentPath, "/")+"/")
  62. for _, decl := range f.Decls {
  63. if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.VAR {
  64. v.Variables[pkgName] = append(v.Variables[pkgName], genDecl)
  65. }
  66. }
  67. }
  68. // find all places calling func RegisterForModule
  69. // analyze the calls and fill it into v.LocalesMap and v.RigisterMap
  70. if callExpr, ok := node.(*ast.CallExpr); ok {
  71. if selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok && selectorExpr.Sel.Name == "RegisterForModule" {
  72. if len(callExpr.Args) == 3 {
  73. if selectorExpr2, ok := callExpr.Args[0].(*ast.SelectorExpr); ok {
  74. if ident, ok := selectorExpr2.X.(*ast.Ident); ok && ident.Name == "language" {
  75. if ident2, ok := callExpr.Args[2].(*ast.Ident); ok {
  76. var messageStruct MessageStruct
  77. messageStruct.PkgName = strings.TrimPrefix(v.currentPkgPath, strings.TrimSuffix(v.projectParentPath, "/")+"/")
  78. messageStruct.StructName = ident2.Name
  79. v.LocalesMap[messageStruct.StructName] = selectorExpr2.Sel.Name
  80. v.RigisterMap[selectorExpr2.Sel.Name] = append(v.RigisterMap[selectorExpr2.Sel.Name], messageStruct)
  81. }
  82. if selectorExpr3, ok := callExpr.Args[2].(*ast.SelectorExpr); ok {
  83. var messageStruct MessageStruct
  84. messageStruct.PkgName = v.currentImportMap[selectorExpr3.X.(*ast.Ident).Name]
  85. messageStruct.StructName = selectorExpr3.Sel.Name
  86. v.LocalesMap[messageStruct.StructName] = selectorExpr2.Sel.Name
  87. v.RigisterMap[selectorExpr2.Sel.Name] = append(v.RigisterMap[selectorExpr2.Sel.Name], messageStruct)
  88. }
  89. }
  90. }
  91. }
  92. }
  93. }
  94. return v
  95. }
  96. // declare a Visitor
  97. // walk all files and fill the RigisterMap, LocalesMap and Variables
  98. func newVisitorAndWalk(fset *token.FileSet, pkgs map[string]*ast.Package, projectPath string) (v *Visitor, err error) {
  99. v = &Visitor{
  100. RigisterMap: make(map[string][]MessageStruct),
  101. LocalesMap: make(map[string]string),
  102. Variables: make(map[string][]*ast.GenDecl),
  103. currentImportMap: make(map[string]string),
  104. fset: fset,
  105. }
  106. for pkgPath, pkg := range pkgs {
  107. for _, f := range pkg.Files {
  108. for _, decl := range f.Decls {
  109. if decl, ok := decl.(*ast.GenDecl); ok {
  110. if decl.Tok == token.IMPORT {
  111. for _, spec := range decl.Specs {
  112. if spec, ok := spec.(*ast.ImportSpec); ok {
  113. var importName string
  114. var importValue string
  115. if spec.Name == nil {
  116. var temp = strings.Split(strings.Trim(spec.Path.Value, "\""), "/")
  117. importName = temp[len(temp)-1]
  118. } else {
  119. importName = spec.Name.Name
  120. }
  121. importValue = strings.Trim(spec.Path.Value, "\"")
  122. v.currentImportMap[importName] = importValue
  123. }
  124. }
  125. }
  126. }
  127. }
  128. v.currentPkgPath = pkgPath
  129. var temp = strings.Split(projectPath, "/")
  130. v.projectParentPath = strings.Join(temp[:len(temp)-1], "/")
  131. ast.Walk(v, f)
  132. }
  133. }
  134. return
  135. }