Cコンパイラ作成入門のメモ #8 (Step11)
はじめに
こちらをやってみたときのメモを書いていく。
今回はStep11
Commit
Step11
調べたこと・理解したこと
トークナイズ結果出力用関数の実装
イメージ図/コード
// トークナイズした結果を出力する 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"); }
メモ
.... 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としてノードに格納している。 直感的には右辺なんだけど、左辺としてデータ登録している部分イマイチしっくりこない。
パーサの文法と、コードの実装の対応
パーサの文法と、コードの実装の対応についてイマイチ腹落ちできていない。文法とコードを横に並べて、どことどこが対応しているか一回整理できれば、おおよそ理解できそうなので、今度一回まとめてみた。
書いて見ると、まぁ大体、対応は理解できた。 新しい構文に対応するときに、文法を自分で考えるのは難しそう。。。
まとめ図
- 画像にするとズレちゃうのでご容赦ください。
テキストに書いてあった複数ret出力されるが気にしないのところの解釈
イマイチしっくりこなかったので、下図にまとめた。
要は、使われることがないretも出力される実装になっている。
でも実装をシンプルにするためであり、実害が無いならそれでもいいよね。 ということと理解した。
まとめ図
エピローグ部分についておさらい
エピローグでやっていることが、やっぱりよくわからなくなったので、図にしてみた。
retでやっていることがいまいち理解できていない。 プログラムカウンタが存在しているはずだが、そこをテキストでは説明されていないので、そこがいまいちしっくり来ていないことが原因なのかも。 retが実行されるとrspは一個上にずれるのは、勝手にされる仕様なのだろうか?
まとめ図
思ったこと
やれば、やるほど、前回のここよくわかってないなぁというのが次々に出てくる。 気づけるだけよいということ?これもブログにoutputしている効能?
同じように9ccテキスト参考に実装してみた系のgithubを見ていると、自分よりもすごい短い期間に実装、コミットされていて自分との距離を感じる。
そんなに長い時間実装集中できないんだよなぁ。
慣れるとできるのかしら?