【问题标题】:Usage of go/parser across packages跨包使用 go/parser
【发布时间】:2015-12-08 12:55:16
【问题描述】:

我使用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的go程序,像这样使用

$ gen_structure example.go

输出将是

> MyObject
- DoMyThing(arg)
- base
    - DoSomething(arg, a)

gen_structure 做了什么?

它解析 example.go 和

  1. 从 main() 函数内部的 example_package.GetItStarted(&MyObject{}) 行中提取“MyObject”。
  2. MyObject 上查找至少有一个参数且第一个参数为*package_example.FirstArg 类型的方法。它会找到 DoMyThing(并忽略 DoMyAnotherThing)。
  3. 识别成员base 并查看内部(通过打开example_package)。
  4. 应用与上述相同的过程查找方法并找到DoSomething
  5. 使用收集的信息,打印所需的输出。

我知道我可以使用go/parser 中的功能解析同一目录中的单个文件或一堆文件。但是,我无法弄清楚如何跨包解析符号(在这种情况下,example_package)。

我该怎么做?

【问题讨论】:

    标签: parsing go code-generation abstract-syntax-tree


    【解决方案1】:

    调用ast.NewPackage 来解析包名。您需要提供一个importer,它为给定的导入路径返回一个*ast.Object。如果您只想将名称解析为路径,则导入器可以简单地返回一个*ast.Object,并将Kind 设置为ast.Pkg,并将Name 设置为包的名称。导入器中的大部分繁重工作都可以使用go/build 包来完成。如果要解析目标包的 AST,则需要解析包并返回包的 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。

    【讨论】:

    • 谢谢。我将尝试基于上述实现。
    • 他们是不同的进口商。 ast.Importer 很简单:调用 go/build 导入函数以获取 *build.Package 并返回 &ast.Object{Kind: ast.Pkg, Name: p.Name},其中 p*build.Package。我会使用 go/types 而不是 go/ast。问题中描述的问题是 go/ast 包可以做什么的限制。如果直接使用 go/types 而不是 go/ast,代码可能会更短。
    猜你喜欢
    • 2014-05-09
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 2013-12-06
    • 2016-01-03
    • 2018-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多