Skip to content

Commit

Permalink
expose positional index to clients; addresses bblfsh#102
Browse files Browse the repository at this point in the history
Signed-off-by: Denys Smirnov <[email protected]>
  • Loading branch information
Denys Smirnov committed Jun 6, 2019
1 parent d546e41 commit 527e6bd
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 31 deletions.
64 changes: 35 additions & 29 deletions gen_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"go/token"
"io/ioutil"
"os"
"path/filepath"
"strings"
)

Expand All @@ -25,47 +26,52 @@ func main() {
}

func run() error {
src, err := ioutil.ReadFile("./src/api.go")
if err != nil {
return err
}
fs := token.NewFileSet()
tree, err := parser.ParseFile(fs, "api.go", src, parser.ParseComments)
if err != nil {
return err
}
buf := new(bytes.Buffer)
buf.WriteString(`
#ifdef __cplusplus
extern "C" {
#endif
`)
for _, dec := range tree.Decls {
fnc, ok := dec.(*ast.FuncDecl)
if !ok {
continue
defer buf.WriteString(`
#ifdef __cplusplus
}
#endif
`)
for _, fname := range []string{
"api.go",
"src_index.go",
} {
src, err := ioutil.ReadFile(filepath.Join("./src", fname))
if err != nil {
return err
}
doc := fnc.Doc.Text()
pref := "export " + fnc.Name.Name
if !strings.Contains(doc, pref) {
continue
fs := token.NewFileSet()
tree, err := parser.ParseFile(fs, fname, src, parser.ParseComments)
if err != nil {
return err
}
buf.WriteString("\n")
for _, c := range fnc.Doc.List {
if strings.HasPrefix(c.Text, "//"+pref) {
for _, dec := range tree.Decls {
fnc, ok := dec.(*ast.FuncDecl)
if !ok {
continue
}
buf.WriteString(c.Text + "\n")
}
if err = writeCSignature(buf, fnc); err != nil {
return err
doc := fnc.Doc.Text()
pref := "export " + fnc.Name.Name
if !strings.Contains(doc, pref) {
continue
}
buf.WriteString("\n")
for _, c := range fnc.Doc.List {
if strings.HasPrefix(c.Text, "//"+pref) {
continue
}
buf.WriteString(strings.TrimRight(c.Text, "\n") + "\n")
}
if err := writeCSignature(buf, fnc); err != nil {
return err
}
}
}
buf.WriteString(`
#ifdef __cplusplus
}
#endif
`)
return writeHeader(buf.Bytes())
}

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ require (
gopkg.in/yaml.v2 v2.2.2 // indirect
)

// FIXME: remove once https://github.com/bblfsh/sdk/pull/403 is merged
replace github.com/bblfsh/sdk/v3 => github.com/dennwc/sdk/v3 v3.0.0-20190603173141-0c143b25c491
// FIXME: remove once https://github.com/bblfsh/sdk/pull/403 is released
replace github.com/bblfsh/sdk/v3 => github.com/bblfsh/sdk/v3 v3.1.1-0.20190606133740-c75dbe779e6b
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ github.com/antchfx/xpath v0.0.0-20190319080838-ce1d48779e67 h1:uj4UuiIs53RhHSySI
github.com/antchfx/xpath v0.0.0-20190319080838-ce1d48779e67/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
github.com/bblfsh/sdk/v3 v3.1.0 h1:V+4DqGlqbxiAEAU0XlpTjjbq/g0ljfiFoh9jsIkfbtU=
github.com/bblfsh/sdk/v3 v3.1.0/go.mod h1:juMiu8rP3lYJN1e4neEkSyzNieqiFceZzN4AOo0Rm1Q=
github.com/bblfsh/sdk/v3 v3.1.1-0.20190606133740-c75dbe779e6b h1:61YtAI8BRF+tWMFlgmuWujS1zzGf5HY76QkMsDBJsMw=
github.com/bblfsh/sdk/v3 v3.1.1-0.20190606133740-c75dbe779e6b/go.mod h1:juMiu8rP3lYJN1e4neEkSyzNieqiFceZzN4AOo0Rm1Q=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/containerd/continuity v0.0.0-20180712174259-0377f7d76720/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
Expand Down
180 changes: 180 additions & 0 deletions src/src_index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package main

/*
#include "uast.h"
#include <stdlib.h>
*/
import "C"

import (
"sync"
"unsafe"

"github.com/bblfsh/sdk/v3/uast/transformer/positioner"
)

var (
srcMu sync.RWMutex
srcLast Handle
srcIndexes = make(map[Handle]*positioner.Index)
)

// newSourceIndex allocates and populates the UastSourceIndex structure for C code to use.
func newSourceIndex(h Handle) *C.UastSourceIndex {
sz := unsafe.Sizeof(C.UastSourceIndex{})
v := (*C.UastSourceIndex)(C.malloc(C.size_t(sz)))
v.handle = C.uintptr_t(h)
return v
}

//export UastSourceIndexNew
// UastSourceIndexNew creates a positional index for a source file. An index can be used
// to convert byte-based node offsets to Unicode character offsets.
func UastSourceIndexNew(source unsafe.Pointer, size C.size_t) *C.UastSourceIndex {
index := positioner.NewIndex(cBytes(source, size), &positioner.IndexOptions{
Unicode: true,
})
srcMu.Lock()
srcLast++
h := srcLast
srcIndexes[h] = index
srcMu.Unlock()
return newSourceIndex(h)
}

//export UastSourceIndexFree
// UastSourceIndexFree release all resources associated with an index.
func UastSourceIndexFree(idx *C.UastSourceIndex) {
if idx == nil {
return
}
h := idx.handle
C.free(unsafe.Pointer(idx))
if h == 0 {
return
}
srcMu.Lock()
delete(srcIndexes, Handle(h))
srcMu.Unlock()
}

// getSourceIndex returns an index for a given handle. It returns nil if handle is invalid.
func getSourceIndex(h Handle) *positioner.Index {
srcMu.RLock()
idx := srcIndexes[h]
srcMu.RUnlock()
return idx
}

// convertOffset is a helper to convert one offset to the other, given a conversion function.
func convertOffset(fnc func(*positioner.Index, int) (int, error),
idx *C.UastSourceIndex, off C.int) C.int {
if idx == nil || idx.handle == 0 || off < 0 {
return -1
}
index := getSourceIndex(Handle(idx.handle))
if index == nil {
return -1
}
roff, err := fnc(index, int(off))
if err != nil {
// TODO: we should return this error somehow
// previously we stored it in Context, but we don't have (nor need) it here
return -1
}
return C.int(roff)
}

// convertToLineCol is a helper to convert one offset to the line-column pair, given a conversion function.
func convertToLineCol(fnc func(*positioner.Index, int) (int, int, error),
idx *C.UastSourceIndex, off C.int) C.UastLineCol {
if idx == nil || idx.handle == 0 || off < 0 {
return C.UastLineCol{-1, -1}
}
index := getSourceIndex(Handle(idx.handle))
if index == nil {
return C.UastLineCol{-1, -1}
}
line, col, err := fnc(index, int(off))
if err != nil {
// TODO: we should return this error somehow
// previously we stored it in Context, but we don't have (nor need) it here
return C.UastLineCol{-1, -1}
}
var pos C.UastLineCol
pos.line = C.int(line)
pos.col = C.int(col)
return pos
}

//export UastSourceIndexFromLineCol
// UastSourceIndexFromLineCol converts one-based line-column pair (in bytes) in the indexed
// source file to a zero-based byte offset. It return -1 in case of failure.
func UastSourceIndexFromLineCol(idx *C.UastSourceIndex, line, col C.int) C.int {
if idx == nil || idx.handle == 0 || line < 0 || col < 0 {
return -1
}
index := getSourceIndex(Handle(idx.handle))
if index == nil {
return -1
}
off, err := index.Offset(int(line), int(col))
if err != nil {
// TODO: we should return this error somehow
// previously we stored it in Context, but we don't have (nor need) it here
return -1
}
return C.int(off)
}

//export UastSourceIndexToLineCol
// UastSourceIndexToLineCol converts zero-based byte offset in the indexed source
// file to a one-based line and one-based column pair (in bytes).
// It return a UastLineCol with both elements set to -1 in case of failure.
func UastSourceIndexToLineCol(idx *C.UastSourceIndex, off C.int) C.UastLineCol {
return convertToLineCol((*positioner.Index).LineCol, idx, off)
}

//export UastSourceIndexFromUnicode
// UastSourceIndexFromUnicode converts zero-based Unicode character offset in the indexed
// source file to a zero-based byte offset. It return -1 in case of failure.
func UastSourceIndexFromUnicode(idx *C.UastSourceIndex, off C.int) C.int {
return convertOffset((*positioner.Index).FromRuneOffset, idx, off)
}

//export UastSourceIndexToUnicode
// UastSourceIndexToUnicode converts zero-based byte offset in the indexed source file to
// a zero-based Unicode character offset. It return -1 in case of failure.
func UastSourceIndexToUnicode(idx *C.UastSourceIndex, off C.int) C.int {
return convertOffset((*positioner.Index).ToRuneOffset, idx, off)
}

//export UastSourceIndexFromUTF16
// UastSourceIndexFromUTF16 converts zero-based UTF-16 code point offset in the indexed
// source file to a zero-based byte offset. It return -1 in case of failure.
func UastSourceIndexFromUTF16(idx *C.UastSourceIndex, off C.int) C.int {
return convertOffset((*positioner.Index).FromUTF16Offset, idx, off)
}

//export UastSourceIndexToUTF16
// UastSourceIndexToUTF16 converts zero-based byte offset in the indexed source file to
// a zero-based UTF-16 code point offset. It return -1 in case of failure.
func UastSourceIndexToUTF16(idx *C.UastSourceIndex, off C.int) C.int {
return convertOffset((*positioner.Index).ToUTF16Offset, idx, off)
}

//export UastSourceIndexToUnicodeLineCol
// UastSourceIndexToUnicodeLineCol converts zero-based byte offset in the indexed source
// file to a one-based line and one-based column pair (in Unicode characters).
// It return a UastLineCol with both elements set to -1 in case of failure.
func UastSourceIndexToUnicodeLineCol(idx *C.UastSourceIndex, off C.int) C.UastLineCol {
return convertToLineCol((*positioner.Index).ToUnicodeLineCol, idx, off)
}

//export UastSourceIndexToUTF16LineCol
// UastSourceIndexToUTF16LineCol converts zero-based byte offset in the indexed source
// file to a one-based line and one-based column pair (in UTF-16 code units).
// It return a UastLineCol with both elements set to -1 in case of failure.
func UastSourceIndexToUTF16LineCol(idx *C.UastSourceIndex, off C.int) C.UastLineCol {
return convertToLineCol((*positioner.Index).ToUTF16LineCol, idx, off)
}
12 changes: 12 additions & 0 deletions src/uast.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ typedef struct UastMemStats {
uint64_t objects; // number of live objects
} UastMemStats;

// UastSourceIndex represents a positional index for a source file.
// It's initialized with UastSourceIndexNew and freed with UastSourceIndexFree.
typedef struct UastSourceIndex {
uintptr_t handle;
} UastSourceIndex;

// UastLineCol is a one-based line-column position in a source file.
typedef struct UastLineCol {
int line;
int col;
} UastLineCol;

/*GO-HEADER*/

#endif // UAST_H_

0 comments on commit 527e6bd

Please sign in to comment.