コンパイラ作成(37) 非等価演算子
今回の目標
比較演算子を増やすよ。
// 非等価演算子 int main() { int a, b; int cmp; a = 6; b = 7; cmp = a != b; printf("cmp = %d\n",cmp); cmp = a != 6; printf("cmp = %d\n",cmp); }
非等価演算子。
Lexerクラス
まずはここから。
# symbolの切り出し elsif m = @line[@idx,@line.length].match(/^(==|!=|=|\+|\-|\*|\/|:|,|;|\(|\)|\{|\})/) then str = m.to_s @idx += str.length return TK::SYMBOL, str end
!=演算子を追加。
expr
次はここ。
# 式の構文解析 def expr2(fkind,fstr,skind,sstr) el, kind, str = read_el fkind, fstr, skind, sstr puts to_str(el) if $opt_d # デバッグ用 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 # デバッグ用 codegen_el el return kind, str end
こっちにも!=演算子を追加した。
codegen_els
最後にコード生成部を修正。
# 式のコード生成(二項演算の右側被演算子) def codegen_els(op, operand) if op.str == "+" then ostr = "add " elsif op.str == "-" then ostr = "sub " elsif op.str == "*" then ostr = "mul " elsif op.str == "/" then ostr = "div " elsif op.str == "==" then ostr = "cmp " elsif op.str == "!=" then ostr = "cmp " else perror "unknown operator \"" + op.str + "\"" end if operand.kind_of?(Array) then if operand[0].size == 2 && operand[0].kind == TK::ID && operand[1].str == "()" then codegen " push rax" codegen " call " + operand[0].str codegen " mov ebx, eax" codegen " pop rax" else codegen " push rax" codegen_el operand codegen " mov ebx, eax" codegen " pop rax" end str = "ebx" elsif operand.kind == TK::ID then v = @lvars[operand.str] if v == nil then perror "undeclared variable \"" + operand.str + "\"" end str = "dword ptr [rbp - " + v[1].to_s + "]" elsif operand.kind == TK::NUMBER then str = operand.str 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 == "*" || op.str == "/" then if str != "ebx" then codegen " mov ebx, " + str end codegen " mov r11, rdx" if op.str == "/" then codegen " xor edx, edx" end codegen " " + ostr + " ebx" codegen " mov rdx, r11" else codegen " " + ostr + " eax, " + str end end
今回、このメソッドを書き換えたよ。今まで同じ様なコードを3ヵ所書いてて、無駄だなあと思ってたんだよね。ちょっと考えたら共通化できたよ。
動作テスト
さてどうかな。
~/myc$ myc -d m17.myc var a var b var cmp [a, =, 6] [[a, =, 6]] [b, =, 7] [[b, =, 7]] [cmp, =, a, !=, b] [[cmp, =, [a, !=, b]]] [cmp] [cmp] [cmp, =, a, !=, 6] [[cmp, =, [a, !=, 6]]] [cmp] [cmp] {"a"=>["int", 4], "b"=>["int", 8], "cmp"=>["int", 12]} {"main"=>["int", []]} ~/myc$ ./m17 cmp = 1 cmp = 0 ~/myc$
上手く動いたよ。今回は本当は関係演算子をがばっと増やしたかったんだけど、一個しか増やせなかったよ。色々気になることとか調べてたりするうちにどんどん時間経っちゃうよ。