1
1
package dir
2
2
3
3
import (
4
- "fmt"
5
4
"go/ast"
6
5
"go/token"
7
- "go/types"
8
6
"log"
9
- "path/filepath"
10
7
"strconv"
11
8
12
9
"golang.org/x/tools/go/packages"
13
10
14
11
"github.com/leonelquinteros/gotext/cli/xgotext/parser"
15
12
)
16
13
17
- // GetterDef describes a getter
18
- type GetterDef struct {
19
- Id int
20
- Plural int
21
- Context int
22
- Domain int
23
- }
24
-
25
- // maxArgIndex returns the largest argument index
26
- func (d * GetterDef ) maxArgIndex () int {
27
- return max (d .Id , d .Plural , d .Context , d .Domain )
28
- }
29
-
30
- // list of supported getter
31
- var gotextGetter = map [string ]GetterDef {
32
- "Get" : {0 , - 1 , - 1 , - 1 },
33
- "GetN" : {0 , 1 , - 1 , - 1 },
34
- "GetD" : {1 , - 1 , - 1 , 0 },
35
- "GetND" : {1 , 2 , - 1 , 0 },
36
- "GetC" : {0 , - 1 , 1 , - 1 },
37
- "GetNC" : {0 , 1 , 3 , - 1 },
38
- "GetDC" : {1 , - 1 , 2 , 0 },
39
- "GetNDC" : {1 , 2 , 4 , 0 },
40
- }
41
-
42
14
// register go parser
43
15
func init () {
44
16
AddParser (goParser )
@@ -72,37 +44,32 @@ func goParser(dirPath, basePath string, data *parser.DomainMap) error {
72
44
// handle each file
73
45
for _ , node := range pkgs [0 ].Syntax {
74
46
file := GoFile {
75
- pkgConf : & conf ,
76
- filePath : fileSet .Position (node .Package ).Filename ,
77
- basePath : basePath ,
78
- data : data ,
79
- fileSet : fileSet ,
80
-
81
- importedPackages : map [string ]* packages.Package {
82
- pkgs [0 ].Name : pkgs [0 ],
47
+ parser.GoFile {
48
+ PkgConf : & conf ,
49
+ FilePath : fileSet .Position (node .Package ).Filename ,
50
+ BasePath : basePath ,
51
+ Data : data ,
52
+ FileSet : fileSet ,
53
+
54
+ ImportedPackages : map [string ]* packages.Package {
55
+ pkgs [0 ].Name : pkgs [0 ],
56
+ },
83
57
},
84
58
}
85
59
86
- ast .Inspect (node , file .inspectFile )
60
+ ast .Inspect (node , file .InspectFile )
87
61
}
88
62
return nil
89
63
}
90
64
91
65
// GoFile handles the parsing of one go file
92
66
type GoFile struct {
93
- filePath string
94
- basePath string
95
- data * parser.DomainMap
96
-
97
- fileSet * token.FileSet
98
- pkgConf * packages.Config
99
-
100
- importedPackages map [string ]* packages.Package
67
+ parser.GoFile
101
68
}
102
69
103
70
// getPackage loads module by name
104
- func (g * GoFile ) getPackage (name string ) (* packages.Package , error ) {
105
- pkgs , err := packages .Load (g .pkgConf , name )
71
+ func (g * GoFile ) GetPackage (name string ) (* packages.Package , error ) {
72
+ pkgs , err := packages .Load (g .PkgConf , name )
106
73
if err != nil {
107
74
return nil , err
108
75
}
@@ -112,158 +79,30 @@ func (g *GoFile) getPackage(name string) (*packages.Package, error) {
112
79
return pkgs [0 ], nil
113
80
}
114
81
115
- // getType from ident object
116
- func (g * GoFile ) getType (ident * ast.Ident ) types.Object {
117
- for _ , pkg := range g .importedPackages {
118
- if obj , ok := pkg .TypesInfo .Uses [ident ]; ok {
119
- return obj
120
- }
121
- }
122
- return nil
123
- }
124
-
125
- func (g * GoFile ) inspectFile (n ast.Node ) bool {
82
+ func (g * GoFile ) InspectFile (n ast.Node ) bool {
126
83
switch x := n .(type ) {
127
84
// get names of imported packages
128
85
case * ast.ImportSpec :
129
86
packageName , _ := strconv .Unquote (x .Path .Value )
130
87
131
- pkg , err := g .getPackage (packageName )
88
+ pkg , err := g .GetPackage (packageName )
132
89
if err != nil {
133
90
log .Printf ("failed to load package %s: %s" , packageName , err )
134
91
} else {
135
92
if x .Name == nil {
136
- g .importedPackages [pkg .Name ] = pkg
93
+ g .ImportedPackages [pkg .Name ] = pkg
137
94
} else {
138
- g .importedPackages [x .Name .Name ] = pkg
95
+ g .ImportedPackages [x .Name .Name ] = pkg
139
96
}
140
97
}
141
98
142
99
// check each function call
143
100
case * ast.CallExpr :
144
- g .inspectCallExpr (x )
101
+ g .InspectCallExpr (x )
145
102
146
103
default :
147
104
print ()
148
105
}
149
106
150
107
return true
151
108
}
152
-
153
- // checkType for gotext object
154
- func (g * GoFile ) checkType (rawType types.Type ) bool {
155
- switch t := rawType .(type ) {
156
- case * types.Pointer :
157
- return g .checkType (t .Elem ())
158
-
159
- case * types.Named :
160
- if t .Obj ().Pkg () == nil || t .Obj ().Pkg ().Path () != "github.com/leonelquinteros/gotext" {
161
- return false
162
- }
163
-
164
- case * types.Alias :
165
- return g .checkType (t .Rhs ())
166
-
167
- default :
168
- return false
169
- }
170
- return true
171
- }
172
-
173
- func (g * GoFile ) inspectCallExpr (n * ast.CallExpr ) {
174
- // must be a selector expression otherwise it is a local function call
175
- expr , ok := n .Fun .(* ast.SelectorExpr )
176
- if ! ok {
177
- return
178
- }
179
-
180
- switch e := expr .X .(type ) {
181
- // direct call
182
- case * ast.Ident :
183
- // object is a package if the Obj is not set
184
- if e .Obj == nil {
185
- pkg , ok := g .importedPackages [e .Name ]
186
- if ! ok || pkg .PkgPath != "github.com/leonelquinteros/gotext" {
187
- return
188
- }
189
-
190
- } else {
191
- // validate type of object
192
- t := g .getType (e )
193
- if t == nil || ! g .checkType (t .Type ()) {
194
- return
195
- }
196
- }
197
-
198
- // call to attribute
199
- case * ast.SelectorExpr :
200
- // validate type of object
201
- t := g .getType (e .Sel )
202
- if t == nil || ! g .checkType (t .Type ()) {
203
- return
204
- }
205
-
206
- default :
207
- return
208
- }
209
-
210
- // convert args
211
- args := make ([]* ast.BasicLit , len (n .Args ))
212
- for idx , arg := range n .Args {
213
- args [idx ], _ = arg .(* ast.BasicLit )
214
- }
215
-
216
- // get position
217
- path , _ := filepath .Rel (g .basePath , g .filePath )
218
- position := fmt .Sprintf ("%s:%d" , path , g .fileSet .Position (n .Lparen ).Line )
219
-
220
- // handle getters
221
- if def , ok := gotextGetter [expr .Sel .String ()]; ok {
222
- g .parseGetter (def , args , position )
223
- return
224
- }
225
- }
226
-
227
- func (g * GoFile ) parseGetter (def GetterDef , args []* ast.BasicLit , pos string ) {
228
- // check if enough arguments are given
229
- if len (args ) < def .maxArgIndex () {
230
- return
231
- }
232
-
233
- // get domain
234
- var domain string
235
- if def .Domain != - 1 {
236
- domain , _ = strconv .Unquote (args [def .Domain ].Value )
237
- }
238
-
239
- // only handle function calls with strings as ID
240
- if args [def .Id ] == nil || args [def .Id ].Kind != token .STRING {
241
- log .Printf ("ERR: Unsupported call at %s (ID not a string)" , pos )
242
- return
243
- }
244
-
245
- msgID , _ := strconv .Unquote (args [def .Id ].Value )
246
- trans := parser.Translation {
247
- MsgId : msgID ,
248
- SourceLocations : []string {pos },
249
- }
250
- if def .Plural > 0 {
251
- // plural ID must be a string
252
- if args [def .Plural ] == nil || args [def .Plural ].Kind != token .STRING {
253
- log .Printf ("ERR: Unsupported call at %s (Plural not a string)" , pos )
254
- return
255
- }
256
- msgIDPlural , _ := strconv .Unquote (args [def .Plural ].Value )
257
- trans .MsgIdPlural = msgIDPlural
258
- }
259
- if def .Context > 0 {
260
- // Context must be a string
261
- if args [def .Context ] == nil || args [def .Context ].Kind != token .STRING {
262
- log .Printf ("ERR: Unsupported call at %s (Context not a string)" , pos )
263
- return
264
- }
265
- trans .Context = args [def .Context ].Value
266
- }
267
-
268
- g .data .AddTranslation (domain , & trans )
269
- }
0 commit comments