c-lesson 第一回「簡易PostScriptインタプリタを作ろう」を終えた

TL;DR

  • c-lesson でC言語の勉強をしている
  • 第一回の PostScript インタプリタを作るを完走して、それがめちゃくちゃ良い勉強になった
  • parser や evaluator を実装し、さらにはC言語のスタックフレームを使わず継続まで実装してちゃんと理解できる良コンテンツ

ずいぶん前に始めたけど途中で止まっていた c-lesson を沖縄で再開していて、ついに第一回が終わった。 元々低レイヤーの勉強をしたいなと思っていたところに @karino2 氏にC言語の勉強とかどうですかと救いの手を差し伸べてもらった形で始めたが、とてもよくできていてめちゃくちゃ勉強になった。 C言語とかほぼ書いたことないような状態から始めたが、基本的なところを押さえつつだいぶ自由が利くようになってきたのを実感できて嬉しい。

自分が実装した PostScript のインプタプリタ実装は ここ にある。 PostScript は parser が比較的容易に書けるので、 メインは eval.c になっていて eval() で入力文字列を parse しながら evaluate するコアの部分になっている。 最終的にはテスト込みで3000行に満たないくらいの実装だが、PostScript の言語使用のシンプルさも相まって完走すれば重要な部分は隅から隅まで実装できるようになっていて、色々な学びがあって実に良かった。

この c-lesson は第二回、第三回と続くものだが、第一回だけでも相当の内容を誇るものなのでここで一旦感想やらを書いておく。

c-lesson とは

そもそも c-lesson とは何かというと、@karino2 氏が暇つぶしでC言語を教えてくれるという企画とのこと。 内容の説明に関しては .md ファイルを見るか、もしくは この Web ページ を読んでみればどんな感じか分かる。

読んでみれば分かる、とか書いたがまずその分量が凄い。ビビる。 しかもその内容はかなりしっかりしていて、C言語の入門書とかを読んだりはしたけどそれなりの規模のちゃんとしたプログラムを書いたことがない、という(これを始める前の自分のような)人がやれば感動すること請け合いである。 これを暇つぶしでやるというのだから、驚くべき暇さ(褒め言葉)である。

詳しい人にとっては今更C言語かとか遠い昔に通った道だとかになるんだろうけど、自分のように大学・大学院時代にプログラミングしてこなかったけど仕事をするようになって Python とかはなんとなく書いていますという人にとっては、このような「ちゃんと理解できる」教材は有り難いことこの上ない。

進め方としては、レポジトリを fork して各章毎に branch を切り、そこにコードを push してレビューしてもらうという形式になっている。 ある程度の素養がある人なら独力で進めることもできるかもしれないが、そうでなくても slack とかでコミュニケーションを取りながらやってくれるので基本的なことが分からなくても教えてもらえる。 特に、こちら側の理解度や理解の仕方をうまく見極めて助言してくれるのでとても分かりやすい(個人の感想です)。

やり方がやり方なので大人数にスケールするものではないけど、興味がある人は氏に話をしてみるといいでしょう。 ただ、(自分のようにC言語詳しくない人が)片手間にやって完走できるようなヤワなものではないかもしれない。 20190416時点でレポジトリを fork している 7 人中 3 人が第一回を完走したという話だが、正直 3 人完走してるのが驚きのレベルである(他 2 人の方はどんな人か知らないですが)。 実際自分も仕事しているときに一旦中断してしまってし、無事に完走できてなんというか本当に無職って最高だなって思います。

c-lesson を始めるのに必要な準備

C言語やったことないけどやってみたい、そして c-lesson を始めるのにどれくらいの準備が必要なのか、ということに関して自分の場合を書いてみる。

自分は 5 年前に社会人になって、Python をそれなりに書くようになったのが 3.5 年前くらいからで、まあ Python しか書けないと言っていい。 C言語に関しては大体以下のような経験だった。

  • 会社のイベントで Ruby インタプリタをいじるときにノリだけでC言語を書いた
    楽しかったけどこの時は全然分かってなかった。周りの人々が進んだ内容をやっている中、ポアソン分布の確率密度関数の実装や!とかやってごまかしていた。すみませんでした。
  • ふつうのLinuxプログラミング 第2版 を読んだ
    正確にはこの本の途中までを読んで登場するコードの写経や章末問題などをやっていた。その後中断していたが、c-lesson の 7,8 章あたりの段階で再開して全部読み切った。C言語の習得自体が目的の本ではないが、読みながら色々調べて良い勉強になった。
  • C言語本格入門 を読んだ
    なんかC言語の本を一冊読んでおこうと思ってとりあえず読んで写経とかした。説明とか易しいけどあまり誤魔化さずに丁寧に書かれている本だった。そんなに自分には響かなかったけど、基本的なことを認識するという意味では役に立った。

