コンパイラ作成(36) バグ修正

バグ3件

比較演算子を追加しようと思ってたんだけど、その前にテストを色々やってたら、バグが3件も見つかったよ。

// 正しくない式
int main()
{
    int a, b;
    != b;
}

妙竹林な代入ね。コンパイルエラーにならずに、コンパイラが異常終了しちゃったよ。

// 代入
int main()
{
    int a, b;
    a = 10;
    b = a;
    printf("b = %d\n",b);
}

a=b;っていう単純な場合のチェックが抜けてたよ。これも異常終了。

// 関数の閉じ中括弧がない
int main()
{
    int a, b;
    a = b;

最後がこれ。関数の閉じ中括弧が無い場合。異常終了はしないけど、動作が怪しくてエラーメッセージもおかしかった。

codegen_assign

代入2件の修正。

  # 代入のコード生成
  def codegen_assign(el)
    if el.size != 3 then perror end
    if !el[2].kind_of?(Array) && el[2].kind == TK::NUMBER then
      codegen "  mov  eax, " + el[2].str
    elsif !el[2].kind_of?(Array) && el[2].kind == TK::ID then
      v = @lvars[el[2].str]
      if v == nil then
        perror "undeclared variable \"" + el[2].str + "\""
      end
      codegen "  mov  eax, dword ptr [rbp - " + v[1].to_s + "]"
    else
      codegen_el el[2]
    end
    if el[0].kind_of?(Array) then perror end
    if el[0].kind != TK::ID then perror end
    v = @lvars[el[0].str]
    if v == nil then
      perror "undeclared variable \"" + el[0].str + "\""
    end
    codegen "  mov  dword ptr [rbp - " + v[1].to_s + "], eax"
  end

修正というかコーディングが足りてなかったよ。

function

3件目の修正。

  # 関数の構文解析
  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
      if kind == TK::EOF then
        perror "expected '}'"
      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

ここもTK::EOFの場合のチェックが入ってなかったよ。異常終了しなかったのは偶然助かってただけだった。

動作テスト

3件まとめていくよ。

~/myc$ myc err13.myc
err13.myc:5:9 error: syntax error
~/myc$ myc m18.myc
~/myc$ ./m18
b = 10
~/myc$ myc err14.myc
err14.myc:5:11 error: expected '}'
~/myc$

3件とも修正できたよ。こういう細かいテストはどうしても時間喰うね。でもやらないわけにもいかないからなあ。明日は比較演算子に進みたい。比較演算子のサポートはそんなに難しくないんだけど、これもテストが大変そうだな。頑張ろう。