コンパイラ作成(33) 多重代入
今回の目標
今日は多重代入だよ。
// 多重代入 int main() { int a, b, c; a = b = c = 6 * 7; printf("a = %d\n",a); printf("b = %d\n",b); printf("c = %d\n",c); }
前回の変数の初期化より難しいのかな。
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
modify_elの呼び出しを一つ追加。代入演算子は右結合なんで、それをパラメータで指定。
modify_el
ここもちょっとだけ修正。
# elの変形 # [2, +, 3, *, 5] # =>[2, +, [3, *, 5]] def modify_el(el, opl, assoc = :l_to_r) mel = [] tel = [] tel << el.shift loop do if el == [] then break end if opl.include? el[0].str then tel << el.shift tel << el.shift else if tel.size == 1 then mel << tel[0] else mel << tel end tel = [] mel << el.shift tel << el.shift end end if assoc == :r_to_l then tel = rtol_el tel end if tel.size == 1 then mel << tel[0] else mel << tel end return mel end
引数にassocを追加して、それが:r_to_lだったらrtol_elを呼ぶようにしたよ。そういやRubyのシンボルって使うの初めてだな。最近やっとRubyのコーディングに慣れてきたよ。Rubyは動的型付けでArrayになんでもぶち込めるのが便利だよね。mycではelの管理にこれを活用してる。入れ子になったArrayってようはリスト構造だよね。LISPっぽいよね。elの表示を[]から()にしたら、それこそLISPの世界か。mycを他言語に移植するにはこの辺がネックになるのかな。mycを色々拡張してセルフコンパイルなんてこともちょっと夢想してみたけど難しそうだな。そういやHaskellは静的型付けなんだよなあ。
rtol_el
ここが肝心なところ。
# 右結合時のelの変形 def rtol_el(el) if el.size <= 3 then return el end mel = [] mel << el.shift mel << el.shift mel << (rtol_el el) return mel end
再帰で処理してってる。思ってたほど難しくはなかったね。
動作テスト
今回もデバッグ情報込みで行くよ。
~/myc$ myc -d m15.myc var a var b var c [a, =, b, =, c, =, 6, *, 7] [[a, =, [b, =, [c, =, [6, *, 7]]]]] [a] [a] [b] [b] [c] [c] {"a"=>["int", 4], "b"=>["int", 8], "c"=>["int", 12]} {"main"=>["int", []]} ~/myc$ ./m15 a = 42 b = 42 c = 42 ~/myc$
動いたよ。思った通りにelが変形されてる。厳密にいうと[]が一個多いんだけど、多い分には問題ないからって放置してるよ。
ところでさあ、ググってみたら結構コンパイラ作成してる人多いんだけど、みんな再帰下降で構文解析してるみたいだね。mycみたいに変なことしてるのは俺ぐらいかな。
次は何やろうかな。このところ変数関連ばっかりだったから違うことしたいなあ。