Tree-sitter 파싱
treesitter 모듈은 go-tree-sitter 바인딩을 통해 Tree-sitter로 소스 코드를 구체적 구문 트리로 파싱합니다.
이 페이지는 부분 파싱 레시피를 포함한 API 레퍼런스입니다. 소스 문자열과 쿼리 패턴은 애플리케이션 입력이며, 노드 수준의 코드 조각은 앞서 오류를 확인하며 파싱한 활성 트리가 있다고 가정합니다. 파서, 트리, 쿼리, 커서는 소유권이 있는 리소스입니다. 마지막 종속 작업이 끝나면 성공적으로 생성된 모든 핸들을 닫으세요.
생성되는 구문 트리는 다음과 같은 특성이 있습니다.
- 소스 코드의 전체 구조를 표현합니다.
- 코드 변경에 따라 점진적으로 업데이트됩니다.
- 구문 오류가 있어도 견고하게 부분 파싱합니다.
- S-표현식을 사용하는 패턴 기반 쿼리를 지원합니다.
로딩
local treesitter = require("treesitter")
지원 언어
| 언어 | 별칭 | 루트 노드 |
|---|---|---|
| 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, ...}
빠른 시작
코드 파싱
local code = [[
func hello() {
return "Hello!"
}
]]
local tree, err = treesitter.parse("go", code)
if err then
return nil, err
end
local root, root_err = tree:root_node()
if root_err then
tree:close()
return nil, root_err
end
print(root:kind()) -- "source_file"
print(root:child_count()) -- number of top-level declarations
tree:close()
구문 트리 쿼리
local code = [[
func hello() {}
func world() {}
]]
local tree, parse_err = treesitter.parse("go", code)
if parse_err then
return nil, parse_err
end
local root, root_err = tree:root_node()
if root_err then
tree:close()
return nil, root_err
end
-- Find all function names
local query, query_err = treesitter.query("go", [[
(function_declaration name: (identifier) @func_name)
]])
if query_err then
tree:close()
return nil, query_err
end
local captures, captures_err = query:captures(root, code)
if captures_err then
query:close()
tree:close()
return nil, captures_err
end
for _, capture in ipairs(captures) do
print(capture.name, capture.text)
end
-- "func_name" "hello"
-- "func_name" "world"
query:close()
tree:close()
파싱
단순 파싱
임시 내부 파서로 소스 코드를 파싱합니다.
local tree, err = treesitter.parse("go", code)
if err then
return nil, err
end
-- Use the tree, then call tree:close().
| 파라미터 | 타입 | 설명 |
|---|---|---|
language |
string | 언어 이름 또는 별칭 |
code |
string | 소스 코드 |
반환: Tree, error
재사용 가능한 파서
반복 파싱이나 점진적 업데이트에 사용할 수 있는 파서를 생성합니다.
local parser, parser_err = treesitter.parser()
if parser_err then
return nil, parser_err
end
local _, language_err = parser:set_language("go")
if language_err then
parser:close()
return nil, language_err
end
local tree1, first_err = parser:parse("package main")
if first_err then
parser:close()
return nil, first_err
end
-- Parse another source with the reusable parser. For an incremental update,
-- edit the old tree first as shown in the complete recipe below.
local tree2, second_err = parser:parse("package main\nfunc foo() {}")
if second_err then
tree1:close()
parser:close()
return nil, second_err
end
tree2:close()
tree1:close()
parser:close()
반환: Parser, error
파서 메서드
| 메서드 | 설명 |
|---|---|
set_language(lang) |
파서 언어를 설정하고 boolean, error를 반환 |
get_language() |
현재 언어 이름 반환 |
parse(code, old_tree?) |
코드를 파싱하며 점진적 파싱에서는 이전 트리를 선택적으로 사용 |
set_timeout(duration) |
파싱 제한 시간 설정 ("1s" 같은 문자열 또는 나노초) |
set_ranges(ranges) |
파싱할 바이트 범위 설정 |
reset() |
파서 상태 초기화 |
close() |
파서 리소스 해제 |
재사용 가능한 파서가 생성한 트리와 파서 자체는 각각 독립적으로 소유됩니다. 성공적으로 생성된 각 핸들을 닫으세요. 노드는 트리 저장소를 빌려 쓰므로 트리가 닫힌 뒤에는 사용할 수 없습니다. 커서도 트리를 빌려 쓰므로 트리보다 먼저 닫아야 합니다. 쿼리는 별도의 네이티브 리소스를 소유하며 close()가 필요합니다. 캡처나 일치 결과를 더 사용하지 않으면 쿼리를 닫으세요. 명시적 정리는 결정적으로 수행되며, 프로세스 리소스 저장소는 프로세스 종료 시까지 열린 핸들에 대한 보조 수단일 뿐입니다.
구문 트리
루트 노드 가져오기
local tree, err = treesitter.parse("go", "package main")
if err then
return nil, err
end
local root, root_err = tree:root_node()
if root_err then
tree:close()
return nil, root_err
end
print(root:kind()) -- "source_file"
local source_text, text_err = root:text()
if text_err then
tree:close()
return nil, text_err
end
print(source_text) -- "package main"
tree:close()
트리 메서드
| 메서드 | 설명 |
|---|---|
root_node() |
트리의 루트 노드 반환 |
root_node_with_offset(bytes, point) |
오프셋이 적용된 루트 반환 |
language() |
트리의 언어 객체 반환 |
copy() |
트리의 깊은 복사본 생성 |
walk() |
순회용 커서 생성 |
edit(edit_table) |
점진적 편집 적용 |
changed_ranges(other_tree) |
변경된 범위 반환 |
included_ranges() |
파싱에 포함된 범위 반환 |
dot_graph() |
DOT 그래프 표현 반환 |
close() |
트리 리소스 해제 |
점진적 편집
변경된 소스 코드를 다시 파싱하기 전에 편집을 적용합니다.
local code = "func main() { x := 1 }"
local tree, parse_err = treesitter.parse("go", code)
if parse_err then
return nil, parse_err
end
-- Mark edit: changed "1" to "100" at byte 19
local _, edit_err = 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
})
if edit_err then
tree:close()
return nil, edit_err
end
-- Re-parse with edited tree (faster than full parse)
local parser, parser_err = treesitter.parser()
if parser_err then
tree:close()
return nil, parser_err
end
local _, language_err = parser:set_language("go")
if language_err then
parser:close()
tree:close()
return nil, language_err
end
local new_tree, new_tree_err = parser:parse("func main() { x := 100 }", tree)
if new_tree_err then
parser:close()
tree:close()
return nil, new_tree_err
end
new_tree:close()
parser:close()
tree:close()
노드
노드는 구문 트리의 요소를 나타냅니다. 아래의 독립적인 코드 조각에서 root, node, func_decl은 아직 열린 트리에서 애플리케이션이 선택한 노드라고 가정합니다.
노드 타입
local node = root:child(0)
-- Type information
print(node:kind()) -- "package_clause"
print(node:type()) -- same as kind()
print(node:is_named()) -- true for significant nodes
print(node:grammar_name()) -- grammar rule name
탐색
-- Children
local child = node:child(0) -- by index (0-based)
local named = node:named_child(0) -- named children only
local count = node:child_count()
local named_count = node:named_child_count()
-- Siblings
local next = node:next_sibling()
local prev = node:prev_sibling()
local next_named = node:next_named_sibling()
local prev_named = node:prev_named_sibling()
-- Parent
local parent = node:parent()
-- By field name
local name_node = func_decl:child_by_field_name("name")
local field = node:field_name_for_child(0)
위치 정보
-- Byte offsets
local start = node:start_byte()
local end_ = node:end_byte()
-- Row/column positions (0-based)
local start_pt = node:start_point() -- {row = 0, column = 0}
local end_pt = node:end_point() -- {row = 0, column = 12}
-- Source text
local source_text, err = node:text()
if err then
return nil, err
end
오류 감지
if root:has_error() then
-- Tree contains syntax errors
end
if node:is_error() then
-- This specific node is an error
end
if node:is_missing() then
-- Parser inserted this to recover from error
end
if node:is_extra() then
-- 노드가 문법에서 요구하지 않는 "extra" (예: 주석)
end
그 외 노드 메서드: descendant_count()와 named_descendant_for_point_range(start_pt, end_pt).
S-표현식
local sexp = node:to_sexp()
-- "(source_file (package_clause (package_identifier)))"
쿼리
Tree-sitter 쿼리는 S-표현식으로 작성한 구문 트리 패턴을 찾습니다.
쿼리 생성
local query, err = treesitter.query("go", [[
(function_declaration
name: (identifier) @func_name
parameters: (parameter_list) @params
)
]])
if err then
return nil, err
end
-- The owner calls query:close() after the final query operation.
| 파라미터 | 타입 | 설명 |
|---|---|---|
language |
string | 언어 이름 |
pattern |
string | S-표현식 구문의 쿼리 패턴 |
반환: Query, error
쿼리 실행
-- Get all captures (flattened)
local captures, captures_err = query:captures(root, source_code)
if captures_err then
query:close()
tree:close()
return nil, captures_err
end
for _, capture in ipairs(captures) do
print(capture.name) -- "func_name"
print(capture.text) -- 실제 텍스트
print(capture.index) -- 캡처 인덱스
-- capture.node는 Node 객체
end
-- Get matches (grouped by pattern)
local matches, matches_err = query:matches(root, source_code)
if matches_err then
query:close()
tree:close()
return nil, matches_err
end
for _, match in ipairs(matches) do
print(match.id, match.pattern)
for _, capture in ipairs(match.captures) do
local captured_text, text_err = capture.node:text()
if text_err then
query:close()
tree:close()
return nil, text_err
end
print(capture.name, captured_text)
end
end
query:close()
tree:close()
Tree-sitter Node 대신 잘못된 타입의 userdata를 전달하면 nil, error가 반환됩니다. 원시 값이나 테이블을 전달하면 이 검사 전에 Lua 인자 오류가 발생합니다. 여기서 root, source_code, query는 아직 열린 트리와 성공적으로 생성된 쿼리에서 가져와야 합니다. 코드 조각은 소유 중인 tree 핸들로 두 리소스를 모두 닫은 뒤 반환합니다.
쿼리 제어
-- Limit query scope
query:set_byte_range(0, 1000)
query:set_point_range({row = 0, column = 0}, {row = 10, column = 0})
-- Limit matches
query:set_match_limit(100)
if query:did_exceed_match_limit() then
-- More matches exist
end
-- Timeout (string duration or nanoseconds)
query:set_timeout("500ms")
query:set_timeout(1000000000) -- 1 second in nanoseconds
-- Disable patterns/captures
query:disable_pattern(0)
query:disable_capture("func_name")
쿼리 검사
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")
추가 조회 메서드: string_count(), start_byte_for_pattern(i), end_byte_for_pattern(i), get_match_limit(), get_timeout(), is_pattern_rooted(i), is_pattern_non_local(i), is_pattern_guaranteed(i), capture_quantifier(pattern, capture), set_max_start_depth(n), get_property_predicates(i), get_property_settings(i), get_text_predicates(i).
트리 커서
트리 커서는 각 단계에서 노드 객체를 만들지 않고 트리를 순회합니다.
기본 순회
local cursor, err = tree:walk()
if err then
return nil, err
end
-- Start at root
print(cursor:current_node():kind()) -- "source_file"
print(cursor:current_depth()) -- 0
-- Navigate
if cursor:goto_first_child() then
print(cursor:current_node():kind())
print(cursor:current_depth()) -- 1
end
if cursor:goto_next_sibling() then
-- moved to next sibling
end
cursor:goto_parent() -- back to parent
cursor:close()
커서 메서드
| 메서드 | 반환 | 설명 |
|---|---|---|
current_node() |
Node |
커서 위치의 노드 |
current_depth() |
integer |
깊이(루트는 0) |
current_field_name() |
string? |
필드 이름(있는 경우) |
current_field_id() |
integer |
필드 ID(없으면 0) |
current_descendant_index() |
integer |
현재 노드의 하위 노드 인덱스 |
goto_parent() |
boolean |
부모로 이동 |
goto_first_child() |
boolean |
첫 번째 자식으로 이동 |
goto_last_child() |
boolean |
마지막 자식으로 이동 |
goto_next_sibling() |
boolean |
다음 형제로 이동 |
goto_previous_sibling() |
boolean |
이전 형제로 이동 |
goto_descendant(index) |
- | 인덱스로 하위 노드로 이동 |
goto_first_child_for_byte(n) |
integer? |
해당 바이트를 포함하는 자식으로 이동 |
goto_first_child_for_point(pt) |
integer? |
해당 지점을 포함하는 자식으로 이동 |
reset(node) |
- | 커서를 노드로 초기화 |
reset_to(cursor) |
- | 다른 커서의 위치로 커서를 초기화 |
copy() |
Cursor |
커서 복사본 생성 |
close() |
- | 리소스 해제 |
언어 메타데이터
local lang, err = treesitter.language("go")
if err then
return nil, err
end
print(lang:version()) -- ABI version
print(lang:node_kind_count()) -- number of node types
print(lang:field_count()) -- number of fields
print(lang:parse_state_count()) -- number of parse states
-- 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)
-- Field lookup
local field_name = lang:field_name_for_id(1)
local field_id = lang:field_id_for_name("name")
오류
| 조건 | 종류 | 재시도 가능 |
|---|---|---|
| 지원되지 않는 언어 | errors.INVALID |
아니요 |
| 바인딩이 없는 언어 | errors.INVALID |
아니요 |
| 잘못된 쿼리 패턴 | errors.INVALID |
아니요 |
| 잘못된 위치 | errors.INVALID |
아니요 |
| 파싱 실패 | errors.INTERNAL |
아니요 |
| 실행 컨텍스트 없음 | errors.INTERNAL |
아니요 |
이미 닫힌 파서, 트리, 쿼리 또는 커서를 다시 닫아도 안전합니다. 닫힌 핸들에서 다른 메서드를 호출하면 Lua 인자 오류가 발생합니다.
오류 처리 방법은 오류 처리를 참조하세요.
쿼리 구문 레퍼런스
Tree-sitter 쿼리는 S-표현식 패턴을 사용합니다.
; Match a node type
(identifier)
; Match with field names
(function_declaration name: (identifier))
; Capture with @name
(function_declaration name: (identifier) @func_name)
; Multiple patterns
[
(function_declaration)
(method_declaration)
] @declaration
; Wildcards
(_) ; any node
(identifier)+ ; one or more
(identifier)* ; zero or more
(identifier)? ; optional
; Predicates
((identifier) @var
(#match? @var "^_")) ; regex match
전체 문서는 Tree-sitter 쿼리 구문을 참조하세요.