Files
codebase-memory-mcp/internal/parser/parser.go
T
Martin Vogel f63c70865e Initial release: codebase-memory-mcp v0.0.1
MCP server that indexes codebases into a queryable knowledge graph.
Single Go binary, SQLite storage, 12 languages via tree-sitter.

Features: call graph, cross-service HTTP linking, dead code detection,
route nodes, Cypher queries, incremental reindex, JSON config scanning.
2026-02-25 00:10:39 +01:00

105 lines
3.5 KiB
Go

package parser
import (
"fmt"
"sync"
"unsafe"
tree_sitter "github.com/tree-sitter/go-tree-sitter"
tree_sitter_cpp "github.com/tree-sitter/tree-sitter-cpp/bindings/go"
tree_sitter_go "github.com/tree-sitter/tree-sitter-go/bindings/go"
tree_sitter_java "github.com/tree-sitter/tree-sitter-java/bindings/go"
tree_sitter_javascript "github.com/tree-sitter/tree-sitter-javascript/bindings/go"
tree_sitter_lua "github.com/tree-sitter-grammars/tree-sitter-lua/bindings/go"
tree_sitter_php "github.com/tree-sitter/tree-sitter-php/bindings/go"
tree_sitter_python "github.com/tree-sitter/tree-sitter-python/bindings/go"
tree_sitter_rust "github.com/tree-sitter/tree-sitter-rust/bindings/go"
tree_sitter_scala "github.com/tree-sitter/tree-sitter-scala/bindings/go"
tree_sitter_typescript "github.com/tree-sitter/tree-sitter-typescript/bindings/go"
"github.com/DeusData/codebase-memory-mcp/internal/lang"
)
var (
languagesOnce sync.Once
languages map[lang.Language]*tree_sitter.Language
)
func initLanguages() {
languagesOnce.Do(func() {
languages = map[lang.Language]*tree_sitter.Language{
lang.Python: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_python.Language())),
lang.JavaScript: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_javascript.Language())),
lang.TypeScript: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_typescript.LanguageTypescript())),
lang.TSX: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_typescript.LanguageTSX())),
lang.Go: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_go.Language())),
lang.Rust: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_rust.Language())),
lang.Java: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_java.Language())),
lang.CPP: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_cpp.Language())),
// C# skipped: upstream module path mismatch (tree-sitter-c-sharp vs tree-sitter-c_sharp)
lang.PHP: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_php.LanguagePHPOnly())),
lang.Lua: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_lua.Language())),
lang.Scala: tree_sitter.NewLanguage(unsafe.Pointer(tree_sitter_scala.Language())),
}
})
}
// GetLanguage returns the tree-sitter Language for a lang.Language.
func GetLanguage(l lang.Language) (*tree_sitter.Language, error) {
initLanguages()
tsLang, ok := languages[l]
if !ok {
return nil, fmt.Errorf("unsupported language: %s", l)
}
return tsLang, nil
}
// Parse parses source code into a tree-sitter AST Tree.
// The caller must call tree.Close() when done.
func Parse(l lang.Language, source []byte) (*tree_sitter.Tree, error) {
tsLang, err := GetLanguage(l)
if err != nil {
return nil, err
}
p := tree_sitter.NewParser()
defer p.Close()
if err := p.SetLanguage(tsLang); err != nil {
return nil, fmt.Errorf("set language %s: %w", l, err)
}
tree := p.Parse(source, nil)
if tree == nil {
return nil, fmt.Errorf("parse failed for language %s", l)
}
return tree, nil
}
// WalkFunc is called for each node during AST traversal.
// Return false to skip children.
type WalkFunc func(node *tree_sitter.Node) bool
// Walk traverses the AST in depth-first order.
func Walk(node *tree_sitter.Node, fn WalkFunc) {
if node == nil {
return
}
if !fn(node) {
return
}
for i := uint(0); i < node.ChildCount(); i++ {
child := node.Child(i)
if child != nil {
Walk(child, fn)
}
}
}
// NodeText returns the text content of a node.
func NodeText(node *tree_sitter.Node, source []byte) string {
return string(source[node.StartByte():node.EndByte()])
}