みちくさを楽しむ

IT関係の雑多な内容の日記

Goのコードから「Goのコードを生成するためのAstのコード」を自動生成できるツールを作った。

 

概要

Goのコードから「Goのコードを生成するためのAstのコード」を生成するツールを作りました。以下のサイトから利用できます。何を言っているかよく分からないと思うので、以下で詳しく機能を説明します。

 

goast-generator.net

機能紹介

例として、次のGoのコードのAstを生成してみます。

package main
 
import (
"fmt"
)
 
func main() {
fmt.Println("Hello, playground")
}

 

冒頭で紹介したページに飛んで、該当のコードを入力して、左上にあるRunを押します。

goastgeneratorの使い方の例

すると、以下の結果が出力されました。


&ast.File{ Name: ast.NewIdent("main"), Decls: []ast.Decl{ &ast.GenDecl{ Tok: token.IMPORT, Specs: []ast.Spec{ &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote("fmt"), }, }, }, }, &ast.FuncDecl{ Name: ast.NewIdent("main"), Type: &ast.FuncType{ Params: &ast.FieldList{ List: []*ast.Field{ }, }, Results: &ast.FieldList{ List: []*ast.Field{ }, }, }, Body: &ast.BlockStmt{ List: []ast.Stmt{ &ast.ExprStmt{ X: &ast.CallExpr{ Fun: &ast.SelectorExpr{ X: ast.NewIdent("fmt"), Sel: ast.NewIdent("Println"), }, Args: []ast.Expr{ &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote("Hello, playground"), }, }, }, }, }, }, }, }, }

 

逆にこれを、復元してみます。次のようなコードで復元できます。

package main
 
import "fmt"
 
func main() {
fmt.Println("Hello, 世界")
fset := token.NewFileSet()
node :=  (上記の出力結果(&ast.Fileからはじまるもの)をコピー)
printer.Fprint(os.Stdout,fset,node)

上のコードを実行してみると下の出力のようになり、正しくAstが作成できていることがわかります。

(実際にgo-playgroudで実行するにはGo Playground - The Go Programming Language )

package main

import "fmt"

func main() {
    fmt.Println("Hello, playground")
}

また、以下のtentenさんの記事で手入力で作ろうとしているコードを入力してみました。

qiita.com

入力したコード

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}

生成したコード

&ast.File{
    Name: ast.NewIdent("main"),
    Decls: []ast.Decl{
        &ast.GenDecl{
            Tok: token.IMPORT,
            Specs: []ast.Spec{
                &ast.ImportSpec{
                    Path: &ast.BasicLit{
                         Kind: token.STRING,
                         Value: strconv.Quote("fmt"),
                    },
                },
            },
        },
        &ast.FuncDecl{
            Name: ast.NewIdent("main"),
            Type: &ast.FuncType{
                Params: &ast.FieldList{
                    List: []*ast.Field{
                    },
                },
                Results: &ast.FieldList{
                    List: []*ast.Field{
                    },
                },
            },
            Body: &ast.BlockStmt{
                List: []ast.Stmt{
                    &ast.ExprStmt{
                        X: &ast.CallExpr{
                            Fun: &ast.SelectorExpr{
                                X: ast.NewIdent("fmt"),
                                Sel: ast.NewIdent("Println"),
                            },
                            Args: []ast.Expr{
                                &ast.BasicLit{
                                     Kind: token.STRING,
                                     Value: strconv.Quote("Hello, 世界"),
                                },
                            },
                        },
                    },
                },
            },
        },
    },
}

tentenさんの記事のコードと同様なコードが出力されることが確認できました。

 

これを活用することで、goの静的解析などでAstを特定のコードに自動的に書き換えたいときなどに使えるのではないかと思っています。

 

作成した経緯

最近、Goの静的解析について興味を持ち、勉強していく中で、GoのAstを使った操作でコードの書き換えを自動化してみたい場面がありました。

 

そこで、Go言語の任意のAstを作るにはどうしたらよいのか調べてみましたが、さきほどの記事のように、手動で作る方法しか見つかりませんでした。

qiita.com


それならば、自分で作ってみようと思って作ったのが上記のサイトです。

入力画面に、Go Playgroundのソースコード (https://github.com/golang/playground)

を流用させていただくことで、ほとんど、サーバーサイドのロジックの作成だけですみました。

 

GoのAstを使った操作をしてみたいという方は、ぜひ使ってみてください!

 

ただし、テストが十分ではないため、対応していない機能やエラーなどがあるかもしれませんので、ご使用の際は自己責任でお願いします。

実装の際は、以下の2つのサイトに大変お世話になりました。

ありがとうございました。

https://yuroyoro.github.io/goast-viewer/

zenn.dev