Cコンパイラ作成入門のメモ #8 (Step11)

はじめに

こちらをやってみたときのメモを書いていく。

www.sigbus.info

今回はStep11

Commit

Step11

github.com

調べたこと・理解したこと

トークナイズ結果出力用関数の実装

イメージ図/コード

// トークナイズした結果を出力する
void token_preview(Token *start)
{
  Token *cur = start;
  fprintf(stderr, ".... start preview tokens ....\n");
  for (Token start; cur; cur = cur->next)
  {
    char c[100];
    memset(c, '\0', sizeof(c));
    memcpy(c,cur->str,cur->len);
    fprintf(stderr, " kind=> %d, str=> %s,\n", cur->kind, c);
  }
  fprintf(stderr, ".... finish!! ....\n");
}

メモ

  • デバッグ用にトークナイズ結果を出力する関数を作ってみた。
  • foo = 1; return foo;出力結果は下記の感じ
.... start preview tokens ....
 kind=> 1, str=> foo,
 kind=> 0, str=> =,
 kind=> 2, str=> 1,
 kind=> 0, str=> ;,
 kind=> 3, str=> return,
 kind=> 1, str=> foo,
 kind=> 0, str=> ;,
 kind=> 4, str=> ,
.... finish!! ....
  • 連結リストを辿っているlvar_findとかを参考に実装した。

参考

文字列用配列の初期化について

文字列を空にする | Programming Place Plus C言語編 逆引き

文字列の切り出しについて

C 言語でサブストリングを取得する | Delft スタック

思ったこと

パーサの実装について

returnの右側をlhsとしてノードに格納しているところ

Node *stmt() {
  Node *node;

  if (consume(TK_RETURN)) {
    node = calloc(1, sizeof(Node));
    node->kind = ND_RETURN;
    node->lhs = expr();
  } else {
    node = expr();
  }

  if (!consume(';'))
    error_at(tokens[pos].str, "';'ではないトークンです");
  return node;
}

上記のパース部分returnの右側をlhsとしてノードに格納している。 直感的には右辺なんだけど、左辺としてデータ登録している部分イマイチしっくりこない。

パーサの文法と、コードの実装の対応

パーサの文法と、コードの実装の対応についてイマイチ腹落ちできていない。文法とコードを横に並べて、どことどこが対応しているか一回整理できれば、おおよそ理解できそうなので、今度一回まとめてみた。

書いて見ると、まぁ大体、対応は理解できた。 新しい構文に対応するときに、文法を自分で考えるのは難しそう。。。

まとめ図

https://raw.githubusercontent.com/lvlnaga/9cc/master/docs/step11/%E7%94%9F%E6%88%90%E8%A6%8F%E5%89%87%E3%81%A8%E5%AE%9F%E8%A3%85%E3%81%AE%E9%96%A2%E4%BF%82.drawio.svg

  • 画像にするとズレちゃうのでご容赦ください。

テキストに書いてあった複数ret出力されるが気にしないのところの解釈

イマイチしっくりこなかったので、下図にまとめた。
要は、使われることがないretも出力される実装になっている。 でも実装をシンプルにするためであり、実害が無いならそれでもいいよね。 ということと理解した。

まとめ図

https://raw.githubusercontent.com/lvlnaga/9cc/master/docs/step11/codegen%E3%81%AEret%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E6%95%B4%E7%90%86.drawio.svg

エピローグ部分についておさらい

エピローグでやっていることが、やっぱりよくわからなくなったので、図にしてみた。

retでやっていることがいまいち理解できていない。 プログラムカウンタが存在しているはずだが、そこをテキストでは説明されていないので、そこがいまいちしっくり来ていないことが原因なのかも。 retが実行されるとrspは一個上にずれるのは、勝手にされる仕様なのだろうか?

まとめ図

https://raw.githubusercontent.com/lvlnaga/9cc/master/docs/step11/ret%E3%81%A8%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%81%AE%E9%96%A2%E4%BF%82.drawio.svg

思ったこと

やれば、やるほど、前回のここよくわかってないなぁというのが次々に出てくる。 気づけるだけよいということ?これもブログにoutputしている効能?

同じように9ccテキスト参考に実装してみた系のgithubを見ていると、自分よりもすごい短い期間に実装、コミットされていて自分との距離を感じる。
そんなに長い時間実装集中できないんだよなぁ。
慣れるとできるのかしら?