Skip to content

Commit 5aa198b

Browse files
Merge pull request #112 from leonelquinteros/function-type-extractor
Refactor parser object
2 parents 22c354b + 60f0adf commit 5aa198b

File tree

4 files changed

+220
-376
lines changed

4 files changed

+220
-376
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ _testmain.go
2929
*.exe
3030
*.test
3131
*.prof
32+
*.DS_Store

cli/xgotext/parser/dir/golang.go

Lines changed: 19 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,16 @@
11
package dir
22

33
import (
4-
"fmt"
54
"go/ast"
65
"go/token"
7-
"go/types"
86
"log"
9-
"path/filepath"
107
"strconv"
118

129
"golang.org/x/tools/go/packages"
1310

1411
"github.com/leonelquinteros/gotext/cli/xgotext/parser"
1512
)
1613

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-
4214
// register go parser
4315
func init() {
4416
AddParser(goParser)
@@ -72,37 +44,32 @@ func goParser(dirPath, basePath string, data *parser.DomainMap) error {
7244
// handle each file
7345
for _, node := range pkgs[0].Syntax {
7446
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+
},
8357
},
8458
}
8559

86-
ast.Inspect(node, file.inspectFile)
60+
ast.Inspect(node, file.InspectFile)
8761
}
8862
return nil
8963
}
9064

9165
// GoFile handles the parsing of one go file
9266
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
10168
}
10269

10370
// 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)
10673
if err != nil {
10774
return nil, err
10875
}
@@ -112,158 +79,30 @@ func (g *GoFile) getPackage(name string) (*packages.Package, error) {
11279
return pkgs[0], nil
11380
}
11481

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 {
12683
switch x := n.(type) {
12784
// get names of imported packages
12885
case *ast.ImportSpec:
12986
packageName, _ := strconv.Unquote(x.Path.Value)
13087

131-
pkg, err := g.getPackage(packageName)
88+
pkg, err := g.GetPackage(packageName)
13289
if err != nil {
13390
log.Printf("failed to load package %s: %s", packageName, err)
13491
} else {
13592
if x.Name == nil {
136-
g.importedPackages[pkg.Name] = pkg
93+
g.ImportedPackages[pkg.Name] = pkg
13794
} else {
138-
g.importedPackages[x.Name.Name] = pkg
95+
g.ImportedPackages[x.Name.Name] = pkg
13996
}
14097
}
14198

14299
// check each function call
143100
case *ast.CallExpr:
144-
g.inspectCallExpr(x)
101+
g.InspectCallExpr(x)
145102

146103
default:
147104
print()
148105
}
149106

150107
return true
151108
}
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

Comments
 (0)