はじめに
Goの標準パッケージには、go
パッケージというものがあり、Goのソースコードを静的解析するのに便利な機能が提供されています。
go
パッケージを使うと、以下のようなツールが簡単に作れます。
- ソースコードを自動生成するツール
- ソースコードに対してlintやフォーマットを行うツール
- リファクタリングツール
- ソースコードから特定の何かを検索するツール
- ミニ言語の処理系
また、go
パッケージを学ぶとコンパイラや言語処理系の知識も学べるので、他の標準パッケージとはまた少し違った知識を得ることができ、プログラミングの知識の幅が広がります。
しかしながら、go
パッケージに関する日本語の文献は少なく、学習しづらいのが辛いところです。
私もこれまでgo
パッケージについて、Qiita上でいくつか記事を書いてきましたが、どの記事が何をするときに役に立つのか分かりづらくなっていました。
そこでこの記事では、どいういうときにどの記事を読めば良いのかをまとめてみました。
公式ドキュメントなど
まずは1次ソースとなる公式ドキュメントを挙げておきたいと思います。
英語がスラスラと読める方には、公式ドキュメントを読むことをオススメします。
また、多少英語が苦手でも、まずは公式ドキュメントを読むと良いと思いますが、英語だけで理解が難しい場合は、標準パッケージのソースコードも合わせても読むと理解が進むと思います。
なおソースコードは、GoDoc上かリンクを辿るか、GitHub上のsrc/go
以下のコードを読むと良いでしょう。
以下に静的解析を行うプログラムを作る際に読むと便利なドキュメントを挙げておきます。
-
言語仕様
- 文法を把握しておくと理解が進むでしょう
-
go
パッケージのドキュメント- いくつかのサブパッケージがあります。詳しくは後述します
-
go/types
のexampleを使って解説したドキュメント-
go/types
パッケージについて詳しく書いたドキュメントで、go/token
パッケージやgo/ast
パッケージを多少勉強してから読むと理解が進むでしょう
-
-
定数についての公式ブログ
- Goの定数については案外知られていないことが多いかもしれません
-
go/types
パッケージやgo/constant
パッケージについて勉強する際に参考になります
go
パッケージ
go
パッケージは、いくつかのサブパッケージから構成されています。
まずは、それらのパッケージがざっくり何をやってるのかを説明します。
-
go/token
パッケージ- Goの字句(トークン)を定義したパッケージ
- ソースコード上の位置を表す
token.Pos
なども定義されてる -
token.FileSet
についてもよく使う
-
go/scanner
パッケージ- 字句解析を行うパッケージ
- 字句解析からやることはあまりないので、あまり使わないかも
-
go/ast
パッケージ- 抽象構文木(AST)を定義したパッケージ
- よく使う
-
go/parser
パッケージ- 構文解析を行うパッケージ
- ソースコードを与えると、字句解析して構文解析し、ASTまで作ってくれる
-
go/constant
パッケージ- Goの定数について定義したパッケージ
- 定数を表すデータ型や演算を行う関数などが定義されてる
-
go/types
パッケージ- 型チェックの機能を提供するパッケージ
- 定数の評価、型推論、識別子の解決などを行う
golang.org/x/tools/go
パッケージ
標準パッケージ以外にも、準標準パッケージのgolang.org/x/tools/go
パッケージでも静的解析に関する機能が提供されています。
結構な数のパッケージがありますが、特に以下の2つは便利だと思いますので、ここでは挙げておきます。
-
golang.org/x/tools/go/ast/astutil
パッケージ-
AST
に関する便利な処理を提供するパッケージ -
astutil.PathEnclosingInterval
で指定したソースコード上の位置のノードを取得したりできます
-
-
golang.org/x/tools/go/types/typeutil
パッケージ-
go/types
パッケージを補助する機能を提供するパッケージ -
types.type
は単純にマップのキーにできないので、typeutil.Map
などを使う
-
-
golang.org/x/tools/go/loader
パッケージ- パースから型チェックまでやってくれる機能を提供するパッケージ
ソースコードを自動生成したい
ソースコードを自動生成するには、おそらくだいたい以下の手順で処理をするでしょう。
- 元になるソースコードを読み込む
- ソースコード上からコメントなどを取得し、そこに書いてある情報を取得
- ソースコードを生成する
- 生成したソースコードをフォーマットを書ける
1.〜2.に関しては、入力となる情報をソースコードから取得する話です。もしかすると、ここはソースコードを解析せずに、引数としてパッケージ名や型名を直接もらってくるかもしれませんね。
ソースコードを読み込むには、ソースコードをパースする必要があるので、以下の記事が参考になるでしょう。
また、取得したASTを解析するのは、以下の記事が参考になります。
コメントに書かれた文言を元に自動生成を行う場合は、以下の記事も参考になります。
さて、ソースコードを生成するには、ASTを直接いじるか、text/template
パッケージなどを使って文字列としてソースコードを生成する方法があるでしょう。
ASTをいじる方法としては、以下の記事が参考になるでしょう。
また、text/template
を使ってソースコードを生成する方法は、go test
やGAE/Goで行っている方法ですが、GAE/Goに関しては以下の記事が参考になるでしょう。
生成したソースコードをフォーマットする必要がありますが、上述の「抽象構文木(AST)をいじってフォーマットをかける」という記事に書いてあります。
リファクタリングツールを作る
go
パッケージを使うと、簡単にGoのソースコードを静的解析し、リファクタリングを行うツールを作ることができます。
たとえば、gorename
は識別子をリネームする際に使うツールですが、内部ではgo
パッケージが使われています。
gorename
はライブラリとしても使うことができ、リファクタリングツールを作る上で、gorename
の構成は非常に参考になります。以下の記事では、gorename
とその構成について書いています。
リファクタリングツールを作る際には、上述したASTを取得したり、トラバースしたりすることはもちろんですが、型情報に関する機能が必要になってきます。go/types
は型チェックを行うパッケージで、上述のgorename
でも使われています。
go/types
パッケージについては、以下の記事で解説しています。
スコープに関する記事は、案外知らなかったGoのスコープについても知ることができるかもしれません。
なお、golang.org/x/tools
パッケージ以下にも多くのリファクタリングツールが存在するので、参考にすると良いでしょう。
処理系を作る
go
パッケージを使うと、字句解析や構文解析の手間を省いて、GoのASTを流用したりすることで、Goと文法が近いミニ言語を簡単に作ることができます。
たとえば、式の評価を行うプログラムなど、案外作るのが面倒な処理もgo/ast
パッケージやgo/constant
パッケージ、go/types
パッケージなどを使うと簡単に作ることができます。
以下の記事を参考にして、自作の処理系などが作れるでしょう。
おわりに
この記事では、今まで書いたgo
パッケージの記事について、何がどう役に立つのかまとめてみました。
今まで「静的解析なんて難しそう」と思っていた方が、go
パッケージを知ることで「よし、何か作ってみるか」という気持ちになってくれれば良いなと思います。