コンパイラ作成(67) for文での変数宣言
今回の目標
前回の続きでfor文の頭での変数宣言だよ。
// for文での変数宣言 extern int puts(char *str); int main() { int a = 10, b = 20; printf("1: a = %d b = %d\n",a,b); for(int i = 0; i < 3; i = i +1) { int a = 55; printf("2: a = %d b = %d\n",a,b); } int c = 30; printf("3: a = %d b = %d c = %d\n",a,b,c); a = 100; printf("4: a = %d b = %d c = %d\n",a,b,c); }
処理としては普通の変数宣言と同じなんでそう苦労することなくできると思う。その前にC言語の仕様をチェック。
JIS X 3010:2003 プログラム言語Cの6.8.5繰返し文を参照すると、変数宣言できるのはfor(a;b;c)のaの部分だけだね。それとwhile文では変数宣言はできないんだね。この辺のことは何となくは知ってたけど、細かいところまでは知らなかったよ。
var_decl
新しいメソッドを追加。
# 変数宣言の処理 def var_decl(kind, str) basetype = str loop do type = basetype kind, str = @lex.gettoken if kind == TK::SYMBOL && str == "*" then type += str kind, str = @lex.gettoken end if kind != TK::ID then perror end print "var "+str+"\n" if $opt_d @lvarsize += sizeof(type) if check_var str then perror "redefinition variable \"" + str +"\"" end set_var str, [type,@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 return kind, str end
statementの変数宣言の処理部から切り出したよ。for文でもこれを呼べば変数宣言できるようになるはず。ただちょっと問題があるのはfor文の方はstatic変数は宣言できない点。これもさっきの仕様書を読んで仕入れた知識だよ。今のところstatic変数はサポートしてないから関係ないんだけど、頭の片隅に入れとかなきゃいけない注意事項だね。
statement
変数処理部
elsif kind == TK::TYPE then # 変数宣言の処理 kind, str = var_decl kind, str if kind != TK::SYMBOL || str != ";" then perror "expected ';' after variables" end
新しいメソッドを呼ぶだけ。続いてfor文の処理部。
elsif kind == TK::RESERVE && str == "for" then # for文の処理 @labelcnt += 1 cond_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s @labelcnt += 1 cont_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s @labelcnt += 1 body_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s @labelcnt += 1 exit_label = ".LBB_" + @funcname + "_" + @labelcnt.to_s breaklabelsave = @breaklabel @breaklabel = exit_label @numblock += 1 @blocks.unshift "B#{@numblock}#" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != "(" then perror end kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ";" then if kind == TK::TYPE then kind, str = var_decl kind, str else kind, str = expr kind, str end end if kind != TK::SYMBOL || str != ";" then perror end codegen cond_label + ":" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ";" then kind, str = expr kind, str codegen " jz " + exit_label end codegen " jmp " + body_label if kind != TK::SYMBOL || str != ";" then perror end codegen cont_label + ":" kind, str = @lex.gettoken if kind != TK::SYMBOL || str != ")" then kind, str = expr kind, str end if kind != TK::SYMBOL || str != ")" then perror end codegen " jmp " + cond_label codegen body_label + ":" kind, str = @lex.gettoken if kind == TK::SYMBOL && str == "{" then kind, str = block kind, str = @lex.gettoken else kind, str = statement kind, str end codegen " jmp " + cont_label codegen exit_label + ":" @blocks.shift @breaklabel = breaklabelsave return kind, str
こっちもvar_declを呼ぶようにしてる。
動作テスト
ほいじゃテスト。
~/myc$ myc m34.myc ~/myc$ ./m34 1: a = 10 b = 20 2: a = 55 b = 20 2: a = 55 b = 20 2: a = 55 b = 20 3: a = 10 b = 20 c = 30 4: a = 100 b = 20 c = 30 ~/myc$
お、上手くいった。さて次回は何やろうかな。