Skip to content
This repository has been archived by the owner on Mar 8, 2020. It is now read-only.

Commit

Permalink
Merge pull request #33 from juanjux/feature/iterators
Browse files Browse the repository at this point in the history
Added libuast's iterators.
  • Loading branch information
juanjux authored Jan 16, 2018
2 parents e77dce3 + 056a565 commit 46ab873
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Package configuration
PROJECT = client-go
LIBUAST_VERSION=1.4.1
LIBUAST_VERSION=1.6.1

# Including ci Makefile
MAKEFILE = Makefile.main
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ alias {
. . internalRole: names
. }
}
iter, err := tools.NewIterator(res.UAST)
if err != nil {
panic(err)
}
defer iter.Dispose()
for node := range iter.Iterate() {
fmt.Println(node)
}
```

Please read the [Babelfish clients](https://doc.bblf.sh/user/language-clients.html) guide section to learn more about babelfish clients and their query language.
Expand Down
111 changes: 109 additions & 2 deletions tools/filter.go → tools/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,32 @@ import (
"gopkg.in/bblfsh/sdk.v1/uast"
)

// #cgo CFLAGS: -I/usr/local/include -I/usr/local/include/libxml2 -I/usr/include -I/usr/include/libxml2
// #cgo CXXFLAGS: -I/usr/local/include -I/usr/local/include/libxml2 -I/usr/include -I/usr/include/libxml2
// #cgo LDFLAGS: -lxml2
// #include "bindings.h"
import "C"

var findMutex sync.Mutex
var itMutex sync.Mutex
var pool cstringPool

// Traversal strategy for UAST trees
type TreeOrder int
const (
// PreOrder traversal
PreOrder TreeOrder = iota
// PostOrder traversal
PostOrder
// LevelOrder (aka breadth-first) traversal
LevelOrder
)

// Iterator allows for traversal over a UAST tree.
type Iterator struct {
iterPtr C.uintptr_t
finished bool
}

func init() {
C.CreateUast()
}
Expand All @@ -32,7 +50,7 @@ func ptrToNode(ptr C.uintptr_t) *uast.Node {

// Filter takes a `*uast.Node` and a xpath query and filters the tree,
// returning the list of nodes that satisfy the given query.
// Filter is thread-safe but not current by an internal global lock.
// Filter is thread-safe but not concurrent by an internal global lock.
func Filter(node *uast.Node, xpath string) ([]*uast.Node, error) {
if len(xpath) == 0 {
return nil, nil
Expand Down Expand Up @@ -208,3 +226,92 @@ func goGetEndCol(ptr C.uintptr_t) C.uint32_t {
}
return 0
}

// NewIterator constructs a new Iterator starting from the given `Node` and
// iterating with the traversal strategy given by the `order` parameter. Once
// the iteration have finished or you don't need the iterator anymore you must
// dispose it with the Dispose() method (or call it with `defer`).
func NewIterator(node *uast.Node, order TreeOrder) (*Iterator, error) {
itMutex.Lock()
defer itMutex.Unlock()

// stop GC
gcpercent := debug.SetGCPercent(-1)
defer debug.SetGCPercent(gcpercent)

ptr := nodeToPtr(node)
it := C.IteratorNew(ptr, C.int(order))
if it == 0 {
error := C.Error()
return nil, fmt.Errorf("UastIteratorNew() failed: %s", C.GoString(error))
C.free(unsafe.Pointer(error))
}

return &Iterator {
iterPtr: it,
finished: false,
}, nil
}

// Next retrieves the next `Node` in the tree's traversal or `nil` if there are no more
// nodes. Calling `Next()` on a finished iterator after the first `nil` will
// return an error.This is thread-safe but not concurrent by an internal global lock.
func (i *Iterator) Next() (*uast.Node, error) {
itMutex.Lock()
defer itMutex.Unlock()

if i.finished {
return nil, fmt.Errorf("Next() called on finished iterator")
}

// stop GC
gcpercent := debug.SetGCPercent(-1)
defer debug.SetGCPercent(gcpercent)

pnode := C.IteratorNext(i.iterPtr);
if pnode == 0 {
// End of the iteration
i.finished = true
return nil, nil
}
return ptrToNode(pnode), nil
}

// Iterate function is similar to Next() but returns the `Node`s in a channel. It's mean
// to be used with the `for node := range myIter.Iterate() {}` loop.
func (i *Iterator) Iterate() <- chan *uast.Node {
c := make(chan *uast.Node)
if i.finished {
close(c)
return c
}

go func() {
for {
n, err := i.Next()
if n == nil || err != nil {
close(c)
break
}

c <- n
}
}()

return c
}

// Dispose must be called once you've finished using the iterator or preventively
// with `defer` to free the iterator resources. Failing to do so would produce
// a memory leak.
func (i *Iterator) Dispose() {
itMutex.Lock()
defer itMutex.Unlock()

if i.iterPtr != 0 {
C.IteratorFree(i.iterPtr)
i.iterPtr = 0
}
i.finished = true
}

18 changes: 15 additions & 3 deletions tools/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,23 @@ static const char *Token(const void *node) {
return goGetToken((uintptr_t)node);
}

static int ChildrenSize(const void *node) {
static size_t ChildrenSize(const void *node) {
return goGetChildrenSize((uintptr_t)node);
}

static void *ChildAt(const void *data, int index) {
return (void*)goGetChild((uintptr_t)data, index);
}

static int RolesSize(const void *node) {
static size_t RolesSize(const void *node) {
return goGetRolesSize((uintptr_t)node);
}

static uint16_t RoleAt(const void *node, int index) {
return goGetRole((uintptr_t)node, index);
}

static int PropertiesSize(const void *node) {
static size_t PropertiesSize(const void *node) {
return goGetPropertiesSize((uintptr_t)node);
}

Expand Down Expand Up @@ -147,6 +147,18 @@ static bool Filter(uintptr_t node_ptr, const char *query) {
return nodes != NULL;
}

static uintptr_t IteratorNew(uintptr_t node_ptr, int order) {
return (uintptr_t)UastIteratorNew(ctx, (void *)node_ptr, order);
}

static uintptr_t IteratorNext(uintptr_t iter) {
return (uintptr_t)UastIteratorNext((void*)iter);
}

static uintptr_t IteratorFree(uintptr_t iter) {
UastIteratorFree((void*)iter);
}

static char *Error() {
return LastError();
}
Expand Down
1 change: 0 additions & 1 deletion tools/filter_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package tools

import (
"testing"

Expand Down
Loading

0 comments on commit 46ab873

Please sign in to comment.