コンパイラ作成(42) for文

今回の目標

今回はfor文。

// for文
int main()
{
    int i;
    puts("start");
    for(i = 0; i < 5; i = i + 1) {
        printf("%d * %d = %d\n",i,i,i*i);
    }
    puts("end");
    return 0;
}

前回のwhile文から書き直しただけ。

statement

修正はここだけ。

    elsif kind == TK::RESERVE && str == "for" then
      # while文の処理
      @labelcnt += 1
      cond_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s
      @labelcnt += 1
      cont_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s
      @labelcnt += 1
      body_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s
      @labelcnt += 1
      exit_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s
      kind, str = @lex.gettoken
      if kind != TK::SYMBOL || str != "(" then perror end
      kind, str = @lex.gettoken
      if kind != TK::SYMBOL || str != ";" then
        kind, str = expr kind, str
      end
      if kind != TK::SYMBOL || str != ";" then perror end
      codegen cond_label + ":"
      kind, str = @lex.gettoken
      if kind != TK::SYMBOL || str != ";" then
        kind, str = expr kind, str
        codegen "  jz   " + exit_label
      end
      codegen "  jmp  " + body_label
      if kind != TK::SYMBOL || str != ";" then perror end
      codegen cont_label + ":"
      kind, str = @lex.gettoken
      if kind != TK::SYMBOL || str != ")" then
        kind, str = expr kind, str
      end
      if kind != TK::SYMBOL || str != ")" then perror end
      codegen "  jmp  " + cond_label
      codegen body_label + ":"
      kind, str = @lex.gettoken
      if kind == TK::SYMBOL && str == "{" then
        kind, str = @lex.gettoken
        loop do
          kind, str = statement kind, str
          if kind == TK::SYMBOL && str == "}" then
            kind, str = @lex.gettoken
            break
          end
        end
      else
        kind, str = statement kind, str
      end
      codegen "  jmp  " + cont_label
      codegen exit_label + ":"
      return kind, str

while文とだいたい同じだけど、for文は括弧の中の式が省略可能だから、それに対応してるよ。これでfor(;;)みたいなのもコンパイルできるはず。

動作テスト

動かしてみるよ。

~/myc$ myc n8.myc
~/myc$ ./n8
start
0 * 0 = 0
1 * 1 = 1
2 * 2 = 4
3 * 3 = 9
4 * 4 = 16
end
~/myc$

大丈夫だね。続けてもういっちょやってみる。

// for文
int main()
{
    int i = 0, square;
    for(;;) {
        square = i * i;
        if(square >= 150) goto ex;
        printf("%2d * %2d = %3d\n",i,i,square);
        i = i + 1;
    }
ex:
    return 0;
}

無限ループの場合。

~/myc$ myc n9.myc
~/myc$ ./n9
 0 *  0 =   0
 1 *  1 =   1
 2 *  2 =   4
 3 *  3 =   9
 4 *  4 =  16
 5 *  5 =  25
 6 *  6 =  36
 7 *  7 =  49
 8 *  8 =  64
 9 *  9 =  81
10 * 10 = 100
11 * 11 = 121
12 * 12 = 144
~/myc$

これも問題ないね。割とサクッとできたよ。でさ、n9.myc見てるとbreakしたくなる。でもbreakってちょっと難しいのかな。今、ループの中にいるのかどうかとか状態を持たないとダメか。多重ループとか考えると面倒だな。ループの中にいないときbreakしたらどうなるのかな。コンパイルエラーかな。こんなことしたことないから分からないなあ。そういやbreakで多重ループを一気に抜けられる言語もあるよね。Javaだったかな。色々調べてみるか。