chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
// Package forest is the long-tail language adapter. It wraps any
|
||||
// alexaandru/go-sitter-forest grammar (510+ supported) as a Gortex
|
||||
// Extractor with signature-only depth: function / method / type /
|
||||
// interface / variable / constant nodes plus EdgeDefines from the
|
||||
// file. Calls and imports are best-effort via @reference.call captures
|
||||
// when the grammar ships a tags.scm.
|
||||
//
|
||||
// Top-tier languages (Go, TS, Python, Rust, …) keep their bespoke
|
||||
// extractors in internal/parser/languages — those have hand-tuned
|
||||
// queries that emit Gortex-specific edges (ORM, contracts, dataflow)
|
||||
// the generic walker can't produce. forest is for everything else.
|
||||
package forest
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/zzet/gortex/internal/graph"
|
||||
"github.com/zzet/gortex/internal/parser"
|
||||
sitter "github.com/zzet/gortex/internal/parser/tsitter"
|
||||
)
|
||||
|
||||
// GetLanguageFn returns the raw C *TSLanguage pointer. Every forest
|
||||
// language module exposes one as `forestpkg.GetLanguage`.
|
||||
type GetLanguageFn func() unsafe.Pointer
|
||||
|
||||
// GetQueryFn returns the bytes of a named .scm query bundled with the
|
||||
// grammar. Forest grammars ship a subset of {tags, highlights, locals,
|
||||
// folds, indents, injections}; we only read tags.scm. The opts
|
||||
// argument is forest's preference flag (NvimFirst / NativeFirst /
|
||||
// NvimOnly / NativeOnly). nil is acceptable when the grammar has no
|
||||
// queries.
|
||||
type GetQueryFn func(kind string, opts ...byte) []byte
|
||||
|
||||
// Extractor is a generic forest-backed signature-only extractor.
|
||||
// Construct one per language via New() and register it with the
|
||||
// parser registry exactly like any other Extractor.
|
||||
type Extractor struct {
|
||||
language string
|
||||
extensions []string
|
||||
|
||||
getLang GetLanguageFn
|
||||
getQuery GetQueryFn
|
||||
|
||||
once sync.Once
|
||||
initErr error
|
||||
lang *sitter.Language
|
||||
tagsQ *parser.PreparedQuery // nil when grammar ships no tags.scm
|
||||
}
|
||||
|
||||
// New builds a forest-backed Extractor. getQuery may be nil for
|
||||
// grammars that do not expose any .scm queries — extraction then
|
||||
// falls back to the generic node-kind walker.
|
||||
func New(language string, extensions []string, getLang GetLanguageFn, getQuery GetQueryFn) *Extractor {
|
||||
return &Extractor{
|
||||
language: language,
|
||||
extensions: extensions,
|
||||
getLang: getLang,
|
||||
getQuery: getQuery,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Extractor) Language() string { return e.language }
|
||||
func (e *Extractor) Extensions() []string { return e.extensions }
|
||||
|
||||
// init compiles the language pointer + tags.scm query exactly once.
|
||||
// Errors are sticky: once an init has failed, every subsequent
|
||||
// Extract returns the same error rather than retrying CGO setup on
|
||||
// every file (which would slow indexing to a crawl on a broken
|
||||
// grammar).
|
||||
func (e *Extractor) init() error {
|
||||
e.once.Do(func() {
|
||||
ptr := e.getLang()
|
||||
if ptr == nil {
|
||||
e.initErr = errNilLanguage
|
||||
return
|
||||
}
|
||||
e.lang = sitter.NewLanguage(ptr)
|
||||
|
||||
if e.getQuery != nil {
|
||||
pattern := e.getQuery("tags")
|
||||
if len(pattern) > 0 {
|
||||
q, err := parser.NewPreparedQuery(string(pattern), e.lang)
|
||||
if err == nil {
|
||||
e.tagsQ = q
|
||||
}
|
||||
// Compile failure is non-fatal: drop to walker.
|
||||
}
|
||||
}
|
||||
})
|
||||
return e.initErr
|
||||
}
|
||||
|
||||
// Extract parses src with the bundled grammar and emits one file
|
||||
// node, plus one node per detected definition (function / method /
|
||||
// type / interface / variable / constant / field / module). Edges:
|
||||
// EdgeDefines from the file to every definition; EdgeCalls
|
||||
// (unresolved::name) for @reference.call captures when tags.scm is
|
||||
// present.
|
||||
func (e *Extractor) Extract(filePath string, src []byte) (*parser.ExtractionResult, error) {
|
||||
if err := e.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tree, err := parser.ParseFile(src, e.lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tree.Close()
|
||||
|
||||
root := tree.RootNode()
|
||||
result := &parser.ExtractionResult{}
|
||||
|
||||
fileNode := &graph.Node{
|
||||
ID: filePath,
|
||||
Kind: graph.KindFile,
|
||||
Name: filePath,
|
||||
FilePath: filePath,
|
||||
StartLine: 1,
|
||||
EndLine: int(root.EndPoint().Row) + 1,
|
||||
Language: e.language,
|
||||
}
|
||||
result.Nodes = append(result.Nodes, fileNode)
|
||||
|
||||
if e.tagsQ != nil {
|
||||
e.extractByTags(root, src, filePath, fileNode, result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
e.extractByWalker(root, src, filePath, fileNode, result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var _ parser.Extractor = (*Extractor)(nil)
|
||||
Reference in New Issue
Block a user