Go言語からGoのコードを生成する

Go言語からGoのコードを生成する

author potpro(ぼとぷろ)
2021/08/24

Go言語からGoのコードを生成する

現在夏休みを満喫しております。私です。

このままだとプログラミングの腕も落ちてしまう!と思ったので、勉強がてらコードジェネレータの勉強として、go-isevenというものを数時間くらいで作りました。

potproject/go-iseven

どういうライブラリかと言うと、最近話題になったsamuelmarina/is-evenのGo移植です。つまり、見てもらったらわかるんですがこんなコード。

https://github.com/potproject/go-iseven/blob/main/iseven.go

ifを使用してevenかどうかを判別する、わかる人だと絶対やらないカーゴカルトなコードです。

iseven

作った後になんでこんなもん移植したんだ・・・となりました。また、こんなコードなのに(だから?)ビルドに凄い時間が掛かってストレスが溜まります。もう絶対にやりません。

ただ、そこに関しては割とどうでもいいです。今回主軸となるものは、このコードを生成する時に用いた技術について。

今回はそこまで詳しいことは書いてません。大体個人用メモです。

Go言語のコードジェネレータ

このコードは、無駄に20008行存在します。こんなコードを手作業で書いているのであれば、非常に大変というか絶対やらないと思います。

そのため、今回はGoのコードからGoのコードを生成する、コードジェネレータを使用しています。

使っているのは、dave/jenniferというライブラリ。

コードジェネレータのライブラリで、メソッドチェーンを使って実際のGoのコードを生成することが可能です。

package main

import (
    "fmt"

    . "github.com/dave/jennifer/jen"
)

func main() {
    f := NewFile("main")
    f.Func().Id("main").Params().Block(
        Qual("fmt", "Println").Call(Lit("Hello, world")),
    )
    fmt.Printf("%#v", f)
}
// Output:

package main

import "fmt"

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

ライブラリ等を使わない場合は、自分でいろいろな制御をして書きこまなくてはなりません。まあ まあ見通しも良くて書きやすいので、非常に便利です。

https://github.com/potproject/go-iseven/blob/main/gen/gen.go

このライブラリを用いることで、大体200行くらいで生成できていますね。

所感

Goのコード、大きくなってくると割とコード自体が長くなってきたりするので、こういったものを使いこなせれば、効率化の助けにもなるかなと思います。

特に、最近は99designs/gqlgenだったり、コード生成をしているライブラリを使うことがあったので、どうやって生成しているか勉強したかったという感じでした。

やっていることは普通に文字を生成してファイルに書き込んでいるだけなんですね、と。

また、より高度なものはASTを使って生成したりする技術があり、これを使う場合は厳密に作れそうです。この記事が参考になります。

Goの抽象構文木(AST)を手入力してHello, Worldを作る #golang

が、このレベルなら学習コストも多分かなり高いのでここまではいいかな・・・。