関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。前回までのメモは関連記事にかかれている ブログ記事まとめ よりどうぞ。
今回は、シンタックスエラーの出力について。
Parseメソッドの結果から取得できるNodeは、自身のエラー状態を持っています。もし、構文エラーが発生している場合はエラーありとなっています。
ルートノードにて HasErrorを呼び出して、エラーが存在すると判定できたら、再帰でノードを下って処理します。
該当ノードから、StartPositionとEndPositionを使うと、何行目の何列目なのかが取得できるので、この情報を元のソースコードと合わせると該当箇所がコード上でも特定できます。
ただし、Tree-sitterはコンパイラでは無いので、エラーとして検出されるのはあくまでも「シンタックスエラー」と判定されるもののみとなります。
例として再帰関数は以下のように実装できますね。
func walk(node *tree_sitter.Node, callback func(*tree_sitter.Node)) { callback(node) for i := uint(0); i < node.ChildCount(); i++ { walk(node.Child(i), callback) } }
こんな感じで使えます。
walk(root, func(n *tree_sitter.Node) { if n.IsError() || n.IsMissing() { var ( start = n.StartPosition() end = n.EndPosition() ) log.Printf("エラー: %d行%d列 - %d行%d列", start.Row+1, start.Column+1, end.Row+1, end.Column+1) // コード行表示 var ( scanner = bufio.NewScanner(bytes.NewReader(srcCode)) line string numLine int ) for ; scanner.Scan(); numLine++ { if numLine == int(start.Row) { line = scanner.Text() log.Printf(" 問題のコード: %s\n", line) break } } } })
ドキュメントを見ると、TreeCursorというカーソルオブジェクトが存在していて、Node.Walkなどから取得できるのですが、再帰的なウォーキングをしてくれるものではないため、上のように普通に自分で再帰処理書いたほうが楽なのでそうしてます。
サンプル
main.go
package main import ( "bufio" "bytes" "errors" "log" 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> int main(void) { int x int y = 10; if { } int = 20; printf("hello %d\n", z) return 0; }` ) var ( srcCode = []byte(C_CODE) ) 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 ( tree = p.Parse(srcCode, nil) ) if tree == nil { return errors.New("解析に失敗: Parse()") } defer tree.Close() // エラーが存在するか検査 var ( root = tree.RootNode() ) if root.HasError() { walk(root, func(n *tree_sitter.Node) { if n.IsError() || n.IsMissing() { var ( start = n.StartPosition() end = n.EndPosition() ) log.Printf("エラー: %d行%d列 - %d行%d列", start.Row+1, start.Column+1, end.Row+1, end.Column+1) // コード行表示 var ( scanner = bufio.NewScanner(bytes.NewReader(srcCode)) line string numLine int ) for ; scanner.Scan(); numLine++ { if numLine == int(start.Row) { line = scanner.Text() log.Printf(" 問題のコード: %s\n", line) break } } } }) } return nil } func walk(node *tree_sitter.Node, callback func(*tree_sitter.Node)) { callback(node) for i := uint(0); i < node.ChildCount(); i++ { walk(node.Child(i), callback) } }
実行結果
$ task task: [default] go run . エラー: 3行7列 - 3行7列 問題のコード: int x エラー: 5行2列 - 5行4列 問題のコード: if { エラー: 7行5列 - 7行5列 問題のコード: int = 20; エラー: 9行25列 - 9行25列 問題のコード: printf("hello %d\n", z)
参考情報
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。





