コンパイラ作成(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*型の処理を一般化しないとなあ。まずはその辺からか。