BackEnd

Goの抽象構文木でコードを解析する

投稿日:2024年5月13日 更新日:

はじめに

Goでアプリケーションを開発していると、 go generate でコードを生成することがよくあると思います。大抵の場合は、コードを解析するのに抽象構文木(Abstract Syntax Tree)が用いられ、そのためにGoでは ast パッケージが提供されています。

最近、コードを自動生成する処理を実装する必要に迫られてきたため、 ast パッケージとそれを使ったコードの解析について調べてみました。

抽象構文木とは

抽象構文木は Abstract Syntax Tree の訳で、プログラムの文法構造を木構造で表したものを指します。

詳細は省きますので、詳しく知りたい方はこちら等をご覧ください

ASTでコードを解析する

サンプルコードを解析する

ASTで解析するためのサンプルコードとして、以下のファイルを用意しました。

まず初めに、このファイルを解析してパッケージ名、インポートしたパッケージ、定義された構造体の名前と変数名をそれぞれ出力してみます。

f, err := parser.ParseFile(fset, "src/book.go", nil, parser.ParseComments) で対象のファイルを ast,File に変換しています。

このコードを実行した際の出力は以下の通りです。

上記の通り、パッケージ名は src 、インポートしているパッケージは fmt 、そして、 Book 構造体が定義されていて、 TitleAuthorPages フィールドがあることがわかります。

このファイルを解析すると第一階層には3つのNodeがあります、それぞれのNodeが表しているものは以下の通りです。

  1. import文
  2. Book構造体
  3. Book構造体のStringメソッド

import文の部分はシンプルなので飛ばして、構造体の部分から見ていきます。

構造体の木構造を確認する

構造体は、 ast.GenDecl の中の ast.StructType で表されています。以下はこの構造体と関連する構造体の一部を抜粋したものです。

BookをFieldListで表すと以下のようになります(最低限のみで簡略化しています)

このように、Namesの中のNameにフィールド名が、Typeの中のNameに型の名前が入っています。

ちなみに、以下のように構造体の定義を type() でまとめると、 ast.GenDecl.Specs の要素が複数になります。

メソッドの木構造を確認する

Bookには (b *Book) String() string メソッドが生えていますが、 ast.StructType には含まれていませんでした。最初に説明した通り、第一階層のBookと別のNodeに含まれており、 ast.FuncDecl の中の ast.FuncType で表されています(ただし、 receiver の情報は ast.FuncDecl に含まれています)

以下はこの構造体と関連する構造体の一部を抜粋したものです。

StringメソッドをFuncTypeで表すと以下のようになります(最低限のみで簡略化しています)

このようにかなり長くなってしまいましたが、 Recv の中の Field[0].Names.Name にreceiver変数名が、 Field[0].Type.X.Name にreceiverの型の名前が入っています。

メソッド名は Name.Name にあり、返り値の型は Type.Results.List[0].Type.Name にあります。

return文は Body.List[1].Results に表されており、その中の各要素がそれぞれ b.Title+ " by " +b.Author を表しています。

任意の対象を捜索する

ASTから特定の対象を見つけるには、 astutil.Apply メソッドを使ってすべてのNodeを再帰的に探索します。

また、 astutil.CursorReplace メソッドを使うことで、対象のNodeを置き換える事もできます。

ASTをファイルに反映する

最後に、ASTをもとにファイルに出力する方法を紹介します。

もともとの book.goString メソッドを一部書き換えて、 book2.go に出力してみます。

出力されたファイルは以下の通りです。

string メソッドの代わりに string2 メソッドになっています。

さいごに

go generate によるファイル生成を行うために、 ast パッケージを使ったファイルの解析と変更について調べてみました。これを応用することで、定型的なコードを自動生成できるようになれば良いなと思います。

おすすめ書籍

Go言語プログラミングエッセンス エンジニア選書 Go言語 100Tips ありがちなミスを把握し、実装を最適化する impress top gearシリーズ 初めてのGo言語 ―他言語プログラマーのためのイディオマティックGo実践ガイド

blog-page_footer_336




blog-page_footer_336




-BackEnd
-

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

Go言語

goroutineとchannelとContext

1 はじめに2 並行処理と並列処理3 goroutine4 channel4.1 channelからデータ受信4.2 for-range でのデータ受信4.3 複数のchannelを受信4.4 buf ...

Go言語

Go 1.16でのgo getとgo installの変更点

1 はじめに2 go getとgo installの役割2.1 go getの役割2.2 go installの役割3 この変更で何が改善されたのか3.1 go:generateと併用する4 その他の ...

laravel logo

LaravelのFacade(ファサード)とは? 何気なく使用していた裏側の仕組みを解説!

1 はじめに1.1 Facadeを使用しているクラス2 Facadeの仕組み3 Facadeの作成3.1 サンプルコードに必要な実装3.2 Serviceの作成3.3 Facadeクラスの作成3.4 ...

Go言語

sqlcを使ってSQLファイルからGoのコードを生成する

1 はじめに2 sqlcとは2.1 コード解析2.2 サポートする言語とデータベース3 sqlcでコードを生成する3.1 準備3.2 DDLを書く3.3 クエリを書く3.4 コードを生成する3.5 生 ...

RSpecの個人的Tips集〜その1〜

1 はじめに2 テストコードの実行をスキップする3 共通のテストコードを用意する4 外部APIの返却値をスタブにする5 さいごに はじめに みなさん、テストコードは書かれているでしょうか? 私も極力書 ...

フォロー

blog-page_side_responsive

2024年5月
 1234
567891011
12131415161718
19202122232425
262728293031  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。