diff --git a/gen_header.go b/gen_header.go index 6d4f58d..54423e1 100644 --- a/gen_header.go +++ b/gen_header.go @@ -9,6 +9,7 @@ import ( "go/token" "io/ioutil" "os" + "path/filepath" "strings" ) @@ -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()) } diff --git a/go.mod b/go.mod index 4f3c616..c835596 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 6d064d4..803aec3 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/src/src_index.go b/src/src_index.go new file mode 100644 index 0000000..0a76a74 --- /dev/null +++ b/src/src_index.go @@ -0,0 +1,180 @@ +package main + +/* +#include "uast.h" +#include +*/ +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) +} diff --git a/src/uast.h b/src/uast.h index 6471dd3..37578ed 100644 --- a/src/uast.h +++ b/src/uast.h @@ -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_ \ No newline at end of file