コンパイラ作成(61) 引数のある関数の修正
バグ
テストをしてたらバグを見つけたよ。
main() { print(2 * 3 + 5); print(2 + 3 * 5); }
これなんだけど、計算結果が合わない。
~/myc$ myc -d i7.myc print.o [[print, (), [2, *, 3, +, 5]]] [[print, (), [2, *, 3, +, 5]]] [[print, (), [2, +, 3, *, 5]]] [[print, (), [2, +, 3, *, 5]]] {} {"main"=>["int", []]} ~/myc$ ./i7 11 25 ~/myc$
二番目の式が電卓式計算になっちゃってる。以前はちゃんと計算合ってたのに退化してるじゃん。うーん、なんでだろ。gitから古いバージョンを順番に取り出して調べてみたら、printとputsの処理を削除したときに紛れ込んだバグだった。もう全部exprで処理できると思ったんだけど論理が足りなかったみたい。
あれ式が変形されてないじゃん。あそうか、関数の引数処理するときread_elした後modify_elを呼んでないからか。なるほどなるほど。
expr
原因が分かったのでさっそく修正。
# 式の構文解析 def expr2(fkind,fstr,skind,sstr) el, kind, str = read_modify_el fkind, fstr, skind, sstr codegen_el el return kind, str end # 式の最後までのトークンを読み込み、変形する 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
メソッドを二つに分けたよ。
reaf_el
関数呼出の処理部を修正。
# 式の最後までのトークンを読み込む # 返り値 # el : expression's token list # kind : 処理しなかったトークン(セミコロンもしくは閉じ括弧) # str : 処理しなかったトークン(セミコロンもしくは閉じ括弧) def read_el(fkind,fstr,skind,sstr) el = [] if fkind == TK::ID && sstr == "(" then # 関数呼出の処理 sel = [] sel << Token.new(fkind,fstr) sel << Token.new(skind,"()") skind, sstr = @lex.gettoken loop do if skind == TK::SYMBOL && sstr == ")" then break end fkind, fstr = skind, sstr skind, sstr = @lex.gettoken pel, skind, sstr = read_modify_el fkind, fstr, skind, sstr sel << pel if skind == TK::SYMBOL && sstr == "," then skind, sstr = @lex.gettoken elsif skind != TK::SYMBOL || sstr != ")" then perror end end el << sel if skind != TK::SYMBOL || sstr != ")" then perror end skind, sstr = @lex.gettoken
read_elを呼んでたのをread_modify_elを呼ぶようにしたよ。これでちゃんと演算子の優先順位に基づいた計算がされるはず。
動作テスト
~/myc$ myc -d i7.myc print.o [2, *, 3, +, 5] [[[2, *, 3], +, 5]] [[print, (), [[[2, *, 3], +, 5]]]] [[print, (), [[[2, *, 3], +, 5]]]] [2, +, 3, *, 5] [[2, +, [3, *, 5]]] [[print, (), [[2, +, [3, *, 5]]]]] [[print, (), [[2, +, [3, *, 5]]]]] {} {"main"=>["int", []]} ~/myc$ ./i7 11 17 ~/myc$
2+3*5の計算がちゃんと17になってる。無事修正できたよ。次は何やろうかな。printfのextern宣言かな。ああ、その前に引数の型チェックか。こっち先にやらないと駄目だな。