/
parse.go
94 lines (86 loc) · 2.48 KB
/
parse.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package gen
import "go/ast"
// Extract type parameter T from `pkgName.symbol[T]`.
func extractSymbolExpr(pkgName, symbol string, expr *ast.IndexExpr) (ast.Expr, bool) {
if sel, ok := expr.X.(*ast.SelectorExpr); ok {
if pkg, ok := sel.X.(*ast.Ident); ok {
if pkg.String() != pkgName {
return nil, false
}
}
if sel.Sel.String() != symbol {
return nil, false
}
}
return expr.Index, true
}
// Extract interface used as enum identifier T from struct (`pkgName.MemberOf[T]`).
func findEnumIdentFromFields(pkgName string, s *ast.StructType) (ast.Expr, bool) {
for _, f := range s.Fields.List {
if len(f.Names) == 0 {
if expr, ok := f.Type.(*ast.IndexExpr); ok {
enumIdent, ok := extractSymbolExpr(pkgName, enumSymbol, expr)
if ok {
return enumIdent, true
}
}
}
}
return nil, false
}
// Extract return type of visitor method from enum identifier interface (`pkgName.VisitorReturns[T]`)
func findVisitorReturnsFromFields(pkgName string, i *ast.InterfaceType) (ast.Expr, bool) {
for _, f := range i.Methods.List {
if len(f.Names) == 0 {
if expr, ok := f.Type.(*ast.IndexExpr); ok {
visitorReturnIdent, ok := extractSymbolExpr(pkgName, visitorReturnsSymbol, expr)
if ok {
return visitorReturnIdent, true
}
}
}
}
return nil, false
}
type enumMemberDefinition struct {
ident *ast.Ident
enumIdent ast.Expr
}
func extractEnumMemberDefinition(enumPackage string, spec *ast.TypeSpec) (*enumMemberDefinition, bool) {
switch s := spec.Type.(type) {
case *ast.StructType:
// type A struct { enumPackage.MemberOf[Ident] }
if ident, ok := findEnumIdentFromFields(enumPackage, s); ok {
return &enumMemberDefinition{
ident: spec.Name,
enumIdent: ident,
}, true
}
case *ast.IndexExpr:
// type A enumPackage.MemberOf[Ident]
if ident, ok := extractSymbolExpr(enumPackage, enumSymbol, s); ok {
return &enumMemberDefinition{
ident: spec.Name,
enumIdent: ident,
}, true
}
}
return nil, false
}
type enumIdentDefinition struct {
ident *ast.Ident
visitorReturnIdent ast.Expr
}
func extractEnumIdentDefinition(enumPackage string, spec *ast.TypeSpec) (*enumIdentDefinition, bool) {
switch s := spec.Type.(type) {
case *ast.InterfaceType:
// type A interface { enumPackage.VisitorReturns[Ident] }
if ident, ok := findVisitorReturnsFromFields(enumPackage, s); ok {
return &enumIdentDefinition{
ident: spec.Name,
visitorReturnIdent: ident,
}, true
}
}
return nil, false
}