跨包使用 go/parser
Usage of go/parser across packages
我已经使用 go/parser 解析了一个 golang 文件并检查了它的 AST。我有一个特定的问题,我想使用 go/parser 但我遇到了障碍。
考虑以下文件存在于 GOPATH/src
$GOPATH/src/
example.go
example_package/
example_package.go
以下是上述文件的内容
example.go
package main
import (
"example_package"
)
type MyObject struct {
base *example_package.BaseObject
}
func DoMyThing(arg *example_package.FirstArg) {
arg.Write(10)
}
func DoMyAnotherThing() {
}
func main() {
example_package.GetItStarted(&MyObject{})
}
example_package.go
package example_package
func GetItStarted(obj interface{}) {
}
type FirstArg interface {
Read() int
Write(x int)
}
type BaseObject struct {
}
func (p *BaseObject) DoSomething(arg *FirstArg, a int) {
arg.Write(arg.Read() + a)
}
我打算写一个叫gen_structure
的围棋程序,像这样使用
$ gen_structure example.go
输出将是
> MyObject
- DoMyThing(arg)
- base
- DoSomething(arg, a)
gen_structure做了什么?
它解析example.go和
- 从 main() 函数内部的行
example_package.GetItStarted(&MyObject{})
中提取 "MyObject"。
- 在
MyObject
上寻找至少有一个参数且第一个参数类型为 *package_example.FirstArg
的方法。它找到 DoMyThing
(并忽略 DoMyAnotherThing
)。
- 识别成员
base
并查看内部(通过打开 example_package
)。
- 应用与上述相同的查找方法并找到
DoSomething
- 使用收集到的信息,打印所需的输出。
我知道我可以使用 go/parser
中的功能解析同一目录中的单个文件或一堆文件。但是,我无法弄清楚如何跨包解析符号(在这种情况下,example_package
)。
我该怎么做?
调用ast.NewPackage to resolve a package names. You will need to supply an importer that returns an *ast.Object for the given import path. If all you want to do is resolve the name to a path, the importer can simply return an *ast.Object with the Kind set to ast.Pkg and the Name set to name of the package. Most of the heavy lifting in the importer can be done with the go/build包。如果要解析目标包的 AST,您需要解析包和 return 包的 ast.Object。为防止多次加载同一个包,请使用导入器的 map 参数作为先前加载包的缓存。
这是一些未经测试的代码,用于从 *ast.SelectorExpr
se
中查找已解析的包路径:
if x, _ := se.X.(*ast.Ident); x != nil {
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
// path is resolved path for selector expression se.
}
}
}
}
go/types 包也可用于获取此信息及更多信息。我建议使用 go/types 而不是直接使用 go/ast。
我已经使用 go/parser 解析了一个 golang 文件并检查了它的 AST。我有一个特定的问题,我想使用 go/parser 但我遇到了障碍。
考虑以下文件存在于 GOPATH/src
$GOPATH/src/
example.go
example_package/
example_package.go
以下是上述文件的内容
example.go
package main
import (
"example_package"
)
type MyObject struct {
base *example_package.BaseObject
}
func DoMyThing(arg *example_package.FirstArg) {
arg.Write(10)
}
func DoMyAnotherThing() {
}
func main() {
example_package.GetItStarted(&MyObject{})
}
example_package.go
package example_package
func GetItStarted(obj interface{}) {
}
type FirstArg interface {
Read() int
Write(x int)
}
type BaseObject struct {
}
func (p *BaseObject) DoSomething(arg *FirstArg, a int) {
arg.Write(arg.Read() + a)
}
我打算写一个叫gen_structure
的围棋程序,像这样使用
$ gen_structure example.go
输出将是
> MyObject
- DoMyThing(arg)
- base
- DoSomething(arg, a)
gen_structure做了什么?
它解析example.go和
- 从 main() 函数内部的行
example_package.GetItStarted(&MyObject{})
中提取 "MyObject"。 - 在
MyObject
上寻找至少有一个参数且第一个参数类型为*package_example.FirstArg
的方法。它找到DoMyThing
(并忽略DoMyAnotherThing
)。 - 识别成员
base
并查看内部(通过打开example_package
)。 - 应用与上述相同的查找方法并找到
DoSomething
- 使用收集到的信息,打印所需的输出。
我知道我可以使用 go/parser
中的功能解析同一目录中的单个文件或一堆文件。但是,我无法弄清楚如何跨包解析符号(在这种情况下,example_package
)。
我该怎么做?
调用ast.NewPackage to resolve a package names. You will need to supply an importer that returns an *ast.Object for the given import path. If all you want to do is resolve the name to a path, the importer can simply return an *ast.Object with the Kind set to ast.Pkg and the Name set to name of the package. Most of the heavy lifting in the importer can be done with the go/build包。如果要解析目标包的 AST,您需要解析包和 return 包的 ast.Object。为防止多次加载同一个包,请使用导入器的 map 参数作为先前加载包的缓存。
这是一些未经测试的代码,用于从 *ast.SelectorExpr
se
中查找已解析的包路径:
if x, _ := se.X.(*ast.Ident); x != nil {
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
// path is resolved path for selector expression se.
}
}
}
}
go/types 包也可用于获取此信息及更多信息。我建议使用 go/types 而不是直接使用 go/ast。