2015-09-11 9 views
0

Я использовал go/parser для анализа файла golang и изучения его AST. У меня есть определенная проблема, для которой я хочу использовать go/parser, но я попал в блокпост.Использование 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 и

  1. экстрактов "MyObject" из линии example_package.GetItStarted(&MyObject{}) из внутри основной функции().
  2. Ищет методы на MyObject, которые имеют по крайней мере один аргумент, первый из которых имеет тип *package_example.FirstArg. Он находит DoMyThing (и игнорируется DoMyAnotherThing).
  3. Идентифицирует элемент base и заглядывает внутрь (открыв example_package).
  4. Применяет тот же процесс для поиска методов, как указано выше, и находит DoSomething
  5. Используя собранную информацию, он печатает требуемый результат.

Я понимаю, что могу разобрать один файл или кучу файлов в том же каталоге, используя функциональность в пределах go/parser. Однако я не могу понять, как разрешать символы через пакеты (в этом случае example_package).

Как это сделать?

ответ

1

Позвоните, чтобы решить эту проблему: ast.NewPackage. Вам нужно будет указать importer, который возвращает *ast.Object для данного пути импорта. Если все, что вы хотите сделать, это разрешить имя пути, импортер может просто вернуть *ast.Object с Kind, установленным в ast.Pkg, и Name, установленным для обозначения пакета. Большая часть тяжелого подъема в импортере может быть выполнена с помощью пакета go/build. Если вы хотите разрешить АСТ для целевого пакета, вам нужно будет разобрать пакет и вернуть объект ast.Object для пакета. Чтобы предотвратить загрузку одного и того же пакета несколько раз, используйте аргумент карты импортеру в качестве кеша ранее загруженных пакетов.

Вот некоторые непроверенные код для нахождения вычисленный путь пакета от *ast.SelectorExprse:

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.

+0

спасибо. Я попытаюсь выполнить реализацию на основе вышеизложенного. –

+0

Могу ли я использовать это для импортера - http://godoc.org/go/importer По умолчанию()? –

+0

Они разные импортеры. 'Ast.Importer' прост: вызовите функцию импорта go/build, чтобы получить' * build.Package' и вернуть '& ast.Object {вид: ast.Pkg, Name: p.Name}' где 'p' '* build.Package'. Я бы использовал go/types вместо go/ast. Проблема, описанная в вопросе, находится на пределе того, что можно сделать с пакетом go/ast. Вероятно, код будет короче, если вы используете go/types вместо go/ast напрямую. –

Смежные вопросы