関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
Goメモ-597 (Tree-sitterメモ-01)(下準備, Goバインディングのインストール) - いろいろ備忘録日記
Goメモ-598 (Tree-sitterメモ-02)(パーサーの生成と言語の設定) - いろいろ備忘録日記
Goメモ-599 (Tree-sitterメモ-03)(解析処理の実行とツリーの取得) - いろいろ備忘録日記
Goメモ-600 (Tree-sitterメモ-04)(シンタックスエラーの出力) - いろいろ備忘録日記
Goメモ-605 (Tree-sitterメモ-05)(ノードタイプ) - いろいろ備忘録日記
概要
以下、自分用のメモです。前回までのメモは関連記事にかかれている ブログ記事まとめ よりどうぞ。
今回は、ノードテキストについて。
ノードのテキスト情報は、tree-sitterを使っていて一番欲しい情報の一つですね。
特定のソースコードを構文解析して何かをしたいわけですので、該当箇所のテキストは一番欲しいものです。
ですが、tree-sitter自身は、ノード自身は元のテキスト上でのポジションしか保持していないため、実際のテキストとして復元するには元データが必要です。
開始位置を取得する StartByte() と終了位置を取得する EndByte() を使ってテキストを抽出する必要があります。
こんな感じ。
sourceCode[node.StartByte():node.EndByte()]
サンプル
main.go
package main import ( "errors" "fmt" "log" "strings" tree_sitter "github.com/tree-sitter/go-tree-sitter" tree_sitter_c "github.com/tree-sitter/tree-sitter-c/bindings/go" ) const ( C_CODE = `#include <stdio.h> #include <stdlib.h> int add(int x, int y) { // ADD return x+y; } int main(void) { int z = add(1, 2); if (z < 10) { printf("%d\n", z); } for (int i = 0; i < z; i++) { switch (i) { case 0: printf("first\n"); return; default: printf("other\n"); return; } } return EXIT_SUCCESS; } ` ) func main() { log.SetFlags(0) if err := run(); err != nil { log.Fatal(err) } } func run() error { var ( p = tree_sitter.NewParser() l = tree_sitter.NewLanguage(tree_sitter_c.Language()) err error ) defer p.Close() if err = p.SetLanguage(l); err != nil { return err } var ( code = []byte(C_CODE) tree = p.Parse(code, nil) ) if tree == nil { return errors.New("構文解析に失敗: Parse()") } defer tree.Close() var ( root = tree.RootNode() callback = func(node *tree_sitter.Node, depth int) { var ( k = node.Kind() s = node.StartByte() e = node.EndByte() ) fmt.Printf("%s%-25s%s%s\n", strings.Repeat(" ", depth*2), k, strings.Repeat(" ", 10), C_CODE[s:e]) } ) walk(root, 0, callback) return nil } func walk(node *tree_sitter.Node, depth int, callback func(*tree_sitter.Node, int)) { if node.Kind() != "translation_unit" { callback(node, depth) } for i := uint(0); i < node.NamedChildCount(); i++ { walk(node.NamedChild(i), depth+1, callback) } }
実行結果
$ task task: [default] go run . preproc_include #include <stdio.h> system_lib_string <stdio.h> preproc_include #include <stdlib.h> system_lib_string <stdlib.h> function_definition int add(int x, int y) { // ADD return x+y; } primitive_type int function_declarator add(int x, int y) identifier add parameter_list (int x, int y) parameter_declaration int x primitive_type int identifier x parameter_declaration int y primitive_type int identifier y compound_statement { // ADD return x+y; } comment // ADD return_statement return x+y; binary_expression x+y identifier x identifier y function_definition int main(void) { int z = add(1, 2); if (z < 10) { printf("%d\n", z); } for (int i = 0; i < z; i++) { switch (i) { case 0: printf("first\n"); return; default: printf("other\n"); return; } } return EXIT_SUCCESS; } primitive_type int function_declarator main(void) identifier main parameter_list (void) parameter_declaration void primitive_type void compound_statement { int z = add(1, 2); if (z < 10) { printf("%d\n", z); } for (int i = 0; i < z; i++) { switch (i) { case 0: printf("first\n"); return; default: printf("other\n"); return; } } return EXIT_SUCCESS; } declaration int z = add(1, 2); primitive_type int init_declarator z = add(1, 2) identifier z call_expression add(1, 2) identifier add argument_list (1, 2) number_literal 1 number_literal 2 if_statement if (z < 10) { printf("%d\n", z); } parenthesized_expression (z < 10) binary_expression z < 10 identifier z number_literal 10 compound_statement { printf("%d\n", z); } expression_statement printf("%d\n", z); call_expression printf("%d\n", z) identifier printf argument_list ("%d\n", z) string_literal "%d\n" string_content %d escape_sequence \n identifier z for_statement for (int i = 0; i < z; i++) { switch (i) { case 0: printf("first\n"); return; default: printf("other\n"); return; } } declaration int i = 0; primitive_type int init_declarator i = 0 identifier i number_literal 0 binary_expression i < z identifier i identifier z update_expression i++ identifier i compound_statement { switch (i) { case 0: printf("first\n"); return; default: printf("other\n"); return; } } switch_statement switch (i) { case 0: printf("first\n"); return; default: printf("other\n"); return; } parenthesized_expression (i) identifier i compound_statement { case 0: printf("first\n"); return; default: printf("other\n"); return; } case_statement case 0: printf("first\n"); return; number_literal 0 expression_statement printf("first\n"); call_expression printf("first\n") identifier printf argument_list ("first\n") string_literal "first\n" string_content first escape_sequence \n return_statement return; case_statement default: printf("other\n"); return; expression_statement printf("other\n"); call_expression printf("other\n") identifier printf argument_list ("other\n") string_literal "other\n" string_content other escape_sequence \n return_statement return; return_statement return EXIT_SUCCESS; identifier EXIT_SUCCESS
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。