素朴な自作言語のコンパイラをCに移植した


移植一覧に戻る


20年ぶりくらいにC言語のコードを書きました。 かなり忘れてます。 やっつけなので汚いです。ライフゲームコンパイルが通ったのでヨシ、というレベルのものです。

github.com


移植元

memo88.hatenablog.com

ベースになっているバージョン: tag:49 のあたり
(追記 2021-05-18: tag:58 の修正まで反映しました)

ただ、Ruby 版より主に Java版 を見ながら書き写してました。

メモ

  • ほとんど忘れてたので、すごく初心者っぽいできあがりになってると思います
    • 「あー、そうだそうだ、こうだったわ」ではなく「そういえばこんなだったっけ……思い出せない……」「えっ、これいちいち範囲チェックしないといけないんだっけか」みたいな感じだった。ほぼ忘れてる。
    • といいつつポインタまわりとか「そもそも構造体とは」とかの概念まで全部忘れているわけでもなく、1週間でなんとか動くところまで持っていけた
  • ひとまず C89/C90 あたりの、20年前に書いていたスタイルで書いてみた
    • #pragmra once は使ってみた。便利。
    • 気が向いたら他の C99/C11 な要素も取り入れて書き直すなどしてみたい
  • パース処理を最初に書き始めるところが重い
    • 最初は入力トークン列を全部無視して固定文字列を出力する
    • 次はツリーを手書きで組み立てて print。 ここでもまだ入力トークン列は全部無視。
    • その次にやっと入力をパースしてツリーを組み立てて置き換えていく
    • みたいな流れにすると1ステップを小さくできる
  • コード生成に入る前の JSON も重い
    • ので、コード生成の前に JSON の読み書きだけのテストも追加した。
    • 肩慣らしでこれを最初に持ってきてもよいかも
      • 規模縮小版のパーサにもなっているので
    • フルセットではなく制限のあるJSONなので割と簡単
      • 要素は入れ子の配列と文字列と数のみ
      • トップレベルは必ず配列
      • 文字列のバックスラッシュエスケープはなし
  • やっぱり一番最初にトークナイズからコード生成まで通すところが重くて、 そこができてしまえば後は割と楽
    • ここをどんどん小さなステップに分解して簡単にしていきたい
  • 文字列操作の関数とか個別にテストしてないのでたぶんいろいろおかしい
    • サイズ指定・範囲のチェックはかなり雑
  • free してないのはめんどくさかったからだけど、一応意図的。 低レイヤを知りたい人のためのCコンパイラ作成入門 の「コラム: 9ccにおけるメモリ管理」も参照。
  • 連結リストを動的に確保したりせずに大きめの固定サイズの配列にしてたりして、かなり無駄が多い
    • 気が向いたら連結リストで書き直したい
  • 正規表現を使わなくてもプリミティブな文字列操作だけでなんとかなると分かった
    • 気が向いたらこれを組み込んだりしてみたい
  • コード生成時にその場ですぐ printf で出力する方式にしたら case の処理がやりにくかったので、生成されるコードを変更して簡単に書けるようにした
  • 今回は疲れたので実験的な要素はひとまずなし。気が向いたら後で。

行数はこんな感じです。

$ wc -l *.c lib/*.{c,h}
  694 vgcg.c
  746 vgparser.c
  223 vgtokenizer.c
  178 lib/json.c
   18 lib/test_json.c
  323 lib/types.c
   96 lib/utils.c
    5 lib/json.h
   86 lib/types.h
   15 lib/utils.h
 2384 合計

パーサの別実装

qiita.com