在包

时间:2015-09-11 21:24:20

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

我使用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. 摘录" MyObject"来自main()函数内的example_package.GetItStarted(&MyObject{})行。
  2. MyObject上查找至少有一个参数且第一个参数类型为*package_example.FirstArg的方法。它找到了DoMyThing(并忽略了DoMyAnotherThing)。
  3. 标识成员base并在内部偷看(通过打开example_package)。
  4. 使用相同的流程查找上述方法并找到DoSomething
  5. 使用收集的信息,打印所需的输出。
  6. 我知道我可以使用go/parser中的功能解析同一目录中的单个文件或一堆文件。但是,我无法弄清楚如何跨包解析符号(在这种情况下,example_package)。

    我该怎么做?

1 个答案:

答案 0 :(得分:3)

调用ast.NewPackage解析包名称。您需要提供importer,以便为给定的导入路径返回*ast.Object。如果您只想将名称解析为路径,则导入者只需返回*ast.ObjectKind设置为ast.PkgName设置为名称包裹。导入器中的大部分繁重工作都可以使用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。