uast.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package hercules
  2. import (
  3. "context"
  4. "gopkg.in/src-d/go-git.v4"
  5. "gopkg.in/src-d/go-git.v4/plumbing"
  6. "gopkg.in/src-d/go-git.v4/plumbing/object"
  7. "gopkg.in/src-d/go-git.v4/utils/merkletrie"
  8. "gopkg.in/bblfsh/client-go.v0"
  9. "github.com/bblfsh/sdk/uast"
  10. "github.com/bblfsh/sdk/protocol"
  11. "errors"
  12. "strings"
  13. )
  14. type UASTExtractor struct {
  15. Endpoint string
  16. Context func() context.Context
  17. client *bblfsh.BblfshClient
  18. }
  19. func (exr *UASTExtractor) Name() string {
  20. return "UAST"
  21. }
  22. func (exr *UASTExtractor) Provides() []string {
  23. arr := [...]string{"uasts"}
  24. return arr[:]
  25. }
  26. func (exr *UASTExtractor) Requires() []string {
  27. arr := [...]string{"changes", "blob_cache"}
  28. return arr[:]
  29. }
  30. func (exr *UASTExtractor) Initialize(repository *git.Repository) {
  31. client, err := bblfsh.NewBblfshClient(exr.Endpoint)
  32. if err != nil {
  33. panic(err)
  34. }
  35. exr.client = client
  36. if exr.Context == nil {
  37. exr.Context = func() context.Context { return context.Background() }
  38. }
  39. }
  40. func (exr *UASTExtractor) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  41. cache := deps["blob_cache"].(map[plumbing.Hash]*object.Blob)
  42. treeDiffs := deps["changes"].(object.Changes)
  43. uasts := map[string]*uast.Node{}
  44. for _, change := range treeDiffs {
  45. action, err := change.Action()
  46. if err != nil {
  47. return nil, err
  48. }
  49. switch action {
  50. case merkletrie.Insert:
  51. uasts[change.To.Name], err = exr.extractUAST(&object.File{
  52. Name: change.To.Name, Blob: *cache[change.To.TreeEntry.Hash]})
  53. case merkletrie.Delete:
  54. continue
  55. case merkletrie.Modify:
  56. uasts[change.To.Name], err = exr.extractUAST(&object.File{
  57. Name: change.To.Name, Blob: *cache[change.To.TreeEntry.Hash]})
  58. }
  59. if err != nil {
  60. return nil, err
  61. }
  62. }
  63. return map[string]interface{}{"uasts": uasts}, nil
  64. }
  65. func (exr *UASTExtractor) Finalize() interface{} {
  66. return nil
  67. }
  68. func (exr *UASTExtractor) extractUAST(file *object.File) (*uast.Node, error) {
  69. request := exr.client.NewParseRequest()
  70. contents, err := file.Contents()
  71. if err != nil {
  72. return nil, err
  73. }
  74. request.Content(contents)
  75. request.Filename(file.Name)
  76. response, err := request.DoWithContext(exr.Context())
  77. if response.Status != protocol.Ok {
  78. return nil, errors.New(strings.Join(response.Errors, "\n"))
  79. }
  80. if err != nil {
  81. return nil, err
  82. }
  83. return response.UAST, nil
  84. }