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言語

GoフレームワークGinでミドルウェアを使ってログインAPIを実装

1 はじめに2 ログインAPIの作成3 ログインのセッション管理4 ミドルウェア4.1 gin.Default()4.2 Logger4.3 Recovery4.4 sessions5 独自ミドルウェ ...

laravel logo

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

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

rails

関連するモデルのレコードを一緒に作成する方法

1 はじめに1.1 前提条件2 実装2.1 モデルの作成2.2 コントローラの作成2.3 Viewの作成3 さいごに はじめに フォームからレコードを作成する際に、関連するモデルのレコードを一緒に作成 ...

Go言語

goのcontextについて

1 はじめに2 contextとは2.1 contextの定義3 contextでキャンセルシグナルを伝達する3.1 cancel関数でキャンセルシグナルを伝達する3.2 キャンセルシグナルの伝達範囲 ...

Go言語

mutexを使ってGoで排他処理をする

1 はじめに1.1 mutexとは2 mutexを使った排他制御2.1 失敗するケース2.2 mutexを使って排他制御した場合2.3 構造体へmutexを埋め込む3 RWMutexを使う4 さいごに ...

フォロー

blog-page_side_responsive

2024年5月
 1234
567891011
12131415161718
19202122232425
262728293031  

アプリ情報

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