ちなみに

火曜日の空は僕を押しつぶした。

地震と眠み

早朝に地震が来てびっくりした。

ドミトリーに泊まっていたので、一斉に地震速報が鳴り出して地獄のようになってみんな起きだした。自分も寝過ごす心配があったのでそのまま起きてしまった。

手荷物検査潜って中に入ってゲートまで行ったら全然違う便の案内をしていて、あれーと思ったらまるまる1時間早く着いていた。完全に寝ぼけている。

虚しいのでラウンジに行ってオレンジジュースを飲みながらぼーっとしていた。仕事しても良かったけど眠くてあんまりやる気にならず。

飛行機でもなんか寝付けずに本を読んでいて、バスでも同様だったので本は読み終わったものの睡眠が足りてない状態。

家に着くまでは気が立っているのかまあまあ元気だったが、家に着いてシャワー浴びたら副交感神経優位になってしまって急に元気がなくなった。

眠すぎて吐き気があったが、普通に平日なので仕事を開始。ミーティングが終わって作業しようとするも眠すぎたのでちょっと昼寝をしてなんとか回復した。

睡眠は大切という話。

出社

今日は出社日で東京に来ています。

夜はオフィスでの懇親会だったので、オフィスなら倒れてもなんとかなるやろと思ってビールを飲んでみたら意外と行けたので助かりました。アレルギーじゃなかった説出てきたので飲めるかもしれない。ただ、飲まない方が健康にいいのはそうなので、出来るだけ飲まないのは継続したい。

明日はいつもよりかは少し遅い飛行機なので寝坊リスクが少なくて安心。

もう寝る

今日はいつも通りに起きて本を読んだりしつつ朝の時間を過ごす。仕事前にきんにくんさんのYouTubeに上がってるウォーミングアップの体操をやってみたらへとへとになってしまい、ソファで寝落ちしてしまった。起きたら10時くらいだったので仕事を開始。

夕方に早めに閉まる店に買い物に行く。醤油、味噌、キムチ、スーパーとそれぞれの店をはしごした。特に味噌が重いので車で行く必要がある。

帰宅して仕事の続きをするなど。

明日は出社日で早いのでもう寝ます。

健康になりたい

最近はいろいろ楽しいことが増えているのだけれど、これを永続的に続けたい。 そうなるとやはり健康がネックになってくるということで、少しだけ健康に興味を持ち始めている。 これまでは妻からセルフネグレクトと言われるくらい健康に興味がなかった。

ひとまず軽い運動や筋トレを始めたり栄養について思いを馳せたりしているが知識がない。 積んでいたこの本を読むときがきたということでゴールデンウィークに読んでいた。

アジャイルというのはちょっとこじつけ感があったけど、健康について科学的な根拠も含めてうんちくが書かれていて完全に理解した気になれました。

健康とは、病気でないとか、弱っていないということではなく、肉体的にも、精神的にも、そして社会的にも、すべてが満たされた状態にあることを言います

WHOのこの定義がなかなかいいなと思ったのでこの精神でやっていきます。いまのところは満たされている気がするので健康です。

Goで特定のパターンのリファクタリングをASTを弄って自動化した

これは日記です。技術記事ではないので読みやすくはないです。

仕事のコードで特定のパターンでちょっと泥臭く書き換える必要のあるリファクタリングが必要になっているのだけれど、単純計算で100ファイル、1000箇所以上の書き換えが必要になっている。 これまでちまちま手作業でいろんな人が片手間で書き換えをやっていたのだけれど、無限に時間がかかりそうだったのでどうにか出来ないかと考えていた。

先行研究として id:hitode909 のASTを使ってリファクタリングするやつが記憶に残っていたのでもうちょっと簡易版で似たようなことをやってみた。

speakerdeck.com

GoのAST周りはあんまり詳しくなくて、社内の静的解析ツールをちょっと弄ったくらいだったのでまずは勉強した。

motemen.github.io

id:motemen さんの Go のための Go を読んでふむふむという感じで理解した気になってとりかかるも、そんなにすぐに身に付くものでもないので ChatGPT (GPT-4) に相談しながら進める。

本格的なツールを作るつもりじゃなくて、リファクタリングを補助したいだけなので以下のような作戦でいった。

  • 大まかな書き換えだけをやる
  • 書き換えた箇所に FIXME コメントを残して確認出来るようにする
  • 書き換えたことによって使わなくなった変数とかは手動で直す

これくらいのことならそんなにがんばらなくても出来そう。

