コンパイラ作成(38) 比較演算子
今回の目標
比較演算子を増やすよ。
// 比較演算子 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); cmp = a < 5; 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
演算子4個追加。
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, ["==","!="] el = modify_el el, ["="], :r_to_l puts to_str(el) if $opt_d # デバッグ用 codegen_el el return kind, str end
modify_elを1行追加。注意点としては今回追加の演算子は==とは優先順位が違うってこと。C の演算子の優先順位 - cppreference.com
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 " elsif op.str == "<" || op.str == "<" || op.str == ">" || op.str == "<=" || 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 == "<" 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 == "/" 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
うーん、単純に並べたけどもうちょっとスマートにしたいなあ。
動作テスト
さてどうかな。
~/myc$ myc m19.myc ~/myc$ ./m19 cmp = 1 cmp = 0 cmp = 0 ~/myc$
動いたよ。他の演算子も同じようチェックしたけど問題なかったよ。
// if文 int main() { int i = 0; lp: printf("%d * %d = %d\n",i,i,i*i); i = i + 1; if(i < 5) goto lp; return 0; }
おまけでこんなのもやってみたよ。
~/myc$ myc n3.myc ~/myc$ ./n3 0 * 0 = 0 1 * 1 = 1 2 * 2 = 4 3 * 3 = 9 4 * 4 = 16 ~/myc$
ちゃんと上手くいってる。
演算子の追加は新規の要素が無いから簡単だけど面白みはないね。ひたすら面倒なだけ。さて次回は何やろうか。whileかな。