ポインタとか配列とかのC言語での事情は頭では把握していたが、コードは写経プラスαくらいしか書いてなかったので全然C言語っぽい書き方ができない、みたいな状態だった。 なのでポインタのポインタとか言われても概念的には分かるけど、それを使ってコードをスラスラ書くのはできないし、デバッガとかも触ったことあるけどそこまで使いこなせてない、とかだった。 こういう状態でも c-lesson は完走できるかなと思う(前述のようにタフなのでガッツは必要)。 ただしこういう状態でかつ他の言語は全く未経験、とかだとさすがに難しいかもしれない。 git もいくつかの基本的な操作(branch を使って色々作業できるくらい)は知っておいた方がいいけど、最低限の説明は書いてあるし分からなければ聞けば教えてくれるのでそんなに気にしなくていいかな。

その他には、会社のイベントで Ruby インタプリタを Ruby で書くというのもやっていた。 講師にだいぶ準備してもらった上でのイベントだったけど、楽しかったのでちゃんと自分で言語処理系作ってみたいというモチベーションも良い感じに高まっていた。 こういうモチベーションがあると続けやすそう。

PostScript インタプリタの実装

ここはぜひ Web ページの説明とか自分のレポジトリ(のそれぞれの branch)とかを見てどんな感じか見てみてほしい(fork しているレポジトリは こちら)。 自分は結構真面目に commit を積んでいるので、Web ページの説明と対応させていけば何をどんな感じでやってきたかを追えると思う。

まず parser を作るところから始まる。 最初は全然C言語書けないところからスタートしたので、最初の方に書いたコードは今の自分が見ると「この人はC言語書いたことがないんだな」というのがよく分かる感じだ。 UnitTest の説明もちゃんとしてくれてテスト駆動的に進めるような構成になっていたり、コラムで parser と tokenizer ってどう区別すべきみたいな話も載ってたりしてためになる。

そして PostScript における言語仕様を少しずつ理解しながらスタック実装に着手して、5章で最小限のインタプリタが完成する。 ここでは足し算だけだが、PostScript の比較的シンプルな言語仕様も相まって、parse して eval するという流れを自分でちゃんと実装して動かせるのは感動ポイントだ。

その後はハッシュテーブルで辞書を実装してリテラルネームを扱えるようにして、関数ポインタを使ってプリミティブも辞書に登録する。 自分の中で扱えるものが増えていってそれに伴い徐々に機能が拡充されていくのは、なかなかに楽しい。

次がまあまあ山場の実行可能配列の実装で、バイトコードにコンパイルしてそれを eval するという方針になっている。 最初にやると色々バグりがちで自分がいまインタプリタ的な処理をしているのかVM的な処理をしているのかも混乱しがち。 ここでちゃんとデバッグしながらどう動いているか確認して直していくことで、何がしたくてどう書くべきなのかがはっきりとしてきてコードの全体像を掴めるようになってくる。 この辺りの実装はバグりがちだけど、ちゃんと根気強く追って修正すれば道は開ける、というのを体験できるのも良いところではないかと思う。

そして PostScript 的な操作を追加してインタプリタが扱える範囲を一気に拡充する。 while のような PostScript にはない独自拡張をしてそれを使ってスクリプトを書く、とかいうのも自分が作ったったぜって感じがして良い。 PostScript の FizzBuzz とかを動かせるようになれば一通りの実装は完了で、感慨も一入だ。 結果は派手じゃないかもしれないが、ちゃんと自分で実装してちゃんと自分で理解した結果が結実し、第一回のクライマックスはやはりここだろう。 関係ないけど、スタックマシンでこういうスクリプトを書くのってパズルみたいですよね。

これで終わりでもいいがここで終わらないのが作者のこだわりが感じられるところである。 処理がネストしている部分はC言語の再帰呼び出しを使っていてC言語のスタックフレームに頼っている。 それでは隅々まで理解したとは言えんだろうといって、継続も実装するのだと続く。

実に熱い展開だ。

そして if や repeat (ループにはローカル変数の実装も必要)などといった制御構造もプリミティブを使ってコンパイルして実行するように変更して、ついにゴールである。 ここまでやると、インタプリタ的には機能が拡充されたわけではないのだが隅々まで「ちゃんと理解できた」という実感が凄いのでとても気分が良い。 ちゃんと理解できるってめちゃくちゃ尊くないですか(尊いです)。

全部やり終えて改めて内容を見返して、これだけのものを提供してくれた作者に尊敬の念を禁じ得ないですね。 スゴい。ほんとスゴい。いやマジで。語彙力なくなっちゃったよ。

ということで第二回以降も楽しみに頑張っていこう。

まとめ

c-lesson の第一回を完走した。 C言語で PostScript インタプリタを実装するという話でそれだけ聞くと「マジ?」という感じだが、とても勉強になるので興味ある人はぜひ見てみると良いのでは。 少なくとも自分にはとても良い勉強になったので、第二回以降も頑張っていく。




Written on April 16, 2019