コンパイラ作成(16) ちょっと複雑な式
今回の目標
前回の続きで式の解析だけど、ちょっとだけ複雑な式に挑戦するよ。
main() { print(32 + 47 + 12 + 6); print(32 * 47 * 12); }
複雑って言ってもちょっとだけだよ。それと前回作ったいい加減な式の処理をもうちょっとまともにしたいなあ。
expr
前回雑に作り過ぎたんで反省してちゃんとコーディング。
# 式の構文解析 def expr(fkind,fstr) kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ";" then return expr2 fkind,fstr,kind,str elsif kind == TK::SYMBOL && str == "+" then return expr2 fkind,fstr,kind,str elsif kind == TK::SYMBOL && str == "*" then return expr2 fkind,fstr,kind,str end end # 式の構文解析 def expr2(fkind,fstr,skind,sstr) if fkind == TK::NUMBER then codegen " mov eax, " + fstr loop do if skind == TK::SYMBOL && sstr == "+" then kind, str = @lex.gettoken if kind == TK::NUMBER then codegen " add eax, " + str end elsif skind == TK::SYMBOL && sstr == "*" then kind, str = @lex.gettoken if kind == TK::NUMBER then codegen " mov ebx, " + str codegen " mul ebx" end else return skind, sstr end skind, sstr = @lex.gettoken # if kind == TK::SYMBOL && str == ";" then return kind, str end # if kind == TK::SYMBOL && str == ")" then return kind, str end end end end
返値で処理しなかったトークン(多分、セミコロンか閉じ括弧)を返すようにしたよ。コンパイルエラーの対処のためにね。前回はこのトークンを握りつぶしちゃってたんで、すごく気持ちの悪いコーディングになってたよ。
statement
こっちはあんまり変わってないよ。
# 文の構文解析 def statement() kind, str = @lex.gettoken if kind == TK::SYMBOL && str == "}" then return false end if kind == TK::ID && str == "return" then # return文の処理 kind, str = @lex.gettoken kind, str = expr kind, str codegen " ret" if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after return statement" return true; elsif kind == TK::ID && str == "goto" then # goto文の処理 kind, str = @lex.gettoken if kind == TK::ID then codegen " jmp .LBB_"+ @funcname + "_" + str end kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after goto" return true; elsif kind == TK::ID && str == "print" then # 組み込み関数printの処理 kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken kind, str = expr kind, str codegen " mov edi, eax" codegen " call print" if kind != TK::SYMBOL || str != ")" then perror end kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after builtin function" return true; elsif kind == TK::ID && str == "puts" then # 標準関数putsの処理 kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken if kind == TK::STRING then label = addliteral str codegen " lea rdi, "+label codegen " call puts" end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ")" then perror end kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after function" return true; elsif kind == TK::ID then # 関数/ラベルの処理 idname = str kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ":" then # ラベルの処理 codegen ".LBB_" + @funcname + "_" + idname + ":" else # 関数呼出の処理 if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ")" then perror end codegen " call "+idname kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after function" end return true; elsif kind == TK::STRING then # 文字列リテラルの処理 lblstr = addliteral(str) kind, str = @lex.gettoken if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after builtin function" return true; end kind, str = expr kind, str if kind == TK::SYMBOL && str == ";" then return true end perror "expected ';' after expression" return true; end
exprから返ってきた値をチェックするようにして、エラーをチェックしてる。あと、return文にも式を書けるように変更した。
動作チェック
早速、動かしてみるよ。ソースは最初のやつだよ。どや?
~/myc$ ./myc.rb i6.myc ~/myc$ clang-5.0 i6.s print.o -o i6 ~/myc$ ./i6 97 18048 ~/myc$
お、できたよ。計算結果合ってるのか確かめてないけどなんとなくそれっぽいし良いんじゃないかな。(ちゃんと確かめろよ俺)
この後しばらくエラーのあるソースをコンパイルして色々やってみたんだけど、コーディングするより動作チェックする方がどうしても時間掛かるねえ。でもこれサボるとダメだしなあ。
main() { 15 + 52; print(2 * 3 + 5); return 7 * 9; }
今んとこ式が書けるのはこの三か所。これもチェック。
~/myc$ ./myc.rb i8.myc ~/myc$ clang-5.0 i8.s print.o -o i8 ~/myc$ ./i8 11 ~/myc$ echo $? 63 ~/myc$
試したらこれもちゃんと動いたよ。