Tree-sitter-Parsing
Parsen Sie Quellcode in konkrete Syntaxbäume mit Tree-sitter. Basiert auf go-tree-sitter Bindings.
Tree-sitter produziert Syntaxbäume, die:
- Die vollständige Struktur des Quellcodes repräsentieren
- Inkrementell aktualisiert werden, wenn sich Code ändert
- Robust gegenüber Syntaxfehlern sind (partielles Parsing)
- Musterbasierte Abfragen mit S-Ausdrücken unterstützen
Laden
local treesitter = require("treesitter")
Unterstützte Sprachen
| Sprache | Aliase | Root-Node |
|---|---|---|
| Go | go, golang |
source_file |
| JavaScript | js, javascript |
program |
| TypeScript | ts, typescript |
program |
| TSX | tsx |
program |
| Python | python, py |
module |
| Lua | lua |
chunk |
| PHP | php |
program |
| C# | csharp, cs, c# |
compilation_unit |
| HTML | html, html5 |
document |
| Markdown | markdown, md |
document |
| SQL | sql |
- |
local langs = treesitter.supported_languages()
-- {go = true, javascript = true, python = true, ...}
Schnellstart
Code parsen
local code = [[
func hello() {
return "Hello!"
}
]]
local tree, err = treesitter.parse("go", code)
if err then
return nil, err
end
local root = tree:root_node()
print(root:kind()) -- "source_file"
print(root:child_count()) -- Anzahl Top-Level-Deklarationen
Syntaxbaum abfragen
local code = [[
func hello() {}
func world() {}
]]
local tree = treesitter.parse("go", code)
local root = tree:root_node()
-- Alle Funktionsnamen finden
local query = treesitter.query("go", [[
(function_declaration name: (identifier) @func_name)
]])
local captures = query:captures(root, code)
for _, capture in ipairs(captures) do
print(capture.name, capture.text)
end
-- "func_name" "hello"
-- "func_name" "world"
Parsing
Einfaches Parsing
Quellcode in einen Syntaxbaum parsen. Erstellt intern einen temporären Parser.
local tree, err = treesitter.parse("go", code)
| Parameter | Typ | Beschreibung |
|---|---|---|
language |
string | Sprachname oder Alias |
code |
string | Quellcode |
Gibt zurück: Tree, error
Wiederverwendbarer Parser
Erstellen Sie einen Parser für wiederholtes Parsing oder inkrementelle Updates.
local parser = treesitter.parser()
parser:set_language("go")
local tree1 = parser:parse("package main")
-- Inkrementelles Parsing mit altem Baum
local tree2 = parser:parse("package main\nfunc foo() {}", tree1)
parser:close()
Gibt zurück: Parser
Parser-Methoden
| Methode | Beschreibung |
|---|---|
set_language(lang) |
Parser-Sprache setzen, gibt boolean, error zurück |
get_language() |
Aktuellen Sprachnamen abrufen |
parse(code, old_tree?) |
Code parsen, optional mit altem Baum für inkrementelles Parsing |
set_timeout(duration) |
Parse-Timeout setzen (String wie "1s" oder Nanosekunden) |
set_ranges(ranges) |
Byte-Bereiche zum Parsen setzen |
reset() |
Parser-Zustand zurücksetzen |
close() |
Parser-Ressourcen freigeben |
Syntaxbäume
Root-Node abrufen
local tree = treesitter.parse("go", "package main")
local root = tree:root_node()
print(root:kind()) -- "source_file"
print(root:text()) -- "package main"
Tree-Methoden
| Methode | Beschreibung |
|---|---|
root_node() |
Root-Node des Baums abrufen |
root_node_with_offset(bytes, point) |
Root mit angewendetem Offset abrufen |
language() |
Sprachobjekt des Baums abrufen |
copy() |
Tiefe Kopie des Baums erstellen |
walk() |
Cursor für Traversierung erstellen |
edit(edit_table) |
Inkrementelle Bearbeitung anwenden |
changed_ranges(other_tree) |
Geänderte Bereiche abrufen |
included_ranges() |
Beim Parsing eingeschlossene Bereiche abrufen |
dot_graph() |
DOT-Graph-Repräsentation abrufen |
close() |
Tree-Ressourcen freigeben |
Inkrementelles Bearbeiten
Aktualisieren Sie den Baum, wenn sich Quellcode ändert:
local code = "func main() { x := 1 }"
local tree = treesitter.parse("go", code)
-- Bearbeitung markieren: "1" zu "100" geändert bei Byte 19
tree:edit({
start_byte = 19,
old_end_byte = 20,
new_end_byte = 22,
start_row = 0,
start_column = 19,
old_end_row = 0,
old_end_column = 20,
new_end_row = 0,
new_end_column = 22
})
-- Mit bearbeitetem Baum neu parsen (schneller als vollständiges Parsing)
local parser = treesitter.parser()
parser:set_language("go")
local new_tree = parser:parse("func main() { x := 100 }", tree)
Nodes
Nodes repräsentieren Elemente im Syntaxbaum.
Node-Typen
local node = root:child(0)
-- Typinformationen
print(node:kind()) -- "package_clause"
print(node:type()) -- gleich wie kind()
print(node:is_named()) -- true für signifikante Nodes
print(node:grammar_name()) -- Grammatik-Regelname
Navigation
-- Kinder
local child = node:child(0) -- nach Index (0-basiert)
local named = node:named_child(0) -- nur benannte Kinder
local count = node:child_count()
local named_count = node:named_child_count()
-- Geschwister
local next = node:next_sibling()
local prev = node:prev_sibling()
local next_named = node:next_named_sibling()
local prev_named = node:prev_named_sibling()
-- Eltern
local parent = node:parent()
-- Nach Feldname
local name_node = func_decl:child_by_field_name("name")
local field = node:field_name_for_child(0)
Positionsinformationen
-- Byte-Offsets
local start = node:start_byte()
local end_ = node:end_byte()
-- Zeile/Spalte-Positionen (0-basiert)
local start_pt = node:start_point() -- {row = 0, column = 0}
local end_pt = node:end_point() -- {row = 0, column = 12}
-- Quelltext
local text = node:text()
Fehlererkennung
if root:has_error() then
-- Baum enthält Syntaxfehler
end
if node:is_error() then
-- Dieser spezifische Node ist ein Fehler
end
if node:is_missing() then
-- Parser hat diesen zur Fehlerwiederherstellung eingefügt
end
S-Ausdruck
local sexp = node:to_sexp()
-- "(source_file (package_clause (package_identifier)))"
Queries
Musterabgleich mit Tree-sitters Abfragesprache (S-Ausdrücke).
Query erstellen
local query, err = treesitter.query("go", [[
(function_declaration
name: (identifier) @func_name
parameters: (parameter_list) @params
)
]])
| Parameter | Typ | Beschreibung |
|---|---|---|
language |
string | Sprachname |
pattern |
string | Query-Muster in S-Ausdruck-Syntax |
Gibt zurück: Query, error
Query ausführen
-- Alle Captures abrufen (flach)
local captures = query:captures(root, source_code)
for _, capture in ipairs(captures) do
print(capture.name) -- "@func_name"
print(capture.text) -- tatsächlicher Text
print(capture.index) -- Capture-Index
-- capture.node ist das Node-Objekt
end
-- Matches abrufen (nach Muster gruppiert)
local matches = query:matches(root, source_code)
for _, match in ipairs(matches) do
print(match.id, match.pattern)
for _, capture in ipairs(match.captures) do
print(capture.name, capture.node:text())
end
end
Query-Steuerung
-- Query-Bereich begrenzen
query:set_byte_range(0, 1000)
query:set_point_range({row = 0, column = 0}, {row = 10, column = 0})
-- Matches begrenzen
query:set_match_limit(100)
if query:did_exceed_match_limit() then
-- Mehr Matches existieren
end
-- Timeout (String-Dauer oder Nanosekunden)
query:set_timeout("500ms")
query:set_timeout(1000000000) -- 1 Sekunde in Nanosekunden
-- Muster/Captures deaktivieren
query:disable_pattern(0)
query:disable_capture("func_name")
Query-Inspektion
local pattern_count = query:pattern_count()
local capture_count = query:capture_count()
local name = query:capture_name_for_id(0)
local id = query:capture_index_for_name("func_name")
Tree-Cursor
Effiziente Traversierung ohne Node-Objekte bei jedem Schritt zu erstellen.
Grundlegende Traversierung
local cursor = tree:walk()
-- Bei Root starten
print(cursor:current_node():kind()) -- "source_file"
print(cursor:current_depth()) -- 0
-- Navigieren
if cursor:goto_first_child() then
print(cursor:current_node():kind())
print(cursor:current_depth()) -- 1
end
if cursor:goto_next_sibling() then
-- zum nächsten Geschwister gewechselt
end
cursor:goto_parent() -- zurück zum Eltern
cursor:close()
Cursor-Methoden
| Methode | Gibt zurück | Beschreibung |
|---|---|---|
current_node() |
Node |
Node an Cursor-Position |
current_depth() |
integer |
Tiefe (0 = Root) |
current_field_name() |
string? |
Feldname falls vorhanden |
goto_parent() |
boolean |
Zum Eltern wechseln |
goto_first_child() |
boolean |
Zum ersten Kind wechseln |
goto_last_child() |
boolean |
Zum letzten Kind wechseln |
goto_next_sibling() |
boolean |
Zum nächsten Geschwister wechseln |
goto_previous_sibling() |
boolean |
Zum vorherigen Geschwister wechseln |
goto_first_child_for_byte(n) |
integer? |
Zum Kind wechseln, das Byte enthält |
goto_first_child_for_point(pt) |
integer? |
Zum Kind wechseln, das Punkt enthält |
reset(node) |
- | Cursor auf Node zurücksetzen |
copy() |
Cursor |
Kopie des Cursors erstellen |
close() |
- | Ressourcen freigeben |
Sprach-Metadaten
local lang = treesitter.language("go")
print(lang:version()) -- ABI-Version
print(lang:node_kind_count()) -- Anzahl Node-Typen
print(lang:field_count()) -- Anzahl Felder
-- Node-Kind-Lookup
local kind = lang:node_kind_for_id(1)
local id = lang:id_for_node_kind("identifier", true)
local is_named = lang:node_kind_is_named(1)
-- Feld-Lookup
local field_name = lang:field_name_for_id(1)
local field_id = lang:field_id_for_name("name")
Fehler
| Bedingung | Art | Wiederholbar |
|---|---|---|
| Sprache nicht unterstützt | errors.INVALID |
nein |
| Sprache hat kein Binding | errors.INVALID |
nein |
| Ungültiges Query-Muster | errors.INVALID |
nein |
| Ungültige Positionen | errors.INVALID |
nein |
| Parsing fehlgeschlagen | errors.INTERNAL |
nein |
Siehe Fehlerbehandlung für die Arbeit mit Fehlern.
Query-Syntax-Referenz
Tree-sitter-Queries verwenden S-Ausdruck-Muster:
; Node-Typ matchen
(identifier)
; Mit Feldnamen matchen
(function_declaration name: (identifier))
; Mit @name capturen
(function_declaration name: (identifier) @func_name)
; Mehrere Muster
[
(function_declaration)
(method_declaration)
] @declaration
; Wildcards
(_) ; beliebiger Node
(identifier)+ ; ein oder mehr
(identifier)* ; null oder mehr
(identifier)? ; optional
; Prädikate
((identifier) @var
(#match? @var "^_")) ; Regex-Match
Siehe Tree-sitter Query Syntax für vollständige Dokumentation.