自作コンパイラ開発メモ(2020/11/22)

低レイヤを知りたい人のための C コンパイラ作成入門を読んでコンパイラ自作してる時の作業記録です。

対象箇所

for 文に対応するアセンブリを出力できるようにしました。

実装

  • {}を解析しトークン化できるようにした。
  • ブロック文のノードを構文木内に配置できるようにした。
  • ブロック文のノードを検知してアセンブリとして出力できるようにした。

メモ

ブロックとは

正式には「compound statement」と呼ばれるらしい。

「compound」は複合という意味。直訳すると複合文となる。
複数の文をまとめて一つの意味のある処理と捉えることができる。

関数の本体部分もブロックである。
制御構文の{}と関数の{}には違いがない。

実装

EBNFにブロックを取り入れると下記のようになる。

program = stmt*
stmt    = expr ";"
        | "{" stmt* "}"
        | "if" "(" expr ")" stmt ("else" stmt)?
        | "while" "(" expr ")" stmt
        | "for" "(" expr? ";" expr? ";" expr? ")" stmt
        | "return" expr ";"
        | ...
...

ブロックは"{" stmt* "}"の部分。

{を検出したら}が検出されるまでstmtを消費する。

stmtの一回以上の繰り返し(stmt*)はprogram = stmt*ですでに実装したので同じ方法で実装した。

if (consume("{"))
{
    int i = 0;
    node = new_node(ND_BLOCK, node, NULL);
    while (!consume("}"))
    {
        node->block[i++] = stmt();
    }
    node->block[i] = NULL;
}

Node構造体のメンバにblockとしてNodeのポインタの配列を宣言した。

Node *block[100];

ハマった箇所

最初Node *block[];のようにNodeのblockの要素の数を指定していなかった。

そしたら、node->kindを参照する箇所で想定しない値が入っていてバグになっていた。
その値は実行する度に違う値になっていたので意図しないアドレスをしているはずだと思い、配列の要素数を指定してみたところ想定する動きになった。