コンパイラ作成(69) 間接参照演算子

今回の目標

前回の続きで間接参照演算子を追加するよ。

// 間接参照演算子

int main()
{
    int a = 55;
    int *p = &a;
    printf("a = %d *p = %d\n",a,*p);
    *p = 123;
    printf("a = %d *p = %d\n",a,*p);
}

右辺値の場合と左辺値の場合があるよ。

read_modify_el

まずここを修正。

  # 式の最後までのトークンを読み込み、変形する
  def read_modify_el(fkind,fstr,skind,sstr)
    el, kind, str = read_el fkind, fstr, skind, sstr
    puts to_str(el) if $opt_d   # デバッグ用
    el = modify_el_unaryop el, ["+","-","&","*"]
    el = modify_el el, ["*","/","%"]
    el = modify_el el, ["+","-"]
    el = modify_el el, ["<",">","<=",">="]
    el = modify_el el, ["==","!="]
    el = modify_el el, ["="], :r_to_l
    puts to_str(el) if $opt_d   # デバッグ用
    return el, kind, str
  end

modify_el_unaryopに"*"を追加。

codegen_unaryop

次はここ。

  # 単行演算子のコード生成
  def codegen_unaryop(op, operand)
    type = "int"
    if op.str == "-" then
      # 単行マイナス演算子
      type = codegen_el operand
      if type != "int" then
        perror "unsupported type with unary '-'"
      end
      codegen "  neg  eax"
    elsif op.str == "+" then
      # 単行プラス演算子
      type = codegen_el operand
    elsif op.str == "&" then
      # アドレス演算子
      if operand[0].kind_of?(Array) then
        perror "invalid operation with '&'"
      end
      if operand[0].kind != TK::ID then
        perror "invalid operation with '&'"
      end
      v = get_var operand[0].str
      if v == nil then
        perror "undeclared variable \"" + el[0].str + "\""
      end
      type = v[0] + "*"
      codegen "  lea  rax, [rbp - " + v[1].to_s + "]"
    elsif op.str == "*" then
      # 間接参照演算子
      type = codegen_el operand
      if !is_pointer_type? type then
        perror
      end
      type = type[0,type.length-1]
      if is_pointer_type? type then
        codegen "  mov  rax, [rax]"
      else
        codegen "  mov  eax, [rax]"
      end
    else
      perror "unknown operator '#{op.str}'"
    end
    return type
  end

右辺値の場合の処理。ポインタ型じゃなかったらint型だとしてる。この辺はサポートする型が増えたら見直さないと駄目だな。

codegen_assign

最後にここ。

  # 代入のコード生成
  def codegen_assign(el)
    if el.size != 3 then perror 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"
        if type_r == "void*" && is_pointer_type?(type_l) then
          type_r = type_l    # 暗黙の型変換
        end
        if type_l != type_r then p type_l,type_r;perror end
        if is_pointer_type? type_l then
          codegen "  mov  qword ptr [r10], rax"
        else
          codegen "  mov  dword ptr [r10], eax"
        end
      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]
      if type_r == "void*" && is_pointer_type?(type_l) then
        type_r = type_l    # 暗黙の型変換
      end
      if type_l != type_r then p type_l,type_r;perror end
      if is_pointer_type? type_l then
        codegen "  mov  qword ptr [rbp - " + v[1].to_s + "], rax"
      else
        codegen "  mov  dword ptr [rbp - " + v[1].to_s + "], eax"
      end
    end
    return type_l
  end

左辺値の場合の処理。

動作テスト

それでは行ってみるよ。

~/myc$ myc o22.myc
~/myc$ ./o22
a = 55 *p = 55
a = 123 *p = 123
~/myc$

お、動いた。結構難しいかと思ってたけどあっさりできちゃったよ。配列の処理って今回のの延長でできるんかな。a[i]って*(a+i)だよね。なんだかできそうな気がしてきた。あ、int*型+int型の計算ができないんだった。前に作ったchar*型の処理を一般化しないとなあ。まずはその辺からか。