編集可能
2019年5月20日

Goコンパイラをゼロから作って147日でセルフホストを達成するまで

進捗を毎日つぶやいてたのでまとめました
23
DQNEO @DQNEO

米国版メルカリの開発をしています。趣味でGoコンパイラを2つ自作して、アメリカGoherConで発表しました。いまはアセンブラ自作中 / Software engineer working for @mercari_app. I made Go compilers from scratch twice.

github.com/DQNEO/babygo

DQNEO @DQNEO

GolanによるGo Compiler 作り始めてみました。150行くらい書いたら足し算が動くようになった。github.com/DQNEO/mgc/blob… 設計は8ccをほぼ踏襲しつつ、9ccの知見を一部取り込んでいます。

2018-10-07 12:19:33
DQNEO @DQNEO

GolangはCと比べて言語仕様にまつわる歴史的紆余曲折が圧倒的に少ない分、その分パーサー実装は楽な気がする。メモリのアロケーション(stack/heapをコンパイラが判定)とかGCは難易度高そうだけど。あと型推論、interfaceあたりもむずそう。

2018-10-07 12:35:04
DQNEO @DQNEO

逆に言うとinterface,型推論,GCなしならC言語より圧倒的に簡単そう。

2018-10-07 12:35:46
DQNEO @DQNEO

* 簡易tokenizerが完成 * 足し算と引き算と掛け算が動いた 自分でもびっくりするくらい進捗したw github.com/DQNEO/mgc/comm…

2018-10-07 16:10:25
DQNEO @DQNEO

早くも関数呼び出しができるようになった! そして hello worldが動いた!! たった1日でここまでいけるとは、自分でもびっくりw pic.twitter.com/o45cBydtHn

2018-10-08 01:10:58
拡大
DQNEO @DQNEO

8ccをCとGoで2回も写経して、それに5ヶ月もかかって、途中何度か心折れそうになったけど、その知識が今めっちゃ役に立ってる。

2018-10-08 01:13:39
DQNEO @DQNEO

hello worldがファイルまるごとコンパイルできるようになったー Tour of Goに2回挫折した私ですが、Goコンパイラの開発はできているという不思議。 pic.twitter.com/whWcFVPLYO

2018-10-08 23:03:25
拡大
DQNEO @DQNEO

putsやprintfがimportなしで動いてるのは、libcのやつを呼んでいるからですね。(チート感

2018-10-08 23:05:20
DQNEO @DQNEO

自作Goコンパイラで hello world 完全版うごいたー pic.twitter.com/hUDcBNWbCS

2018-10-11 00:04:53
拡大
DQNEO @DQNEO

演算子の優先順位を考慮した足し算・引き算・掛け算ができるようになったー pic.twitter.com/nw2uh12LC8

2018-10-12 23:28:33
拡大
DQNEO @DQNEO

ローカル変数の宣言、代入、参照ができるようになったー github.com/DQNEO/mgc/comm…

2018-10-13 11:52:11
DQNEO @DQNEO

設計の参考にするためにGo本家のコンパイラのソースを読んでみたんだけど、意外と読めた。特に ASTの構造体の設計がめちゃきれいだった。 github.com/golang/go/blob…

2018-10-13 22:11:01
DQNEO @DQNEO

Go本体を読んでて気づいたんだけど、Goって代入文は式じゃないのね。 a = b = 3 はコンパイルエラーになった。 そして、宣言文( var i =1 ) も式じゃないっぽい。 処理系本体を読んで言語仕様を知るの、普通の学び方とは逆な気がするけど、こういう学び方もアリだな。

2018-10-13 22:19:52
DQNEO @DQNEO

Go文法の改行とセミコロンの仕様を完全に理解した( = 実装した) github.com/DQNEO/mgc/comm…

2018-10-14 00:35:50
DQNEO @DQNEO

ローカル変数とグローバル変数の宣言、代入、参照を実装した。(型はintのみ) あと大胆に内部設計をリファクタリングして、8cc/9cc風の巨大構造体を使うのをやめて、小さい構造体をたくさん定義してみた。この方がIDE補完効くのと、Go本家の設計に寄せたかったという理由。 github.com/DQNEO/minigo/c…

2018-10-14 22:38:52
DQNEO @DQNEO

* インラインコメント、ブロックコメント機能を実装した。 * 変数スコープを多段入れ子になるようにちゃんと実装した。 * 内部的に使ってたスペーストークンを廃止。Goの文法はセミコロンと改行、コメントとスペースの関係が少しややこしいので、いったんシンプルにした github.com/DQNEO/minigo/c…

2018-10-15 20:42:38
DQNEO @DQNEO

コンパイラ作り始めて1週間で、こんなコードがコンパイルできるようになった。それなりにプログラミング言語っぽく見えるw github.com/DQNEO/minigo/b…

2018-10-15 20:52:06
DQNEO @DQNEO

関数パラメータが動いた〜 睡眠時間が... pic.twitter.com/Y80BidOt3l

2018-10-16 03:08:59
拡大
DQNEO @DQNEO

自作コンパイラ開発は、初期段階はイテレーティブ(字句解析・構文解析・コード生成をそれぞれ少しずついじって育てていくアプローチ)にやる方がいいのは間違いないけど、ある段階まできたらウォーターフォルに切り替えた方が効率がいいと思う。字句解析器だけ先に完成させてしまうみたいな。

2018-10-18 20:38:18
DQNEO @DQNEO

あと、似た機能は同時にまとめて開発した方がよい。比較演算==を実装するなら!=も一緒にやる。大小比較(<=, <)も一緒にやる。フロー制御をやるならfor,while,if,elseは一緒にやる。

2018-10-18 20:45:18
DQNEO @DQNEO

構文解析で宣言のパーズをやるときは、変数・定数・型のパーズを一気にやる。そのときはパーズだけ書いてemitterはやらない、みたいな。 そうすれば脳のモード切替をしなくてすむので圧倒的にスピードが速い。

2018-10-18 20:46:58
DQNEO @DQNEO

とにかくコンパイラというのは複雑なソフトウェアなので、いかに脳みその負担を減らすかが重要。コンパイラ内部の動きを可視化するのも重要。 ロガーを仕込むとかエラーメッセージをわかりやすくするとか、パーサの入れ子になったスタックトレースをログ表示のインデントで可視化するとか。

2018-10-18 20:52:16
DQNEO @DQNEO

そう考えて可視化をやってみたら、めちゃくちゃ生産性があがった。ログ出力のために多少コードのきれいさを犠牲にしてもよいことにした。可視化の方が圧倒的に重要。 pic.twitter.com/jLxo2EL8nP

2018-10-18 20:57:57
拡大
DQNEO @DQNEO

上の画像のfor文のパースに失敗してエラーになった例。for文を式(Expr)として解析しようとしているが、for文は文(Statement)なので、for文のパースを開始する際の前提が間違っていることがわかる。

2018-10-18 21:04:02
残りを読む(270)

コメント

コメントがまだありません。感想を最初に伝えてみませんか?