コンパイラ作成(101) 配列への代入

今回の目標

今回は代入だよ。

// 配列
int main()
{
    int a[10];
    a[0] = 42;
    printf("a[0] = %d\n", a[0]);
}

まずは単純な場合から。

codegen_assign

今回、大きく書き換えたよ。

  # 代入のコード生成
  def codegen_assign(el)
    if el.size != 3 then perror end
    if el[2] == nil then
      perror "broken expression"
    end
    type_r = codegen_el [el[2]]
    if el[0].kind_of?(Array) then
      if el[0][0].str == "*" then
        codegen "  sub  rsp, 8"
        codegen "  push rax"
        type_l = codegen_el [el[0][1]]
        if !is_pointer_type? type_l then
          perror
        end
        type_l = type_l[0,type_l.length-1]
        codegen "  mov  r10, rax"
        codegen "  pop  rax"
        codegen "  add  rsp, 8"
        str = "r10"
      elsif el[0][1].str == "[]" then
        codegen "  sub  rsp, 8"
        codegen "  push rax"
        type_ll = codegen_el [el[0][0]]
        codegen "  sub  rsp, 8"
        codegen "  push rax"
        type_lr = codegen_el [el[0][2]]
        codegen "  mov  r10, rax"
        codegen "  pop  rax"
        codegen "  add  rsp, 8"
        if is_array_type?(type_ll) && type_lr == "int" then
          # 配列+int型の処理
          type_ll = array_to_pointer type_ll
          codegen_pointer_int type_ll, el[0][1], "add ", "r10d"
          type_l = type_ll[0,type_ll.length-1]
        elsif type_l == "int" && is_array_type?(type_r) then
          # int型+配列の処理
          type_lr = array_to_pointer type_lr
          codegen_int_pointer type_lr, el[0][1], "add ", "r10d"
          type_l = type_lr[0,type_lr.length-1]
        else
          perror
        end
        codegen "  mov  r10, rax"
        codegen "  pop  rax"
        codegen "  add  rsp, 8"
        str = "r10"
      else
        perror
      end
    else
      if el[0].kind != TK::ID then perror end
      v = get_var el[0].str
      if v == nil then
        perror "undeclared variable \"" + el[0].str + "\""
      end
      type_l = v[0]
      str = "rbp -  #{v[1]}"
    end
    # 代入
    if type_r == "void*" && is_pointer_type?(type_l) then
      type_r = type_l    # 暗黙の型変換
    elsif type_r == "int" && type_l == "char" then
      type_r = type_l    # 暗黙の型変換
    elsif type_r == "int" && type_l == "double" then
      codegen "  cvtsi2sd xmm8, eax"
      type_r = type_l    # 暗黙の型変換
    elsif is_array_type? type_r then
      type_r = array_to_pointer type_r
    end
    if type_l != type_r then perror end
    if is_pointer_type? type_l then
      codegen "  mov  qword ptr [#{str}], rax"
    elsif type_l == "double"
      codegen "  movsd  qword ptr [#{str}], xmm8"
    elsif type_l == "char"
      codegen "  mov  byte ptr [#{str}], al"
    else
      codegen "  mov  dword ptr [#{str}], eax"
    end
    return type_l
  end

同じような処理を二箇所に書いてたんだけど、それだと色々修正していく上で問題になるんで、一個に纏めたよ。本題の配列への対応だけど、左辺値を求める処理を組み込んだよ。ただ、この方法で良いかは正直良く分かんないな。多次元配列とかに対応できるか不安だよ。もっと真面目に左辺値を求めるようにしないと駄目かも。この辺要検討だな。

動作テスト
~/myc$ myc q6.myc
~/myc$ ./q6
a[0] = 42
~/myc$

大丈夫かな。そういやテストプログラムの配列の添え字、いつも0だなあ。違うのもやってみるか。

// エラトステネスの篩
int main()
{
    int a[100];
    for(int i = 0; i < 100; i = i + 1)
        a[i] = 1;
    for(int i = 2; i < 100; i = i + 1) {
        for(int j = i * 2; j < 100; j = j + i) {
            a[j] = 0;
        }
    }
    for(int i = 2; i < 100; i = i + 1)
        if( a[i] == 1 ) printf("%d ",i);
    printf("\n");
}

ちょろちょろっと書いたプログラムなんで効率悪いけど、配列のテストにはなるよね。

~/myc$ myc q7.myc
~/myc$ ./q7
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 
~/myc$

おお、ちゃんと素数が表示されてる。代入は参照より楽だったよ。もうちょっと苦労するかと思ったんだけどね。これで配列のサポートは大方できたかな。次回以降残ったとことエラー処理周りをやってくよ。