コンパイラ作成(35) if-else文
今日の目標
else付きのif文行ってみるよ。
// if文 int main() { int a; a = 0; if(a == 0) printf("%d is zero\n",a); else printf("%d is not zero\n",a); a = 1; if(a == 0) { printf("%d is zero\n",a); printf("%d is zero\n",a); } else { printf("%d is not zero\n",a); printf("%d is not zero\n",a); } }
if文二つで、二つ目はブロックのタイプだよ。
statement、function
今回、statementメソッドのインターフェースを変更したよ。
# 文の構文解析 def statement(kind, str) if kind == TK::RESERVE && str == "return" then # return文の処理 kind, str = @lex.gettoken kind, str = expr kind, str codegen " jmp .RET_" + @funcname if kind != TK::SYMBOL || str != ";" then perror "expected ';' after return statement" end elsif kind == TK::RESERVE && str == "goto" then # goto文の処理 kind, str = @lex.gettoken if kind == TK::ID then codegen " jmp .LBB_"+ @funcname + "_" + str else perror "expected identifier" end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ";" then perror "expected ';' after goto" end elsif kind == TK::RESERVE && str == "if" then # if文の処理 kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken kind, str = expr kind, str if kind != TK::SYMBOL || str != ")" then perror end @labelcnt += 1 e_label = @labelcnt codegen " jz .LBB_" + @funcname + "_" + @labelcnt.to_s kind, str = @lex.gettoken if kind == TK::SYMBOL && str == "{" then kind, str = @lex.gettoken loop do kind, str = statement kind, str if kind == TK::SYMBOL && str == "}" then kind, str = @lex.gettoken break end end else kind, str = statement kind, str end if kind == TK::RESERVE && str == "else" then @labelcnt += 1 codegen " jmp .LBB_" + @funcname + "_" + @labelcnt.to_s codegen ".LBB_" + @funcname + "_" + e_label.to_s + ":" kind, str = @lex.gettoken if kind == TK::SYMBOL && str == "{" then kind, str = @lex.gettoken loop do kind, str = statement kind, str if kind == TK::SYMBOL && str == "}" then kind, str = @lex.gettoken break end end else kind, str = statement kind, str end codegen ".LBB_" + @funcname + "_" + @labelcnt.to_s + ":" else codegen ".LBB_" + @funcname + "_" + e_label.to_s + ":" end return kind, str elsif kind == TK::RESERVE && str == "int" then # 変数宣言の処理 loop do kind, str = @lex.gettoken if kind != TK::ID then perror end print "var "+str+"\n" if $opt_d @lvarsize += 4 if @lvars[str] then perror "redefinition variable \"" + str +"\"" end @lvars[str] = ["int",@lvarsize] skind, sstr = @lex.gettoken if skind == TK::SYMBOL && sstr == "=" then kind, str = expr2 kind, str, skind, sstr; else kind, str = skind, sstr; end if kind != TK::SYMBOL || str != "," then break end end if kind != TK::SYMBOL || str != ";" then perror "expected ';' after variables" end elsif kind == TK::RESERVE && str == "print" then # 組み込み関数printの処理 @needprint = true 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 perror "expected ';' after builtin function" end elsif kind == TK::RESERVE && 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 perror "expected ';' after function" end elsif kind == TK::RESERVE && str == "printf" then # 標準関数printfの処理 regs32 = ["edi", "esi","edx","ecx","r8d","r9d"] regs64 = ["rdi", "rsi","rdx","rcx","r8", "r9" ] kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken i = 0 loop do if kind == TK::STRING then label = addliteral str codegen " lea "+regs64[i]+", "+label kind, str = @lex.gettoken else kind, str = expr kind, str codegen " mov "+regs32[i]+", eax" end if kind == TK::SYMBOL && str == ")" then break end if kind != TK::SYMBOL || str != "," then perror end kind, str = @lex.gettoken i += 1 end codegen " mov al, 0" codegen " call printf" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ";" then perror "expected ';' after function" end elsif kind == TK::ID then # 式/ラベルの処理 idname = str skind, sstr = @lex.gettoken if skind == TK::SYMBOL && sstr == ":" then # ラベルの処理 codegen ".LBB_" + @funcname + "_" + idname + ":" else # 式の処理 kind, str =expr2 kind, str, skind, sstr if kind != TK::SYMBOL || str != ";" then perror "expected ';' after expression" end end elsif kind == TK::STRING then # 文字列リテラルの処理 lblstr = addliteral(str) kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ";" then perror "expected ';' after builtin function" end elsif kind == TK::RESERVE then perror "unsupported reserved word" else kind, str = expr kind, str if kind != TK::SYMBOL || str != ";" then perror "expected ';' after expression" end end return @lex.gettoken; end # 関数の構文解析 def function() @labelcnt = 0 @lvars = Hash.new @lvarsize = 0; rettype = nil kind, str = @lex.gettoken if kind == TK::RESERVE && str == "int" then rettype = str kind, str = @lex.gettoken end if kind == TK::EOF then return false end if kind != TK::ID then perror "expected identifier" end @funcname = str kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ")" then perror end codegen ".global "+@funcname codegen @funcname+":" codegen " push rbp" codegen " mov rbp, rsp" codegen " sub rsp, 64" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "{" then perror end kind, str = @lex.gettoken loop do kind, str = statement kind, str if kind == TK::SYMBOL && str == "}" then break end end codegen ".RET_" + @funcname + ":" codegen " add rsp, 64" codegen " pop rbp" codegen " ret" if @functions[@funcname] != nil then perror "redefinition of \"" + @funcname + "\"" end @functions[@funcname] = [rettype,[]] p @lvars if $opt_d # デバッグ用 @funcname = nil return true end
exprと同じように処理しなかったトークンを返すようにした。前回の記事でちょこっと書いた件ね。elseの処理自体はifと同じような感じだよ。
動作テスト
今回はあちこち修正したんでまず今までのテストが全部通るかをチェックしたよ。OKだったんでelse付きif文いってみるよ。
~/myc$ myc n2.myc ~/myc$ ./n2 0 is zero 1 is not zero 1 is not zero ~/myc$
うまくいったよ。次はwhileやforかな。あ、その前に比較演算子増やしたいなあ。