コンパイラ作成(65) 剰余演算子
今回の目標
久々に演算子を追加するよ。
// 剰余 int main() { printf("17 * 4 = %d\n",17*4); printf("17 / 4 = %d\n",17/4); printf("17 % 4 = %d\n",17%4); }
剰余演算子だよ。
gettoken
修正いくよ。
# symbolの切り出し elsif m = @line[@idx,@line.length] .match(/^(==|!=|<=|>=|=|<|>|\+|\-|\*|\/|\%|:|,|;|\(|\)|\{|\})/) then str = m.to_s @idx += str.length return TK::SYMBOL, str end
演算子を追加。
read_modify_el
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
優先順位は乗算・除算といっしょだよね。C の演算子の優先順位 - cppreference.comをもう一回見て確かめたよ。
codegen_els
コード生成は除算とほとんど一緒。
# 式のコード生成(二項演算の右側被演算子) def codegen_els(op, operand, type_l) if op.str == "+" then ostr = "add " elsif op.str == "-" then ostr = "sub " elsif op.str == "*" then ostr = "imul" elsif op.str == "/" then ostr = "idiv" elsif op.str == "%" then ostr = "idiv" elsif op.str == "==" then ostr = "cmp " elsif op.str == "!=" then ostr = "cmp " elsif op.str == "<" || op.str == "<" || op.str == ">" || op.str == "<=" || op.str == ">=" then ostr = "cmp " else perror "unknown operator \"" + op.str + "\"" end # 右被演算子を評価 type_r = "int" if operand.kind_of?(Array) then if operand[0].size == 2 && operand[0].kind == TK::ID && operand[1].str == "()" then codegen " sub rsp, 8" codegen " push rax" type_r = codegen_func operand codegen " mov r10d, eax" codegen " pop rax" codegen " add rsp, 8" else codegen " sub rsp, 8" codegen " push rax" type_r = codegen_el operand codegen " mov r10d, eax" codegen " pop rax" codegen " add rsp, 8" end str = "r10d" elsif operand.kind == TK::ID then v = @lvars[operand.str] if v == nil then perror "undeclared variable \"" + operand.str + "\"" end type_r = v[0] if type_r == "char*" str = "qword ptr [rbp - " + v[1].to_s + "]" else str = "dword ptr [rbp - " + v[1].to_s + "]" end elsif operand.kind == TK::NUMBER then str = operand.str elsif operand.kind == TK::STRING then type_r = "char*" label = addliteral operand.str codegen " lea r10, "+label str = "r10" else perror end # 型チェック if type_l != type_r then if type_l == "char*" && type_r == "int" then if op.str != "+" && op.str != "-" then perror "mismatched types to binary operation" end elsif type_l == "int" && type_r == "char*" then if op.str != "+" && op.str != "-" then perror "mismatched types to binary operation" end else perror "mismatched types to binary operation" end elsif type_l == "char*" then perror "mismatched types to binary operation" end # 左被演算子と右被演算子とで計算 if op.str == "==" then codegen " " + ostr + " eax, " + str codegen " sete al" codegen " and eax, 1" elsif op.str == "!=" then codegen " " + ostr + " eax, " + str codegen " setne al" codegen " and eax, 1" elsif op.str == "<" then codegen " " + ostr + " eax, " + str codegen " setl al" codegen " and eax, 1" elsif op.str == ">" then codegen " " + ostr + " eax, " + str codegen " setg al" codegen " and eax, 1" elsif op.str == "<=" then codegen " " + ostr + " eax, " + str codegen " setle al" codegen " and eax, 1" elsif op.str == ">=" then codegen " " + ostr + " eax, " + str codegen " setge al" codegen " and eax, 1" elsif op.str == "*" || op.str == "/" || op.str == "%" then if str != "r10d" then codegen " mov r10d, " + str end codegen " mov r11, rdx" if op.str == "/" || op.str == "%" then codegen " cdq" end codegen " " + ostr + " r10d" if op.str == "%" then codegen " mov eax, edx" end codegen " mov rdx, r11" else if type_l == "char*" && type_r == "int" then if str == op.str then codegen " " + ostr + " rax, " + str elsif str == "r10d" then codegen " movsx r10, r10d" codegen " " + ostr + " rax, r10" else codegen " mov r10d, " + str codegen " movsx r10, r10d" codegen " " + ostr + " rax, r10" end elsif type_l == "int" && type_r == "char*" then codegen " movsx rax, eax" codegen " " + ostr + " rax, " + str type_l = "char*" else codegen " " + ostr + " eax, " + str end end return type_l end
このメソッド大分ごちゃごちゃしてきたなあ。サポートする型を増やすともっとひどくなりそうだよ。うーむ、どうしたもんかな。
動作テスト
そいではテスト。
~/myc$ myc m28.myc ~/myc$ ./m28 17 * 4 = 68 17 / 4 = 4 17 % 4 = 1 ~/myc$
大丈夫だね。違うのもやってみるよ。
// FizzBuzz int main() { int i; for(i = 1; i <= 30; i = i + 1) { if( i % 15 == 0 ) printf("FizzBuzz "); else if( i % 3 == 0 ) printf("Fizz "); else if( i % 5 == 0 ) printf("Buzz "); else printf("%d ",i); } printf("\n"); }
~/myc$ myc m29.myc ~/myc$ ./m29 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz ~/myc$
おお、できたできた。でもこれ%演算子のテストにはあんまりなってないのかな。else ifとかのテストになってるからまあ良いか。
mycも大分C言語っぽくなってきたよ。まだやらなきゃいけないこと多くて全然ゴールは見えないけど、ちょっとずつ頑張ってるよ。