最初は ast.Inspect しか知らなくて書き換えるの大変だなあと思っていたのだけれど、 astutil.Apply というのが使えると知ったのでこちらで進める。 書き換え前の状態と、書き換え後の状態でそれぞれコールバックを渡せるが、今回は書き換え前のみ使う。

astutil.Apply(f, func(c *astutil.Cursor) bool {
    // TODO: ここで対象を見つけて書き換える
}, nil)

コールバックの引数に渡ってくる astutil.CursorReplace とか Deleta を持っているのでこれを使って書き換えていく。

書き換え対象を見つけるにはいま着目しているのがなんの型なのかを調べる必要がる。Cursorから取得できるNodeは ast.Node interface の状態なので型をちまちま見ていく。

n := c.Node()
switch x := n.(type) {
    case *ast.AssignStmt:
        // 代入だったらここ
    case *ast.CallExpr:
        // メソッド呼び出しだったらここ
}

ここからさらに詳細な型にキャストしていく必要がある。例えばメソッド呼び出しだった場合は以下のような感じでレシーバを取得できる。

if sel, ok := x.Fun.(*ast.SelectorExpr); ok {
    if ident, ok := sel.X.(*ast.Ident); ok {
        if ident.Name == "hoge" {
            // hoge.(何かのメソッド) という呼び出し
        }
    }
}

型は素晴らしいのだけれど、こういうちょっとしたことをするにはちょっと面倒。

メソッド呼び出しの場合は引数などの情報を持っているのでこれも使う。

for _, arg := range x.Args {
    // もちろん arg は ast.Node 型なのでここでも型を見極める必要がある
}

元の呼び出しを新しいメソッドの呼び出しに変えるにはASTを手動で組み立てて先述の Replace メソッドを使う。

replaced := &ast.CallExpr{
    Fun: &ast.SelectorExpr{
        X: ast.NewIdent("hoge"),
        Sel: ast.NewIdent("Bar"),
    },
    Args: x.Args,
}
c.Replace(replaced)

これで例えば hoge.Piyo みたいな呼び出しだったのが hoge.Bar に書き換えられる。この例では引数はそのままにしているが、実際にはここも弄ることになったのでちょっと泥臭い感じに。

コメントは別途 ast.File のフィールドとして持っているので、適切なポジションを与えつつ append するだけでよい。ただし parse.ParseFile するときの mode 引数に parser.ParseComments を与えておく必要がある。

f.Comments = append(f.Comments, &ast.CommentGroup{
    List: []*ast.Comment{
        {
            Slash: pos,
            Text:  "// FIXME: 自動で置き換えたので確認してください",
        },
    },
})

append するだけで良いと言ったが実はこれでは駄目で、最後に出力するときに正しい位置に配置するにはポジション順にソートされている必要がある。これにしばらくハマってしまった。

sort.Slice(f.Comments, func(i, j int) bool {
    return f.Comments[i].Pos() < f.Comments[j].Pos()
})

最後にASTをGoのコードとして正しくフォーマットしつつ元のファイルに書き戻すには format.Node を使う。第一引数を os.Stdout にすると標準出力に出せるのでdry runとかを用意して確認用に出力すると良さそうです。

file, err := os.Create(filename)
if err != nil {
    panic(err)
}
defer file.Close()
format.Node(file, fset, f)

ここで少し問題があって、各Nodeが持っているポジションは元のコードの位置なので、例えば、引数の数を減らして書き戻したりすると不要な空行が生まれたりします。 この辺りは雑にやってしまったのでまだうまい解決方法を見つけていなくて課題になっています。 いい方法があったら教えてください。

だいぶ雑ながらも人力で置き換え箇所を見つけてちまちま書き換える必要がなくなってだいぶ楽になりました。 引数の数が可変だったり、残すもの、残さないものがあったりと人力でやるにも機械的にはやりにくいところだったのではかどりそうです。

久しぶりにゲームをした日

www.jp.square-enix.com

ちょっと気になりつつやってなかったが id:yashigani_w さんがやっているのを見て暇だしと手を出してみた。 昨日の夜から初めて今日の夕方くらいに終わったのでちょうと良い感じのボリュームだった。 アドベンチャーゲームくらいが忙しくなくてちょっとした息抜きには良い感じ。 もっとどんでん返しみたいなのを期待していたけど、まあそうだよねって感じのオチだったのでもうちょっとだけ驚きたかった。 でもおもしろいのでやってみてどうぞ。