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