Goone(剛腕)ってヘルパーツールを書いた

Goone(剛腕)ってツールを書いた。 これは引数で与えられたGoのソースファイルが依存している同一パッケージ内の全てのソースコードを集めて1つのファイルを生成してくれる。

競プロ(AtCoder)に挑戦してみて、スニペットなりライブラリなりを準備しないと厳しいなぁと思った。 AC 取るだけの書き捨てコードと割り切って大きめの多重配列を作ってしまうとか性格が合わなかったりとコードが大きくなりがちなのでスピードが追いつかない。 なのでライブラリを書いた後にsonictemplateとしてコピペして使っている。

スニペット作りに勤しんでいると、切り出して一部の型を{{_input_:item_type}}などと置換したりとミスがおきないか心配になってくる。 また kruskal の実装で union_find を使うけど場合によっては union_find だけが欲しくなったりする。 これをコピーでやっていると管理が非常に大変になって union_find に便利なメソッド追加したけど kruskal の方には追加してないから応用で両方使いたい時には結局自力で実装しなきゃみたいな事態になる。 そもそもあまり楽しくない生産性の低い作業を続けることになり辛くなってくる。

そんなわけで競プロ用にパッケージのソースコードと関連した依存ソースを抜き出して1つにまとめるツールが欲しくなった。これがあれば、単一ライブラリの中の必要な部分を抜き出してsonictemplateを作ったり、なんなら :r!goone ... などと貼り付けることができる。

なんか力技な解決策なのと Go で1つ(one)にまとめるのでGooneって名前にした。

使い方

どういうことか下のような具体例で説明する。ディレクトリが下のようになっているパッケージがあるとする。

1
2
3
4
5
6
7
8
$ tree ./sample/
./sample/
|-- a.go
|-- b.go
|-- c.go
`-- main.go

0 directories, 4 files

ファイル内容は次みたいになっている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ ls ./sample/| xargs -I{} bash -c 'cat ./sample/{}; echo ---'
package main

import "fmt"

func a(v string) {
        fmt.Println(b(v))
}
---
package main

import "strings"

func b(v string) string {
        return strings.Repeat(v, 2)
}
---
package main

import "strings"

func c(v string) string {
        return "not packed"
}
---
package main

func main() {
        a("hoge")
}
---
$ go run ./sample/
hogehoge

定義の依存関係を書くと下になる。

main.go -> a.go -> b.go
c.go

c.goはどこにも関わっていない。 Goone を使うと必要な範囲を1つにまとめられる。 下のように a.go のみに依存する定義を抜き出すことができる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ ./bin/goone pack ./sample/a.go --package main

{{_cursor_}}
// package: main
// packed src of [sample/a.go sample/b.go] with goone.

func a(v string) {
        fmt.Println(b(v))
}
func b(v string) string {
        return strings.Repeat(v, 2)
}

{{_cursor_}} はsonictemplateを意識している。 邪魔なら –template(-t) オプションでGoのテンプレートを与えることができるので下みたいに自作のテンプレートを使って差し替える。 またデフォルトで $HOME/.config/goone/config.toml に設定ファイルが置かれそこでもテンプレートファイルを指定できる。

テンプレートをしたみたいに準備して使う。 使える値はここ

1
2
// from {{ .Package }}
{{ .Decls }}
1
$ ./bin/goone pack ./sample/a.go --package main -t ./template.go

参考資料

作ってみて

Goを仕事で使わなくなってアルゴリズムばかりだったので、普通のツールを書く感覚が少しもどってよかった。 ast, token, format を使ってコード生成をやってみたけど難しくない。 リファクタツールとか作りためていくのは楽しいだろうと思った。なにかネタを探して続けていきたい。

使わなかったけど下のパッケージも気になってる。

comments powered by Disqus