🍣

Claude Projectsを活用して、Deno / TypeScriptで言語処理系を書いてみた話

2024/12/04に公開

背景

  • Claude Projectsをもっと活用できるようになりたい
  • Deno / TypeScriptで何か作りたい

という理由で、なんとなく「プログラミング言語的なものをClaudeと一緒に作ってみるか」と思いつきました。作業ログや学び的なものをせっかくなので残しておきます。

作成したのは以下:
https://github.com/danimal141/poor-go

基本的に私は指示役の男としてClaudeにコードを書かせることに徹しています。また、また今回ChatGPT、Cursorなどその他の生成AI系サービスは一切使っておりません。

開発方針

今回、言語をリッチに作り込むことが目的ではないため、比較的言語仕様がシンプルなGo言語を極限まで劣化させた言語を作ることとします。名前はpoor-go と名付けました。コマンドや拡張子は pgoとします。

最小ゴールとして、以下の hello-world.pgo がコンパイルでき、実行すると hello world が出力される状態までを目指します。

package main

func main() {
    print("hello world")
}

作るものの全体像としては下図のようなものです。今回はLLVM IRを吐き出してコンパイルする形式としました。Lexer -> Parser -> Semantic -> LLVM codegen -> Compilerの流れで作っていきます。

開発上の工夫点

Claudeとのコンテキスト共有

目的に沿ったコードを吐いてもらう上で、Claudeとコンテキストや最新状況をすり合わせることは非常に重要です。

今回は以下のリポジトリを参考に generate_project_summary.py を開発リポジトリ直下に置いて、常に最新の開発状況サマリをtextで出力してClaudeに食わせる戦術を取りました。

https://github.com/Olemi-llm-apprentice/generate-project-summary

Claude上のProjectの雰囲気はこんな感じです:

そして、目的や方針を明確に伝えることも重要です。今回はまず、

  • Deno / TypeScriptを使って実装したい
    • TSのコンパイルオプションも deno.json記載のルールに従ってほしい
  • 上述のようなHello Worldを出力するところまで進みたい
    • テストも書いて、Passしながら着実に前進していきたい

という方針を伝えて、言語仕様的なデザインドキュメントを書かせました (執筆時点でこの仕様はまだまだ満たせていないのですが...)

https://github.com/danimal141/poor-go/blob/main/design.md

あとはこの仕様ドキュメントと最新のコードを python generate_project_summary.py でテキスト化しながら常にClaude Projectsの情報として食わせ、「今はLexerのテストまで通りました。Parserを実装していきましょう」のようにステップごとに区切りながら指示していきました。

余談ですが、Nodeをずっと使っていた身としては、Denoはめちゃめちゃ体験が良いですね。環境構築で、あれも入れてこれも入れて、みたいなことをやらなくてもDenoでほしいものが大体完結するので。TypeScriptを動かすまでのリードタイムが圧倒的に短く感じました。

難所と乗り越え方

Claudeの良いところは「Lexerを書いて!テストケースはhello-world.pgoに記載のようなコードを字句解析できることを確認して」ぐらいの抽象度のリクエストで、ほぼ一発目で動くコードを出してくれるところです (まあTSのコンパイルエラーとかはちらほら目立ちましたが、エラー情報をまんまメッセージしたら大体解決してくれます)。

とはいえ、Parserなどをいきなり完成形まで作らせようとすると、なかなかテストが通らないような状況が続き、エラーを共有しても延々問題が発生し続ける状態に陥ることも多かったです。いわゆる「ハマっている」状態ですね。

そういった時は、まさに人間と同じで「いったん冷静になりましょう。改めて今何をゴールとしていて、現状はどういう問題がありますか」という問いかけをするようにしていました。

  • 「テストケースって本当にこれでいいんでしたっけ」
  • 「いきなり盛大なことをやろうとしすぎじゃないですか」
  • 「まずは最小のテストケースを用意してPassするところを目指しません?」

みたいに冷静にさせていくことが意外と有用だったなと思います。

  • 小さいゴールを用意して突破させる -> 達成したゴールをProject Instructionに追加して次のゴールを目指させる

みたいな流れを作ってスモールWINを積み重ねるイメージですね。

また、「問題を特定するために、ログを仕込んでみましょう」と提案して、ログを食わせると「ああ、わかりました!」と突破できることが多かったです。若手エンジニアみたいな反応でかわいいですね。

その他

あとは出力したコードを解説させると、コードコメントを入れてくれたり、サマリを教えてくれたり、細かい関数レベルで実装の解説までしてくれるので普通に勉強にもなります。

特にLLVM IRの生成などは自分としてもあまり理解していなかったので面白かったです。言語処理系の全体像を理解する上でも有益だったと思います。

まとめ

結局、Claudeとソフトウェアを作る上で

  • 今目指しているゴールを明確にしてあげる
  • 必要なコンテキストをアップデート含めてこまめにすり合わせる
  • 今起きている問題の理解を疎かにしない

が重要だったと思います。人間と同じですね。普段エンジニアリングマネージャーとしてやっていることとあまり変わらんなと思いました。

生成AI時代、仕事でよりレバレッジを効かせる上でも目標設定力、課題発見力が極めて重要になりそうですね。

Discussion